├── .gitignore ├── bsconfig.json ├── CHANGELOG.md ├── LICENSE ├── package.json ├── README.md ├── src ├── Downshift.rei └── Downshift.re ├── yarn.lock └── examples └── reason_downshift.re /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | *.log 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log* 6 | 7 | # Dependency directories 8 | node_modules/ 9 | 10 | # Optional npm cache directory 11 | .npm 12 | 13 | # Optional eslint cache 14 | .eslintcache 15 | 16 | # Yarn Integrity file 17 | .yarn-integrity 18 | 19 | # BuckleScript 20 | .merlin 21 | .bsb.lock 22 | lib 23 | *.bs.js -------------------------------------------------------------------------------- /bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bs-downshift", 3 | "sources": [ 4 | { 5 | "dir": "src", 6 | "public": "all" 7 | }, 8 | { 9 | "dir": "examples", 10 | "type": "dev" 11 | } 12 | ], 13 | "bs-dependencies": ["reason-react"], 14 | "bsc-flags": ["-bs-super-errors"], 15 | "reason": { 16 | "react-jsx": 2 17 | }, 18 | "refmt": 3, 19 | "package-specs": [ 20 | { 21 | "module": "es6", 22 | "in-source": true 23 | } 24 | ], 25 | "suffix": ".bs.js" 26 | } 27 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # v1.0.1 2 | 3 | ### Bug fixes 4 | 5 | * fix: external `extGetItemProps` should bind to `getItemProps` ([#9] by @kgoggin) 6 | 7 | [#9]: https://github.com/reasonml-community/bs-downshift/pull/9 8 | 9 | # v1.0.0 10 | 11 | ### Maintenance 12 | 13 | * upgrade to `bs-platform` version `3` 14 | * upgrade to `reason-react` version `0.4` 15 | 16 | # v0.2.0 17 | 18 | Compatible with `downshift` [v1.25.0](https://github.com/paypal/downshift/releases/tag/v1.25.0). 19 | 20 | # v0.1.1 21 | 22 | ### Bug fixes 23 | 24 | * correctly bind `index` in `getItemProps` ([#5] by @kgoggin) 25 | 26 | [#5]: https://github.com/reasonml-community/bs-downshift/pull/5 27 | 28 | # v0.1.0 29 | 30 | Compatible with `downshift` [v1.22.5](https://github.com/paypal/downshift/releases/tag/v1.22.5). 31 | 32 | * initial release and bindings 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018, Nicola Molinari 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bs-downshift", 3 | "version": "1.0.1", 4 | "description": "BuckleScript bindings for Downshift", 5 | "license": "MIT", 6 | "author": "Nicola Molinari (http://emmenko.org)", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/reasonml-community/bs-downshift.git" 10 | }, 11 | "homepage": "https://github.com/reasonml-community/bs-downshift#readme", 12 | "bugs": "https://github.com/reasonml-community/bs-downshift/issues", 13 | "scripts": { 14 | "build": "bsb -make-world", 15 | "start": "bsb -make-world -w", 16 | "clean": "bsb -clean-world", 17 | "test": "echo \"Error: no test specified\" && exit 1" 18 | }, 19 | "dependencies": { 20 | "downshift": "1.25.0" 21 | }, 22 | "peerDependencies": { 23 | "bs-platform": "^3.0.0" 24 | }, 25 | "devDependencies": { 26 | "bs-platform": "^3.0.0", 27 | "downshift": "1.25.0", 28 | "prop-types": "^15.6.1", 29 | "react": "^16.3.2", 30 | "reason-react": "^0.4.1" 31 | }, 32 | "keywords": [ 33 | "BuckleScript", 34 | "Downshift", 35 | "React", 36 | "Reason" 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `bs-downshift` 2 | 3 | [BuckleScript](https://github.com/bucklescript/bucklescript) bindings for [Downshift](https://github.com/paypal/downshift) 4 | 5 | [![npm](https://img.shields.io/npm/v/bs-downshift.svg)](https://npmjs.org/bs-downshift) 6 | [![Issues](https://img.shields.io/github/issues/reasonml-community/bs-downshift.svg)](https://github.com/reasonml-community/bs-downshift/issues) 7 | [![Dependencies](https://img.shields.io/david/peer/reasonml-community/bs-downshift.svg)](https://github.com/reasonml-community/bs-downshift/blob/master/package.json) 8 | [![Issues](https://img.shields.io/github/issues/reasonml-community/bs-downshift.svg)](https://github.com/reasonml-community/bs-downshift/issues) 9 | [![Last Commit](https://img.shields.io/github/last-commit/reasonml-community/bs-downshift.svg)]() 10 | 11 | ## Demo 12 | 13 | _Coming soon_ 14 | 15 | ## Compatibility with original `downshift` library 16 | 17 | Compatible with `downshift` [v1.25.0](https://github.com/paypal/downshift/releases/tag/v1.25.0). 18 | 19 | > Using newer versions of `downshift` might break the bindings as the API might have changed. Use at your own risk. 20 | 21 | ## Install and setup 22 | 23 | #### yarn 24 | 25 | ```bash 26 | $ yarn add bs-downshift 27 | ``` 28 | 29 | #### bsconfig 30 | 31 | Add `bs-downshift` to your `bs-dependencies`: **bsconfig.json** 32 | 33 | ```json 34 | "bs-dependencies": [ 35 | "bs-downshift", 36 | "reason-react" 37 | ] 38 | ``` 39 | 40 | ## Usage 41 | 42 | See [examples](./examples) folder. 43 | 44 | ### Usage of `render` function 45 | 46 | The `render` prop is a function that passes an object which contains _methods_ and _values_. 47 | 48 | In ReasonML, this object is represented as a module called `ControllerStateAndHelpers`. 49 | 50 | To make use of the _methods_ and _values_ within that module, you need to call it by passing the argument type (which is of type `ControllerStateAndHelpers.t` and does effectively the binding to the JS object method) like: 51 | 52 | ```js 53 | render=( 54 | t => { 55 | ControllerStateAndHelpers.toggleMenu(t, ()); 56 | /* ... */ 57 | } 58 | ) 59 | ``` 60 | 61 | You can see that in the [examples](./examples) folder. 62 | -------------------------------------------------------------------------------- /src/Downshift.rei: -------------------------------------------------------------------------------- 1 | type any; 2 | 3 | external toAny : 'a => any = "%identity"; 4 | 5 | external toJsObj : any => Js.t({..}) = "%identity"; 6 | 7 | type item = any; 8 | 9 | type otherStateToSet = Js.Dict.t(string); 10 | 11 | type cb = unit => unit; 12 | 13 | type itemToString = item => string; 14 | 15 | type selectedItemChanged = (item, item) => bool; 16 | 17 | type a11yStatusMessageOptions = { 18 | . 19 | "highlightedIndex": option(int), 20 | "highlightedValue": any, 21 | "inputValue": string, 22 | "isOpen": bool, 23 | "itemToString": itemToString, 24 | "previousResultCount": int, 25 | "resultCount": int, 26 | "selectedItem": item 27 | }; 28 | 29 | type getA11yStatusMessage = a11yStatusMessageOptions => string; 30 | 31 | type rootPropsOptions = {. "refKey": string}; 32 | 33 | type itemPropsOptions = { 34 | . 35 | "index": Js.Nullable.t(int), 36 | "item": any 37 | }; 38 | 39 | module ControllerStateAndHelpers: { 40 | type t; 41 | [@bs.send] external getRootProps : (t, rootPropsOptions) => any = ""; 42 | [@bs.send] 43 | external getButtonProps : 44 | (t, ~options: ReactDOMRe.reactDOMProps=?, unit) => any = 45 | ""; 46 | [@bs.send] 47 | external getLabelProps : 48 | (t, ~options: ReactDOMRe.reactDOMProps=?, unit) => any = 49 | ""; 50 | [@bs.send] 51 | external getInputProps : 52 | (t, ~options: ReactDOMRe.reactDOMProps=?, unit) => any = 53 | ""; 54 | [@bs.send] external extGetItemProps : (t, itemPropsOptions) => any = "getItemProps"; 55 | [@bs.send] 56 | external itemPropsOptions : (t, ~options: itemPropsOptions) => any = ""; 57 | [@bs.send] external openMenu : (t, ~cb: cb=?, unit) => unit = ""; 58 | [@bs.send] external closeMenu : (t, ~cb: cb=?, unit) => unit = ""; 59 | [@bs.send] 60 | external toggleMenu : 61 | (t, ~otherStateToSet: otherStateToSet=?, ~cb: cb=?, unit) => unit = 62 | ""; 63 | [@bs.send] 64 | external reset : 65 | (t, ~otherStateToSet: otherStateToSet=?, ~cb: cb=?, unit) => unit = 66 | ""; 67 | [@bs.send] 68 | external selectItem : 69 | (t, ~item: item, ~otherStateToSet: otherStateToSet=?, ~cb: cb=?, unit) => 70 | unit = 71 | ""; 72 | [@bs.send] 73 | external selectItemAtIndex : 74 | (t, ~index: int, ~otherStateToSet: otherStateToSet=?, ~cb: cb=?, unit) => 75 | unit = 76 | ""; 77 | [@bs.send] 78 | external selectHighlightedItem : 79 | (t, ~otherStateToSet: otherStateToSet=?, ~cb: cb=?, unit) => unit = 80 | ""; 81 | [@bs.send] 82 | external setHighlightedIndex : 83 | (t, ~index: int, ~otherStateToSet: otherStateToSet=?, ~cb: cb=?, unit) => 84 | unit = 85 | ""; 86 | [@bs.send] external clearSelection : (t, ~cb: cb=?, unit) => unit = ""; 87 | [@bs.send] external clearItems : (t, unit) => unit = ""; 88 | [@bs.send] external itemToString : (t, item) => unit = ""; 89 | [@bs.get] external highlightedIndex : t => Js.Nullable.t(int) = ""; 90 | [@bs.get] external inputValue : t => Js.Nullable.t(string) = ""; 91 | [@bs.get] external isOpen : t => bool = ""; 92 | [@bs.get] external selectedItem : t => item = ""; 93 | let getItemProps: (t, ~item: any, ~index: int=?, unit) => any; 94 | }; 95 | 96 | type stateChangeOptions = { 97 | . 98 | "type": string, 99 | "highlightedIndex": int, 100 | "inputValue": string, 101 | "isOpen": bool, 102 | "selectedItem": item 103 | }; 104 | 105 | type onChange = (any, ControllerStateAndHelpers.t) => unit; 106 | 107 | type onSelect = (any, ControllerStateAndHelpers.t) => unit; 108 | 109 | type onStateChange = (stateChangeOptions, ControllerStateAndHelpers.t) => unit; 110 | 111 | type onInputValueChange = (string, ControllerStateAndHelpers.t) => unit; 112 | 113 | type renderFunc = ControllerStateAndHelpers.t => ReasonReact.reactElement; 114 | 115 | let make: 116 | ( 117 | ~defaultSelectedItem: any=?, 118 | ~defaultHighlightedIndex: int=?, 119 | ~defaultInputValue: string=?, 120 | ~defaultIsOpen: bool=?, 121 | ~itemToString: itemToString=?, 122 | ~selectedItemChanged: selectedItemChanged=?, 123 | ~getA11yStatusMessage: getA11yStatusMessage=?, 124 | ~onChange: onChange=?, 125 | ~onSelect: onSelect=?, 126 | ~onStateChange: onStateChange=?, 127 | ~onInputValueChange: onInputValueChange=?, 128 | ~itemCount: int=?, 129 | ~highlightedIndex: int=?, 130 | ~inputValue: string=?, 131 | ~isOpen: bool=?, 132 | ~selectedItem: any=?, 133 | ~render: renderFunc, 134 | ~id: string=?, 135 | ~environment: Dom.window=?, 136 | ~onOuterClick: unit => unit=?, 137 | 'a 138 | ) => 139 | ReasonReact.component( 140 | ReasonReact.stateless, 141 | ReasonReact.noRetainedProps, 142 | ReasonReact.actionless 143 | ); 144 | 145 | [@bs.module "downshift"] 146 | external resetIdCounter : unit => unit = "resetIdCounter"; -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | asap@~2.0.3: 6 | version "2.0.6" 7 | resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" 8 | 9 | bs-platform@^3.0.0: 10 | version "3.0.0" 11 | resolved "https://registry.yarnpkg.com/bs-platform/-/bs-platform-3.0.0.tgz#38f200730db52fdea37819376b6ac3dfb20244c0" 12 | 13 | core-js@^1.0.0: 14 | version "1.2.7" 15 | resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" 16 | 17 | downshift@1.25.0: 18 | version "1.25.0" 19 | resolved "https://registry.yarnpkg.com/downshift/-/downshift-1.25.0.tgz#7f6e2dda4aa5ddbb2932401bd61e7b741e92c02e" 20 | 21 | encoding@^0.1.11: 22 | version "0.1.12" 23 | resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" 24 | dependencies: 25 | iconv-lite "~0.4.13" 26 | 27 | fbjs@^0.8.16: 28 | version "0.8.16" 29 | resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db" 30 | dependencies: 31 | core-js "^1.0.0" 32 | isomorphic-fetch "^2.1.1" 33 | loose-envify "^1.0.0" 34 | object-assign "^4.1.0" 35 | promise "^7.1.1" 36 | setimmediate "^1.0.5" 37 | ua-parser-js "^0.7.9" 38 | 39 | iconv-lite@~0.4.13: 40 | version "0.4.19" 41 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" 42 | 43 | is-stream@^1.0.1: 44 | version "1.1.0" 45 | resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" 46 | 47 | isomorphic-fetch@^2.1.1: 48 | version "2.2.1" 49 | resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" 50 | dependencies: 51 | node-fetch "^1.0.1" 52 | whatwg-fetch ">=0.10.0" 53 | 54 | js-tokens@^3.0.0: 55 | version "3.0.2" 56 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" 57 | 58 | loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1: 59 | version "1.3.1" 60 | resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" 61 | dependencies: 62 | js-tokens "^3.0.0" 63 | 64 | node-fetch@^1.0.1: 65 | version "1.7.3" 66 | resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" 67 | dependencies: 68 | encoding "^0.1.11" 69 | is-stream "^1.0.1" 70 | 71 | object-assign@^4.1.0, object-assign@^4.1.1: 72 | version "4.1.1" 73 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 74 | 75 | promise@^7.1.1: 76 | version "7.3.1" 77 | resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" 78 | dependencies: 79 | asap "~2.0.3" 80 | 81 | prop-types@^15.6.0: 82 | version "15.6.0" 83 | resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.0.tgz#ceaf083022fc46b4a35f69e13ef75aed0d639856" 84 | dependencies: 85 | fbjs "^0.8.16" 86 | loose-envify "^1.3.1" 87 | object-assign "^4.1.1" 88 | 89 | prop-types@^15.6.1: 90 | version "15.6.1" 91 | resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.1.tgz#36644453564255ddda391191fb3a125cbdf654ca" 92 | dependencies: 93 | fbjs "^0.8.16" 94 | loose-envify "^1.3.1" 95 | object-assign "^4.1.1" 96 | 97 | "react-dom@>=15.0.0 || >=16.0.0": 98 | version "16.2.0" 99 | resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.2.0.tgz#69003178601c0ca19b709b33a83369fe6124c044" 100 | dependencies: 101 | fbjs "^0.8.16" 102 | loose-envify "^1.1.0" 103 | object-assign "^4.1.1" 104 | prop-types "^15.6.0" 105 | 106 | "react@>=15.0.0 || >=16.0.0": 107 | version "16.2.0" 108 | resolved "https://registry.yarnpkg.com/react/-/react-16.2.0.tgz#a31bd2dab89bff65d42134fa187f24d054c273ba" 109 | dependencies: 110 | fbjs "^0.8.16" 111 | loose-envify "^1.1.0" 112 | object-assign "^4.1.1" 113 | prop-types "^15.6.0" 114 | 115 | react@^16.3.2: 116 | version "16.3.2" 117 | resolved "https://registry.yarnpkg.com/react/-/react-16.3.2.tgz#fdc8420398533a1e58872f59091b272ce2f91ea9" 118 | dependencies: 119 | fbjs "^0.8.16" 120 | loose-envify "^1.1.0" 121 | object-assign "^4.1.1" 122 | prop-types "^15.6.0" 123 | 124 | reason-react@^0.4.1: 125 | version "0.4.1" 126 | resolved "https://registry.yarnpkg.com/reason-react/-/reason-react-0.4.1.tgz#f1c8bf0d116be6bf43109912ee4d95e58dd7c64f" 127 | dependencies: 128 | react ">=15.0.0 || >=16.0.0" 129 | react-dom ">=15.0.0 || >=16.0.0" 130 | 131 | setimmediate@^1.0.5: 132 | version "1.0.5" 133 | resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" 134 | 135 | ua-parser-js@^0.7.9: 136 | version "0.7.17" 137 | resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.17.tgz#e9ec5f9498b9ec910e7ae3ac626a805c4d09ecac" 138 | 139 | whatwg-fetch@>=0.10.0: 140 | version "2.0.3" 141 | resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84" 142 | -------------------------------------------------------------------------------- /examples/reason_downshift.re: -------------------------------------------------------------------------------- 1 | type arrayOfItems = array(string); 2 | 3 | module BasicAutocomplete = { 4 | let component = ReasonReact.statelessComponent("BasicAutocomplete"); 5 | let make = (~items: arrayOfItems, ~onChange, _children) => { 6 | ...component, 7 | render: _self => 8 | 12 |
13 | ( 14 | /* 15 | NOTE: spreading props is discouraged in ReasonReact! 16 | https://reasonml.github.io/reason-react/docs/en/props-spread.html 17 | */ 18 | ReasonReact.cloneElement( 19 | , 20 | /* The method `getInputProps` returns a type `any`, we simply 21 | cast it to a `{..}` type. */ 22 | ~props= 23 | Downshift.toJsObj( 24 | Downshift.ControllerStateAndHelpers.getInputProps( 25 | t, 26 | ~options= 27 | ReactDOMRe.props( 28 | ~placeholder="Favorite color ?", 29 | (), 30 | ), 31 | (), 32 | ), 33 | ), 34 | [||], 35 | ) 36 | ) 37 | ( 38 | if (Downshift.ControllerStateAndHelpers.isOpen(t)) { 39 |
43 | ( 44 | ReasonReact.array( 45 | { 46 | let index = ref(0); 47 | /* NOTE: I'm using `Array.fold_left` because 48 | `Array.filter` is not supported yet. 49 | However we need to track the `index` ourselves. */ 50 | Array.fold_left( 51 | (filteredArray, item) => { 52 | index := index^ + 1; 53 | let inputValue = 54 | Js.Nullable.toOption( 55 | Downshift.ControllerStateAndHelpers.inputValue( 56 | t, 57 | ), 58 | ); 59 | let shouldFilterItem = 60 | switch (inputValue) { 61 | | None => true 62 | | Some(v) => 63 | Js.String.includes( 64 | String.lowercase(v), 65 | String.lowercase(item), 66 | ) 67 | }; 68 | shouldFilterItem ? 69 | filteredArray : 70 | { 71 | let backgroundColor = 72 | if (Downshift.ControllerStateAndHelpers.highlightedIndex( 73 | t, 74 | ) 75 | == Js.Nullable.return(index^)) { 76 | "gray"; 77 | } else { 78 | "white"; 79 | }; 80 | let fontWeight = 81 | if (Downshift.ControllerStateAndHelpers.selectedItem( 82 | t, 83 | ) 84 | == Downshift.toAny(item)) { 85 | "bold"; 86 | } else { 87 | "normal"; 88 | }; 89 | let elem = 90 | ReasonReact.cloneElement( 91 |
, 101 | ~props= 102 | Downshift.toJsObj( 103 | Downshift.ControllerStateAndHelpers.getItemProps( 104 | t, 105 | ~item=Downshift.toAny(item), 106 | (), 107 | ), 108 | ), 109 | [|ReasonReact.string(item)|], 110 | ); 111 | Array.append(filteredArray, [|elem|]); 112 | }; 113 | }, 114 | [||], 115 | items, 116 | ); 117 | }, 118 | ) 119 | ) 120 |
; 121 | } else { 122 | ReasonReact.null; 123 | } 124 | ) 125 |
126 | ) 127 | />, 128 | }; 129 | }; 130 | 131 | module App = { 132 | let component = ReasonReact.statelessComponent("App"); 133 | let make = _children => { 134 | ...component, 135 | render: _self => 136 | Js.log(selectedItem)) 139 | />, 140 | }; 141 | }; -------------------------------------------------------------------------------- /src/Downshift.re: -------------------------------------------------------------------------------- 1 | /* Common types */ 2 | type any; 3 | 4 | /* Helpers */ 5 | external toAny : 'a => any = "%identity"; 6 | 7 | external toJsObj : any => Js.t({..}) = "%identity"; 8 | 9 | /* Types for Downshift API */ 10 | type item = any; 11 | 12 | type otherStateToSet = Js.Dict.t(string); 13 | 14 | type cb = unit => unit; 15 | 16 | type itemToString = item => string; 17 | 18 | type selectedItemChanged = (item, item) => bool; 19 | 20 | type a11yStatusMessageOptions = { 21 | . 22 | "highlightedIndex": option(int), 23 | "highlightedValue": any, 24 | "inputValue": string, 25 | "isOpen": bool, 26 | "itemToString": itemToString, 27 | "previousResultCount": int, 28 | "resultCount": int, 29 | "selectedItem": item, 30 | }; 31 | 32 | type getA11yStatusMessage = a11yStatusMessageOptions => string; 33 | 34 | type rootPropsOptions = {. "refKey": string}; 35 | 36 | type itemPropsOptions = { 37 | . 38 | "index": Js.Nullable.t(int), 39 | "item": any, 40 | }; 41 | 42 | module ControllerStateAndHelpers = { 43 | type t; 44 | /* Getters */ 45 | [@bs.send] external getRootProps : (t, rootPropsOptions) => any = ""; 46 | [@bs.send] 47 | external getButtonProps : 48 | (t, ~options: ReactDOMRe.reactDOMProps=?, unit) => any = 49 | ""; 50 | [@bs.send] 51 | external getLabelProps : 52 | (t, ~options: ReactDOMRe.reactDOMProps=?, unit) => any = 53 | ""; 54 | [@bs.send] 55 | external getInputProps : 56 | (t, ~options: ReactDOMRe.reactDOMProps=?, unit) => any = 57 | ""; 58 | [@bs.send] external extGetItemProps : (t, itemPropsOptions) => any = "getItemProps"; 59 | [@bs.send] 60 | external itemPropsOptions : (t, ~options: itemPropsOptions) => any = ""; 61 | /* Actions */ 62 | [@bs.send] external openMenu : (t, ~cb: cb=?, unit) => unit = ""; 63 | [@bs.send] external closeMenu : (t, ~cb: cb=?, unit) => unit = ""; 64 | [@bs.send] 65 | external toggleMenu : 66 | (t, ~otherStateToSet: otherStateToSet=?, ~cb: cb=?, unit) => unit = 67 | ""; 68 | [@bs.send] 69 | external reset : 70 | (t, ~otherStateToSet: otherStateToSet=?, ~cb: cb=?, unit) => unit = 71 | ""; 72 | [@bs.send] 73 | external selectItem : 74 | (t, ~item: item, ~otherStateToSet: otherStateToSet=?, ~cb: cb=?, unit) => 75 | unit = 76 | ""; 77 | [@bs.send] 78 | external selectItemAtIndex : 79 | (t, ~index: int, ~otherStateToSet: otherStateToSet=?, ~cb: cb=?, unit) => 80 | unit = 81 | ""; 82 | [@bs.send] 83 | external selectHighlightedItem : 84 | (t, ~otherStateToSet: otherStateToSet=?, ~cb: cb=?, unit) => unit = 85 | ""; 86 | [@bs.send] 87 | external setHighlightedIndex : 88 | (t, ~index: int, ~otherStateToSet: otherStateToSet=?, ~cb: cb=?, unit) => 89 | unit = 90 | ""; 91 | [@bs.send] external clearSelection : (t, ~cb: cb=?, unit) => unit = ""; 92 | [@bs.send] external clearItems : (t, unit) => unit = ""; 93 | [@bs.send] external itemToString : (t, item) => unit = ""; 94 | /* State */ 95 | [@bs.get] external highlightedIndex : t => Js.Nullable.t(int) = ""; 96 | [@bs.get] external inputValue : t => Js.Nullable.t(string) = ""; 97 | [@bs.get] external isOpen : t => bool = ""; 98 | [@bs.get] external selectedItem : t => item = ""; 99 | let getItemProps = (t, ~item: any, ~index=?, ()) : any => { 100 | let itemPropsOpt = { 101 | "item": item, 102 | "index": Js.Nullable.fromOption(index), 103 | }; 104 | extGetItemProps(t, itemPropsOpt); 105 | }; 106 | }; 107 | 108 | type stateChangeOptions = { 109 | . 110 | "type": string, 111 | "highlightedIndex": int, 112 | "inputValue": string, 113 | "isOpen": bool, 114 | "selectedItem": item, 115 | }; 116 | 117 | type onChange = (any, ControllerStateAndHelpers.t) => unit; 118 | 119 | type onSelect = (any, ControllerStateAndHelpers.t) => unit; 120 | 121 | type onStateChange = (stateChangeOptions, ControllerStateAndHelpers.t) => unit; 122 | 123 | type onInputValueChange = (string, ControllerStateAndHelpers.t) => unit; 124 | 125 | type renderFunc = ControllerStateAndHelpers.t => ReasonReact.reactElement; 126 | 127 | /* Expose the React component with the mapped props */ 128 | [@bs.module "downshift"] 129 | external reactClass : ReasonReact.reactClass = "default"; 130 | 131 | let make = 132 | ( 133 | ~defaultSelectedItem: option(any)=?, 134 | ~defaultHighlightedIndex: option(int)=?, 135 | ~defaultInputValue: option(string)=?, 136 | ~defaultIsOpen: option(bool)=?, 137 | ~itemToString: option(itemToString)=?, 138 | ~selectedItemChanged: option(selectedItemChanged)=?, 139 | ~getA11yStatusMessage: option(getA11yStatusMessage)=?, 140 | ~onChange: option(onChange)=?, 141 | ~onSelect: option(onSelect)=?, 142 | ~onStateChange: option(onStateChange)=?, 143 | ~onInputValueChange: option(onInputValueChange)=?, 144 | ~itemCount: option(int)=?, 145 | ~highlightedIndex: option(int)=?, 146 | ~inputValue: option(string)=?, 147 | ~isOpen: option(bool)=?, 148 | ~selectedItem: option(any)=?, 149 | ~render: renderFunc, 150 | ~id: option(string)=?, 151 | ~environment: option(Dom.window)=?, 152 | ~onOuterClick: option(unit => unit)=?, 153 | _children, 154 | ) => 155 | ReasonReact.wrapJsForReason( 156 | ~reactClass, 157 | ~props={ 158 | "defaultSelectedItem": 159 | Js.Null_undefined.fromOption(defaultSelectedItem), 160 | "defaultHighlightedIndex": 161 | Js.Null_undefined.fromOption(defaultHighlightedIndex), 162 | "defaultInputValue": Js.Null_undefined.fromOption(defaultInputValue), 163 | "defaultIsOpen": Js.Null_undefined.fromOption(defaultIsOpen), 164 | "itemToString": Js.Null_undefined.fromOption(itemToString), 165 | "selectedItemChanged": 166 | Js.Null_undefined.fromOption(selectedItemChanged), 167 | "getA11yStatusMessage": 168 | Js.Null_undefined.fromOption(getA11yStatusMessage), 169 | "onChange": Js.Null_undefined.fromOption(onChange), 170 | "onSelect": Js.Null_undefined.fromOption(onSelect), 171 | "onStateChange": Js.Null_undefined.fromOption(onStateChange), 172 | "onInputValueChange": Js.Null_undefined.fromOption(onInputValueChange), 173 | "itemCount": Js.Null_undefined.fromOption(itemCount), 174 | "highlightedIndex": Js.Null_undefined.fromOption(highlightedIndex), 175 | "inputValue": Js.Null_undefined.fromOption(inputValue), 176 | "isOpen": Js.Null_undefined.fromOption(isOpen), 177 | "selectedItem": Js.Null_undefined.fromOption(selectedItem), 178 | "render": render, 179 | "id": Js.Null_undefined.fromOption(id), 180 | "environment": Js.Null_undefined.fromOption(environment), 181 | "onOuterClick": Js.Null_undefined.fromOption(onOuterClick), 182 | }, 183 | [||], 184 | ); 185 | 186 | [@bs.module "downshift"] 187 | external resetIdCounter : unit => unit = "resetIdCounter"; --------------------------------------------------------------------------------