-
Notifications
You must be signed in to change notification settings - Fork 2
/
Import.purs
111 lines (105 loc) · 4.88 KB
/
Import.purs
1
2
3
4
5
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
-- | This module provides types to support FFI-importing React components into
-- | Elmish parlance. A typical import of a React component consists of three
-- | parts:
-- |
-- | * A row of props, with optional props denoted via `Opt`.
-- | * Actual FFI-import of the component constructor. This import is weakly
-- | typed and shouldn't be exported from the module. Consider it internal
-- | implementation detail.
-- | * Strongly-typed, PureScript-friendly function that constructs the
-- | component. The body of such function usually consists of just a call
-- | to `createElement` (or `createElement'` for childless components), its
-- | only purpose being the type signature. This function is what should be
-- | exported for use by consumers.
-- |
-- | Classes and type aliases provided in this module, when applied to the
-- | constructor function, make it possible to pass only partial props to it,
-- | while still ensuring their correct types and presence of non-optional ones.
-- | This is facilitated by the
-- | https://github.com/paluh/purescript-undefined-is-not-a-problem/ library.
-- |
-- | Example:
-- |
-- | // JSX
-- | // `world` prop is required, `hello` and `highlight` are optional
-- | export const MyComponent = ({ hello, world, highlight }) =>
-- | <div>
-- | <span>{hello || "Hello"}, </span>
-- | <span style={{ color: highlight ? "red" : "" }}>{world}</span>
-- | </div>
-- |
-- |
-- | -- PureScript
-- | module MyComponent(Props, OptProps, myComponent) where
-- |
-- | import Data.Undefined.NoProblem (Opt)
-- | import Elmish.React (createElement)
-- | import Elmish.React.Import (ImportedReactComponentConstructor, ImportedReactComponent)
-- |
-- | type Props = ( world :: String, hello :: Opt String, highlight :: Opt Boolean )
-- |
-- | myComponent :: ImportedReactComponentConstructor Props OptProps
-- | myComponent = createElement myComponent_
-- |
-- | foreign import myComponent_ :: ImportedReactComponent
-- |
-- |
-- | -- PureScript use site
-- | import MyComponent (myComponent)
-- | import Elmish.React.DOM (fragment)
-- |
-- | view :: ...
-- | view = H.fragment
-- | [ myComponent { world: "world" }
-- | , myComponent { hello: "Goodbye", world: "cruel world!", highlight: true }
-- | ]
-- |
module Elmish.React.Import
( CommonProps
, ImportedReactComponentConstructor'
, ImportedReactComponentConstructor
, ImportedReactComponentConstructorWithContent
, ImportedReactComponent
) where
import Data.Undefined.NoProblem (Opt)
import Data.Undefined.NoProblem.Closed as Closed
import Elmish.React (class ReactChildren, class ValidReactProps, ReactComponent, ReactElement)
import Type.Row (type (+))
-- | Row of props that are common to all React components, without having to
-- | declare them.
type CommonProps r = ( key :: Opt String | r )
-- | Type of a function used to create a React JSX-imported component that is
-- | generic in such a way as to allow only subset of properties to be passed
-- | in, while ensuring that all non-optional props are present and have the
-- | right types.
type ImportedReactComponentConstructor' allowedProps result =
forall props
. Closed.Coerce props { | CommonProps + allowedProps }
=> ValidReactProps props
=> props
-> result
-- | Type of a function used to create a React JSX-imported component that
-- | doesn't admit children. The function is generic in such a way as to allow
-- | only subset of properties to be passed in, while ensuring that all
-- | non-optional props are present and have the right types.
type ImportedReactComponentConstructor allowedProps =
ImportedReactComponentConstructor' allowedProps ReactElement
-- | Type of a function used to create a React JSX-imported component that can
-- | include children. The function is generic in such a way as to allow only
-- | subset of properties to be passed in, while ensuring that all non-optional
-- | props are present and have the right types. The children are polymorphic,
-- | expressed via the `ReactChildren` type class.
type ImportedReactComponentConstructorWithContent allowedProps =
forall content
. ReactChildren content
=> ImportedReactComponentConstructor' allowedProps (content -> ReactElement)
-- | A React component directly imported from JavaScript.
-- |
-- | NOTE: This type has an unconstrained type parameter, which reflects the
-- | fact that React components don't actually have any hard constraints on the
-- | props they take. The corollary is that these FFI-imported components are
-- | not supposed to be public (which, TBH, applies to all FFI imports), and the
-- | type safety is supposed to come from a wrapper function of type
-- | `ImportedReactComponentConstructor` (see above), which would have the
-- | appropriate props constraints.
type ImportedReactComponent = forall r. ReactComponent r