├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── elm-package.json ├── examples ├── .gitignore ├── Counter │ ├── .gitignore │ ├── Main.elm │ ├── README.md │ ├── app.json │ ├── elm-package.json │ ├── index.android.js │ ├── index.ios.js │ └── package.json └── NavigationDemo │ ├── .gitignore │ ├── app.json │ ├── elm-package.json │ ├── index.android.js │ ├── index.ios.js │ ├── package.json │ └── src │ ├── CardStack │ ├── Example.elm │ ├── NavigationMsg.elm │ ├── Navigator.elm │ └── Scene.elm │ ├── ExampleRow.elm │ ├── Main.elm │ └── Tab │ ├── Example.elm │ ├── Header.elm │ ├── NavigationMsg.elm │ ├── Navigator.elm │ ├── Scene.elm │ └── Tab.elm ├── img ├── chilicorn.png ├── elm-native-160.png ├── elm-native.svg ├── screenshot-android.png └── screenshot-ios.png └── src ├── Native ├── NativeUi.js └── NativeUi │ ├── Alert.js │ ├── AsyncStorage.js │ ├── Dimensions.js │ ├── Elements.js │ ├── ListView.js │ ├── Platform.js │ ├── PushNotificationIOS.js │ └── StyleSheet.js ├── NativeApi ├── Animated.elm ├── Dimensions.elm ├── NavigationStateUtil.elm ├── Platform.elm ├── PushNotificationIOS.elm └── StyleSheet.elm ├── NativeUi.elm └── NativeUi ├── Alert.elm ├── AsyncStorage.elm ├── Elements.elm ├── Events.elm ├── Image.elm ├── ListView.elm ├── NavigationExperimental.elm ├── Properties.elm └── Style.elm /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IJ 26 | # 27 | .idea 28 | .gradle 29 | local.properties 30 | 31 | # node.js 32 | # 33 | node_modules/ 34 | npm-debug.log 35 | 36 | # elm 37 | elm-stuff/ 38 | 39 | # generator 40 | rn-modules.json 41 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | 3 | cache: 4 | directories: 5 | - elm-stuff/build-artifacts 6 | - sysconfcpus 7 | 8 | env: 9 | matrix: 10 | - ELM_VERSION=0.18.0 TARGET_NODE_VERSION=node 11 | 12 | before_install: 13 | - | # epic build time improvement - see https://github.com/elm-lang/elm-compiler/issues/1473#issuecomment-245704142 14 | if [ ! -d sysconfcpus/bin ]; 15 | then 16 | git clone https://github.com/obmarg/libsysconfcpus.git; 17 | cd libsysconfcpus; 18 | ./configure --prefix=$TRAVIS_BUILD_DIR/sysconfcpus; 19 | make && make install; 20 | cd ..; 21 | fi 22 | install: 23 | - nvm install $TARGET_NODE_VERSION 24 | - nvm use $TARGET_NODE_VERSION 25 | - node --version 26 | - npm --version 27 | - npm install -g elm@$ELM_VERSION 28 | - mv $(npm config get prefix)/bin/elm-make $(npm config get prefix)/bin/elm-make-old 29 | - printf "#\041/bin/bash\n\necho \"Running elm-make with sysconfcpus -n 2\"\n\n$TRAVIS_BUILD_DIR/sysconfcpus/bin/sysconfcpus -n 2 elm-make-old \"\$@\"" > $(npm config get prefix)/bin/elm-make 30 | - chmod +x $(npm config get prefix)/bin/elm-make 31 | 32 | script: 33 | - printf "#\041/bin/bash\n\necho \"Running elm-make with sysconfcpus -n 2\"\n\n$TRAVIS_BUILD_DIR/sysconfcpus/bin/sysconfcpus -n 2 elm-make-old \"\$@\"" > $(npm config get prefix)/bin/elm-make 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The BSD 3-Clause License 2 | 3 | Copyright (c) 2015, Ossi Hanhinen and André Staltz 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 11 | 12 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Elm Native UI [![Build Status](https://travis-ci.org/ohanhi/elm-native-ui.svg?branch=master)](https://travis-ci.org/ohanhi/elm-native-ui) 2 | 3 | **THIS PROJECT IS NOT MAINTAINED.** If you want to become the maintainer, see [issue #90](https://github.com/ohanhi/elm-native-ui/issues/90). The code is from 2017 and the facilities used here are not applicable to current versions of Elm. 4 | 5 | 6 | ![](img/elm-native-160.png) 7 | 8 | Experimental support for writing native iOS and Android applications in the beautiful functional [Elm language](http://elm-lang.org/). 9 | This project builds on Facebook's [React Native](https://facebook.github.io/react-native/), using it as the JavaScript environment for Elm. 10 | 11 | ## Frequently Asked Questions 12 | 13 | ### Is This Production Ready? 14 | 15 | No. This is really just an experiment, and it is not under active development. [People have used it](https://robots.thoughtbot.com/elm-native-ui-in-production) for applications despite this. 16 | 17 | ### Why React Native? 18 | 19 | Mobile applications are expected to look and feel at home on the device and the OS it is running. This goal cannot really be achieved with a WebView-based solution (such as Cordova). React Native applications use the same pieces of UI as an app built with Objective-C or Java, choosing the platform-specific native variant of the UI feature for you. 20 | 21 | ### Will it always be React Native? 22 | 23 | In the long term, not necessarily. The ultimate goal would be for Elm Native UI to be a standalone project, complete with the mobile platform groundwork that has been put into React Native already. It has not been fully assessed how much work this would in reality entail. 24 | 25 | Addendum 2017-10-17: [elm-ios](https://github.com/pzp1997/elm-ios) was a Google Summer of Code project that explored this idea for iOS. 26 | 27 | 28 | ## Get it running 29 | 30 | ### Caution: Experimental software! 31 | 32 | Elm Native UI may get breaking changes at any time, and it might only work with an older version of React Native. 33 | 34 | ### Actually getting it running 35 | 36 | Install React Native following [their guide](https://facebook.github.io/react-native/docs/getting-started.html#content). Check that you can create a new project: 37 | 38 | ```bash 39 | $ react-native init MyAppName --version 0.44.3 40 | ``` 41 | 42 | and try running it on a real or virtual device. 43 | 44 | Once that works, clone this repository in the same directory where you ran `react-native init` in. You should have a directory structure similar to this: 45 | 46 | ``` 47 | │ 48 | ├── elm-native-ui 49 | └── MyAppName 50 | ``` 51 | 52 | Go ahead and copy the files from the [`examples/Counter`](examples/Counter) folder into your React Native app directory now. 53 | 54 | Now try to compile the Elm code with 55 | 56 | ```bash 57 | $ npm run compile 58 | ``` 59 | 60 | This will create the `elm-package.json` and `elm-stuff` for you, even though it won't compile yet. 61 | 62 | Edit the `index.*.js` files' last lines in case your React Native app is not called 'MyAppName' at this point. You may also need to rename the project in the `package.json` file. 63 | 64 | You can then use e.g. [elm_self_publish](https://github.com/NoRedInk/elm-ops-tooling#elm_self_publish) to publish the Elm Native UI package into our project. 65 | 66 | Assuming we're in the parent directory where `elm-native-ui` reside: 67 | 68 | ```bash 69 | $ python path/to/elm_self_publish.py ./elm-native-ui ./MyAppName 70 | ``` 71 | 72 | Now we are ready to rock! 🤘🎸 73 | 74 | Just to list out the basics: 75 | 76 | ```bash 77 | # install dependencies 78 | $ npm install 79 | # compile Elm with 80 | $ npm run compile 81 | # run app on iOS 82 | $ react-native run-ios 83 | # or run on Android 84 | $ react-native run-android 85 | ``` 86 | 87 | When you make changes to the code, you only need to recompile Elm and press Cmd-R on the Simulator (iOS) or refresh the emulator (Android). 88 | 89 | If you wish, you can also start a file watcher for \*.elm files, which will recompile whenever you make a change: 90 | 91 | ```bash 92 | $ npm start 93 | ``` 94 | 95 | ### React native versions 96 | 97 | If you use the package.json files from the example projects, you'll get the react native version that has been tested and known to be working with the examples. 98 | 99 | That is currently react-native 0.44.3 100 | 101 | Later versions of react native may work, however, specifically in the subsequent react native 0.45.x release the "Navigation Experimental" module was depricated and moved to an external library, so the Navigation Example will not work. The intention is to port this to "React Navigation", which is now the accepted approach for react apps. The counter app still works, so if you don't use navigation your app should work with more recent versions. 102 | 103 | ## How it works 104 | 105 | This section was outdated, but for an overview of the older structure, read this blog post: [Elm Native UI: Writing a React Native app in Elm](http://ohanhi.github.io/elm-native-ui.html) 106 | 107 | You can also watch this [ElmCast Live episode](https://www.livecoding.tv/elmcast/videos/JjbOK-elmcast-live-2), where @ohanhi explains some of the differences between the old and the new versions. (You can safely skip the first 5 minutes, as we had technical issues in the beginning.) 108 | 109 | 110 | ## Screenshots 111 | 112 | iOS | Android 113 | ----|-------- 114 | ![](img/screenshot-ios.png) | ![](img/screenshot-android.png) 115 | 116 | ## To Do 117 | 118 | - [x] Basic PoC 119 | - [x] Show something from Elm 120 | - [x] Make basic VTree work 121 | - [x] Add some kind of event handlers 122 | - [x] Library 123 | - [x] Make `main` support our VTree ([see this suggestion](https://github.com/ohanhi/elm-native/commit/0a35edeb0c21985394b6f3b296140da431aa936c#commitcomment-14303291)) 124 | - [ ] Styles 125 | - [x] Basic types for styles 126 | - [x] Support object type styles - _transform styles and `shadowOffset`_ 127 | - [x] Make enum type styles safer 128 | - [ ] Allow the `StyleSheet.create` method for styles 129 | - [x] Props 130 | - [x] Improved event handlers 131 | - [x] Support props besides styles and event handlers 132 | - [x] Unify syntax for styles, handlers and other props 133 | - [ ] Core features 134 | - [x] Navigation (NavigationExperimental) 135 | - [x] Tasks (HTTP calls, storage) 136 | - [ ] UIExplorer / Component examples 137 | 138 | ## License 139 | 140 | [BSD (3-clause)](LICENSE) 141 | -------------------------------------------------------------------------------- /elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "summary": "Experimental support for writing React Native powered mobile applications", 4 | "repository": "https://github.com/ohanhi/elm-native-ui.git", 5 | "license": "BSD3", 6 | "source-directories": [ 7 | "src" 8 | ], 9 | "exposed-modules": [ 10 | "NativeUi", 11 | "NativeUi.Alert", 12 | "NativeUi.Image", 13 | "NativeUi.Style", 14 | "NativeUi.Elements", 15 | "NativeUi.Properties", 16 | "NativeUi.Events", 17 | "NativeUi.AsyncStorage", 18 | "NativeUi.ListView", 19 | "NativeUi.NavigationExperimental", 20 | "NativeApi.Animated", 21 | "NativeApi.Dimensions", 22 | "NativeApi.NavigationStateUtil", 23 | "NativeApi.Platform", 24 | "NativeApi.StyleSheet", 25 | "NativeApi.PushNotificationIOS" 26 | ], 27 | "native-modules": true, 28 | "dependencies": { 29 | "elm-lang/core": "5.0.0 <= v < 6.0.0" 30 | }, 31 | "elm-version": "0.18.0 <= v < 0.19.0" 32 | } 33 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | android 2 | ios 3 | __tests__ 4 | .babelrc 5 | .buckconfig 6 | .flowconfig 7 | .watchmanconfig 8 | elm.js -------------------------------------------------------------------------------- /examples/Counter/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IJ 26 | # 27 | *.iml 28 | .idea 29 | .gradle 30 | local.properties 31 | 32 | # node.js 33 | # 34 | node_modules/ 35 | npm-debug.log 36 | 37 | # BUCK 38 | buck-out/ 39 | \.buckd/ 40 | android/app/libs 41 | android/keystores/debug.keystore -------------------------------------------------------------------------------- /examples/Counter/Main.elm: -------------------------------------------------------------------------------- 1 | module Main exposing (..) 2 | 3 | import NativeUi as Ui exposing (Node) 4 | import NativeUi.Style as Style exposing (defaultTransform) 5 | import NativeUi.Elements as Elements exposing (..) 6 | import NativeUi.Events exposing (..) 7 | import NativeUi.Image as Image exposing (..) 8 | 9 | 10 | -- MODEL 11 | 12 | 13 | type alias Model = 14 | Int 15 | 16 | 17 | model : Model 18 | model = 19 | 9000 20 | 21 | 22 | 23 | -- UPDATE 24 | 25 | 26 | type Msg 27 | = Increment 28 | | Decrement 29 | 30 | 31 | update : Msg -> Model -> ( Model, Cmd Msg ) 32 | update msg model = 33 | case msg of 34 | Increment -> 35 | ( model + 1, Cmd.none ) 36 | 37 | Decrement -> 38 | ( model - 1, Cmd.none ) 39 | 40 | 41 | 42 | -- VIEW 43 | 44 | 45 | view : Model -> Node Msg 46 | view count = 47 | let 48 | imageSource = 49 | { uri = "https://raw.githubusercontent.com/futurice/spiceprogram/master/assets/img/logo/chilicorn_no_text-128.png" 50 | , cache = Just ForceCache 51 | } 52 | in 53 | Elements.view 54 | [ Ui.style [ Style.alignItems "center" ] 55 | ] 56 | [ image 57 | [ Ui.style 58 | [ Style.height 64 59 | , Style.width 64 60 | , Style.marginBottom 30 61 | , Style.marginTop 30 62 | ] 63 | , source imageSource 64 | ] 65 | [] 66 | , text 67 | [ Ui.style 68 | [ Style.textAlign "center" 69 | , Style.marginBottom 30 70 | ] 71 | ] 72 | [ Ui.string ("Counter: " ++ toString count) 73 | ] 74 | , Elements.view 75 | [ Ui.style 76 | [ Style.width 80 77 | , Style.flexDirection "row" 78 | , Style.justifyContent "space-between" 79 | ] 80 | ] 81 | [ button Decrement "#d33" "-" 82 | , button Increment "#3d3" "+" 83 | ] 84 | ] 85 | 86 | 87 | button : Msg -> String -> String -> Node Msg 88 | button msg color content = 89 | text 90 | [ Ui.style 91 | [ Style.color "white" 92 | , Style.textAlign "center" 93 | , Style.backgroundColor color 94 | , Style.paddingTop 5 95 | , Style.paddingBottom 5 96 | , Style.width 30 97 | , Style.fontWeight "bold" 98 | , Style.shadowColor "#000" 99 | , Style.shadowOpacity 0.25 100 | , Style.shadowOffset 1 1 101 | , Style.shadowRadius 5 102 | , Style.transform { defaultTransform | rotate = Just "10deg" } 103 | ] 104 | , onPress msg 105 | ] 106 | [ Ui.string content ] 107 | 108 | 109 | 110 | -- PROGRAM 111 | 112 | 113 | main : Program Never Model Msg 114 | main = 115 | Ui.program 116 | { init = ( model, Cmd.none ) 117 | , view = view 118 | , update = update 119 | , subscriptions = \_ -> Sub.none 120 | } 121 | -------------------------------------------------------------------------------- /examples/Counter/README.md: -------------------------------------------------------------------------------- 1 | # Counter Example 2 | 3 | Assuming you already have react-native and elm setup. From the command 4 | line run in the examples directory (not this directory!): 5 | 6 | ```bash 7 | $ react-native init Counter --version 0.44.3 8 | ``` 9 | 10 | When asked if you want to overwrite index.ios.js and index.android.js 11 | - decline with "n" 12 | 13 | Using git revert the changes react-native made to the package.json file 14 | and run: 15 | 16 | ```bash 17 | $ cd Counter 18 | $ npm run compile 19 | ``` 20 | 21 | finally: 22 | 23 | ```bash 24 | $ react-native run-ios 25 | ``` 26 | -------------------------------------------------------------------------------- /examples/Counter/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Counter", 3 | "displayName": "Counter" 4 | } -------------------------------------------------------------------------------- /examples/Counter/elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "summary": "helpful summary of your project, less than 80 characters", 4 | "repository": "https://github.com/ohanhi/elm-native-ui.git", 5 | "license": "BSD3", 6 | "source-directories": [ 7 | ".", 8 | "../../src" 9 | ], 10 | "exposed-modules": [], 11 | "native-modules": true, 12 | "dependencies": { 13 | "elm-lang/core": "5.0.0 <= v < 6.0.0", 14 | "elm-lang/html": "2.0.0 <= v < 3.0.0" 15 | }, 16 | "elm-version": "0.18.0 <= v < 0.19.0" 17 | } 18 | -------------------------------------------------------------------------------- /examples/Counter/index.android.js: -------------------------------------------------------------------------------- 1 | const { AppRegistry } = require('react-native'); 2 | const Elm = require('./elm'); 3 | const component = Elm.Main.start(); 4 | 5 | AppRegistry.registerComponent('Counter', () => component); 6 | -------------------------------------------------------------------------------- /examples/Counter/index.ios.js: -------------------------------------------------------------------------------- 1 | const { AppRegistry } = require('react-native'); 2 | const Elm = require('./elm'); 3 | const component = Elm.Main.start(); 4 | 5 | AppRegistry.registerComponent('Counter', () => component); 6 | -------------------------------------------------------------------------------- /examples/Counter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Counter", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "precompile": "rm -f elm.js", 7 | "compile": "elm-make Main.elm --output elm.js", 8 | "start": "node node_modules/react-native/local-cli/cli.js start", 9 | "test": "jest", 10 | "watch": "chokidar '**/*.elm' -c 'npm run compile'" 11 | }, 12 | "dependencies": { 13 | "react": "16.0.0-alpha.6", 14 | "react-native": "0.43.4" 15 | }, 16 | "jest": { 17 | "preset": "jest-react-native" 18 | }, 19 | "devDependencies": { 20 | "babel-jest": "18.0.0", 21 | "babel-preset-react-native": "1.9.0", 22 | "chokidar-cli": "^1.2.0", 23 | "jest": "19.0.2", 24 | "jest-react-native": "18.0.0", 25 | "react-test-renderer": "16.0.0-alpha.6" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/NavigationDemo/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IJ 26 | # 27 | *.iml 28 | .idea 29 | .gradle 30 | local.properties 31 | 32 | # node.js 33 | # 34 | node_modules/ 35 | npm-debug.log 36 | 37 | # BUCK 38 | buck-out/ 39 | \.buckd/ 40 | android/app/libs 41 | android/keystores/debug.keystore 42 | -------------------------------------------------------------------------------- /examples/NavigationDemo/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "NavigationDemo", 3 | "displayName": "NavigationDemo" 4 | } -------------------------------------------------------------------------------- /examples/NavigationDemo/elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "summary": "helpful summary of your project, less than 80 characters", 4 | "repository": "https://github.com/ohanhi/elm-native-ui.git", 5 | "license": "BSD3", 6 | "source-directories": [ 7 | "./src", 8 | "../../src" 9 | ], 10 | "exposed-modules": [], 11 | "native-modules": true, 12 | "dependencies": { 13 | "elm-lang/core": "5.0.0 <= v < 6.0.0", 14 | "elm-lang/html": "2.0.0 <= v < 3.0.0" 15 | }, 16 | "elm-version": "0.18.0 <= v < 0.19.0" 17 | } 18 | -------------------------------------------------------------------------------- /examples/NavigationDemo/index.android.js: -------------------------------------------------------------------------------- 1 | const { AppRegistry } = require('react-native'); 2 | const Elm = require('./elm'); 3 | const component = Elm.Main.start(); 4 | 5 | AppRegistry.registerComponent('NavigationDemo', () => component); 6 | -------------------------------------------------------------------------------- /examples/NavigationDemo/index.ios.js: -------------------------------------------------------------------------------- 1 | const { AppRegistry } = require('react-native'); 2 | const Elm = require('./elm'); 3 | const component = Elm.Main.start(); 4 | 5 | AppRegistry.registerComponent('NavigationDemo', () => component); 6 | -------------------------------------------------------------------------------- /examples/NavigationDemo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "NavigationDemo", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "node node_modules/react-native/local-cli/cli.js start", 7 | "compile": "elm make ./src/Main.elm --output elm.js", 8 | "build": "npm run compile --silent", 9 | "test": "jest", 10 | "watch-elm": "chokidar 'src/**/*.elm' -c 'npm run compile' --silent", 11 | "watch-ios": "react-native run-ios", 12 | "watch": "npm run compile && npm run watch-ios && npm run watch-elm" 13 | }, 14 | "dependencies": { 15 | "react": "16.0.0-alpha.6", 16 | "react-native": "0.43.4" 17 | }, 18 | "jest": { 19 | "preset": "jest-react-native" 20 | }, 21 | "devDependencies": { 22 | "babel-jest": "18.0.0", 23 | "babel-preset-react-native": "1.9.0", 24 | "chokidar-cli": "^1.2.0", 25 | "jest": "19.0.2", 26 | "jest-react-native": "18.0.0", 27 | "react-test-renderer": "16.0.0-alpha.6" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/NavigationDemo/src/CardStack/Example.elm: -------------------------------------------------------------------------------- 1 | module CardStack.Example exposing (Model, Msg(Start), init, update, view) 2 | 3 | import CardStack.NavigationMsg exposing (NavigationMsg(Exit, None, Pop, Push)) 4 | import CardStack.Navigator as Navigator 5 | import NativeApi.NavigationStateUtil exposing (pop, push) 6 | import NativeUi exposing (Node, Property, node) 7 | import NativeUi.NavigationExperimental exposing (NavigationRoute, NavigationState, navigationState) 8 | 9 | 10 | -- MODEL 11 | 12 | 13 | type alias Model = 14 | { navigator : Navigator.NavigatorModel 15 | , counter : Int 16 | } 17 | 18 | 19 | init : Bool -> Model 20 | init enableGestures = 21 | { navigator = 22 | { enableGestures = enableGestures 23 | , navigationState = 24 | { index = 0 25 | , routes = [ { key = "Welcome", title = Nothing } ] 26 | } 27 | } 28 | , counter = 1 29 | } 30 | 31 | 32 | 33 | -- UPDATE 34 | 35 | 36 | type Msg 37 | = Start Bool 38 | | NavigationChange Navigator.NavigatorMsg 39 | 40 | 41 | update : Msg -> Model -> ( Model, Bool ) 42 | update msg model = 43 | case msg of 44 | Start enableGestures -> 45 | ( init enableGestures, False ) 46 | 47 | NavigationChange navigatorMsg -> 48 | let 49 | ( navigator, navigationMsg ) = 50 | Navigator.update navigatorMsg model.navigator 51 | in 52 | case navigationMsg of 53 | Exit -> 54 | ( model, True ) 55 | 56 | None -> 57 | ( { model | navigator = navigator }, False ) 58 | 59 | Pop -> 60 | let 61 | newNavigator = 62 | { navigator | navigationState = pop navigator.navigationState } 63 | in 64 | ( { model 65 | | navigator = newNavigator 66 | , counter = model.counter - 1 67 | } 68 | , False 69 | ) 70 | 71 | Push -> 72 | let 73 | route = 74 | { key = "route - " ++ toString model.counter, title = Nothing } 75 | 76 | newNavigator = 77 | { navigator | navigationState = push route navigator.navigationState } 78 | in 79 | ( { model 80 | | navigator = newNavigator 81 | , counter = model.counter + 1 82 | } 83 | , False 84 | ) 85 | 86 | 87 | 88 | -- VIEW 89 | 90 | 91 | view : Model -> Node Msg 92 | view model = 93 | Navigator.view model.navigator |> NativeUi.map NavigationChange 94 | -------------------------------------------------------------------------------- /examples/NavigationDemo/src/CardStack/NavigationMsg.elm: -------------------------------------------------------------------------------- 1 | module CardStack.NavigationMsg exposing (NavigationMsg(Exit, None, Pop, Push)) 2 | 3 | 4 | type NavigationMsg 5 | = Exit 6 | | None 7 | | Pop 8 | | Push 9 | -------------------------------------------------------------------------------- /examples/NavigationDemo/src/CardStack/Navigator.elm: -------------------------------------------------------------------------------- 1 | module CardStack.Navigator exposing (NavigatorModel, NavigatorMsg, update, view) 2 | 3 | import CardStack.NavigationMsg exposing (NavigationMsg(Exit, None, Pop, Push)) 4 | import CardStack.Scene as Scene 5 | import NativeUi as Ui exposing (Node, node, map) 6 | import NativeUi.Elements as Elements 7 | import NativeUi.Events as Events 8 | import NativeUi.NavigationExperimental as NE 9 | import NativeUi.Properties exposing (enableGestures) 10 | import NativeUi.Style as Style 11 | 12 | 13 | type alias NavigatorModel = 14 | { enableGestures : Bool 15 | , navigationState : NE.NavigationState 16 | } 17 | 18 | 19 | type NavigatorMsg 20 | = Back 21 | | Scene NavigationMsg 22 | 23 | 24 | update : NavigatorMsg -> NavigatorModel -> ( NavigatorModel, NavigationMsg ) 25 | update msg model = 26 | case msg of 27 | Back -> 28 | ( model, Pop ) 29 | 30 | Scene navigationMsg -> 31 | ( model, navigationMsg ) 32 | 33 | 34 | 35 | -- VIEW 36 | 37 | 38 | view : NavigatorModel -> Node NavigatorMsg 39 | view model = 40 | Elements.navigationCardStack 41 | [ Ui.style 42 | [ Style.flex 1 ] 43 | , NE.navigationState model.navigationState 44 | , Events.onNavigateBack Back 45 | , NE.renderScene viewScene 46 | , enableGestures model.enableGestures 47 | ] 48 | [] 49 | 50 | 51 | viewScene : NE.NavigationSceneRenderer -> Node NavigatorMsg 52 | viewScene props = 53 | Scene.view props |> Ui.map Scene 54 | -------------------------------------------------------------------------------- /examples/NavigationDemo/src/CardStack/Scene.elm: -------------------------------------------------------------------------------- 1 | module CardStack.Scene exposing (view) 2 | 3 | import CardStack.NavigationMsg exposing (NavigationMsg(Exit, None, Pop, Push)) 4 | import ExampleRow as Row 5 | import NativeUi exposing (Node) 6 | import NativeUi.Elements as Elements 7 | import NativeUi.NavigationExperimental exposing (NavigationSceneRenderer) 8 | 9 | 10 | view : NavigationSceneRenderer -> Node NavigationMsg 11 | view props = 12 | Elements.scrollView 13 | [] 14 | [ Row.label props.scene.route.key 15 | , Row.button "Push Route" Push 16 | , Row.button "Pop Route" Pop 17 | , Row.button "Exit Card Stack Example" Exit 18 | ] 19 | -------------------------------------------------------------------------------- /examples/NavigationDemo/src/ExampleRow.elm: -------------------------------------------------------------------------------- 1 | module ExampleRow exposing (button, label, underlayColor) 2 | 3 | import Json.Encode 4 | import NativeUi as Ui exposing (Node, Property, property, string, style) 5 | import NativeUi.Elements exposing (touchableHighlight, text, view) 6 | import NativeUi.Events as Events exposing (onPress) 7 | import NativeUi.Style as Style 8 | 9 | 10 | label : String -> Node msg 11 | label label = 12 | view [ Ui.style styleRow ] 13 | [ text 14 | [ Ui.style 15 | [ Style.fontSize 17 ] 16 | ] 17 | [ Ui.string label ] 18 | ] 19 | 20 | 21 | button : String -> msg -> Node msg 22 | button label onPressMsg = 23 | touchableHighlight 24 | [ style styleRow 25 | , underlayColor "#D0D0D0" 26 | , Events.onPress onPressMsg 27 | ] 28 | [ text 29 | [ Ui.style 30 | [ Style.fontSize 17 31 | , Style.fontWeight "500" 32 | ] 33 | ] 34 | [ Ui.string label ] 35 | ] 36 | 37 | 38 | styleRow : List Style.Style 39 | styleRow = 40 | [ Style.padding 15 41 | , Style.backgroundColor "white" 42 | , Style.borderBottomWidth 1 43 | , Style.borderBottomColor "#CDCDCD" 44 | ] 45 | 46 | 47 | {-| 48 | Custom property definition - for when items are missing from elm-native-ui 49 | -} 50 | underlayColor : String -> Property msg 51 | underlayColor val = 52 | property "underlayColor" (Json.Encode.string val) 53 | -------------------------------------------------------------------------------- /examples/NavigationDemo/src/Main.elm: -------------------------------------------------------------------------------- 1 | module Main exposing (..) 2 | 3 | {-| 4 | Elm implementation of the NavigationExperimental example from the react-native UIExplorer example: 5 | https://github.com/facebook/react-native/tree/master/Examples/UIExplorer/js/NavigationExperimental 6 | -} 7 | 8 | import CardStack.Example as CS 9 | import ExampleRow exposing (underlayColor) 10 | import NativeUi as Ui exposing (Node) 11 | import NativeUi.Elements as Elements exposing (scrollView, text, touchableHighlight, view) 12 | import NativeUi.Events exposing (onPress) 13 | import NativeUi.Properties as Properties 14 | import NativeUi.Style as Style 15 | import Tab.Example as Tabs 16 | 17 | 18 | -- PROGRAM 19 | 20 | 21 | main : Program Never Model Msg 22 | main = 23 | Ui.program 24 | { init = init 25 | , view = view 26 | , update = update 27 | , subscriptions = \_ -> Sub.none 28 | } 29 | 30 | 31 | 32 | -- MODEL 33 | 34 | 35 | type alias Model = 36 | { current : Current 37 | , cardStack : CS.Model 38 | , cardStackNoGesture : CS.Model 39 | , tabs : Tabs.Model 40 | } 41 | 42 | 43 | init : ( Model, Cmd Msg ) 44 | init = 45 | ( { current = None 46 | , cardStack = CS.init True 47 | , cardStackNoGesture = CS.init False 48 | , tabs = Tabs.init 49 | } 50 | , Cmd.none 51 | ) 52 | 53 | 54 | 55 | -- UPDATE 56 | 57 | 58 | type Msg 59 | = CardStack CS.Msg 60 | | CardStackNoGesture CS.Msg 61 | | CardStackTabs Tabs.Msg 62 | 63 | 64 | type Current 65 | = None 66 | | ExampleCardStack 67 | | ExampleCardStackNoGesture 68 | | ExampleCardStackTabs 69 | 70 | 71 | update : Msg -> Model -> ( Model, Cmd Msg ) 72 | update msg model = 73 | case msg of 74 | CardStack cardStackMsg -> 75 | let 76 | ( current, cardStack ) = 77 | updateSubModel cardStackMsg model.cardStack CS.update ExampleCardStack 78 | in 79 | ( { model | current = current, cardStack = cardStack }, Cmd.none ) 80 | 81 | CardStackNoGesture cardStackMsg -> 82 | let 83 | ( current, cardStack ) = 84 | updateSubModel cardStackMsg model.cardStackNoGesture CS.update ExampleCardStackNoGesture 85 | in 86 | ( { model | current = current, cardStack = cardStack }, Cmd.none ) 87 | 88 | CardStackTabs tabsMsg -> 89 | let 90 | ( current, tabs ) = 91 | updateSubModel tabsMsg model.tabs Tabs.update ExampleCardStackTabs 92 | in 93 | ( { model | current = current, tabs = tabs }, Cmd.none ) 94 | 95 | 96 | updateSubModel : msg -> model -> (msg -> model -> ( model, Bool )) -> Current -> ( Current, model ) 97 | updateSubModel subMsg subModel updateFunc current = 98 | let 99 | ( newSubModel, exit ) = 100 | updateFunc subMsg subModel 101 | in 102 | if exit then 103 | ( None, newSubModel ) 104 | else 105 | ( current, newSubModel ) 106 | 107 | 108 | 109 | -- VIEW 110 | 111 | 112 | view : Model -> Node Msg 113 | view model = 114 | case model.current of 115 | None -> 116 | viewNone 117 | 118 | ExampleCardStack -> 119 | viewCardStack model.cardStack 120 | 121 | ExampleCardStackNoGesture -> 122 | viewCardStack model.cardStackNoGesture 123 | 124 | ExampleCardStackTabs -> 125 | Tabs.view model.tabs |> Ui.map CardStackTabs 126 | 127 | 128 | viewNone : Node Msg 129 | viewNone = 130 | Elements.scrollView 131 | [] 132 | [ label "Navigation Experimental" 133 | , row "CardStack + Header + Tabs Example" <| CardStackTabs Tabs.Start 134 | , row "CardStack Example" <| CardStack <| CS.Start True 135 | , row "CardStack Without Gestures Example" <| CardStack <| CS.Start False 136 | ] 137 | 138 | 139 | viewCardStack : CS.Model -> Node Msg 140 | viewCardStack model = 141 | CS.view model |> Ui.map CardStack 142 | 143 | 144 | label : String -> Node a 145 | label label = 146 | Elements.view [ Ui.style styleRow ] 147 | [ Elements.text 148 | [ Ui.style 149 | [ Style.fontSize 20 150 | , Style.textAlign "center" 151 | ] 152 | , Properties.numberOfLines 1.0 153 | ] 154 | [ Ui.string label ] 155 | ] 156 | 157 | 158 | row : String -> msg -> Node msg 159 | row label message = 160 | Elements.touchableHighlight 161 | [ Ui.style styleRow 162 | , underlayColor "#D0D0D0" 163 | , onPress message 164 | ] 165 | [ Elements.text 166 | [ Ui.style 167 | [ Style.fontSize 17 168 | , Style.fontWeight "500" 169 | ] 170 | ] 171 | [ Ui.string label ] 172 | ] 173 | 174 | 175 | styleRow : List Style.Style 176 | styleRow = 177 | [ Style.padding 15 178 | , Style.backgroundColor "white" 179 | , Style.borderBottomWidth 1 180 | , Style.borderBottomColor "#CDCDCD" 181 | ] 182 | -------------------------------------------------------------------------------- /examples/NavigationDemo/src/Tab/Example.elm: -------------------------------------------------------------------------------- 1 | module Tab.Example exposing (Model, Msg(Start), init, update, view) 2 | 3 | import Dict as Dict 4 | import NativeApi.NavigationStateUtil exposing (jumpTo, pop, push) 5 | import NativeUi exposing (Node, Property, node) 6 | import NativeUi.NavigationExperimental exposing (NavigationRoute, NavigationState, navigationState) 7 | import Tab.NavigationMsg exposing (NavigationMsg(Exit, Jump, None, Pop, Push)) 8 | import Tab.Navigator as Navigator 9 | 10 | 11 | -- MODEL 12 | 13 | 14 | type alias Model = 15 | { navigator : Navigator.NavigatorModel 16 | } 17 | 18 | 19 | init : Model 20 | init = 21 | let 22 | apple = 23 | { counter = 1 24 | , state = 25 | { index = 0 26 | , routes = [ { key = "apple", title = Just "Apple Home" } ] 27 | } 28 | } 29 | 30 | banana = 31 | { counter = 1 32 | , state = 33 | { index = 0 34 | , routes = [ { key = "banana", title = Just "Banana Home" } ] 35 | } 36 | } 37 | 38 | orange = 39 | { counter = 1 40 | , state = 41 | { index = 0 42 | , routes = [ { key = "orange", title = Just "Orange Home" } ] 43 | } 44 | } 45 | 46 | tabState = 47 | Dict.empty 48 | |> Dict.insert "apple" apple 49 | |> Dict.insert "banana" banana 50 | |> Dict.insert "orange" orange 51 | in 52 | { navigator = 53 | { tabs = 54 | { index = 0 55 | , routes = 56 | [ { key = "apple", title = Nothing } 57 | , { key = "banana", title = Nothing } 58 | , { key = "orange", title = Nothing } 59 | ] 60 | } 61 | , tabState = tabState 62 | } 63 | } 64 | 65 | 66 | 67 | -- UPDATE 68 | 69 | 70 | type Msg 71 | = Start 72 | | NavigationChange Navigator.NavigatorMsg 73 | 74 | 75 | update : Msg -> Model -> ( Model, Bool ) 76 | update msg model = 77 | case msg of 78 | Start -> 79 | ( init, False ) 80 | 81 | NavigationChange navigatorMsg -> 82 | let 83 | ( navigator, navigationMsg ) = 84 | Navigator.update navigatorMsg model.navigator 85 | in 86 | case navigationMsg of 87 | Exit -> 88 | ( model, True ) 89 | 90 | Jump key -> 91 | ( { model | navigator = changeTab key model.navigator }, False ) 92 | 93 | None -> 94 | ( { model | navigator = navigator }, False ) 95 | 96 | Pop -> 97 | ( { model | navigator = updateNavigator popTabState model.navigator }, False ) 98 | 99 | Push -> 100 | ( { model | navigator = updateNavigator pushTabState model.navigator }, False ) 101 | 102 | 103 | updateNavigator : (Maybe Navigator.NavigatorTabState -> Maybe Navigator.NavigatorTabState) -> Navigator.NavigatorModel -> Navigator.NavigatorModel 104 | updateNavigator updateValue model = 105 | case Navigator.getActiveTabKey model of 106 | Nothing -> 107 | model 108 | 109 | Just key -> 110 | { model | tabState = Dict.update key updateValue model.tabState } 111 | 112 | 113 | changeTab : String -> Navigator.NavigatorModel -> Navigator.NavigatorModel 114 | changeTab key model = 115 | { model | tabs = jumpTo key model.tabs } 116 | 117 | 118 | popTabState : Maybe Navigator.NavigatorTabState -> Maybe Navigator.NavigatorTabState 119 | popTabState maybeTabState = 120 | case maybeTabState of 121 | Nothing -> 122 | Nothing 123 | 124 | Just tabState -> 125 | Just 126 | { tabState 127 | | counter = tabState.counter - 1 128 | , state = pop tabState.state 129 | } 130 | 131 | 132 | pushTabState : Maybe Navigator.NavigatorTabState -> Maybe Navigator.NavigatorTabState 133 | pushTabState maybeTabState = 134 | case maybeTabState of 135 | Nothing -> 136 | Nothing 137 | 138 | Just tabState -> 139 | let 140 | route = 141 | { key = "route - " ++ toString tabState.counter, title = Nothing } 142 | in 143 | Just 144 | { tabState 145 | | counter = tabState.counter + 1 146 | , state = push route tabState.state 147 | } 148 | 149 | 150 | 151 | -- VIEW 152 | 153 | 154 | view : Model -> Node Msg 155 | view model = 156 | Navigator.view model.navigator |> NativeUi.map NavigationChange 157 | -------------------------------------------------------------------------------- /examples/NavigationDemo/src/Tab/Header.elm: -------------------------------------------------------------------------------- 1 | module Tab.Header exposing (view) 2 | 3 | import ExampleRow as Row 4 | import NativeUi as Ui 5 | import NativeUi exposing (Node) 6 | import NativeUi.Elements as Elements 7 | import NativeUi.Events as Events 8 | import NativeUi.NavigationExperimental as NE 9 | import Tab.NavigationMsg exposing (NavigationMsg(Exit, None, Pop, Push)) 10 | 11 | 12 | view : NE.NavigationSceneRenderer -> Node NavigationMsg 13 | view props = 14 | let 15 | rendererProps = 16 | NE.navigationSceneRendererToPropertyList props 17 | 18 | headerProps = 19 | [ NE.renderTitleComponent viewTitle 20 | , Events.onNavigateBack Pop 21 | ] 22 | in 23 | Elements.navigationHeader 24 | (rendererProps ++ headerProps) 25 | [] 26 | 27 | 28 | viewTitle : NE.NavigationSceneRenderer -> Node NavigationMsg 29 | viewTitle props = 30 | Elements.navigationHeaderTitle 31 | [] 32 | [ Ui.string <| Maybe.withDefault props.scene.route.key props.scene.route.title ] 33 | -------------------------------------------------------------------------------- /examples/NavigationDemo/src/Tab/NavigationMsg.elm: -------------------------------------------------------------------------------- 1 | module Tab.NavigationMsg exposing (NavigationMsg(Exit, Jump, None, Pop, Push)) 2 | 3 | 4 | type NavigationMsg 5 | = Exit 6 | | Jump String 7 | | None 8 | | Pop 9 | | Push 10 | -------------------------------------------------------------------------------- /examples/NavigationDemo/src/Tab/Navigator.elm: -------------------------------------------------------------------------------- 1 | module Tab.Navigator exposing (NavigatorModel, NavigatorTabState, NavigatorMsg, getActiveTabKey, update, view) 2 | 3 | import Dict as Dict 4 | import NativeApi.NavigationStateUtil as NS 5 | import NativeUi as Ui exposing (Node, node, map) 6 | import NativeUi.Elements as Elements 7 | import NativeUi.Events as Events 8 | import NativeUi.NavigationExperimental as NE 9 | import NativeUi.Properties as Properties 10 | import NativeUi.Style as Style 11 | import Tab.Header as Header 12 | import Tab.NavigationMsg exposing (NavigationMsg(Exit, Jump, None, Pop, Push)) 13 | import Tab.Scene as Scene 14 | import Tab.Tab as Tab 15 | 16 | 17 | type alias NavigatorModel = 18 | { tabs : NE.NavigationState 19 | , tabState : Dict.Dict String NavigatorTabState 20 | } 21 | 22 | 23 | type alias NavigatorTabState = 24 | { counter : Int 25 | , state : NE.NavigationState 26 | } 27 | 28 | 29 | type NavigatorMsg 30 | = Back NavigationMsg 31 | | Scene NavigationMsg 32 | | SelectTab NavigationMsg 33 | 34 | 35 | update : NavigatorMsg -> NavigatorModel -> ( NavigatorModel, NavigationMsg ) 36 | update msg model = 37 | case msg of 38 | Back navigationMsg -> 39 | ( model, navigationMsg ) 40 | 41 | Scene navigationMsg -> 42 | ( model, navigationMsg ) 43 | 44 | SelectTab navigationMsg -> 45 | ( model, navigationMsg ) 46 | 47 | 48 | 49 | -- VIEW 50 | 51 | 52 | view : NavigatorModel -> Node NavigatorMsg 53 | view model = 54 | let 55 | maybeTabKey = 56 | getActiveTabKey model 57 | 58 | maybeState = 59 | getTabState model maybeTabKey 60 | in 61 | case ( maybeTabKey, maybeState ) of 62 | ( Nothing, _ ) -> 63 | Debug.crash <| (++) "Tab not found at index: " <| toString model.tabs.index 64 | 65 | ( Just tabKey, Nothing ) -> 66 | Debug.crash <| "Unknown tab key: " ++ tabKey 67 | 68 | ( Just tabKey, Just tabState ) -> 69 | Elements.view 70 | [ Ui.style [ Style.flex 1 ] ] 71 | [ Elements.navigationCardStack 72 | [ Ui.style 73 | [ Style.flex 20 ] 74 | , Properties.key <| "stack_" ++ tabKey 75 | , NE.navigationState tabState.state 76 | , Events.onNavigateBack <| Back Pop 77 | , NE.renderHeader viewHeader 78 | , NE.renderScene viewScene 79 | ] 80 | [] 81 | , Elements.view 82 | [ Ui.style 83 | [ Style.flex 1 84 | , Style.flexDirection "row" 85 | ] 86 | , NE.navigationState model.tabs 87 | ] 88 | (List.map (viewTab tabState.state) model.tabs.routes) 89 | ] 90 | 91 | 92 | viewHeader : NE.NavigationSceneRenderer -> Node NavigatorMsg 93 | viewHeader props = 94 | Header.view props |> Ui.map Back 95 | 96 | 97 | viewScene : NE.NavigationSceneRenderer -> Node NavigatorMsg 98 | viewScene props = 99 | Scene.view props |> Ui.map Scene 100 | 101 | 102 | viewTab : NE.NavigationState -> NE.NavigationRoute -> Node NavigatorMsg 103 | viewTab selectedState route = 104 | NS.has route selectedState 105 | |> Tab.tab route 106 | |> Ui.map SelectTab 107 | 108 | 109 | getActiveTabKey : NavigatorModel -> Maybe String 110 | getActiveTabKey model = 111 | NS.getRoute model.tabs.index model.tabs 112 | |> Maybe.map (\x -> x.key) 113 | 114 | 115 | getTabState : NavigatorModel -> Maybe String -> Maybe NavigatorTabState 116 | getTabState model maybeTabKey = 117 | case maybeTabKey of 118 | Nothing -> 119 | Nothing 120 | 121 | Just tabKey -> 122 | Dict.get tabKey model.tabState 123 | -------------------------------------------------------------------------------- /examples/NavigationDemo/src/Tab/Scene.elm: -------------------------------------------------------------------------------- 1 | module Tab.Scene exposing (view) 2 | 3 | import ExampleRow as Row 4 | import NativeUi exposing (Node) 5 | import NativeUi.Elements as Elements 6 | import NativeUi.NavigationExperimental exposing (NavigationSceneRenderer) 7 | import Tab.NavigationMsg exposing (NavigationMsg(Exit, Pop, Push)) 8 | 9 | 10 | view : NavigationSceneRenderer -> Node NavigationMsg 11 | view props = 12 | Elements.scrollView 13 | [] 14 | [ Row.button "Push Route" Push 15 | , Row.button "Pop Route" Pop 16 | , Row.button "Exit Header + Scenes + Tabs Example" Exit 17 | ] 18 | -------------------------------------------------------------------------------- /examples/NavigationDemo/src/Tab/Tab.elm: -------------------------------------------------------------------------------- 1 | module Tab.Tab exposing (tab) 2 | 3 | import NativeUi as Ui exposing (Node, Property, string, style) 4 | import NativeUi.Elements exposing (touchableOpacity, text, view) 5 | import NativeUi.Events as Events exposing (onPress) 6 | import NativeUi.NavigationExperimental exposing (NavigationRoute) 7 | import NativeUi.Style as Style 8 | import Tab.NavigationMsg exposing (NavigationMsg(Jump)) 9 | 10 | 11 | tab : NavigationRoute -> Bool -> Node NavigationMsg 12 | tab route selected = 13 | touchableOpacity 14 | [ style styleTab 15 | , Events.onPress <| Jump route.key 16 | ] 17 | [ text 18 | [ style <| styleText selected ] 19 | [ Ui.string route.key ] 20 | ] 21 | 22 | 23 | styleTab : List Style.Style 24 | styleTab = 25 | [ Style.alignItems "center" 26 | , Style.backgroundColor "#FFF" 27 | , Style.flex 1 28 | , Style.justifyContent "center" 29 | ] 30 | 31 | 32 | styleText : Bool -> List Style.Style 33 | styleText selected = 34 | let 35 | baseStyle = 36 | [ Style.fontWeight "500" ] 37 | in 38 | if selected then 39 | Style.color "blue" :: baseStyle 40 | else 41 | Style.color "#222" :: baseStyle 42 | -------------------------------------------------------------------------------- /img/chilicorn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohanhi/elm-native-ui/ce08188adf294de855771db437b26a3f98a44a1b/img/chilicorn.png -------------------------------------------------------------------------------- /img/elm-native-160.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohanhi/elm-native-ui/ce08188adf294de855771db437b26a3f98a44a1b/img/elm-native-160.png -------------------------------------------------------------------------------- /img/elm-native.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | elm-native 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /img/screenshot-android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohanhi/elm-native-ui/ce08188adf294de855771db437b26a3f98a44a1b/img/screenshot-android.png -------------------------------------------------------------------------------- /img/screenshot-ios.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ohanhi/elm-native-ui/ce08188adf294de855771db437b26a3f98a44a1b/img/screenshot-ios.png -------------------------------------------------------------------------------- /src/Native/NativeUi.js: -------------------------------------------------------------------------------- 1 | var _ohanhi$elm_native_ui$Native_NativeUi = (function () { 2 | 3 | var ReactNative = require('react-native'); 4 | var React = require('react'); 5 | var toArray = _elm_lang$core$Native_List.toArray; 6 | 7 | // PROPS 8 | 9 | /** 10 | * Declares a message decoder to be run on an event for a particular node 11 | */ 12 | function on(eventName, decoder) { 13 | return { 14 | type: 'event', 15 | eventName: eventName, 16 | decoder: decoder 17 | }; 18 | } 19 | /** 20 | * Declares a ref attribute for a node, callback receives the reference of the node 21 | */ 22 | function ref(callback) { 23 | return { 24 | type: 'ref', 25 | callback: callback, 26 | } 27 | } 28 | /** 29 | * Declares a style attribute for a node, expressed as an inline styles for 30 | * the moment. 31 | */ 32 | function style(attrs) { 33 | return { 34 | type: 'style', 35 | sheet: attrs 36 | }; 37 | } 38 | 39 | /** 40 | * Declares any other kind of property for a node. 41 | */ 42 | function property(propName, value) { 43 | return { 44 | type: 'prop', 45 | propName: propName, 46 | value: value 47 | }; 48 | } 49 | 50 | function renderProperty(propName, value) { 51 | return { 52 | type: 'renderProp', 53 | propName: propName, 54 | value: value, 55 | }; 56 | } 57 | 58 | function unsafeRenderDecodedProperty(propName, decoder, value) { 59 | return { 60 | type: 'renderDecodedProp', 61 | propName: propName, 62 | value: value, 63 | decoder: decoder 64 | }; 65 | } 66 | 67 | // ELEMENTS 68 | 69 | /** 70 | * A plain string node 71 | */ 72 | function string(text) { 73 | return { 74 | type: 'string', 75 | string: text 76 | }; 77 | } 78 | 79 | /** 80 | * A node that renders a React Native component with props and children 81 | */ 82 | function node(tagName) { 83 | return F2(function(factList, childList) { 84 | return { 85 | type: 'component', 86 | tagName: tagName, 87 | facts: toArray(factList), 88 | children: toArray(childList) 89 | }; 90 | }); 91 | } 92 | 93 | /** 94 | * A node that renders a React Native component with props, but no children. 95 | * This can improve performance by reducing function calls during 96 | * tree-building and can allow for optimizations later on as well. 97 | */ 98 | function voidNode(tagName) { 99 | return function (factList) { 100 | return { 101 | type: 'component', 102 | tagName: tagName, 103 | facts: toArray(factList), 104 | children: [] 105 | }; 106 | }; 107 | } 108 | 109 | /** 110 | * A non-standard node that renders a React Native component with props and children 111 | */ 112 | function customNode(tagName, nativeComponent) { 113 | return F2(function(factList, childList) { 114 | return { 115 | type: 'component', 116 | tagName: tagName, 117 | facts: toArray(factList), 118 | children: toArray(childList), 119 | nativeComponent: nativeComponent 120 | }; 121 | }); 122 | } 123 | 124 | /** 125 | * Maps another node onto a different message type 126 | */ 127 | function map(tagger, node) { 128 | return { 129 | type: 'tagger', 130 | tagger: tagger, 131 | node: node 132 | }; 133 | } 134 | 135 | // RENDER 136 | 137 | /** 138 | * Converts a stack of `on` handlers and `map` nodes into a final function 139 | * that can be passed into a React Native component's `onSomeEvent` props 140 | */ 141 | function makeEventHandler(eventNode, decoder) { 142 | function eventHandler(event) { 143 | var decoder = eventHandler.decoder; 144 | var message = decodeValue(eventHandler.decoder, event); 145 | 146 | var currentEventNode = eventNode; 147 | while (currentEventNode) { 148 | var tagger = currentEventNode.tagger; 149 | 150 | if (typeof tagger === 'function') { 151 | message = tagger(message); 152 | } else { 153 | for (var i = tagger.length; i--; ) { 154 | message = tagger[i](message); 155 | } 156 | } 157 | 158 | currentEventNode = currentEventNode.parent; 159 | } 160 | } 161 | 162 | eventHandler.decoder = decoder; 163 | 164 | return eventHandler; 165 | } 166 | 167 | /** 168 | * Converts a fact whose value is a function that renders a subTree into a 169 | * function that constructs the subTree to render with the provided props. 170 | * This is used by NavigationExperimental to pass the header and scene views 171 | * into the component 172 | */ 173 | function makeRenderNodePropHandler(fact, eventNode, key) { 174 | function handler(props) { 175 | return renderTree(handler.component(props), eventNode, key); 176 | }; 177 | 178 | handler.component = fact.value; 179 | 180 | return handler; 181 | } 182 | 183 | /** 184 | * Converts a fact whose value is a function that renders a subTree into a 185 | * function that constructs the subTree to render with the provided props. 186 | * This is used by NavigationExperimental to pass the header and scene views 187 | * into the component 188 | */ 189 | function makeRenderNodeDecodedPropHandler(fact, eventNode, key) { 190 | function handler(props) { 191 | var decodedProps = decodeValue(handler.decoder, props); 192 | 193 | return renderTree(handler.component(decodedProps), eventNode, key); 194 | }; 195 | 196 | handler.component = fact.value; 197 | handler.decoder = fact.decoder; 198 | 199 | return handler; 200 | } 201 | 202 | /** 203 | * Decodes properties from elm into json props for react-native 204 | */ 205 | function decodeValue(decoder, value) { 206 | var decodedValue = A2(_elm_lang$core$Native_Json.run, decoder, value); 207 | 208 | if (decodedValue.ctor !== 'Ok') { 209 | throw Error(decodedValue._0); 210 | } 211 | 212 | return decodedValue._0; 213 | } 214 | 215 | /** 216 | * Converts a string node back to a plain string for React Native to render 217 | */ 218 | function renderString(node) { 219 | return node.string; 220 | } 221 | 222 | /** 223 | * Composes taggers created by `map` 224 | */ 225 | function renderTagger(node, eventNode, key) { 226 | var subNode = node.node; 227 | var tagger = node.tagger; 228 | 229 | while (subNode.type === 'tagger') { 230 | typeof tagger !== 'object' ? 231 | tagger = [tagger, subNode.tagger] : 232 | tagger.push(subNode.tagger); 233 | 234 | subNode = subNode.node; 235 | } 236 | 237 | var subEventRoot = { tagger: tagger, parent: eventNode }; 238 | return renderTree(subNode, subEventRoot, key); 239 | } 240 | 241 | /** 242 | * Converts a component node into an actual React Native node. Builds the 243 | * children array and props object, looks up the component by name on the 244 | * React Native module and calls into React.createElement. 245 | */ 246 | function renderComponent(node, eventNode, key) { 247 | var children = []; 248 | for (var i = 0; i < node.children.length; i++) { 249 | children.push(renderTree(node.children[i], eventNode, i)); 250 | } 251 | 252 | var finalProps = {}; 253 | 254 | for (var j = 0; j < node.facts.length; j++) { 255 | var fact = node.facts[j]; 256 | 257 | switch (fact.type) { 258 | case 'prop': 259 | finalProps[fact.propName] = fact.value; 260 | break; 261 | case 'ref': 262 | finalProps['ref'] = fact.callback; 263 | break; 264 | case 'renderProp': 265 | finalProps[fact.propName] = makeRenderNodePropHandler(fact, eventNode, key); 266 | break; 267 | 268 | case 'renderDecodedProp': 269 | finalProps[fact.propName] = makeRenderNodeDecodedPropHandler(fact, eventNode, key); 270 | break; 271 | 272 | case 'event': 273 | finalProps[fact.eventName] = makeEventHandler(eventNode, fact.decoder); 274 | break; 275 | 276 | case 'style': 277 | finalProps.style = fact.sheet; 278 | break; 279 | } 280 | } 281 | 282 | if(!finalProps.key) { 283 | finalProps.key = 'elm-native-ui-auto-added-' + key; 284 | } 285 | 286 | if (children.length === 1) { 287 | finalProps.children = children[0]; 288 | } else if (children.length) { 289 | finalProps.children = children; 290 | } 291 | if (ReactNative[node.tagName]) { 292 | return React.createElement(ReactNative[node.tagName], finalProps); 293 | } else { 294 | if (!node.nativeComponent) { 295 | throw Error('Unable to find a node called ' + node.tagName + ' in ReactNative. Try defining it as a customNode'); 296 | } 297 | 298 | return React.createElement(node.nativeComponent, finalProps); 299 | } 300 | } 301 | 302 | /** 303 | * Renders the whole tree! 304 | */ 305 | function renderTree(node, eventNode, key) { 306 | switch (node.type) { 307 | case 'string': 308 | return renderString(node); 309 | 310 | case 'tagger': 311 | return renderTagger(node, eventNode, key); 312 | 313 | case 'component': 314 | return renderComponent(node, eventNode, key); 315 | 316 | } 317 | } 318 | 319 | // PROGRAM 320 | 321 | /** 322 | * Takes am Elm Native UI program implementation and turns into a React 323 | * component that will begin rendering the virtual tree as soon as the Elm 324 | * program starts running 325 | */ 326 | function makeComponent(impl, onAppReady, flags) { 327 | return React.createClass({ 328 | getInitialState: function getInitialState() { 329 | return {}; 330 | }, 331 | 332 | onAppReady: onAppReady, 333 | 334 | componentDidMount: function componentDidMount() { 335 | this.eventNode = { tagger: function() {}, parent: undefined }; 336 | 337 | this._app = _elm_lang$core$Native_Platform.initialize( 338 | typeof flags === 'undefined' ? impl.init : impl.init(flags), 339 | impl.update, 340 | impl.subscriptions, 341 | this.renderer 342 | ); 343 | 344 | if (typeof this.onAppReady === "function") { 345 | this.onAppReady(this._app); 346 | } 347 | }, 348 | 349 | renderer: function renderer(onMessage, initialModel) { 350 | this.eventNode.tagger = onMessage; 351 | this.updateModel(initialModel); 352 | return this.updateModel; 353 | }, 354 | 355 | updateModel: function updateModel(model) { 356 | this.setState({ model: model }); 357 | }, 358 | 359 | render: function render() { 360 | // There won't be a model to render right away so we'll check that it 361 | // exists before trying to call the view function 362 | return typeof this.state.model !== 'undefined' ? 363 | renderTree(impl.view(this.state.model), this.eventNode, 0) : 364 | null; 365 | } 366 | }); 367 | } 368 | 369 | /** 370 | * Makes an Elm program from the standard init, model, update, view 371 | * specification and adds a function to your module called `start` that, 372 | * when called in JavaScript-land, will return a React component that you can 373 | * render or register with the AppRegistry. In the future this function will 374 | * also deal with decoding flags from JavaScript and setting up debugging 375 | * facilities. But for now it just returns a `Program Never model msg` that 376 | * you can pass to `main`. 377 | */ 378 | function program(impl) { 379 | return function(flagDecoder) { 380 | return function(object, moduleName, debugMetadata) { 381 | object.start = function start(onAppReady) { 382 | return makeComponent(impl, onAppReady); 383 | }; 384 | }; 385 | }; 386 | } 387 | 388 | /** 389 | * Run the provided `flagDecoder` before passing to Elm code for type safety. 390 | */ 391 | function programWithFlags(impl) { 392 | return function(flagDecoder) { 393 | return function(object, moduleName, debugMetadata) { 394 | object.start = function start(onAppReady, flags = {}) { 395 | if (typeof flagDecoder === 'undefined') { 396 | throw new Error( 397 | 'Are you trying to sneak a Never value into Elm? Trickster!\n' 398 | + 'It looks like ' + moduleName + '.main is defined with `programWithFlags` but has type `Program Never`.\n' 399 | + 'Use `program` instead if you do not want flags.' 400 | ); 401 | } 402 | var result = A2(_elm_lang$core$Native_Json.run, flagDecoder, flags); 403 | if (result.ctor === 'Err') 404 | { 405 | throw new Error( 406 | moduleName + '.start(...) was called with an unexpected argument.\n' 407 | + 'I tried to convert it to an Elm value, but ran into this problem:\n\n' 408 | + result._0 409 | ); 410 | } 411 | return makeComponent(impl, onAppReady, result._0); 412 | }; 413 | }; 414 | }; 415 | } 416 | 417 | // UTILS 418 | 419 | /** 420 | * Useful for encoding a Date.Date as a Json.Encode.Value since Date.Date is 421 | * just a plain JavaScript Date. This can be used for props on things like 422 | * DatePickerIOS that expect date values. 423 | */ 424 | function identity(value) { 425 | return value; 426 | } 427 | 428 | /** 429 | * When combined with `Decode.andThen`, creates a decoder for a Date.Date from 430 | * a plain JavaScript Date. This can be used for event handlers that pass a 431 | * Date. 432 | */ 433 | function parseDate(value) { 434 | if (value instanceof Date) { 435 | return _elm_lang$core$Native_Json.succeed(value); 436 | } else { 437 | return _elm_lang$core$Native_Json.fail('Expected a Date, but did not find one'); 438 | } 439 | } 440 | 441 | return { 442 | program: program, 443 | programWithFlags: programWithFlags, 444 | node: node, 445 | voidNode: voidNode, 446 | customNode: F2(customNode), 447 | string: string, 448 | map: F2(map), 449 | on: F2(on), 450 | ref: ref, 451 | style: style, 452 | property: F2(property), 453 | renderProperty: F2(renderProperty), 454 | unsafeRenderDecodedProperty: F3(unsafeRenderDecodedProperty), 455 | encodeDate: identity, 456 | parseDate: parseDate 457 | }; 458 | }()); 459 | -------------------------------------------------------------------------------- /src/Native/NativeUi/Alert.js: -------------------------------------------------------------------------------- 1 | const _ohanhi$elm_native_ui$Native_NativeUi_Alert = function () { 2 | const { Alert } = require("react-native"); 3 | const toArray = _elm_lang$core$Native_List.toArray; 4 | const unit = { ctor: "_Tuple0" }; 5 | 6 | function alert(title, message, buttons) { 7 | return _elm_lang$core$Native_Scheduler.nativeBinding(function(callback) { 8 | const buttonArray = toArray(buttons).map(function(button) { 9 | const { text, value } = button; 10 | 11 | return { 12 | text, 13 | onPress: () => { 14 | callback(_elm_lang$core$Native_Scheduler.succeed(value)); 15 | }, 16 | }; 17 | }); 18 | 19 | Alert.alert(title, message, buttonArray); 20 | }); 21 | } 22 | 23 | return { 24 | alert: F3(alert), 25 | }; 26 | }(); 27 | -------------------------------------------------------------------------------- /src/Native/NativeUi/AsyncStorage.js: -------------------------------------------------------------------------------- 1 | const _ohanhi$elm_native_ui$Native_NativeUi_AsyncStorage = function () { 2 | const { AsyncStorage } = require("react-native"); 3 | const unit = { ctor: "_Tuple0" }; 4 | const toArray = _elm_lang$core$Native_List.toArray; 5 | const fromArray = _elm_lang$core$Native_List.fromArray; 6 | 7 | function setItem(key, value) { 8 | return _elm_lang$core$Native_Scheduler.nativeBinding(function(callback) { 9 | AsyncStorage.setItem(key, value) 10 | .then(function() { 11 | return callback(_elm_lang$core$Native_Scheduler.succeed(unit)); 12 | }) 13 | .catch(failWithError(callback)); 14 | }); 15 | } 16 | 17 | function getItem(key) { 18 | return _elm_lang$core$Native_Scheduler.nativeBinding(function(callback) { 19 | AsyncStorage.getItem(key) 20 | .then(function(value) { 21 | const result = maybe(value); 22 | return callback(_elm_lang$core$Native_Scheduler.succeed(result)); 23 | }) 24 | .catch(failWithError(callback)); 25 | }); 26 | } 27 | 28 | function removeItem(key) { 29 | return _elm_lang$core$Native_Scheduler.nativeBinding(function(callback) { 30 | AsyncStorage.removeItem(key) 31 | .then(function() { 32 | return callback(_elm_lang$core$Native_Scheduler.succeed(unit)); 33 | }) 34 | .catch(failWithError(callback)); 35 | }); 36 | } 37 | 38 | function multiGet(keys) { 39 | return _elm_lang$core$Native_Scheduler.nativeBinding(function(callback) { 40 | AsyncStorage.multiGet(toArray(keys)) 41 | .then(function(keysWithValues) { 42 | const result = fromArray(toTupleArray(keysWithValues)); 43 | return callback(_elm_lang$core$Native_Scheduler.succeed(result)); 44 | }) 45 | .catch(failWithError(callback)); 46 | }); 47 | } 48 | 49 | function toTupleArray(keysWithValues) { 50 | return keysWithValues.map(([key, value]) => { 51 | return tuple2(key, maybe(value)); 52 | }); 53 | } 54 | 55 | function failWithError(callback) { 56 | return function(e) { 57 | const errorValue = { ctor: 'Error', _0: e.message }; 58 | return callback(_elm_lang$core$Native_Scheduler.fail(errorValue)); 59 | }; 60 | } 61 | 62 | function maybe(value) { 63 | if (value) { 64 | return { ctor: 'Just', _0: value }; 65 | } else { 66 | return { ctor: 'Nothing' }; 67 | } 68 | } 69 | 70 | function tuple2(a, b) { 71 | return { ctor: '_Tuple2', _0: a, _1: b }; 72 | } 73 | 74 | return { 75 | setItem: F2(setItem), 76 | getItem: getItem, 77 | removeItem: removeItem, 78 | multiGet: multiGet, 79 | }; 80 | }(); 81 | -------------------------------------------------------------------------------- /src/Native/NativeUi/Dimensions.js: -------------------------------------------------------------------------------- 1 | const _ohanhi$elm_native_ui$Native_NativeUi_Dimensions = function () { 2 | const { Dimensions } = require('react-native'); 3 | const { height, width } = Dimensions.get('window'); 4 | 5 | return { 6 | windowHeight: height, 7 | windowWidth: width, 8 | }; 9 | }(); 10 | -------------------------------------------------------------------------------- /src/Native/NativeUi/Elements.js: -------------------------------------------------------------------------------- 1 | const _ohanhi$elm_native_ui$Native_NativeUi_Elements = function () { 2 | return { 3 | navigationCardStack: require("NavigationCardStack"), 4 | navigationHeader: require("NavigationHeader"), 5 | navigationHeaderTitle: require("NavigationHeaderTitle"), 6 | }; 7 | }(); 8 | -------------------------------------------------------------------------------- /src/Native/NativeUi/ListView.js: -------------------------------------------------------------------------------- 1 | const _ohanhi$elm_native_ui$Native_NativeUi_ListView = function () { 2 | const React = require('react'); 3 | const { ListView, Text } = require('react-native'); 4 | 5 | const emptyDataSource = new ListView.DataSource({ 6 | rowHasChanged: function (a, b) { return a !== b; } 7 | }); 8 | 9 | function unencodedProperty(dataSource) { 10 | return { 11 | type: 'prop', 12 | propName: 'dataSource', 13 | value: dataSource 14 | }; 15 | } 16 | 17 | function updateDataSource(data, dataSource) { 18 | var result = _elm_lang$core$Native_List.toArray(data); 19 | return dataSource.cloneWithRows(result); 20 | } 21 | 22 | return { 23 | view: ListView, 24 | emptyDataSource: emptyDataSource, 25 | unencodedProperty: unencodedProperty, 26 | updateDataSource: F2(updateDataSource), 27 | }; 28 | }(); 29 | -------------------------------------------------------------------------------- /src/Native/NativeUi/Platform.js: -------------------------------------------------------------------------------- 1 | const _ohanhi$elm_native_ui$Native_NativeUi_Platform = function () { 2 | const { Platform } = require("react-native"); 3 | 4 | return { 5 | os: Platform.OS, 6 | }; 7 | }(); 8 | -------------------------------------------------------------------------------- /src/Native/NativeUi/PushNotificationIOS.js: -------------------------------------------------------------------------------- 1 | const _ohanhi$elm_native_ui$Native_NativeUi_PushNotificationIOS = function () { 2 | const { PushNotificationIOS } = require("react-native"); 3 | 4 | const register = _elm_lang$core$Native_Scheduler.nativeBinding(function(callback) { 5 | PushNotificationIOS.addEventListener('register', token => { 6 | callback(_elm_lang$core$Native_Scheduler.succeed(token)); 7 | }); 8 | 9 | PushNotificationIOS.addEventListener('registrationError', e => { 10 | callback(_elm_lang$core$Native_Scheduler.fail(e.message)); 11 | }); 12 | 13 | PushNotificationIOS.requestPermissions(); 14 | }); 15 | 16 | return { 17 | register, 18 | }; 19 | }(); 20 | -------------------------------------------------------------------------------- /src/Native/NativeUi/StyleSheet.js: -------------------------------------------------------------------------------- 1 | const _ohanhi$elm_native_ui$Native_NativeUi_StyleSheet = function () { 2 | const { StyleSheet } = require("react-native"); 3 | 4 | const listFromArray = _elm_lang$core$Native_List.fromArray; 5 | const dictFromList = _elm_lang$core$Dict$fromList; 6 | 7 | function tuple2(a, b) { 8 | return { ctor: '_Tuple2', _0: a, _1: b }; 9 | } 10 | 11 | function styleSheet(a){ 12 | return { ctor: 'StyleSheet', _0: a} 13 | } 14 | 15 | function create(style){ 16 | const stylesheet = StyleSheet.create(style); 17 | 18 | const array = 19 | Object.keys(stylesheet) 20 | .map(function(key){ 21 | return tuple2(key, styleSheet(stylesheet[key])); 22 | }); 23 | 24 | return dictFromList(listFromArray(array)); 25 | } 26 | 27 | return { 28 | create: create, 29 | }; 30 | }(); 31 | -------------------------------------------------------------------------------- /src/NativeApi/Animated.elm: -------------------------------------------------------------------------------- 1 | module NativeApi.Animated exposing (AnimatedValue, decodeAnimatedValue, encodeAnimatedValue) 2 | 3 | {-| 4 | @docs AnimatedValue, decodeAnimatedValue, encodeAnimatedValue 5 | -} 6 | 7 | import Json.Decode as Decode 8 | import Json.Encode as Encode 9 | 10 | 11 | -- ANIMATED VALUE 12 | 13 | 14 | {-| -} 15 | type alias AnimatedValue = 16 | Decode.Value 17 | 18 | 19 | {-| -} 20 | encodeAnimatedValue : AnimatedValue -> Encode.Value 21 | encodeAnimatedValue = 22 | identity 23 | 24 | 25 | {-| -} 26 | decodeAnimatedValue : Decode.Decoder AnimatedValue 27 | decodeAnimatedValue = 28 | Decode.value 29 | -------------------------------------------------------------------------------- /src/NativeApi/Dimensions.elm: -------------------------------------------------------------------------------- 1 | module NativeApi.Dimensions exposing (window) 2 | 3 | {-| elm-native-ui Dimensions 4 | 5 | @docs window 6 | -} 7 | 8 | import Native.NativeUi.Dimensions 9 | 10 | 11 | {-| -} 12 | type alias WindowSize = 13 | { height : Float, width : Float } 14 | 15 | 16 | {-| -} 17 | window : WindowSize 18 | window = 19 | WindowSize 20 | Native.NativeUi.Dimensions.windowHeight 21 | Native.NativeUi.Dimensions.windowWidth 22 | -------------------------------------------------------------------------------- /src/NativeApi/NavigationStateUtil.elm: -------------------------------------------------------------------------------- 1 | module NativeApi.NavigationStateUtil exposing (back, get, getRoute, forward, has, indexOf, jumpTo, jumpToIndex, pop, push, replaceAt, replaceAtIndex, reset) 2 | 3 | {-| 4 | Elm implementation of the NavigationExperimental example from the react-native UIExplorer example: 5 | https://github.com/facebook/react-native/blob/master/Libraries/NavigationExperimental/NavigationStateUtils.js 6 | 7 | @docs back, get, getRoute, forward, has, indexOf, jumpTo, jumpToIndex, pop, push, replaceAt, replaceAtIndex, reset 8 | -} 9 | 10 | import Array as Array 11 | import NativeUi.NavigationExperimental exposing (NavigationRoute, NavigationScene, NavigationState) 12 | import Tuple exposing (first, second) 13 | 14 | 15 | {-| -} 16 | getRoute : Int -> NavigationState -> Maybe NavigationRoute 17 | getRoute index state = 18 | List.indexedMap (\i r -> ( i, r )) state.routes 19 | |> List.filter (\( i, r ) -> i == index) 20 | |> List.head 21 | |> Maybe.map second 22 | 23 | 24 | {-| -} 25 | get : String -> NavigationState -> Maybe NavigationRoute 26 | get key state = 27 | List.filter (\r -> r.key == key) state.routes 28 | |> List.head 29 | 30 | 31 | {-| -} 32 | indexOf : String -> NavigationState -> Maybe Int 33 | indexOf key state = 34 | List.indexedMap (\i r -> ( i, r )) state.routes 35 | |> List.filter (\( i, r ) -> r.key == key) 36 | |> List.head 37 | |> Maybe.map first 38 | 39 | 40 | {-| -} 41 | has : NavigationRoute -> NavigationState -> Bool 42 | has route state = 43 | List.filter (\x -> x.key == route.key) state.routes 44 | |> List.isEmpty 45 | |> not 46 | 47 | 48 | {-| -} 49 | pop : NavigationState -> NavigationState 50 | pop state = 51 | case List.reverse state.routes of 52 | [] -> 53 | state 54 | 55 | [ x ] -> 56 | state 57 | 58 | x :: xs -> 59 | let 60 | routes = 61 | List.reverse xs 62 | in 63 | { state 64 | | index = (List.length routes) - 1 65 | , routes = routes 66 | } 67 | 68 | 69 | {-| -} 70 | push : NavigationRoute -> NavigationState -> NavigationState 71 | push route state = 72 | let 73 | exists = 74 | List.any (\x -> x.key == route.key) state.routes 75 | in 76 | if exists then 77 | Debug.crash <| "should not push route with duplicated key " ++ route.key 78 | else 79 | let 80 | routes = 81 | List.append state.routes [ route ] 82 | in 83 | { state 84 | | index = (List.length routes) - 1 85 | , routes = routes 86 | } 87 | 88 | 89 | {-| -} 90 | jumpToIndex : Int -> NavigationState -> NavigationState 91 | jumpToIndex index state = 92 | if index == state.index then 93 | state 94 | else if index + 1 > (List.length state.routes) then 95 | Debug.crash <| "invalid index " ++ (toString index) ++ " to jump to" 96 | else 97 | { state | index = index } 98 | 99 | 100 | {-| -} 101 | jumpTo : String -> NavigationState -> NavigationState 102 | jumpTo key state = 103 | case indexOf key state of 104 | Nothing -> 105 | Debug.crash <| "invalid key '" ++ key ++ "' to jump to" 106 | 107 | Just index -> 108 | jumpToIndex index state 109 | 110 | 111 | {-| -} 112 | back : NavigationState -> NavigationState 113 | back state = 114 | case getRoute (state.index - 1) state of 115 | Nothing -> 116 | state 117 | 118 | Just route -> 119 | jumpToIndex (state.index - 1) state 120 | 121 | 122 | {-| -} 123 | forward : NavigationState -> NavigationState 124 | forward state = 125 | case getRoute (state.index + 1) state of 126 | Nothing -> 127 | state 128 | 129 | Just route -> 130 | jumpToIndex (state.index + 1) state 131 | 132 | 133 | {-| -} 134 | replaceAt : String -> NavigationRoute -> NavigationState -> NavigationState 135 | replaceAt key route state = 136 | case indexOf key state of 137 | Nothing -> 138 | Debug.crash <| "invalid key '" ++ key ++ "' for replacing route " ++ route.key 139 | 140 | Just index -> 141 | replaceAtIndex index route state 142 | 143 | 144 | {-| -} 145 | replaceAtIndex : Int -> NavigationRoute -> NavigationState -> NavigationState 146 | replaceAtIndex index route state = 147 | let 148 | maybeOriginal = 149 | getRoute index state 150 | in 151 | case maybeOriginal of 152 | Nothing -> 153 | Debug.crash <| "invalid index " ++ (toString index) ++ " for replacing route " ++ route.key 154 | 155 | Just original -> 156 | if original == route then 157 | { state | index = index } 158 | else 159 | { state | index = index, routes = List.map (substitute original route) state.routes } 160 | 161 | 162 | {-| -} 163 | substitute : NavigationRoute -> NavigationRoute -> NavigationRoute -> NavigationRoute 164 | substitute old new value = 165 | if old.key == value.key then 166 | new 167 | else 168 | value 169 | 170 | 171 | {-| -} 172 | reset : Maybe Int -> List NavigationRoute -> NavigationState 173 | reset maybeIndex routes = 174 | let 175 | maxIndex = 176 | (List.length routes) - 1 177 | 178 | index = 179 | Maybe.withDefault maxIndex maybeIndex 180 | in 181 | { index = index 182 | , routes = routes 183 | } 184 | -------------------------------------------------------------------------------- /src/NativeApi/Platform.elm: -------------------------------------------------------------------------------- 1 | module NativeApi.Platform exposing (OS(..), os) 2 | 3 | {-| elm-native-ui Platform 4 | 5 | @docs OS, os 6 | -} 7 | 8 | import Native.NativeUi.Platform 9 | 10 | 11 | {-| -} 12 | type OS 13 | = Android 14 | | IOS 15 | 16 | 17 | {-| -} 18 | os : OS 19 | os = 20 | if Native.NativeUi.Platform.os == "ios" then 21 | IOS 22 | else 23 | Android 24 | -------------------------------------------------------------------------------- /src/NativeApi/PushNotificationIOS.elm: -------------------------------------------------------------------------------- 1 | module NativeApi.PushNotificationIOS exposing (register) 2 | 3 | {-| 4 | @docs register 5 | -} 6 | 7 | import Task exposing (Task) 8 | import Native.NativeUi.PushNotificationIOS 9 | 10 | 11 | {-| Returns a Task that resolves with the push token for this app installation 12 | if the user accepts the system permissions dialog. If they don't accept the 13 | dialog, the task doesn't resolve. If there is an error (like running on a 14 | simulator), the Task will fail with an error message String. 15 | -} 16 | register : Task String String 17 | register = 18 | Native.NativeUi.PushNotificationIOS.register 19 | -------------------------------------------------------------------------------- /src/NativeApi/StyleSheet.elm: -------------------------------------------------------------------------------- 1 | module NativeApi.StyleSheet exposing (create) 2 | 3 | {-| elm-native-ui StyleSheet 4 | 5 | @docs create 6 | -} 7 | 8 | import Native.NativeUi.StyleSheet 9 | import NativeUi.Style exposing (Style, StyleSheet) 10 | import Json.Encode 11 | import Dict exposing (Dict) 12 | 13 | 14 | {-| -} 15 | create : List ( String, List Style ) -> Dict String StyleSheet 16 | create list = 17 | list 18 | |> List.map (Tuple.mapSecond NativeUi.Style.encode) 19 | |> Json.Encode.object 20 | |> Native.NativeUi.StyleSheet.create 21 | -------------------------------------------------------------------------------- /src/NativeUi.elm: -------------------------------------------------------------------------------- 1 | module NativeUi 2 | exposing 3 | ( Node 4 | , customNode 5 | , node 6 | , string 7 | , style 8 | , styleSheet 9 | , on 10 | , ref 11 | , Property 12 | , property 13 | , map 14 | , program 15 | , programWithFlags 16 | , renderProperty 17 | , unsafeRenderDecodedProperty 18 | ) 19 | 20 | {-| Render your application as a React Native app. 21 | 22 | # Common Helpers 23 | @docs node, string, customNode, style, styleSheet, property, map, renderProperty, unsafeRenderDecodedProperty 24 | 25 | # Events 26 | @docs on 27 | 28 | # Ref 29 | @docs ref 30 | 31 | # Types 32 | @docs Node, Property 33 | 34 | # Program 35 | @docs program, programWithFlags 36 | -} 37 | 38 | import Json.Decode as Decode exposing (Value, Decoder) 39 | import Native.NativeUi 40 | import NativeUi.Style as Style 41 | 42 | 43 | {-| -} 44 | type Node msg 45 | = Node 46 | 47 | 48 | {-| -} 49 | type Property msg 50 | = Property 51 | 52 | 53 | {-| This type represents a reference to a React component. 54 | 55 | You should not use this type from Elm. It only exists to keep track of 56 | JavaScript components that are passed through Elm to Native modules. 57 | -} 58 | type NativeComponent 59 | = NativeComponent 60 | 61 | 62 | {-| -} 63 | node : String -> List (Property msg) -> List (Node msg) -> Node msg 64 | node = 65 | Native.NativeUi.node 66 | 67 | 68 | {-| -} 69 | customNode : String -> NativeComponent -> List (Property msg) -> List (Node msg) -> Node msg 70 | customNode = 71 | Native.NativeUi.customNode 72 | 73 | 74 | {-| -} 75 | string : String -> Node msg 76 | string = 77 | Native.NativeUi.string 78 | 79 | 80 | {-| -} 81 | property : String -> Value -> Property msg 82 | property = 83 | Native.NativeUi.property 84 | 85 | 86 | {-| Returns a property representing a rendering function 87 | 88 | This is usually used for properties where the values passed to the rendering 89 | function where created in Elm. To decode values passed from JavaScript, use 90 | `unsafeRenderDecodedProperty`. 91 | 92 | -} 93 | renderProperty : String -> (a -> Node b) -> Property msg 94 | renderProperty = 95 | Native.NativeUi.renderProperty 96 | 97 | 98 | {-| Returns a property representing a rendering function 99 | 100 | Runs values through the decoder before being passed to the rendering function. 101 | 102 | This is usually used for properties where the values passed to the 103 | rendering function were created in JavaScript, and thus need to be decoded. If 104 | the values are passed from Elm, you can use `renderProperty` to avoid 105 | encoding/decoding. 106 | 107 | Crashes the program if decoding fails. 108 | -} 109 | unsafeRenderDecodedProperty : String -> Decoder a -> (a -> Node b) -> Property msg 110 | unsafeRenderDecodedProperty = 111 | Native.NativeUi.unsafeRenderDecodedProperty 112 | 113 | 114 | {-| -} 115 | style : List Style.Style -> Property msg 116 | style = 117 | Native.NativeUi.style << Style.encode 118 | 119 | 120 | {-| -} 121 | styleSheet : List Style.StyleSheet -> Property msg 122 | styleSheet = 123 | Native.NativeUi.style << Style.encodeSheet 124 | 125 | 126 | {-| -} 127 | on : String -> Decoder msg -> Property msg 128 | on eventName = 129 | let 130 | realEventName = 131 | if String.startsWith "on" eventName then 132 | eventName 133 | else 134 | "on" ++ eventName 135 | in 136 | Native.NativeUi.on realEventName 137 | 138 | 139 | {-| -} 140 | ref : (a -> b) -> Property msg 141 | ref = 142 | Native.NativeUi.ref 143 | 144 | 145 | {-| -} 146 | map : (a -> b) -> Node a -> Node b 147 | map = 148 | Native.NativeUi.map 149 | 150 | 151 | {-| -} 152 | program : 153 | { view : model -> Node msg 154 | , update : msg -> model -> ( model, Cmd msg ) 155 | , subscriptions : model -> Sub msg 156 | , init : ( model, Cmd msg ) 157 | } 158 | -> Program Never model msg 159 | program = 160 | Native.NativeUi.program 161 | 162 | 163 | {-| -} 164 | programWithFlags : 165 | { view : model -> Node msg 166 | , update : msg -> model -> ( model, Cmd msg ) 167 | , subscriptions : model -> Sub msg 168 | , init : flags -> ( model, Cmd msg ) 169 | } 170 | -> Program flags model msg 171 | programWithFlags = 172 | Native.NativeUi.programWithFlags 173 | -------------------------------------------------------------------------------- /src/NativeUi/Alert.elm: -------------------------------------------------------------------------------- 1 | module NativeUi.Alert exposing (alert) 2 | 3 | {-| 4 | @docs alert 5 | -} 6 | 7 | import Task exposing (Task) 8 | import Native.NativeUi.Alert 9 | 10 | 11 | type alias AlertButton = 12 | { text : String 13 | , value : Bool 14 | } 15 | 16 | 17 | {-| Show a system alert dialog, with the specified title, message, and list of 18 | buttons. The returned Task resolves to the Bool value of the button that was 19 | pressed. 20 | 21 | NativeUi.alert 22 | "Alert title" 23 | "Alert message" 24 | [ { text = "No thanks", value = False } 25 | , { text = "Yes please", value = True } 26 | ] 27 | -} 28 | alert : String -> String -> List AlertButton -> Task String Bool 29 | alert title message buttons = 30 | Native.NativeUi.Alert.alert title message buttons 31 | -------------------------------------------------------------------------------- /src/NativeUi/AsyncStorage.elm: -------------------------------------------------------------------------------- 1 | module NativeUi.AsyncStorage exposing (Error, getItem, setItem, removeItem, multiGet) 2 | 3 | {-| elm-native-ui AsyncStorage 4 | 5 | @docs Error, setItem, getItem, removeItem, multiGet 6 | -} 7 | 8 | import Native.NativeUi.AsyncStorage 9 | import Task exposing (Task) 10 | import Dict exposing (Dict, fromList) 11 | 12 | 13 | {-| -} 14 | type Error 15 | = Error String 16 | 17 | 18 | {-| -} 19 | getItem : String -> Task Error (Maybe String) 20 | getItem = 21 | Native.NativeUi.AsyncStorage.getItem 22 | 23 | 24 | {-| -} 25 | setItem : String -> String -> Task Error () 26 | setItem = 27 | Native.NativeUi.AsyncStorage.setItem 28 | 29 | 30 | {-| -} 31 | removeItem : String -> Task Error () 32 | removeItem = 33 | Native.NativeUi.AsyncStorage.removeItem 34 | 35 | 36 | {-| -} 37 | multiGet : List String -> Task Error (Dict String (Maybe String)) 38 | multiGet strings = 39 | Task.map Dict.fromList (Native.NativeUi.AsyncStorage.multiGet strings) 40 | -------------------------------------------------------------------------------- /src/NativeUi/Elements.elm: -------------------------------------------------------------------------------- 1 | module NativeUi.Elements exposing (text, image, activityIndicator, picker, progressBar, progressView, refreshControl, scrollView, segmentedControl, slider, statusBar, switch, tabBar, textInput, toolbar, touchableHighlight, touchableOpacity, view, navigationCardStack, navigationHeader, navigationHeaderTitle) 2 | 3 | {-| elm-native-ui Elements 4 | 5 | @docs text, image, activityIndicator, picker, progressBar, progressView, refreshControl, scrollView, segmentedControl, slider, statusBar, switch, tabBar, textInput, toolbar, touchableHighlight, touchableOpacity, view, navigationCardStack, navigationHeader, navigationHeaderTitle 6 | -} 7 | 8 | import NativeUi exposing (Property, Node, customNode, node) 9 | import Native.NativeUi.Elements 10 | 11 | 12 | {-| -} 13 | text : List (Property msg) -> List (Node msg) -> Node msg 14 | text = 15 | node "Text" 16 | 17 | 18 | {-| -} 19 | image : List (Property msg) -> List (Node msg) -> Node msg 20 | image = 21 | node "Image" 22 | 23 | 24 | {-| -} 25 | activityIndicator : List (Property msg) -> List (Node msg) -> Node msg 26 | activityIndicator = 27 | node "ActivityIndicator" 28 | 29 | 30 | {-| -} 31 | picker : List (Property msg) -> List (Node msg) -> Node msg 32 | picker = 33 | node "Picker" 34 | 35 | 36 | {-| -} 37 | progressBar : List (Property msg) -> List (Node msg) -> Node msg 38 | progressBar = 39 | node "ProgressBar" 40 | 41 | 42 | {-| -} 43 | progressView : List (Property msg) -> List (Node msg) -> Node msg 44 | progressView = 45 | node "ProgressView" 46 | 47 | 48 | {-| -} 49 | refreshControl : List (Property msg) -> List (Node msg) -> Node msg 50 | refreshControl = 51 | node "RefreshControl" 52 | 53 | 54 | {-| -} 55 | scrollView : List (Property msg) -> List (Node msg) -> Node msg 56 | scrollView = 57 | node "ScrollView" 58 | 59 | 60 | {-| -} 61 | segmentedControl : List (Property msg) -> List (Node msg) -> Node msg 62 | segmentedControl = 63 | node "SegmentedControl" 64 | 65 | 66 | {-| -} 67 | slider : List (Property msg) -> List (Node msg) -> Node msg 68 | slider = 69 | node "Slider" 70 | 71 | 72 | {-| -} 73 | statusBar : List (Property msg) -> List (Node msg) -> Node msg 74 | statusBar = 75 | node "StatusBar" 76 | 77 | 78 | {-| -} 79 | switch : List (Property msg) -> List (Node msg) -> Node msg 80 | switch = 81 | node "Switch" 82 | 83 | 84 | {-| -} 85 | tabBar : List (Property msg) -> List (Node msg) -> Node msg 86 | tabBar = 87 | node "TabBar" 88 | 89 | 90 | {-| -} 91 | textInput : List (Property msg) -> List (Node msg) -> Node msg 92 | textInput = 93 | node "TextInput" 94 | 95 | 96 | {-| -} 97 | toolbar : List (Property msg) -> List (Node msg) -> Node msg 98 | toolbar = 99 | node "Toolbar" 100 | 101 | 102 | {-| -} 103 | touchableHighlight : List (Property msg) -> List (Node msg) -> Node msg 104 | touchableHighlight = 105 | node "TouchableHighlight" 106 | 107 | 108 | {-| -} 109 | touchableOpacity : List (Property msg) -> List (Node msg) -> Node msg 110 | touchableOpacity = 111 | node "TouchableOpacity" 112 | 113 | 114 | {-| -} 115 | view : List (Property msg) -> List (Node msg) -> Node msg 116 | view = 117 | node "View" 118 | 119 | 120 | {-| -} 121 | navigationCardStack : List (Property msg) -> List (Node msg) -> Node msg 122 | navigationCardStack = 123 | customNode "NavigationCardStack" Native.NativeUi.Elements.navigationCardStack 124 | 125 | 126 | {-| -} 127 | navigationHeader : List (Property msg) -> List (Node msg) -> Node msg 128 | navigationHeader = 129 | customNode "NavigationHeader" Native.NativeUi.Elements.navigationHeader 130 | 131 | 132 | {-| -} 133 | navigationHeaderTitle : List (Property msg) -> List (Node msg) -> Node msg 134 | navigationHeaderTitle = 135 | customNode "NavigationHeaderTitle" Native.NativeUi.Elements.navigationHeaderTitle 136 | -------------------------------------------------------------------------------- /src/NativeUi/Events.elm: -------------------------------------------------------------------------------- 1 | module NativeUi.Events exposing (onLayout, onPress, onLongPress, onRegionChange, onRegionChangeComplete, onAnnotationPress, onPickerValueChange, onRefresh, onScroll, onScrollAnimationEnd, onContentSizeChange, onShowUnderlay, onHideUnderlay, onNavigateBack, onChangeText) 2 | 3 | {-| elm-native-ui Events 4 | 5 | @docs onLayout, onPress, onLongPress, onRegionChange, onRegionChangeComplete, onAnnotationPress, onPickerValueChange, onRefresh, onScroll, onScrollAnimationEnd, onContentSizeChange, onShowUnderlay, onHideUnderlay, onNavigateBack, onChangeText 6 | -} 7 | 8 | import Json.Decode as Decode exposing (Value, Decoder) 9 | import NativeUi exposing (Property, on) 10 | 11 | 12 | constantMsgEvent : String -> msg -> Property msg 13 | constantMsgEvent name msg = 14 | on name (Decode.succeed msg) 15 | 16 | 17 | {-| -} 18 | onLayout : msg -> Property msg 19 | onLayout = 20 | constantMsgEvent "Layout" 21 | 22 | 23 | {-| -} 24 | onPress : msg -> Property msg 25 | onPress = 26 | constantMsgEvent "Press" 27 | 28 | 29 | {-| -} 30 | onLongPress : msg -> Property msg 31 | onLongPress = 32 | constantMsgEvent "LongPress" 33 | 34 | 35 | {-| -} 36 | onRegionChange : msg -> Property msg 37 | onRegionChange = 38 | constantMsgEvent "RegionChange" 39 | 40 | 41 | {-| -} 42 | onRegionChangeComplete : msg -> Property msg 43 | onRegionChangeComplete = 44 | constantMsgEvent "RegionChangeComplete" 45 | 46 | 47 | {-| -} 48 | onAnnotationPress : msg -> Property msg 49 | onAnnotationPress = 50 | constantMsgEvent "AnnotationPress" 51 | 52 | 53 | {-| -} 54 | onPickerValueChange : (String -> msg) -> Property msg 55 | onPickerValueChange tagger = 56 | on "PickerValueChange" (Decode.map tagger Decode.string) 57 | 58 | 59 | {-| -} 60 | onRefresh : msg -> Property msg 61 | onRefresh = 62 | constantMsgEvent "Refresh" 63 | 64 | 65 | {-| -} 66 | onScroll : msg -> Property msg 67 | onScroll = 68 | constantMsgEvent "Scroll" 69 | 70 | 71 | {-| -} 72 | onScrollAnimationEnd : msg -> Property msg 73 | onScrollAnimationEnd = 74 | constantMsgEvent "ScrollAnimationEnd" 75 | 76 | 77 | {-| -} 78 | onContentSizeChange : msg -> Property msg 79 | onContentSizeChange = 80 | constantMsgEvent "ContentSizeChange" 81 | 82 | 83 | {-| -} 84 | onShowUnderlay : msg -> Property msg 85 | onShowUnderlay = 86 | constantMsgEvent "ShowUnderlay" 87 | 88 | 89 | {-| -} 90 | onHideUnderlay : msg -> Property msg 91 | onHideUnderlay = 92 | constantMsgEvent "HideUnderlay" 93 | 94 | 95 | {-| -} 96 | onNavigateBack : msg -> Property msg 97 | onNavigateBack = 98 | constantMsgEvent "NavigateBack" 99 | 100 | 101 | {-| -} 102 | onChangeText : (String -> msg) -> Property msg 103 | onChangeText tagger = 104 | on "ChangeText" (Decode.map tagger Decode.string) 105 | -------------------------------------------------------------------------------- /src/NativeUi/Image.elm: -------------------------------------------------------------------------------- 1 | module NativeUi.Image 2 | exposing 3 | ( Source 4 | , CacheStrategy(..) 5 | , source 6 | , defaultSource 7 | ) 8 | 9 | {-| elm-native-ui Image 10 | 11 | @docs Source, CacheStrategy, defaultSource, source 12 | -} 13 | 14 | import Json.Encode as Encode 15 | import Json.Decode as Decode 16 | import NativeUi exposing (Property, Node, property) 17 | 18 | 19 | {-| -} 20 | type CacheStrategy 21 | = Default 22 | | Reload 23 | | ForceCache 24 | | OnlyIfCached 25 | 26 | 27 | {-| -} 28 | type alias Source = 29 | { uri : String 30 | , cache : Maybe CacheStrategy 31 | } 32 | 33 | 34 | {-| -} 35 | defaultSource : String -> Source 36 | defaultSource uri = 37 | { uri = uri, cache = Nothing } 38 | 39 | 40 | {-| -} 41 | source : Source -> Property msg 42 | source val = 43 | property "source" (encodeSource val) 44 | 45 | 46 | encodeSource : Source -> Encode.Value 47 | encodeSource source = 48 | let 49 | uriEncode = 50 | ( "uri", Encode.string source.uri ) 51 | 52 | mapFunc x = 53 | ( "cache", encodeCacheStrategy x ) 54 | 55 | encoding = 56 | [ Just uriEncode, Maybe.map mapFunc source.cache ] 57 | in 58 | Encode.object <| (List.filterMap identity encoding) 59 | 60 | 61 | encodeCacheStrategy : CacheStrategy -> Encode.Value 62 | encodeCacheStrategy cacheStrategy = 63 | case cacheStrategy of 64 | Default -> 65 | Encode.string "default" 66 | 67 | Reload -> 68 | Encode.string "reload" 69 | 70 | ForceCache -> 71 | Encode.string "force-cache" 72 | 73 | OnlyIfCached -> 74 | Encode.string "only-if-cached" 75 | 76 | 77 | decodeSource : Decode.Decoder Source 78 | decodeSource = 79 | Decode.map2 Source 80 | (Decode.field "uri" Decode.string) 81 | (Decode.field "cache" (Decode.nullable Decode.string) |> Decode.andThen decodeCacheStrategy) 82 | 83 | 84 | decodeCacheStrategy : Maybe String -> Decode.Decoder (Maybe CacheStrategy) 85 | decodeCacheStrategy optionalStringValue = 86 | case optionalStringValue of 87 | Nothing -> 88 | Decode.succeed Nothing 89 | 90 | Just stringValue -> 91 | case stringValue of 92 | "default" -> 93 | Decode.succeed (Just Default) 94 | 95 | "reload" -> 96 | Decode.succeed (Just Reload) 97 | 98 | "force-cache" -> 99 | Decode.succeed (Just ForceCache) 100 | 101 | "only-if-cached" -> 102 | Decode.succeed (Just OnlyIfCached) 103 | 104 | _ -> 105 | Decode.fail (stringValue ++ " is not a valid image cache value") 106 | -------------------------------------------------------------------------------- /src/NativeUi/ListView.elm: -------------------------------------------------------------------------------- 1 | module NativeUi.ListView 2 | exposing 3 | ( DataSource 4 | , emptyDataSource 5 | , listView 6 | , updateDataSource 7 | , initialListSize 8 | , pageSize 9 | , removeClippedSubviews 10 | , scrollRenderAheadDistance 11 | ) 12 | 13 | {-| elm-native-ui ListView 14 | 15 | @docs DataSource, emptyDataSource, listView, updateDataSource, initialListSize, pageSize, removeClippedSubviews, scrollRenderAheadDistance 16 | -} 17 | 18 | import Json.Encode 19 | import NativeUi exposing (Property, Node, node, renderProperty, property) 20 | import Native.NativeUi.ListView 21 | 22 | 23 | {-| -} 24 | type DataSource a 25 | = DataSource a 26 | 27 | 28 | {-| -} 29 | listView : DataSource a -> (a -> Node msg) -> List (Property msg) -> Node msg 30 | listView ds render props = 31 | node "ListView" 32 | ((unencodedProperty 33 | ds 34 | ) 35 | :: renderRow render 36 | :: props 37 | ) 38 | [] 39 | 40 | 41 | {-| -} 42 | renderRow : (a -> Node msg) -> Property msg 43 | renderRow = 44 | renderProperty "renderRow" 45 | 46 | 47 | {-| -} 48 | emptyDataSource : DataSource a 49 | emptyDataSource = 50 | Native.NativeUi.ListView.emptyDataSource 51 | 52 | 53 | {-| -} 54 | updateDataSource : List a -> DataSource a -> DataSource a 55 | updateDataSource = 56 | Native.NativeUi.ListView.updateDataSource 57 | 58 | 59 | {-| -} 60 | unencodedProperty : DataSource a -> Property msg 61 | unencodedProperty = 62 | Native.NativeUi.ListView.unencodedProperty 63 | 64 | 65 | {-| -} 66 | initialListSize : Int -> Property msg 67 | initialListSize = 68 | property "initialListSize" << Json.Encode.int 69 | 70 | 71 | {-| -} 72 | pageSize : Int -> Property msg 73 | pageSize = 74 | property "pageSize" << Json.Encode.int 75 | 76 | 77 | {-| -} 78 | removeClippedSubviews : Bool -> Property msg 79 | removeClippedSubviews = 80 | property "removeClippedSubviews" << Json.Encode.bool 81 | 82 | 83 | {-| -} 84 | scrollRenderAheadDistance : Int -> Property msg 85 | scrollRenderAheadDistance = 86 | property "scrollRenderAheadDistance" << Json.Encode.int 87 | -------------------------------------------------------------------------------- /src/NativeUi/NavigationExperimental.elm: -------------------------------------------------------------------------------- 1 | module NativeUi.NavigationExperimental exposing (NavigationSceneRenderer, NavigationRoute, NavigationScene, NavigationState, layout, navigationState, renderHeader, renderScene, renderTitleComponent, scene, navigationSceneRendererToPropertyList) 2 | 3 | {-| 4 | @docs NavigationSceneRenderer, NavigationRoute, NavigationScene, NavigationState, layout, navigationState, renderHeader, renderScene, renderTitleComponent, scene, navigationSceneRendererToPropertyList 5 | -} 6 | 7 | import Json.Decode as Decode 8 | import Json.Encode as Encode exposing (Value, bool, int, list, object, string) 9 | import Native.NativeUi 10 | import NativeApi.Animated exposing (AnimatedValue, decodeAnimatedValue, encodeAnimatedValue) 11 | import NativeUi exposing (Node, Property, on, property, unsafeRenderDecodedProperty) 12 | import NativeUi.Style as Style 13 | 14 | 15 | -- RENDER 16 | 17 | 18 | {-| -} 19 | renderHeader : (NavigationSceneRenderer -> Node a) -> Property msg 20 | renderHeader = 21 | unsafeRenderDecodedProperty "renderHeader" decodeNavigationSceneRenderer 22 | 23 | 24 | {-| -} 25 | renderScene : (NavigationSceneRenderer -> Node a) -> Property msg 26 | renderScene = 27 | unsafeRenderDecodedProperty "renderScene" decodeNavigationSceneRenderer 28 | 29 | 30 | {-| -} 31 | renderTitleComponent : (NavigationSceneRenderer -> Node a) -> Property msg 32 | renderTitleComponent = 33 | unsafeRenderDecodedProperty "renderTitleComponent" decodeNavigationSceneRenderer 34 | 35 | 36 | 37 | -- NAVIGATION LAYOUT 38 | 39 | 40 | type alias NavigationLayout = 41 | { height : AnimatedValue 42 | , initHeight : Float 43 | , initWidth : Float 44 | , isMeasured : Bool 45 | , width : AnimatedValue 46 | } 47 | 48 | 49 | {-| -} 50 | layout : NavigationLayout -> Property msg 51 | layout val = 52 | property "layout" (encodeNavigationLayout val) 53 | 54 | 55 | encodeNavigationLayout : NavigationLayout -> Value 56 | encodeNavigationLayout layout = 57 | Encode.object <| 58 | [ ( "height", encodeAnimatedValue layout.height ) 59 | , ( "initHeight", Encode.float layout.initHeight ) 60 | , ( "initWidth", Encode.float layout.initWidth ) 61 | , ( "isMeasured", Encode.bool layout.isMeasured ) 62 | , ( "width", encodeAnimatedValue layout.width ) 63 | ] 64 | 65 | 66 | decodeLayout : Decode.Decoder NavigationLayout 67 | decodeLayout = 68 | Decode.map5 NavigationLayout 69 | (Decode.field "height" decodeAnimatedValue) 70 | (Decode.field "initHeight" Decode.float) 71 | (Decode.field "initWidth" Decode.float) 72 | (Decode.field "isMeasured" Decode.bool) 73 | (Decode.field "width" decodeAnimatedValue) 74 | 75 | 76 | 77 | -- NAVIGATION ROUTE 78 | 79 | 80 | {-| -} 81 | type alias NavigationRoute = 82 | { key : String 83 | , title : Maybe String 84 | } 85 | 86 | 87 | encodeRoute : NavigationRoute -> Value 88 | encodeRoute route = 89 | Encode.object <| 90 | [ ( "key", Encode.string route.key ) 91 | , ( "title", Maybe.map Encode.string route.title |> Maybe.withDefault Encode.null ) 92 | ] 93 | 94 | 95 | decodeRoute : Decode.Decoder NavigationRoute 96 | decodeRoute = 97 | Decode.map2 NavigationRoute 98 | (Decode.field "key" Decode.string) 99 | (Decode.maybe (Decode.field "title" Decode.string)) 100 | 101 | 102 | 103 | -- NAVIGATION SCENE 104 | 105 | 106 | {-| -} 107 | type alias NavigationScene = 108 | { index : Int 109 | , isActive : Bool 110 | , isStale : Bool 111 | , key : String 112 | , route : NavigationRoute 113 | } 114 | 115 | 116 | {-| -} 117 | scene : NavigationScene -> Property msg 118 | scene val = 119 | property "scene" (encodeNavigationScene val) 120 | 121 | 122 | encodeNavigationScene : NavigationScene -> Value 123 | encodeNavigationScene scene = 124 | Encode.object <| 125 | [ ( "index", Encode.int scene.index ) 126 | , ( "isActive", Encode.bool scene.isActive ) 127 | , ( "isStale", Encode.bool scene.isStale ) 128 | , ( "key", Encode.string scene.key ) 129 | , ( "route", encodeRoute scene.route ) 130 | ] 131 | 132 | 133 | decodeNavigationScene : Decode.Decoder NavigationScene 134 | decodeNavigationScene = 135 | Decode.map5 NavigationScene 136 | (Decode.field "index" Decode.int) 137 | (Decode.field "isActive" Decode.bool) 138 | (Decode.field "isStale" Decode.bool) 139 | (Decode.field "key" Decode.string) 140 | (Decode.field "route" decodeRoute) 141 | 142 | 143 | 144 | -- NAVIGATION STATE 145 | 146 | 147 | {-| -} 148 | type alias NavigationState = 149 | { index : Int 150 | , routes : List NavigationRoute 151 | } 152 | 153 | 154 | {-| -} 155 | navigationState : NavigationState -> Property msg 156 | navigationState val = 157 | property "navigationState" (encodeNavigationState val) 158 | 159 | 160 | encodeNavigationState : NavigationState -> Value 161 | encodeNavigationState state = 162 | Encode.object <| 163 | [ ( "index", Encode.int state.index ) 164 | , ( "routes", Encode.list <| List.map encodeRoute state.routes ) 165 | ] 166 | 167 | 168 | decodeNavigationState : Decode.Decoder NavigationState 169 | decodeNavigationState = 170 | Decode.map2 NavigationState 171 | (Decode.field "index" Decode.int) 172 | (Decode.field "routes" (Decode.list decodeRoute)) 173 | 174 | 175 | 176 | -- NAVIGATION SCENE RENDERER PROPS 177 | 178 | 179 | {-| -} 180 | type alias NavigationSceneRenderer = 181 | { layout : NavigationLayout 182 | , navigationState : NavigationState 183 | , position : AnimatedValue 184 | , progress : AnimatedValue 185 | , scene : NavigationScene 186 | , scenes : List NavigationScene 187 | } 188 | 189 | 190 | decodeNavigationSceneRenderer : Decode.Decoder NavigationSceneRenderer 191 | decodeNavigationSceneRenderer = 192 | Decode.map6 NavigationSceneRenderer 193 | (Decode.field "layout" decodeLayout) 194 | (Decode.field "navigationState" decodeNavigationState) 195 | (Decode.field "position" decodeAnimatedValue) 196 | (Decode.field "progress" decodeAnimatedValue) 197 | (Decode.field "scene" decodeNavigationScene) 198 | (Decode.field "scenes" (Decode.list decodeNavigationScene)) 199 | 200 | 201 | position : AnimatedValue -> Property msg 202 | position val = 203 | property "position" (encodeAnimatedValue val) 204 | 205 | 206 | progress : AnimatedValue -> Property msg 207 | progress val = 208 | property "progress" (encodeAnimatedValue val) 209 | 210 | 211 | scenes : List NavigationScene -> Property msg 212 | scenes val = 213 | property "scenes" (Encode.list <| List.map encodeNavigationScene val) 214 | 215 | 216 | {-| -} 217 | navigationSceneRendererToPropertyList : NavigationSceneRenderer -> List (Property msg) 218 | navigationSceneRendererToPropertyList props = 219 | [ layout props.layout 220 | , navigationState props.navigationState 221 | , position props.position 222 | , progress props.progress 223 | , scene props.scene 224 | , scenes props.scenes 225 | ] 226 | -------------------------------------------------------------------------------- /src/NativeUi/Properties.elm: -------------------------------------------------------------------------------- 1 | module NativeUi.Properties exposing (TextEllipsizeMode(..), PickerMode(..), ScrollViewIndicatorStyle(..), ScrollViewKeyboardDismissMode(..), ScrollViewSnapToAlignment(..), StatusBarBarStyle(..), StatusBarShowHideTransition(..), TabBarItemPositioning(..), key, ellipsizeMode, numberOfLines, selectable, suppressHighlighting, testID, allowFontScaling, accessible, adjustsFontSizeToFit, minimumFontScale, source, sourceUri, defaultSource, defaultSourceUri, showsUserLocation, followUserLocation, showsPointsOfInterest, showsCompass, zoomEnabled, rotateEnabled, pitchEnabled, scrollEnabled, maxDelta, minDelta, active, enabled, mode, prompt, refreshing, title, progressViewOffset, automaticallyAdjustContentInsets, bounces, bouncesZoom, alwaysBounceHorizontal, alwaysBounceVertical, centerContent, horizontal, indicatorStyle, directionalLockEnabled, canCancelContentTouches, keyboardDismissMode, keyboardShouldPersistTaps, maximumZoomScale, minimumZoomScale, pagingEnabled, scrollEventThrottle, scrollsToTop, showsHorizontalScrollIndicator, showsVerticalScrollIndicator, snapToInterval, snapToAlignment, removeClippedSubviews, zoomScale, scrollPerfTag, hidden, animated, translucent, barStyle, networkActivityIndicatorVisible, showHideTransition, itemPositioning, activeOpacity, gestureResponseDistance, enableGestures, statusBarHeight, underlayColor) 2 | 3 | {-| elm-native-ui Properties 4 | 5 | @docs TextEllipsizeMode(..), PickerMode(..), ScrollViewIndicatorStyle(..), ScrollViewKeyboardDismissMode(..), ScrollViewSnapToAlignment(..), StatusBarBarStyle(..), StatusBarShowHideTransition(..), TabBarItemPositioning(..), key, ellipsizeMode, numberOfLines, selectable, suppressHighlighting, testID, allowFontScaling, accessible, adjustsFontSizeToFit, minimumFontScale, source, sourceUri, defaultSource, defaultSourceUri, showsUserLocation, followUserLocation, showsPointsOfInterest, showsCompass, zoomEnabled, rotateEnabled, pitchEnabled, scrollEnabled, maxDelta, minDelta, active, enabled, mode, prompt, refreshing, title, progressViewOffset, automaticallyAdjustContentInsets, bounces, bouncesZoom, alwaysBounceHorizontal, alwaysBounceVertical, centerContent, horizontal, indicatorStyle, directionalLockEnabled, canCancelContentTouches, keyboardDismissMode, keyboardShouldPersistTaps, maximumZoomScale, minimumZoomScale, pagingEnabled, scrollEventThrottle, scrollsToTop, showsHorizontalScrollIndicator, showsVerticalScrollIndicator, snapToInterval, snapToAlignment, removeClippedSubviews, zoomScale, scrollPerfTag, hidden, animated, translucent, barStyle, networkActivityIndicatorVisible, showHideTransition, itemPositioning, activeOpacity, gestureResponseDistance, enableGestures, statusBarHeight, underlayColor 6 | 7 | -} 8 | 9 | import Json.Encode 10 | import NativeUi exposing (Property, property) 11 | 12 | 13 | {-| -} 14 | key : String -> Property msg 15 | key val = 16 | property "key" (Json.Encode.string val) 17 | 18 | 19 | {-| -} 20 | type TextEllipsizeMode 21 | = TextEllipsizeModeHead 22 | | TextEllipsizeModeMiddle 23 | | TextEllipsizeModeTail 24 | | TextEllipsizeModeClip 25 | 26 | 27 | {-| -} 28 | ellipsizeMode : TextEllipsizeMode -> Property msg 29 | ellipsizeMode val = 30 | let 31 | stringValue = 32 | case val of 33 | TextEllipsizeModeHead -> 34 | "head" 35 | 36 | TextEllipsizeModeMiddle -> 37 | "middle" 38 | 39 | TextEllipsizeModeTail -> 40 | "tail" 41 | 42 | TextEllipsizeModeClip -> 43 | "clip" 44 | 45 | jsonValue = 46 | Json.Encode.string stringValue 47 | in 48 | property "ellipsizeMode" jsonValue 49 | 50 | 51 | {-| -} 52 | numberOfLines : Float -> Property msg 53 | numberOfLines val = 54 | property "numberOfLines" (Json.Encode.float val) 55 | 56 | 57 | {-| -} 58 | selectable : Bool -> Property msg 59 | selectable val = 60 | property "selectable" (Json.Encode.bool val) 61 | 62 | 63 | {-| -} 64 | suppressHighlighting : Bool -> Property msg 65 | suppressHighlighting val = 66 | property "suppressHighlighting" (Json.Encode.bool val) 67 | 68 | 69 | {-| -} 70 | testID : String -> Property msg 71 | testID val = 72 | property "testID" (Json.Encode.string val) 73 | 74 | 75 | {-| -} 76 | allowFontScaling : Bool -> Property msg 77 | allowFontScaling val = 78 | property "allowFontScaling" (Json.Encode.bool val) 79 | 80 | 81 | {-| -} 82 | accessible : Bool -> Property msg 83 | accessible val = 84 | property "accessible" (Json.Encode.bool val) 85 | 86 | 87 | {-| -} 88 | adjustsFontSizeToFit : Bool -> Property msg 89 | adjustsFontSizeToFit val = 90 | property "adjustsFontSizeToFit" (Json.Encode.bool val) 91 | 92 | 93 | {-| -} 94 | minimumFontScale : Float -> Property msg 95 | minimumFontScale val = 96 | property "minimumFontScale" (Json.Encode.float val) 97 | 98 | 99 | {-| -} 100 | source : String -> Property msg 101 | source image = 102 | property "source" (Json.Encode.string image) 103 | 104 | 105 | {-| -} 106 | sourceUri : String -> Property msg 107 | sourceUri uri = 108 | property "source" (Json.Encode.object [ ( "uri", Json.Encode.string uri ) ]) 109 | 110 | 111 | {-| -} 112 | defaultSource : String -> Property msg 113 | defaultSource image = 114 | property "defaultSource" (Json.Encode.string image) 115 | 116 | 117 | {-| -} 118 | defaultSourceUri : String -> Property msg 119 | defaultSourceUri uri = 120 | property "defaultSource" (Json.Encode.object [ ( "uri", Json.Encode.string uri ) ]) 121 | 122 | 123 | {-| -} 124 | showsUserLocation : Bool -> Property msg 125 | showsUserLocation val = 126 | property "showsUserLocation" (Json.Encode.bool val) 127 | 128 | 129 | {-| -} 130 | followUserLocation : Bool -> Property msg 131 | followUserLocation val = 132 | property "followUserLocation" (Json.Encode.bool val) 133 | 134 | 135 | {-| -} 136 | showsPointsOfInterest : Bool -> Property msg 137 | showsPointsOfInterest val = 138 | property "showsPointsOfInterest" (Json.Encode.bool val) 139 | 140 | 141 | {-| -} 142 | showsCompass : Bool -> Property msg 143 | showsCompass val = 144 | property "showsCompass" (Json.Encode.bool val) 145 | 146 | 147 | {-| -} 148 | zoomEnabled : Bool -> Property msg 149 | zoomEnabled val = 150 | property "zoomEnabled" (Json.Encode.bool val) 151 | 152 | 153 | {-| -} 154 | rotateEnabled : Bool -> Property msg 155 | rotateEnabled val = 156 | property "rotateEnabled" (Json.Encode.bool val) 157 | 158 | 159 | {-| -} 160 | pitchEnabled : Bool -> Property msg 161 | pitchEnabled val = 162 | property "pitchEnabled" (Json.Encode.bool val) 163 | 164 | 165 | {-| -} 166 | scrollEnabled : Bool -> Property msg 167 | scrollEnabled val = 168 | property "scrollEnabled" (Json.Encode.bool val) 169 | 170 | 171 | {-| -} 172 | maxDelta : Float -> Property msg 173 | maxDelta val = 174 | property "maxDelta" (Json.Encode.float val) 175 | 176 | 177 | {-| -} 178 | minDelta : Float -> Property msg 179 | minDelta val = 180 | property "minDelta" (Json.Encode.float val) 181 | 182 | 183 | {-| -} 184 | active : Bool -> Property msg 185 | active val = 186 | property "active" (Json.Encode.bool val) 187 | 188 | 189 | {-| -} 190 | enabled : Bool -> Property msg 191 | enabled val = 192 | property "enabled" (Json.Encode.bool val) 193 | 194 | 195 | {-| -} 196 | type PickerMode 197 | = PickerModeDialog 198 | | PickerModeDropdown 199 | 200 | 201 | {-| -} 202 | mode : PickerMode -> Property msg 203 | mode val = 204 | let 205 | stringValue = 206 | case val of 207 | PickerModeDialog -> 208 | "dialog" 209 | 210 | PickerModeDropdown -> 211 | "dropdown" 212 | 213 | jsonValue = 214 | Json.Encode.string stringValue 215 | in 216 | property "mode" jsonValue 217 | 218 | 219 | {-| -} 220 | prompt : String -> Property msg 221 | prompt val = 222 | property "prompt" (Json.Encode.string val) 223 | 224 | 225 | {-| -} 226 | refreshing : Bool -> Property msg 227 | refreshing val = 228 | property "refreshing" (Json.Encode.bool val) 229 | 230 | 231 | {-| -} 232 | title : String -> Property msg 233 | title val = 234 | property "title" (Json.Encode.string val) 235 | 236 | 237 | {-| -} 238 | progressViewOffset : Float -> Property msg 239 | progressViewOffset val = 240 | property "progressViewOffset" (Json.Encode.float val) 241 | 242 | 243 | {-| -} 244 | automaticallyAdjustContentInsets : Bool -> Property msg 245 | automaticallyAdjustContentInsets val = 246 | property "automaticallyAdjustContentInsets" (Json.Encode.bool val) 247 | 248 | 249 | {-| -} 250 | bounces : Bool -> Property msg 251 | bounces val = 252 | property "bounces" (Json.Encode.bool val) 253 | 254 | 255 | {-| -} 256 | bouncesZoom : Bool -> Property msg 257 | bouncesZoom val = 258 | property "bouncesZoom" (Json.Encode.bool val) 259 | 260 | 261 | {-| -} 262 | alwaysBounceHorizontal : Bool -> Property msg 263 | alwaysBounceHorizontal val = 264 | property "alwaysBounceHorizontal" (Json.Encode.bool val) 265 | 266 | 267 | {-| -} 268 | alwaysBounceVertical : Bool -> Property msg 269 | alwaysBounceVertical val = 270 | property "alwaysBounceVertical" (Json.Encode.bool val) 271 | 272 | 273 | {-| -} 274 | centerContent : Bool -> Property msg 275 | centerContent val = 276 | property "centerContent" (Json.Encode.bool val) 277 | 278 | 279 | {-| -} 280 | horizontal : Bool -> Property msg 281 | horizontal val = 282 | property "horizontal" (Json.Encode.bool val) 283 | 284 | 285 | {-| -} 286 | type ScrollViewIndicatorStyle 287 | = ScrollViewIndicatorStyleDefault 288 | | ScrollViewIndicatorStyleBlack 289 | | ScrollViewIndicatorStyleWhite 290 | 291 | 292 | {-| -} 293 | indicatorStyle : ScrollViewIndicatorStyle -> Property msg 294 | indicatorStyle val = 295 | let 296 | stringValue = 297 | case val of 298 | ScrollViewIndicatorStyleDefault -> 299 | "default" 300 | 301 | ScrollViewIndicatorStyleBlack -> 302 | "black" 303 | 304 | ScrollViewIndicatorStyleWhite -> 305 | "white" 306 | 307 | jsonValue = 308 | Json.Encode.string stringValue 309 | in 310 | property "indicatorStyle" jsonValue 311 | 312 | 313 | {-| -} 314 | directionalLockEnabled : Bool -> Property msg 315 | directionalLockEnabled val = 316 | property "directionalLockEnabled" (Json.Encode.bool val) 317 | 318 | 319 | {-| -} 320 | canCancelContentTouches : Bool -> Property msg 321 | canCancelContentTouches val = 322 | property "canCancelContentTouches" (Json.Encode.bool val) 323 | 324 | 325 | {-| -} 326 | type ScrollViewKeyboardDismissMode 327 | = ScrollViewKeyboardDismissModeNone 328 | | ScrollViewKeyboardDismissModeInteractive 329 | | ScrollViewKeyboardDismissModeOnDrag 330 | 331 | 332 | {-| -} 333 | keyboardDismissMode : ScrollViewKeyboardDismissMode -> Property msg 334 | keyboardDismissMode val = 335 | let 336 | stringValue = 337 | case val of 338 | ScrollViewKeyboardDismissModeNone -> 339 | "none" 340 | 341 | ScrollViewKeyboardDismissModeInteractive -> 342 | "interactive" 343 | 344 | ScrollViewKeyboardDismissModeOnDrag -> 345 | "on-drag" 346 | 347 | jsonValue = 348 | Json.Encode.string stringValue 349 | in 350 | property "keyboardDismissMode" jsonValue 351 | 352 | 353 | {-| -} 354 | keyboardShouldPersistTaps : Bool -> Property msg 355 | keyboardShouldPersistTaps val = 356 | property "keyboardShouldPersistTaps" (Json.Encode.bool val) 357 | 358 | 359 | {-| -} 360 | maximumZoomScale : Float -> Property msg 361 | maximumZoomScale val = 362 | property "maximumZoomScale" (Json.Encode.float val) 363 | 364 | 365 | {-| -} 366 | minimumZoomScale : Float -> Property msg 367 | minimumZoomScale val = 368 | property "minimumZoomScale" (Json.Encode.float val) 369 | 370 | 371 | {-| -} 372 | pagingEnabled : Bool -> Property msg 373 | pagingEnabled val = 374 | property "pagingEnabled" (Json.Encode.bool val) 375 | 376 | 377 | {-| -} 378 | scrollEventThrottle : Float -> Property msg 379 | scrollEventThrottle val = 380 | property "scrollEventThrottle" (Json.Encode.float val) 381 | 382 | 383 | {-| -} 384 | scrollsToTop : Bool -> Property msg 385 | scrollsToTop val = 386 | property "scrollsToTop" (Json.Encode.bool val) 387 | 388 | 389 | {-| -} 390 | showsHorizontalScrollIndicator : Bool -> Property msg 391 | showsHorizontalScrollIndicator val = 392 | property "showsHorizontalScrollIndicator" (Json.Encode.bool val) 393 | 394 | 395 | {-| -} 396 | showsVerticalScrollIndicator : Bool -> Property msg 397 | showsVerticalScrollIndicator val = 398 | property "showsVerticalScrollIndicator" (Json.Encode.bool val) 399 | 400 | 401 | {-| -} 402 | snapToInterval : Float -> Property msg 403 | snapToInterval val = 404 | property "snapToInterval" (Json.Encode.float val) 405 | 406 | 407 | {-| -} 408 | type ScrollViewSnapToAlignment 409 | = ScrollViewSnapToAlignmentStart 410 | | ScrollViewSnapToAlignmentCenter 411 | | ScrollViewSnapToAlignmentEnd 412 | 413 | 414 | {-| -} 415 | snapToAlignment : ScrollViewSnapToAlignment -> Property msg 416 | snapToAlignment val = 417 | let 418 | stringValue = 419 | case val of 420 | ScrollViewSnapToAlignmentStart -> 421 | "start" 422 | 423 | ScrollViewSnapToAlignmentCenter -> 424 | "center" 425 | 426 | ScrollViewSnapToAlignmentEnd -> 427 | "end" 428 | 429 | jsonValue = 430 | Json.Encode.string stringValue 431 | in 432 | property "snapToAlignment" jsonValue 433 | 434 | 435 | {-| -} 436 | removeClippedSubviews : Bool -> Property msg 437 | removeClippedSubviews val = 438 | property "removeClippedSubviews" (Json.Encode.bool val) 439 | 440 | 441 | {-| -} 442 | zoomScale : Float -> Property msg 443 | zoomScale val = 444 | property "zoomScale" (Json.Encode.float val) 445 | 446 | 447 | {-| -} 448 | scrollPerfTag : String -> Property msg 449 | scrollPerfTag val = 450 | property "scrollPerfTag" (Json.Encode.string val) 451 | 452 | 453 | {-| -} 454 | hidden : Bool -> Property msg 455 | hidden val = 456 | property "hidden" (Json.Encode.bool val) 457 | 458 | 459 | {-| -} 460 | animated : Bool -> Property msg 461 | animated val = 462 | property "animated" (Json.Encode.bool val) 463 | 464 | 465 | {-| -} 466 | translucent : Bool -> Property msg 467 | translucent val = 468 | property "translucent" (Json.Encode.bool val) 469 | 470 | 471 | {-| -} 472 | type StatusBarBarStyle 473 | = StatusBarBarStyleDefault 474 | | StatusBarBarStyleLightContent 475 | 476 | 477 | {-| -} 478 | barStyle : StatusBarBarStyle -> Property msg 479 | barStyle val = 480 | let 481 | stringValue = 482 | case val of 483 | StatusBarBarStyleDefault -> 484 | "default" 485 | 486 | StatusBarBarStyleLightContent -> 487 | "light-content" 488 | 489 | jsonValue = 490 | Json.Encode.string stringValue 491 | in 492 | property "barStyle" jsonValue 493 | 494 | 495 | {-| -} 496 | networkActivityIndicatorVisible : Bool -> Property msg 497 | networkActivityIndicatorVisible val = 498 | property "networkActivityIndicatorVisible" (Json.Encode.bool val) 499 | 500 | 501 | {-| -} 502 | type StatusBarShowHideTransition 503 | = StatusBarShowHideTransitionFade 504 | | StatusBarShowHideTransitionSlide 505 | 506 | 507 | {-| -} 508 | showHideTransition : StatusBarShowHideTransition -> Property msg 509 | showHideTransition val = 510 | let 511 | stringValue = 512 | case val of 513 | StatusBarShowHideTransitionFade -> 514 | "fade" 515 | 516 | StatusBarShowHideTransitionSlide -> 517 | "slide" 518 | 519 | jsonValue = 520 | Json.Encode.string stringValue 521 | in 522 | property "showHideTransition" jsonValue 523 | 524 | 525 | {-| -} 526 | type TabBarItemPositioning 527 | = TabBarItemPositioningFill 528 | | TabBarItemPositioningCenter 529 | | TabBarItemPositioningAuto 530 | 531 | 532 | {-| -} 533 | itemPositioning : TabBarItemPositioning -> Property msg 534 | itemPositioning val = 535 | let 536 | stringValue = 537 | case val of 538 | TabBarItemPositioningFill -> 539 | "fill" 540 | 541 | TabBarItemPositioningCenter -> 542 | "center" 543 | 544 | TabBarItemPositioningAuto -> 545 | "auto" 546 | 547 | jsonValue = 548 | Json.Encode.string stringValue 549 | in 550 | property "itemPositioning" jsonValue 551 | 552 | 553 | {-| -} 554 | activeOpacity : Float -> Property msg 555 | activeOpacity val = 556 | property "activeOpacity" (Json.Encode.float val) 557 | 558 | 559 | {-| -} 560 | gestureResponseDistance : Float -> Property msg 561 | gestureResponseDistance val = 562 | property "gestureResponseDistance" (Json.Encode.float val) 563 | 564 | 565 | {-| -} 566 | enableGestures : Bool -> Property msg 567 | enableGestures val = 568 | property "enableGestures" (Json.Encode.bool val) 569 | 570 | 571 | {-| -} 572 | statusBarHeight : Float -> Property msg 573 | statusBarHeight val = 574 | property "statusBarHeight" (Json.Encode.float val) 575 | 576 | 577 | {-| -} 578 | underlayColor : String -> Property msg 579 | underlayColor val = 580 | property "underlayColor" (Json.Encode.string val) 581 | -------------------------------------------------------------------------------- /src/NativeUi/Style.elm: -------------------------------------------------------------------------------- 1 | module NativeUi.Style 2 | exposing 3 | ( Style 4 | , StyleSheet 5 | , encode 6 | , encodeSheet 7 | , color 8 | , fontFamily 9 | , fontSize 10 | , fontStyle 11 | , fontWeight 12 | , letterSpacing 13 | , lineHeight 14 | , textAlign 15 | , textAlignVertical 16 | , textDecorationLine 17 | , textDecorationStyle 18 | , textDecorationColor 19 | , writingDirection 20 | , backfaceVisibility 21 | , backgroundColor 22 | , borderColor 23 | , borderStyle 24 | , borderWidth 25 | , borderRadius 26 | , borderTopColor 27 | , borderTopWidth 28 | , borderTopLeftRadius 29 | , borderTopRightRadius 30 | , borderLeftColor 31 | , borderLeftWidth 32 | , borderBottomColor 33 | , borderBottomWidth 34 | , borderBottomLeftRadius 35 | , borderBottomRightRadius 36 | , borderRightColor 37 | , borderRightWidth 38 | , overflow 39 | , opacity 40 | , shadowColor 41 | , shadowOffset 42 | , shadowRadius 43 | , shadowOpacity 44 | , resizeMode 45 | , tintColor 46 | , alignItems 47 | , alignSelf 48 | , bottom 49 | , flex 50 | , flexDirection 51 | , flexWrap 52 | , height 53 | , justifyContent 54 | , left 55 | , margin 56 | , marginBottom 57 | , marginLeft 58 | , marginRight 59 | , marginTop 60 | , marginHorizontal 61 | , marginVertical 62 | , minHeight 63 | , maxHeight 64 | , padding 65 | , paddingLeft 66 | , paddingRight 67 | , paddingTop 68 | , paddingBottom 69 | , paddingHorizontal 70 | , paddingVertical 71 | , position 72 | , right 73 | , top 74 | , width 75 | , zIndex 76 | , Transform 77 | , defaultTransform 78 | , transform 79 | ) 80 | 81 | {-| Style your elements 82 | 83 | @docs Style, StyleSheet, encode, encodeSheet, color, fontFamily, fontSize, fontStyle, fontWeight, letterSpacing, lineHeight, textAlign, textAlignVertical, textDecorationLine, textDecorationStyle, textDecorationColor, writingDirection, backfaceVisibility, backgroundColor, borderColor, borderStyle, borderWidth, borderRadius, borderTopColor, borderTopWidth, borderTopLeftRadius, borderTopRightRadius, borderLeftColor, borderLeftWidth, borderBottomColor, borderBottomWidth, borderBottomLeftRadius, borderBottomRightRadius, borderRightColor, borderRightWidth, overflow, opacity, shadowColor, shadowOffset, shadowRadius, shadowOpacity, resizeMode, tintColor, alignItems, alignSelf, bottom, flex, flexDirection, flexWrap, height, justifyContent, left, margin, marginBottom, marginLeft, marginRight, marginTop, marginHorizontal, marginVertical, maxHeight, minHeight, padding, paddingLeft, paddingRight, paddingTop, paddingBottom, paddingHorizontal, paddingVertical, position, right, top, width, Transform, defaultTransform, transform, zIndex 84 | 85 | -} 86 | 87 | import Json.Encode 88 | 89 | 90 | type Value 91 | = StringValue String 92 | | NumberValue Float 93 | | ObjectValue (List Declaration) 94 | | ListValue (List (Maybe Declaration)) 95 | | StyleSheetValue StyleSheet 96 | 97 | 98 | stringDeclaration : String -> String -> Declaration 99 | stringDeclaration name value = 100 | ( name, StringValue value ) 101 | 102 | 103 | numberDeclaration : String -> Float -> Declaration 104 | numberDeclaration name value = 105 | ( name, NumberValue value ) 106 | 107 | 108 | objectDeclaration : String -> List Declaration -> Declaration 109 | objectDeclaration name value = 110 | ( name, ObjectValue value ) 111 | 112 | 113 | listDeclaration : String -> List (Maybe Declaration) -> Declaration 114 | listDeclaration name value = 115 | ( name, ListValue value ) 116 | 117 | 118 | stringStyle : String -> String -> Style 119 | stringStyle name value = 120 | StringStyle (stringDeclaration name value) 121 | 122 | 123 | numberStyle : String -> Float -> Style 124 | numberStyle name value = 125 | NumberStyle (numberDeclaration name value) 126 | 127 | 128 | objectStyle : String -> List Declaration -> Style 129 | objectStyle name list = 130 | ObjectStyle (objectDeclaration name list) 131 | 132 | 133 | listStyle : String -> List (Maybe Declaration) -> Style 134 | listStyle name list = 135 | ListStyle (listDeclaration name list) 136 | 137 | 138 | type alias Declaration = 139 | ( String, Value ) 140 | 141 | 142 | {-| -} 143 | type Style 144 | = StringStyle Declaration 145 | | NumberStyle Declaration 146 | | ObjectStyle Declaration 147 | | ListStyle Declaration 148 | 149 | 150 | {-| -} 151 | type StyleSheet 152 | = StyleSheet Int 153 | 154 | 155 | encodeValue : Value -> Json.Encode.Value 156 | encodeValue value = 157 | case value of 158 | NumberValue float -> 159 | Json.Encode.float float 160 | 161 | StringValue string -> 162 | Json.Encode.string string 163 | 164 | ObjectValue list -> 165 | Json.Encode.object (List.map encodeDeclaration list) 166 | 167 | ListValue list -> 168 | Json.Encode.list (List.map encodeObject (List.filterMap identity list)) 169 | 170 | StyleSheetValue (StyleSheet styleSheet) -> 171 | Json.Encode.int styleSheet 172 | 173 | 174 | encodeDeclaration : ( String, Value ) -> ( String, Json.Encode.Value ) 175 | encodeDeclaration ( name, value ) = 176 | ( name, encodeValue value ) 177 | 178 | 179 | encodeObject : ( String, Value ) -> Json.Encode.Value 180 | encodeObject ( name, value ) = 181 | Json.Encode.object [ ( name, (encodeValue value) ) ] 182 | 183 | 184 | toJsonProperty : Style -> ( String, Json.Encode.Value ) 185 | toJsonProperty style = 186 | case style of 187 | StringStyle ( name, value ) -> 188 | ( name, encodeValue value ) 189 | 190 | NumberStyle ( name, value ) -> 191 | ( name, encodeValue value ) 192 | 193 | ObjectStyle ( name, value ) -> 194 | ( name, encodeValue value ) 195 | 196 | ListStyle ( name, value ) -> 197 | ( name, encodeValue value ) 198 | 199 | 200 | {-| -} 201 | encode : List Style -> Json.Encode.Value 202 | encode styles = 203 | styles 204 | |> List.map toJsonProperty 205 | |> Json.Encode.object 206 | 207 | 208 | {-| -} 209 | encodeSheet : List StyleSheet -> Json.Encode.Value 210 | encodeSheet styleSheets = 211 | styleSheets 212 | |> List.map (encodeValue << StyleSheetValue) 213 | |> Json.Encode.list 214 | 215 | 216 | 217 | -- Text Styles 218 | 219 | 220 | {-| -} 221 | color : String -> Style 222 | color = 223 | stringStyle "color" 224 | 225 | 226 | {-| -} 227 | fontFamily : String -> Style 228 | fontFamily = 229 | stringStyle "fontFamily" 230 | 231 | 232 | {-| -} 233 | fontSize : Float -> Style 234 | fontSize = 235 | numberStyle "fontSize" 236 | 237 | 238 | {-| enum('normal', 'italic') 239 | -} 240 | fontStyle : String -> Style 241 | fontStyle = 242 | stringStyle "fontStyle" 243 | 244 | 245 | {-| enum("normal", 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900') 246 | -} 247 | fontWeight : String -> Style 248 | fontWeight = 249 | stringStyle "fontWeight" 250 | 251 | 252 | {-| -} 253 | letterSpacing : Float -> Style 254 | letterSpacing = 255 | numberStyle "letterSpacing" 256 | 257 | 258 | {-| -} 259 | lineHeight : Float -> Style 260 | lineHeight = 261 | numberStyle "lineHeight" 262 | 263 | 264 | {-| enum("auto", 'left', 'right', 'center', 'justify') 265 | -} 266 | textAlign : String -> Style 267 | textAlign = 268 | stringStyle "textAlign" 269 | 270 | 271 | {-| enum("auto", 'top', 'bottom', 'center') 272 | -} 273 | textAlignVertical : String -> Style 274 | textAlignVertical = 275 | stringStyle "textAlignVertical" 276 | 277 | 278 | {-| enum("none", 'underline', 'line-through', 'underline line-through') 279 | -} 280 | textDecorationLine : String -> Style 281 | textDecorationLine = 282 | stringStyle "textDecorationLine" 283 | 284 | 285 | {-| enum("solid", 'double', 'dotted', 'dashed') 286 | -} 287 | textDecorationStyle : String -> Style 288 | textDecorationStyle = 289 | stringStyle "textDecorationStyle" 290 | 291 | 292 | {-| -} 293 | textDecorationColor : String -> Style 294 | textDecorationColor = 295 | stringStyle "textDecorationColor" 296 | 297 | 298 | {-| enum("auto", 'ltr', 'rtl') 299 | -} 300 | writingDirection : String -> Style 301 | writingDirection = 302 | stringStyle "writingDirection" 303 | 304 | 305 | 306 | --View Styles 307 | 308 | 309 | {-| enum('visible', 'hidden') 310 | -} 311 | backfaceVisibility : String -> Style 312 | backfaceVisibility = 313 | stringStyle "backfaceVisibility" 314 | 315 | 316 | {-| -} 317 | backgroundColor : String -> Style 318 | backgroundColor = 319 | stringStyle "backgroundColor" 320 | 321 | 322 | {-| -} 323 | borderColor : String -> Style 324 | borderColor = 325 | stringStyle "borderColor" 326 | 327 | 328 | {-| -} 329 | borderTopColor : String -> Style 330 | borderTopColor = 331 | stringStyle "borderTopColor" 332 | 333 | 334 | {-| -} 335 | borderRightColor : String -> Style 336 | borderRightColor = 337 | stringStyle "borderRightColor" 338 | 339 | 340 | {-| -} 341 | borderBottomColor : String -> Style 342 | borderBottomColor = 343 | stringStyle "borderBottomColor" 344 | 345 | 346 | {-| -} 347 | borderLeftColor : String -> Style 348 | borderLeftColor = 349 | stringStyle "borderLeftColor" 350 | 351 | 352 | {-| -} 353 | borderRadius : Float -> Style 354 | borderRadius = 355 | numberStyle "borderRadius" 356 | 357 | 358 | {-| -} 359 | borderTopLeftRadius : Float -> Style 360 | borderTopLeftRadius = 361 | numberStyle "borderTopLeftRadius" 362 | 363 | 364 | {-| -} 365 | borderTopRightRadius : Float -> Style 366 | borderTopRightRadius = 367 | numberStyle "borderTopRightRadius" 368 | 369 | 370 | {-| -} 371 | borderBottomLeftRadius : Float -> Style 372 | borderBottomLeftRadius = 373 | numberStyle "borderBottomLeftRadius" 374 | 375 | 376 | {-| -} 377 | borderBottomRightRadius : Float -> Style 378 | borderBottomRightRadius = 379 | numberStyle "borderBottomRightRadius" 380 | 381 | 382 | {-| enum('solid', 'dotted', 'dashed') 383 | -} 384 | borderStyle : String -> Style 385 | borderStyle = 386 | stringStyle "borderStyle" 387 | 388 | 389 | {-| -} 390 | borderWidth : Float -> Style 391 | borderWidth = 392 | numberStyle "borderWidth" 393 | 394 | 395 | {-| -} 396 | borderTopWidth : Float -> Style 397 | borderTopWidth = 398 | numberStyle "borderTopWidth" 399 | 400 | 401 | {-| -} 402 | borderRightWidth : Float -> Style 403 | borderRightWidth = 404 | numberStyle "borderRightWidth" 405 | 406 | 407 | {-| -} 408 | borderBottomWidth : Float -> Style 409 | borderBottomWidth = 410 | numberStyle "borderBottomWidth" 411 | 412 | 413 | {-| -} 414 | borderLeftWidth : Float -> Style 415 | borderLeftWidth = 416 | numberStyle "borderLeftWidth" 417 | 418 | 419 | {-| -} 420 | opacity : Float -> Style 421 | opacity = 422 | numberStyle "opacity" 423 | 424 | 425 | {-| enum('visible', 'hidden') 426 | -} 427 | overflow : String -> Style 428 | overflow = 429 | stringStyle "overflow" 430 | 431 | 432 | {-| -} 433 | shadowColor : String -> Style 434 | shadowColor = 435 | stringStyle "shadowColor" 436 | 437 | 438 | {-| -} 439 | shadowOffset : Float -> Float -> Style 440 | shadowOffset width height = 441 | objectStyle "shadowOffset" 442 | [ numberDeclaration "width" width 443 | , numberDeclaration "height" height 444 | ] 445 | 446 | 447 | {-| -} 448 | shadowOpacity : Float -> Style 449 | shadowOpacity = 450 | numberStyle "shadowOpacity" 451 | 452 | 453 | {-| -} 454 | shadowRadius : Float -> Style 455 | shadowRadius = 456 | numberStyle "shadowRadius" 457 | 458 | 459 | 460 | --Image Styles 461 | 462 | 463 | {-| enum('cover', 'contain', 'stretch') 464 | -} 465 | resizeMode : String -> Style 466 | resizeMode = 467 | stringStyle "resizeMode" 468 | 469 | 470 | {-| -} 471 | tintColor : String -> Style 472 | tintColor = 473 | stringStyle "tintColor" 474 | 475 | 476 | 477 | --Flex Styles 478 | 479 | 480 | {-| enum('flex-start', 'flex-end', 'center', 'stretch') 481 | -} 482 | alignItems : String -> Style 483 | alignItems = 484 | stringStyle "alignItems" 485 | 486 | 487 | {-| enum('auto', 'flex-start', 'flex-end', 'center', 'stretch') 488 | -} 489 | alignSelf : String -> Style 490 | alignSelf = 491 | stringStyle "alignSelf" 492 | 493 | 494 | {-| -} 495 | bottom : Float -> Style 496 | bottom = 497 | numberStyle "bottom" 498 | 499 | 500 | {-| -} 501 | flex : Float -> Style 502 | flex = 503 | numberStyle "flex" 504 | 505 | 506 | {-| enum('row', 'column') 507 | -} 508 | flexDirection : String -> Style 509 | flexDirection = 510 | stringStyle "flexDirection" 511 | 512 | 513 | {-| enum('wrap', 'nowrap') 514 | -} 515 | flexWrap : String -> Style 516 | flexWrap = 517 | stringStyle "flexWrap" 518 | 519 | 520 | {-| -} 521 | height : Float -> Style 522 | height = 523 | numberStyle "height" 524 | 525 | 526 | {-| enum('flex-start', 'flex-end', 'center', 'space-between', 'space-around') 527 | -} 528 | justifyContent : String -> Style 529 | justifyContent = 530 | stringStyle "justifyContent" 531 | 532 | 533 | {-| -} 534 | left : Float -> Style 535 | left = 536 | numberStyle "left" 537 | 538 | 539 | {-| -} 540 | margin : Float -> Style 541 | margin = 542 | numberStyle "margin" 543 | 544 | 545 | {-| -} 546 | marginBottom : Float -> Style 547 | marginBottom = 548 | numberStyle "marginBottom" 549 | 550 | 551 | {-| -} 552 | marginHorizontal : Float -> Style 553 | marginHorizontal = 554 | numberStyle "marginHorizontal" 555 | 556 | 557 | {-| -} 558 | marginLeft : Float -> Style 559 | marginLeft = 560 | numberStyle "marginLeft" 561 | 562 | 563 | {-| -} 564 | marginRight : Float -> Style 565 | marginRight = 566 | numberStyle "marginRight" 567 | 568 | 569 | {-| -} 570 | marginTop : Float -> Style 571 | marginTop = 572 | numberStyle "marginTop" 573 | 574 | 575 | {-| -} 576 | marginVertical : Float -> Style 577 | marginVertical = 578 | numberStyle "marginVertical" 579 | 580 | 581 | {-| -} 582 | minHeight : Float -> Style 583 | minHeight = 584 | numberStyle "minHeight" 585 | 586 | 587 | {-| -} 588 | maxHeight : Float -> Style 589 | maxHeight = 590 | numberStyle "maxHeight" 591 | 592 | 593 | {-| -} 594 | padding : Float -> Style 595 | padding = 596 | numberStyle "padding" 597 | 598 | 599 | {-| -} 600 | paddingBottom : Float -> Style 601 | paddingBottom = 602 | numberStyle "paddingBottom" 603 | 604 | 605 | {-| -} 606 | paddingHorizontal : Float -> Style 607 | paddingHorizontal = 608 | numberStyle "paddingHorizontal" 609 | 610 | 611 | {-| -} 612 | paddingLeft : Float -> Style 613 | paddingLeft = 614 | numberStyle "paddingLeft" 615 | 616 | 617 | {-| -} 618 | paddingRight : Float -> Style 619 | paddingRight = 620 | numberStyle "paddingRight" 621 | 622 | 623 | {-| -} 624 | paddingTop : Float -> Style 625 | paddingTop = 626 | numberStyle "paddingTop" 627 | 628 | 629 | {-| -} 630 | paddingVertical : Float -> Style 631 | paddingVertical = 632 | numberStyle "paddingVertical" 633 | 634 | 635 | {-| enum('absolute', 'relative') 636 | -} 637 | position : String -> Style 638 | position = 639 | stringStyle "position" 640 | 641 | 642 | {-| -} 643 | right : Float -> Style 644 | right = 645 | numberStyle "right" 646 | 647 | 648 | {-| -} 649 | top : Float -> Style 650 | top = 651 | numberStyle "top" 652 | 653 | 654 | {-| -} 655 | width : Float -> Style 656 | width = 657 | numberStyle "width" 658 | 659 | 660 | {-| -} 661 | zIndex : Float -> Style 662 | zIndex = 663 | numberStyle "zIndex" 664 | 665 | 666 | 667 | --Transform Styles 668 | 669 | 670 | {-| -} 671 | type alias Transform = 672 | { perspective : Maybe Float 673 | , rotate : Maybe String 674 | , rotateX : Maybe String 675 | , rotateY : Maybe String 676 | , rotateZ : Maybe String 677 | , scale : Maybe Float 678 | , scaleX : Maybe Float 679 | , scaleY : Maybe Float 680 | , translateX : Maybe Float 681 | , translateY : Maybe Float 682 | , skewX : Maybe String 683 | , skewY : Maybe String 684 | } 685 | 686 | 687 | {-| -} 688 | defaultTransform : Transform 689 | defaultTransform = 690 | { perspective = Nothing 691 | , rotate = Nothing 692 | , rotateX = Nothing 693 | , rotateY = Nothing 694 | , rotateZ = Nothing 695 | , scale = Nothing 696 | , scaleX = Nothing 697 | , scaleY = Nothing 698 | , translateX = Nothing 699 | , translateY = Nothing 700 | , skewX = Nothing 701 | , skewY = Nothing 702 | } 703 | 704 | 705 | {-| -} 706 | transform : Transform -> Style 707 | transform options = 708 | listStyle "transform" 709 | [ Maybe.map (numberDeclaration "perspective") options.perspective 710 | , Maybe.map (stringDeclaration "rotate") options.rotate 711 | , Maybe.map (stringDeclaration "rotateX") options.rotateX 712 | , Maybe.map (stringDeclaration "rotateY") options.rotateY 713 | , Maybe.map (stringDeclaration "rotateZ") options.rotateZ 714 | , Maybe.map (numberDeclaration "scale") options.scale 715 | , Maybe.map (numberDeclaration "scaleX") options.scaleX 716 | , Maybe.map (numberDeclaration "scaleY") options.scaleY 717 | , Maybe.map (numberDeclaration "translateX") options.translateX 718 | , Maybe.map (numberDeclaration "translateY") options.translateY 719 | , Maybe.map (stringDeclaration "skewX") options.skewX 720 | , Maybe.map (stringDeclaration "skewY") options.skewY 721 | ] 722 | 723 | 724 | 725 | --TODO 726 | --transformMatrix : TransformMatrixPropType -> Style 727 | --------------------------------------------------------------------------------