diff --git a/.eslintrc.json b/.eslintrc.json index 7f8e075..9d6a62b 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,7 +1,6 @@ { - "env": { "commonjs": true }, "extends": "eslint:recommended", - "parserOptions": { "ecmaVersion": 5 }, + "parserOptions": { "ecmaVersion": 6, "sourceType": "module" }, "rules": { "block-scoped-var": "error", "consistent-return": "error", diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index c47a263..8d7661e 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -2,7 +2,7 @@ blank_issues_enabled: false contact_links: - name: PureScript Discourse url: https://discourse.purescript.org/ - about: Ask and answer questions here. - - name: Functional Programming Slack - url: https://functionalprogramming.slack.com - about: For casual chat and questions (use https://fpchat-invite.herokuapp.com to join). + about: Ask and answer questions on the PureScript discussion forum. + - name: PureScript Discord + url: https://purescript.org/chat + about: Ask and answer questions on the PureScript chat. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0b3ca67..fc54710 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,6 +15,9 @@ jobs: - name: Set up PureScript toolchain uses: purescript-contrib/setup-purescript@main + with: + purescript: "unstable" + purs-tidy: "latest" - name: Cache PureScript dependencies uses: actions/cache@v2 @@ -25,9 +28,9 @@ jobs: output - name: Set up Node toolchain - uses: actions/setup-node@v1 + uses: actions/setup-node@v2 with: - node-version: "12.x" + node-version: "14.x" - name: Cache NPM dependencies uses: actions/cache@v2 @@ -49,3 +52,15 @@ jobs: - name: Run tests run: npm run test + + - name: Check formatting + run: purs-tidy check src test + + - name: Verify Bower & Pulp + run: | + npm install bower pulp@16.0.0-0 + npx bower install + npx pulp build -- --censor-lib --strict + if [ -d "test" ]; then + npx pulp test + fi diff --git a/.gitignore b/.gitignore index 5a54e2f..6a45203 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ !.gitignore !.github !.editorconfig +!.tidyrc.json !.eslintrc.json output diff --git a/.tidyrc.json b/.tidyrc.json new file mode 100644 index 0000000..4f013c1 --- /dev/null +++ b/.tidyrc.json @@ -0,0 +1,10 @@ +{ + "importSort": "source", + "importWrap": "source", + "indent": 2, + "operatorsFile": null, + "ribbon": 1, + "typeArrowPlacement": "first", + "unicode": "never", + "width": null +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 28aede8..a175810 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,29 @@ Bugfixes: Other improvements: +## [v11.0.0](https://github.com/purescript-contrib/purescript-react/releases/tag/v11.0.0) - 2023-01-04 + +Breaking changes: +- Update the Monoid/Semigroup instance of `ReactElement` to use `null` for `mempty` instead of an empty fragment. (#187) + +## [v10.0.1](https://github.com/purescript-contrib/purescript-react/releases/tag/v10.0.1) - 2022-04-27 + +Other improvements: +- Minifier-friendly refereces to properties (#183 by @sd-yip) + +## [v10.0.0](https://github.com/purescript-contrib/purescript-react/releases/tag/v10.0.0) - 2022-04-27 + +Breaking changes: +- Migrate FFI to ES modules (#185 by @JordanMartinez) +- Replaced polymorphic proxies with monomorphic `Proxy` (#185 by @JordanMartinez) + +New features: + +Bugfixes: + +Other improvements: +- Added `purs-tidy` formatter (#182 by @thomashoneyman) + ## [v9.0.0](https://github.com/purescript-contrib/purescript-react/releases/tag/v9.0.0) - 2021-02-26 Breaking changes: diff --git a/README.md b/README.md index b51ecec..96c2f47 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ The quick start hasn't been written yet (contributions are welcome!). The quick If you get stuck, there are several ways to get help: - [Open an issue](https://github.com/purescript-contrib/purescript-react/issues) if you have encountered a bug or problem. -- [Search or start a thread on the PureScript Discourse](https://discourse.purescript.org) if you have general questions. You can also ask questions in the `#purescript` and `#purescript-beginners` channels on the [Functional Programming Slack](https://functionalprogramming.slack.com) ([invite link](https://fpchat-invite.herokuapp.com/)). +- Ask general questions on the [PureScript Discourse](https://discourse.purescript.org) forum or the [PureScript Discord](https://purescript.org/chat) chat. ## Contributing diff --git a/bower.json b/bower.json index b11275c..6709f7a 100644 --- a/bower.json +++ b/bower.json @@ -17,15 +17,15 @@ "url": "https://github.com/purescript-contrib/purescript-react.git" }, "dependencies": { - "purescript-effect": "^3.0.0", - "purescript-exceptions": "^5.0.0", - "purescript-maybe": "^5.0.0", - "purescript-nullable": "^5.0.0", - "purescript-prelude": "^5.0.0", - "purescript-typelevel-prelude": "^6.0.0", - "purescript-unsafe-coerce": "^5.0.0" + "purescript-effect": "^4.0.0", + "purescript-exceptions": "^6.0.0", + "purescript-maybe": "^6.0.0", + "purescript-nullable": "^6.0.0", + "purescript-prelude": "^6.0.0", + "purescript-typelevel-prelude": "^7.0.0", + "purescript-unsafe-coerce": "^6.0.0" }, "devDependencies": { - "purescript-console": "^5.0.0" + "purescript-console": "^6.0.0" } } diff --git a/package.json b/package.json index acf39bb..5617636 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,6 @@ }, "devDependencies": { "eslint": "^7.10.0", - "purescript-psa": "^0.8.0" + "purescript-psa": "^0.8.2" } } diff --git a/packages.dhall b/packages.dhall index 9c3ee6f..bc9c4ee 100644 --- a/packages.dhall +++ b/packages.dhall @@ -1,4 +1,5 @@ let upstream = - https://raw.githubusercontent.com/purescript/package-sets/prepare-0.14/src/packages.dhall + https://github.com/purescript/package-sets/releases/download/psc-0.15.4-20221208/packages.dhall + sha256:e3549e48d0170e14838d8f0c44172253947dcb6117b51a763f33dca34f00ba43 in upstream diff --git a/spago.dhall b/spago.dhall index 4e5b7a6..0119ab6 100644 --- a/spago.dhall +++ b/spago.dhall @@ -6,7 +6,6 @@ , "maybe" , "nullable" , "prelude" - , "psci-support" , "typelevel-prelude" , "unsafe-coerce" ] diff --git a/src/React.js b/src/React.js index 91d4c45..ea3bced 100644 --- a/src/React.js +++ b/src/React.js @@ -1,43 +1,19 @@ -/* global exports */ -"use strict"; - -var React = require("react"); +import React from "react"; function createClass(baseClass) { - function bindProperty(instance, prop, value) { - switch (prop) { - case 'state': - case 'render': - case 'componentDidMount': - case 'componentWillUnmount': - instance[prop] = value; - break; - - case 'componentDidCatch': - case 'componentWillUpdate': - case 'shouldComponentUpdate': - case 'getSnapshotBeforeUpdate': - instance[prop] = function (a, b) { return value(a)(b)(); }; - break; - - case 'componentDidUpdate': - instance[prop] = function (a, b, c) { return value(a)(b)(c)(); }; - break; - - case 'unsafeComponentWillMount': - instance['UNSAFE_componentWillMount'] = value; - break; - - case 'unsafeComponentWillReceiveProps': - instance['UNSAFE_componentWillReceiveProps'] = function (a) { return value(a)(); }; - break; - - case 'unsafeComponentWillUpdate': - instance['UNSAFE_componentWillUpdate'] = function (a, b) { return value(a)(b)(); }; - break; - - default: - throw new Error('[purescript-react] Not a component property: ' + prop); + function invoke1(f) { + return f === undefined ? f : function (a) { + return f(a)() + } + } + function invoke2(f) { + return f === undefined ? f : function (a, b) { + return f(a)(b)() + } + } + function invoke3(f) { + return f === undefined ? f : function (a, b, c) { + return f(a)(b)(c)() } } @@ -46,9 +22,19 @@ function createClass(baseClass) { var Constructor = function (props) { baseClass.call(this, props); var spec = ctrFn(this)(); - for (var k in spec) { - bindProperty(this, k, spec[k]); - } + + this.state = spec.state; + this.render = spec.render; + this.componentDidMount = spec.componentDidMount; + this.componentWillUnmount = spec.componentWillUnmount; + this.componentDidCatch = invoke2(spec.componentDidCatch); + this.componentWillUpdate = invoke2(spec.componentWillUpdate); + this.shouldComponentUpdate = invoke2(spec.shouldComponentUpdate); + this.getSnapshotBeforeUpdate = invoke2(spec.getSnapshotBeforeUpdate); + this.componentDidUpdate = invoke3(spec.componentDidUpdate); + this.UNSAFE_componentWillMount = spec.unsafeComponentWillMount; + this.UNSAFE_componentWillReceiveProps = invoke1(spec.unsafeComponentWillReceiveProps); + this.UNSAFE_componentWillUpdate = invoke2(spec.unsafeComponentWillUpdate); }; Constructor.displayName = displayName; @@ -60,6 +46,10 @@ function createClass(baseClass) { }; } +var componentImpl = createClass(React.Component); +export {componentImpl}; + +// eslint-disable-next-line no-unused-vars function createClassWithDerivedState(classCtr) { return function(displayName) { return function(getDerivedStateFromProps) { @@ -72,28 +62,22 @@ function createClassWithDerivedState(classCtr) { }; } -var componentImpl = createClass(React.Component); -exports.componentImpl = componentImpl; -exports.componentWithDerivedStateImpl = createClassWithDerivedState(componentImpl); +export const componentWithDerivedStateImpl = createClassWithDerivedState(componentImpl); var pureComponentImpl = createClass(React.PureComponent); -exports.pureComponentImpl = pureComponentImpl; -exports.pureComponentWithDerivedStateImpl = createClassWithDerivedState(pureComponentImpl); - -exports.statelessComponent = function(x) { return x; }; - -exports.fragment = React.Fragment; +export {pureComponentImpl}; +export const pureComponentWithDerivedStateImpl = createClassWithDerivedState(pureComponentImpl); +export function statelessComponent(x) { return x; } +export const fragment = React.Fragment; function getProps(this_) { return function(){ return this_.props; }; } -exports.getProps = getProps; - -exports.childrenToArray = React.Children.toArray; - -exports.childrenCount = React.Children.count; +export {getProps}; +export const childrenToArray = React.Children.toArray; +export const childrenCount = React.Children.count; function setStateImpl(this_) { return function(state){ @@ -102,7 +86,7 @@ function setStateImpl(this_) { }; }; } -exports.setStateImpl = setStateImpl; +export {setStateImpl}; function setStateWithCallbackImpl(this_) { return function(state){ @@ -113,17 +97,17 @@ function setStateWithCallbackImpl(this_) { }; }; } -exports.setStateWithCallbackImpl = setStateWithCallbackImpl; +export {setStateWithCallbackImpl}; function getState(this_) { return function(){ if (!this_.state) { - throw new Error('[purescript-react] Cannot get state within constructor'); + throw new Error("[purescript-react] Cannot get state within constructor"); } return this_.state; }; } -exports.getState = getState; +export {getState}; function forceUpdateWithCallback(this_) { return function(cb) { @@ -132,7 +116,7 @@ function forceUpdateWithCallback(this_) { }; }; } -exports.forceUpdateWithCallback = forceUpdateWithCallback; +export {forceUpdateWithCallback}; function createElement(class_) { return function(props){ @@ -141,15 +125,15 @@ function createElement(class_) { }; }; } -exports.createElementImpl = createElement; -exports.createElementTagName = createElement; +export {createElement as createElementImpl}; +export {createElement as createElementTagName}; function createLeafElement(class_) { return function(props) { return React.createElement(class_, props); }; } -exports.createLeafElementImpl = createLeafElement; +export {createLeafElement as createLeafElementImpl}; function createElementDynamic(class_) { return function(props) { @@ -157,9 +141,9 @@ function createElementDynamic(class_) { return React.createElement(class_, props, children); }; }; -}; -exports.createElementDynamicImpl = createElementDynamic; -exports.createElementTagNameDynamic = createElementDynamic; +} +export {createElementDynamic as createElementDynamicImpl}; +export {createElementDynamic as createElementTagNameDynamic}; function createContext(defaultValue) { var context = React.createContext(defaultValue); @@ -168,4 +152,12 @@ function createContext(defaultValue) { provider: context.Provider }; } -exports.createContext = createContext; +export {createContext}; + +export var emptyReactElement = null; + +function isEmptyReactElement(a) { + return a === emptyReactElement; +}; + +export {isEmptyReactElement}; diff --git a/src/React.purs b/src/React.purs index ff7b217..269e2e3 100644 --- a/src/React.purs +++ b/src/React.purs @@ -81,11 +81,18 @@ type TagName = String -- | A virtual DOM node, or component. foreign import data ReactElement :: Type +foreign import emptyReactElement :: ReactElement + +foreign import isEmptyReactElement :: ReactElement -> Boolean + instance semigroupReactElement :: Semigroup ReactElement where - append a b = toElement [ a, b ] + append a b + | isEmptyReactElement a = b + | isEmptyReactElement b = a + | otherwise = toElement [ a, b ] instance monoidReactElement :: Monoid ReactElement where - mempty = toElement ([] :: Array ReactElement) + mempty = emptyReactElement -- | A mounted react component foreign import data ReactComponent :: Type @@ -156,21 +163,19 @@ type ReactSpecShouldComponentUpdate props state = ( shouldComponentUpdate :: ShouldComponentUpdate props state ) -type ReactSpecAll props state snapshot - = ReactSpecRequired state - + ReactSpecOptional props state snapshot - + ReactSpecShouldComponentUpdate props state +type ReactSpecAll props state snapshot = + ReactSpecRequired state + + ReactSpecOptional props state snapshot + + ReactSpecShouldComponentUpdate props state -type ReactSpecPure props state snapshot - = ReactSpecRequired state - + ReactSpecOptional props state snapshot () +type ReactSpecPure props state snapshot = + ReactSpecRequired state + + ReactSpecOptional props state snapshot () -- | The signature for a ReactClass constructor. A constructor takes the -- | `ReactThis` context and returns a record with appropriate lifecycle -- | methods. -type ReactClassConstructor props state r = - ReactThis props state -> - Effect (Record r) +type ReactClassConstructor props state r = ReactThis props state -> Effect (Record r) class ReactComponentSpec :: Type -> Type -> Type -> Row Type -> Row Type -> Constraint class ReactComponentSpec props state snapshot (given :: Row Type) (spec :: Row Type) @@ -191,64 +196,73 @@ instance reactPureComponentSpec :: ReactPureComponentSpec props state snapshot given spec -- | Creates a `ReactClass` inherited from `React.Component`. -component :: forall props state snapshot given spec. - ReactComponentSpec (Record props) (Record state) snapshot given spec => - String -> - ReactClassConstructor (Record props) (Record state) given -> - ReactClass (Record props) +component + :: forall props state snapshot given spec + . ReactComponentSpec (Record props) (Record state) snapshot given spec + => String + -> ReactClassConstructor (Record props) (Record state) given + -> ReactClass (Record props) component = componentImpl -- | Like `component`, but takes a `getDerivedStateFromProps` handler. -componentWithDerivedState :: forall props state snapshot given spec. - ReactComponentSpec (Record props) (Record state) snapshot given spec => - String -> - (Record props -> Record state -> Record state) -> - ReactClassConstructor (Record props) (Record state) given -> - ReactClass (Record props) +componentWithDerivedState + :: forall props state snapshot given spec + . ReactComponentSpec (Record props) (Record state) snapshot given spec + => String + -> (Record props -> Record state -> Record state) + -> ReactClassConstructor (Record props) (Record state) given + -> ReactClass (Record props) componentWithDerivedState = componentWithDerivedStateImpl -- | Creates a `ReactClass` inherited from `React.PureComponent`. -pureComponent :: forall props state snapshot given spec. - ReactPureComponentSpec (Record props) (Record state) snapshot given spec => - String -> - ReactClassConstructor (Record props) (Record state) given -> - ReactClass (Record props) +pureComponent + :: forall props state snapshot given spec + . ReactPureComponentSpec (Record props) (Record state) snapshot given spec + => String + -> ReactClassConstructor (Record props) (Record state) given + -> ReactClass (Record props) pureComponent = pureComponentImpl -- | Like `pureComponent`, but takes a `getDerivedStateFromProps` handler. -pureComponentWithDerivedState :: forall props state snapshot given spec. - ReactPureComponentSpec (Record props) (Record state) snapshot given spec => - String -> - (Record props -> Record state -> Record state) -> - ReactClassConstructor (Record props) (Record state) given -> - ReactClass (Record props) +pureComponentWithDerivedState + :: forall props state snapshot given spec + . ReactPureComponentSpec (Record props) (Record state) snapshot given spec + => String + -> (Record props -> Record state -> Record state) + -> ReactClassConstructor (Record props) (Record state) given + -> ReactClass (Record props) pureComponentWithDerivedState = componentWithDerivedStateImpl -foreign import componentImpl :: forall this props r. - String -> - (this -> Effect r) -> - ReactClass props - -foreign import componentWithDerivedStateImpl :: forall this props state r. - String -> - (props -> state -> state) -> - (this -> Effect r) -> - ReactClass props - -foreign import pureComponentImpl :: forall this props r. - String -> - (this -> Effect r) -> - ReactClass props - -foreign import pureComponentWithDerivedStateImpl :: forall this props state r. - String -> - (props -> state -> state) -> - (this -> Effect r) -> - ReactClass props - -foreign import statelessComponent :: forall props. - (Record props -> ReactElement) -> - ReactClass (Record props) +foreign import componentImpl + :: forall this props r + . String + -> (this -> Effect r) + -> ReactClass props + +foreign import componentWithDerivedStateImpl + :: forall this props state r + . String + -> (props -> state -> state) + -> (this -> Effect r) + -> ReactClass props + +foreign import pureComponentImpl + :: forall this props r + . String + -> (this -> Effect r) + -> ReactClass props + +foreign import pureComponentWithDerivedStateImpl + :: forall this props state r + . String + -> (props -> state -> state) + -> (this -> Effect r) + -> ReactClass props + +foreign import statelessComponent + :: forall props + . (Record props -> ReactElement) + -> ReactClass (Record props) -- | React class for components. foreign import data ReactClass :: Type -> Type @@ -258,73 +272,83 @@ type role ReactClass representational foreign import fragment :: ReactClass { children :: Children } -- | Read the component props. -foreign import getProps :: forall props state. - ReactThis props state -> - Effect props - -foreign import setStateImpl :: forall props state update. - ReactThis props state -> - update -> - Effect Unit - -foreign import setStateWithCallbackImpl :: forall props state update. - ReactThis props state -> - update -> - Effect Unit -> - Effect Unit +foreign import getProps + :: forall props state + . ReactThis props state + -> Effect props + +foreign import setStateImpl + :: forall props state update + . ReactThis props state + -> update + -> Effect Unit + +foreign import setStateWithCallbackImpl + :: forall props state update + . ReactThis props state + -> update + -> Effect Unit + -> Effect Unit -- | Get the component state. -foreign import getState :: forall props state. - ReactThis props state -> - Effect state +foreign import getState + :: forall props state + . ReactThis props state + -> Effect state -- | Update component state given some sub-set of state properties. -setState :: forall props given rest all. - Row.Union given rest all => - ReactThis props (Record all) -> - Record given -> - Effect Unit +setState + :: forall props given rest all + . Row.Union given rest all + => ReactThis props (Record all) + -> Record given + -> Effect Unit setState = setStateImpl -- | Update component state given some sub-set of state properties, while -- | also invoking a callback when applied. -setStateWithCallback :: forall props given rest all. - Row.Union given rest all => - ReactThis props (Record all) -> - Record given -> - Effect Unit -> - Effect Unit +setStateWithCallback + :: forall props given rest all + . Row.Union given rest all + => ReactThis props (Record all) + -> Record given + -> Effect Unit + -> Effect Unit setStateWithCallback = setStateWithCallbackImpl -- | Update component state. -writeState :: forall props all. - ReactThis props (Record all) -> - Record all -> - Effect Unit +writeState + :: forall props all + . ReactThis props (Record all) + -> Record all + -> Effect Unit writeState = setStateImpl -- | Update component state, while also invoking a callback when applied. -writeStateWithCallback :: forall props all. - ReactThis props (Record all) -> - Record all -> - Effect Unit -> - Effect Unit +writeStateWithCallback + :: forall props all + . ReactThis props (Record all) + -> Record all + -> Effect Unit + -> Effect Unit writeStateWithCallback = setStateWithCallbackImpl -- | Update component state given a modification function. -modifyState :: forall props state. - ReactThis props state -> - (state -> state) -> - Effect Unit +modifyState + :: forall props state + . ReactThis props state + -> (state -> state) + -> Effect Unit modifyState = setStateImpl -- | Update component state given a modification function, while also invoking -- | a callback when applied. -modifyStateWithCallback :: forall props state. - ReactThis props state -> - (state -> state) -> - Effect Unit -> - Effect Unit +modifyStateWithCallback + :: forall props state + . ReactThis props state + -> (state -> state) + -> Effect Unit + -> Effect Unit modifyStateWithCallback = setStateWithCallbackImpl -- | Force render of a react component. @@ -332,10 +356,11 @@ forceUpdate :: forall props state. ReactThis props state -> Effect Unit forceUpdate this = forceUpdateWithCallback this (pure unit) -- | Force render and then run an Effect. -foreign import forceUpdateWithCallback :: forall props state. - ReactThis props state -> - Effect Unit -> - Effect Unit +foreign import forceUpdateWithCallback + :: forall props state + . ReactThis props state + -> Effect Unit + -> Effect Unit class ReactPropFields (required :: Row Type) (given :: Row Type) @@ -352,75 +377,100 @@ instance reactPropFields :: ReactPropFields required given -- | Create an element from a React class spreading the children array. Used when the children are known up front. -createElement :: forall required given. - ReactPropFields required given => - ReactClass { children :: Children | required } -> - { | given } -> - Array ReactElement -> - ReactElement +createElement + :: forall required given + . ReactPropFields required given + => ReactClass { children :: Children | required } + -> { | given } + -> Array ReactElement + -> ReactElement createElement = createElementImpl -- | An unsafe version of `createElement` which does not enforce the reserved -- | properties "key" and "ref". -unsafeCreateElement :: forall props. - ReactClass { children :: Children | props } -> - { | props } -> - Array ReactElement -> - ReactElement +unsafeCreateElement + :: forall props + . ReactClass { children :: Children | props } + -> { | props } + -> Array ReactElement + -> ReactElement unsafeCreateElement = createElementImpl -- | Create an element from a React class passing the children array. Used for a dynamic array of children. -createElementDynamic :: forall required given. - ReactPropFields required given => - ReactClass { children :: Children | required } -> - { | given } -> - Array ReactElement -> - ReactElement +createElementDynamic + :: forall required given + . ReactPropFields required given + => ReactClass { children :: Children | required } + -> { | given } + -> Array ReactElement + -> ReactElement createElementDynamic = createElementDynamicImpl -- | An unsafe version of `createElementDynamic` which does not enforce the reserved -- | properties "key" and "ref". -unsafeCreateElementDynamic :: forall props. - ReactClass { children :: Children | props } -> - { | props } -> - Array ReactElement -> - ReactElement +unsafeCreateElementDynamic + :: forall props + . ReactClass { children :: Children | props } + -> { | props } + -> Array ReactElement + -> ReactElement unsafeCreateElementDynamic = createElementDynamicImpl -foreign import createElementImpl :: forall required given children. - ReactClass required -> given -> Array children -> ReactElement +foreign import createElementImpl + :: forall required given children + . ReactClass required + -> given + -> Array children + -> ReactElement -foreign import createElementDynamicImpl :: forall required given children. - ReactClass required -> given -> Array children -> ReactElement +foreign import createElementDynamicImpl + :: forall required given children + . ReactClass required + -> given + -> Array children + -> ReactElement -- | Create an element from a React class that does not require children. Additionally it can be used -- | when the children are represented /only/ through the `children` prop - for instance, a `ContextConsumer` -- | would be turned into a `ReactElement` with `createLeafElement someContext.consumer { children: \x -> ... }`. -createLeafElement :: forall required given. - ReactPropFields required given => - ReactClass { | required } -> - { | given } -> - ReactElement +createLeafElement + :: forall required given + . ReactPropFields required given + => ReactClass { | required } + -> { | given } + -> ReactElement createLeafElement = createLeafElementImpl -- | An unsafe version of `createLeafElement` which does not enforce the reserved -- | properties "key" and "ref". -unsafeCreateLeafElement :: forall props. - ReactClass props -> - props -> - ReactElement +unsafeCreateLeafElement + :: forall props + . ReactClass props + -> props + -> ReactElement unsafeCreateLeafElement = createLeafElementImpl -foreign import createLeafElementImpl :: forall required given. - ReactClass required -> given -> ReactElement +foreign import createLeafElementImpl + :: forall required given + . ReactClass required + -> given + -> ReactElement -- | Create an element from a tag name spreading the children array. Used when the children are known up front. -foreign import createElementTagName :: forall props. - TagName -> props -> Array ReactElement -> ReactElement +foreign import createElementTagName + :: forall props + . TagName + -> props + -> Array ReactElement + -> ReactElement -- | Create an element from a tag name passing the children array. Used for a dynamic array of children. -foreign import createElementTagNameDynamic :: forall props. - TagName -> props -> Array ReactElement -> ReactElement +foreign import createElementTagNameDynamic + :: forall props + . TagName + -> props + -> Array ReactElement + -> ReactElement -- | Internal representation for the children elements passed to a component foreign import data Children :: Type @@ -450,7 +500,9 @@ instance isReactElementReactElement :: IsReactElement ReactElement where toElement = identity instance isReactElementArray :: IsReactElement (Array ReactElement) where - toElement = createElement fragment {} + toElement = case _ of + [] -> mempty + children -> createElement fragment {} children -- | Creates a keyed fragment. fragmentWithKey :: String -> Array ReactElement -> ReactElement diff --git a/src/React/DOM.purs b/src/React/DOM.purs index 3c1fbf0..2755cb1 100644 --- a/src/React/DOM.purs +++ b/src/React/DOM.purs @@ -6,15 +6,14 @@ import Unsafe.Coerce (unsafeCoerce) newtype IsDynamic = IsDynamic Boolean -mkDOM :: - IsDynamic -> TagName -> Array Props -> Array ReactElement -> ReactElement +mkDOM :: IsDynamic -> TagName -> Array Props -> Array ReactElement -> ReactElement mkDOM dynamic tag props = createElement tag (unsafeFromPropsArray props) where createElement :: TagName -> Array Props -> Array ReactElement -> ReactElement createElement = case dynamic of - IsDynamic false -> createElementTagName - IsDynamic true -> createElementTagNameDynamic + IsDynamic false -> createElementTagName + IsDynamic true -> createElementTagNameDynamic text :: String -> ReactElement text = unsafeCoerce diff --git a/src/React/DOM/Dynamic.purs b/src/React/DOM/Dynamic.purs index 8a013d6..e5fe3c4 100644 --- a/src/React/DOM/Dynamic.purs +++ b/src/React/DOM/Dynamic.purs @@ -1,8 +1,8 @@ module React.DOM.Dynamic where import React (ReactElement) -import React.DOM.Props (Props) import React.DOM as DOM +import React.DOM.Props (Props) text :: String -> ReactElement text = DOM.text diff --git a/src/React/DOM/Props.js b/src/React/DOM/Props.js index 68e4a39..f82d507 100644 --- a/src/React/DOM/Props.js +++ b/src/React/DOM/Props.js @@ -1,8 +1,3 @@ -/* global exports */ -"use strict"; - -var React = require("react"); - function unsafeMkProps(key) { return function(value){ var result = {}; @@ -10,7 +5,7 @@ function unsafeMkProps(key) { return result; }; } -exports.unsafeMkProps = unsafeMkProps; +export {unsafeMkProps}; function unsafeUnfoldProps(key) { return function(value){ @@ -19,7 +14,7 @@ function unsafeUnfoldProps(key) { props[key] = result; for (var subprop in value) { - if (value.hasOwnProperty(subprop)) { + if (Object.hasOwnProperty.apply(value, [subprop])) { result[subprop] = value[subprop]; } } @@ -27,14 +22,14 @@ function unsafeUnfoldProps(key) { return props; }; } -exports.unsafeUnfoldProps = unsafeUnfoldProps; +export {unsafeUnfoldProps}; function unsafePrefixProps(prefix) { return function(value){ var result = {}; for (var prop in value) { - if (value.hasOwnProperty(prop)) { + if (Object.hasOwnProperty.apply(value, [prop])) { result[prefix + prop] = value[prop]; } } @@ -42,7 +37,7 @@ function unsafePrefixProps(prefix) { return result; }; } -exports.unsafePrefixProps = unsafePrefixProps; +export {unsafePrefixProps}; function unsafeFromPropsArray(props) { var result = {}; @@ -51,12 +46,12 @@ function unsafeFromPropsArray(props) { var prop = props[i]; for (var key in prop) { - if (prop.hasOwnProperty(key)) { + if (Object.hasOwnProperty.apply(prop, [key])) { result[key] = prop[key]; } } } return result; -}; -exports.unsafeFromPropsArray = unsafeFromPropsArray; +} +export {unsafeFromPropsArray}; diff --git a/src/React/DOM/Props.purs b/src/React/DOM/Props.purs index 34d90e4..65e9e1e 100644 --- a/src/React/DOM/Props.purs +++ b/src/React/DOM/Props.purs @@ -6,13 +6,13 @@ import Effect (Effect) import Effect.Uncurried (mkEffectFn1) import React.Ref as Ref import React.SyntheticEvent - ( SyntheticEvent - , SyntheticAnimationEvent + ( SyntheticAnimationEvent , SyntheticClipboardEvent , SyntheticCompositionEvent + , SyntheticEvent + , SyntheticFocusEvent , SyntheticInputEvent , SyntheticKeyboardEvent - , SyntheticFocusEvent , SyntheticMouseEvent , SyntheticTouchEvent , SyntheticTransitionEvent diff --git a/src/React/Ref.js b/src/React/Ref.js index a08fe02..865a9c6 100644 --- a/src/React/Ref.js +++ b/src/React/Ref.js @@ -1,13 +1,10 @@ -"use strict"; +import React from "react"; +export const createRef = React.createRef; -var React = require("react"); - -exports.createRef = React.createRef; - -exports.liftCallbackRef = function(ref) { +export function liftCallbackRef(ref) { return { current: ref }; } -exports.getCurrentRef_ = function(ref) { +export function getCurrentRef_(ref) { return ref.current; } diff --git a/src/React/Ref.purs b/src/React/Ref.purs index a8a57ef..4cd8be9 100644 --- a/src/React/Ref.purs +++ b/src/React/Ref.purs @@ -11,11 +11,12 @@ module React.Ref ) where import Prelude -import Effect (Effect) + import Data.Maybe (Maybe) import Data.Nullable (Nullable) import Data.Nullable as Nullable -import Effect.Uncurried (EffectFn1, runEffectFn1, mkEffectFn1) +import Effect (Effect) +import Effect.Uncurried (EffectFn1, mkEffectFn1, runEffectFn1) import Unsafe.Coerce (unsafeCoerce) --- | An instance of a React class. @@ -33,28 +34,22 @@ foreign import data RefHandler :: Type -> Type type role RefHandler representational - foreign import createRef :: forall a. Effect (Ref a) foreign import liftCallbackRef :: forall a. Ref a -> Ref a - createNodeRef :: Effect (Ref NativeNode) createNodeRef = createRef - createInstanceRef :: Effect (Ref ReactInstance) createInstanceRef = createRef - fromRef :: forall a. Ref a -> RefHandler a fromRef = unsafeCoerce - fromEffect :: forall a. (Ref a -> Effect Unit) -> RefHandler a fromEffect f = unsafeCoerce $ mkEffectFn1 (f <<< liftCallbackRef) - foreign import getCurrentRef_ :: forall a. EffectFn1 (Ref a) (Nullable a) getCurrentRef :: forall a. Ref a -> Effect (Maybe a) diff --git a/src/React/SyntheticEvent.js b/src/React/SyntheticEvent.js index 6d39ee0..499161e 100644 --- a/src/React/SyntheticEvent.js +++ b/src/React/SyntheticEvent.js @@ -1,47 +1,45 @@ -'use strict'; - -exports.preventDefault = function preventDefault(event) { +export function preventDefault(event) { return function() { return event.preventDefault(); }; -}; +} -exports.isDefaultPrevented = function isDefaultPrevented(event) { +export function isDefaultPrevented(event) { return function() { return event.isDefaultPrevented(); }; -}; +} -exports.stopPropagation = function stopPropagation(event) { +export function stopPropagation(event) { return function() { return event.stopPropagation(); }; -}; +} -exports.isPropagationStopped = function isPropagationStopped(event) { +export function isPropagationStopped(event) { return function() { return event.isPropagationStopped(); }; -}; +} -exports.persist = function persist(event) { +export function persist(event) { return function() { return event.persist(); }; -}; +} -exports.getModifierState = function getModifierState(key) { +export function getModifierState(key) { return function(event) { return function() { return event.getModifierState(key); }; }; -}; +} -exports.unsafeGet = function unsafeGet(key) { +export function unsafeGet(key) { return function (event) { return function () { return event[key]; }; }; -}; +} diff --git a/src/React/SyntheticEvent.purs b/src/React/SyntheticEvent.purs index 25592a6..4695065 100644 --- a/src/React/SyntheticEvent.purs +++ b/src/React/SyntheticEvent.purs @@ -98,41 +98,29 @@ import Effect (Effect) import Prim.Row as Row import Type.Proxy (Proxy(..)) -type SyntheticEvent - = SyntheticEvent_ (SyntheticEvent' ()) +type SyntheticEvent = SyntheticEvent_ (SyntheticEvent' ()) -type SyntheticAnimationEvent - = SyntheticEvent_ (SyntheticAnimationEvent' (SyntheticEvent' ())) +type SyntheticAnimationEvent = SyntheticEvent_ (SyntheticAnimationEvent' (SyntheticEvent' ())) -type SyntheticClipboardEvent - = SyntheticEvent_ (SyntheticClipboardEvent' (SyntheticEvent' ())) +type SyntheticClipboardEvent = SyntheticEvent_ (SyntheticClipboardEvent' (SyntheticEvent' ())) -type SyntheticCompositionEvent - = SyntheticEvent_ (SyntheticCompositionEvent' (SyntheticUIEvent' (SyntheticEvent' ()))) +type SyntheticCompositionEvent = SyntheticEvent_ (SyntheticCompositionEvent' (SyntheticUIEvent' (SyntheticEvent' ()))) -type SyntheticInputEvent - = SyntheticEvent_ (SyntheticUIEvent' (SyntheticEvent' ())) +type SyntheticInputEvent = SyntheticEvent_ (SyntheticUIEvent' (SyntheticEvent' ())) -type SyntheticKeyboardEvent - = SyntheticEvent_ (SyntheticKeyboardEvent' (SyntheticUIEvent' (SyntheticEvent' ()))) +type SyntheticKeyboardEvent = SyntheticEvent_ (SyntheticKeyboardEvent' (SyntheticUIEvent' (SyntheticEvent' ()))) -type SyntheticFocusEvent - = SyntheticEvent_ (SyntheticFocusEvent' (SyntheticUIEvent' (SyntheticEvent' ()))) +type SyntheticFocusEvent = SyntheticEvent_ (SyntheticFocusEvent' (SyntheticUIEvent' (SyntheticEvent' ()))) -type SyntheticMouseEvent - = SyntheticEvent_ (SyntheticMouseEvent' (SyntheticUIEvent' (SyntheticEvent' ()))) +type SyntheticMouseEvent = SyntheticEvent_ (SyntheticMouseEvent' (SyntheticUIEvent' (SyntheticEvent' ()))) -type SyntheticTouchEvent - = SyntheticEvent_ (SyntheticTouchEvent' (SyntheticUIEvent' (SyntheticEvent' ()))) +type SyntheticTouchEvent = SyntheticEvent_ (SyntheticTouchEvent' (SyntheticUIEvent' (SyntheticEvent' ()))) -type SyntheticTransitionEvent - = SyntheticEvent_ (SyntheticTransitionEvent' (SyntheticEvent' ())) +type SyntheticTransitionEvent = SyntheticEvent_ (SyntheticTransitionEvent' (SyntheticEvent' ())) -type SyntheticUIEvent - = SyntheticEvent_ (SyntheticUIEvent' (SyntheticEvent' ())) +type SyntheticUIEvent = SyntheticEvent_ (SyntheticUIEvent' (SyntheticEvent' ())) -type SyntheticWheelEvent - = SyntheticEvent_ (SyntheticWheelEvent' (SyntheticMouseEvent' (SyntheticEvent' ()))) +type SyntheticWheelEvent = SyntheticEvent_ (SyntheticWheelEvent' (SyntheticMouseEvent' (SyntheticEvent' ()))) foreign import data SyntheticEvent_ :: Row Type -> Type @@ -148,108 +136,108 @@ foreign import data NativeAbstractView :: Type foreign import data NativeTouchList :: Type -type SyntheticEvent' r - = ( bubbles :: Boolean - , cancelable :: Boolean - , currentTarget :: NativeEventTarget - , defaultPrevented :: Boolean - , eventPhase :: Number - , isTrusted :: Boolean - , nativeEvent :: NativeEvent - , target :: NativeEventTarget - , timeStamp :: Number - , type :: String - | r - ) - -type SyntheticAnimationEvent' r - = ( animationName :: String - , pseudoElement :: String - , elapsedTime :: Number - | r - ) - -type SyntheticClipboardEvent' r - = ( clipboardData :: NativeDataTransfer - | r - ) - -type SyntheticCompositionEvent' r - = ( data :: String - | r - ) - -type SyntheticFocusEvent' r - = ( relatedTarget :: NativeEventTarget - | r - ) - -type SyntheticKeyboardEvent' r - = ( altKey :: Boolean - , ctrlKey :: Boolean - , getModifierState :: String -> Boolean - , charCode :: Int - , key :: String - , keyCode :: Number - , locale :: String - , location :: Number - , metaKey :: Boolean - , repeat :: Boolean - , shiftKey :: Boolean - , which :: Number - | r - ) - -type SyntheticMouseEvent' r - = ( altKey :: Boolean - , button :: Number - , buttons :: Number - , clientX :: Number - , clientY :: Number - , ctrlKey :: Boolean - , getModifierState :: String -> Boolean - , metaKey :: Boolean - , pageX :: Number - , pageY :: Number - , relatedTarget :: NativeEventTarget - , screenX :: Number - , screenY :: Number - , shiftKey :: Boolean - | r - ) - -type SyntheticTouchEvent' r - = ( altKey :: Boolean - , changedTouches :: NativeTouchList - , ctrlKey :: Boolean - , getModifierState :: String -> Boolean - , metaKey :: Boolean - , targetTouches :: NativeTouchList - , shiftKey :: Boolean - , touches :: NativeTouchList - | r - ) - -type SyntheticTransitionEvent' r - = ( propertyName :: String - , pseudoElement :: String - , elapsedTime :: Number - | r - ) - -type SyntheticUIEvent' r - = ( detail :: Number - , view :: NativeAbstractView - | r - ) - -type SyntheticWheelEvent' r - = ( deltaMode :: Number - , deltaX :: Number - , deltaY :: Number - , deltaZ :: Number - | r - ) +type SyntheticEvent' r = + ( bubbles :: Boolean + , cancelable :: Boolean + , currentTarget :: NativeEventTarget + , defaultPrevented :: Boolean + , eventPhase :: Number + , isTrusted :: Boolean + , nativeEvent :: NativeEvent + , target :: NativeEventTarget + , timeStamp :: Number + , type :: String + | r + ) + +type SyntheticAnimationEvent' r = + ( animationName :: String + , pseudoElement :: String + , elapsedTime :: Number + | r + ) + +type SyntheticClipboardEvent' r = + ( clipboardData :: NativeDataTransfer + | r + ) + +type SyntheticCompositionEvent' r = + ( data :: String + | r + ) + +type SyntheticFocusEvent' r = + ( relatedTarget :: NativeEventTarget + | r + ) + +type SyntheticKeyboardEvent' r = + ( altKey :: Boolean + , ctrlKey :: Boolean + , getModifierState :: String -> Boolean + , charCode :: Int + , key :: String + , keyCode :: Number + , locale :: String + , location :: Number + , metaKey :: Boolean + , repeat :: Boolean + , shiftKey :: Boolean + , which :: Number + | r + ) + +type SyntheticMouseEvent' r = + ( altKey :: Boolean + , button :: Number + , buttons :: Number + , clientX :: Number + , clientY :: Number + , ctrlKey :: Boolean + , getModifierState :: String -> Boolean + , metaKey :: Boolean + , pageX :: Number + , pageY :: Number + , relatedTarget :: NativeEventTarget + , screenX :: Number + , screenY :: Number + , shiftKey :: Boolean + | r + ) + +type SyntheticTouchEvent' r = + ( altKey :: Boolean + , changedTouches :: NativeTouchList + , ctrlKey :: Boolean + , getModifierState :: String -> Boolean + , metaKey :: Boolean + , targetTouches :: NativeTouchList + , shiftKey :: Boolean + , touches :: NativeTouchList + | r + ) + +type SyntheticTransitionEvent' r = + ( propertyName :: String + , pseudoElement :: String + , elapsedTime :: Number + | r + ) + +type SyntheticUIEvent' r = + ( detail :: Number + , view :: NativeAbstractView + | r + ) + +type SyntheticWheelEvent' r = + ( deltaMode :: Number + , deltaX :: Number + , deltaY :: Number + , deltaZ :: Number + | r + ) bubbles :: forall r. SyntheticEvent_ (bubbles :: Boolean | r) -> Effect Boolean bubbles = get (Proxy :: Proxy "bubbles") @@ -396,13 +384,17 @@ foreign import isPropagationStopped :: forall r. SyntheticEvent_ r -> Effect Boo foreign import persist :: forall r. SyntheticEvent_ r -> Effect Unit -foreign import getModifierState :: forall r. String -> SyntheticEvent_ (getModifierState :: String -> Boolean | r) -> Effect Boolean +foreign import getModifierState + :: forall r + . String + -> SyntheticEvent_ (getModifierState :: String -> Boolean | r) + -> Effect Boolean get - :: forall l r s a proxy + :: forall l r s a . Row.Cons l a r s => IsSymbol l - => proxy l + => Proxy l -> SyntheticEvent_ s -> Effect a get l r = unsafeGet (reflectSymbol l) r