Skip to content

Commit 467b35f

Browse files
authored
Merge pull request #45 from JordanMartinez/exposeInput
Expose `input` type in Select components and combine `spec` and `input` into `Select.component`
2 parents 21d63d1 + e6fcd98 commit 467b35f

7 files changed

Lines changed: 151 additions & 154 deletions

File tree

examples/Components/Dropdown.purs

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ module Components.Dropdown where
22

33
import Prelude
44

5-
import Data.Const (Const)
65
import Effect.Aff (Aff)
76
import Data.Array ((!!), mapWithIndex, length)
87
import Data.Maybe (Maybe(..), fromMaybe)
@@ -18,7 +17,7 @@ type Slot =
1817

1918
type State =
2019
( items :: Array String
21-
, selection :: Maybe String
20+
, selection :: Maybe String
2221
, buttonLabel :: String
2322
)
2423

@@ -32,30 +31,35 @@ type Input =
3231
, buttonLabel :: String
3332
}
3433

35-
input :: Input -> S.Input State
36-
input { items, buttonLabel } =
37-
{ inputType: S.Toggle
38-
, search: Nothing
39-
, debounceTime: Nothing
40-
, getItemCount: length <<< _.items
41-
, items
42-
, buttonLabel
43-
, selection: Nothing
34+
component :: H.Component HH.HTML S.Query' Input Message Aff
35+
component = S.component input $ S.defaultSpec
36+
{ render = render
37+
, handleEvent = handleEvent
4438
}
45-
46-
spec :: S.Spec State (Const Void) Void () Message Aff
47-
spec = S.defaultSpec { render = render, handleMessage = handleMessage }
4839
where
49-
handleMessage = case _ of
40+
input :: Input -> S.Input State
41+
input { items, buttonLabel } =
42+
{ inputType: S.Toggle
43+
, search: Nothing
44+
, debounceTime: Nothing
45+
, getItemCount: length <<< _.items
46+
, items
47+
, buttonLabel
48+
, selection: Nothing
49+
}
50+
51+
handleEvent :: S.Event -> H.HalogenM (S.State State) S.Action' () Message Aff Unit
52+
handleEvent = case _ of
5053
S.Selected ix -> do
5154
st <- H.get
5255
let selection = st.items !! ix
5356
H.modify_ _ { selection = selection, visibility = S.Off }
5457
H.raise $ SelectionChanged st.selection selection
5558
_ -> pure unit
5659

57-
render st =
58-
HH.div
60+
render :: S.State State -> H.ComponentHTML S.Action' () Aff
61+
render st =
62+
HH.div
5963
[ class_ "Dropdown" ]
6064
[ renderToggle, renderContainer ]
6165
where
@@ -70,13 +74,12 @@ spec = S.defaultSpec { render = render, handleMessage = handleMessage }
7074
( renderItem `mapWithIndex` st.items )
7175
where
7276
renderItem index item =
73-
HH.div
74-
( SS.setItemProps index
75-
[ classes_
77+
HH.div
78+
( SS.setItemProps index
79+
[ classes_
7680
[ "Dropdown__item"
7781
, "Dropdown__item--highlighted" # guard (st.highlightedIndex == Just index)
7882
]
7983
]
80-
)
84+
)
8185
[ HH.text item ]
82-

examples/Components/Typeahead.purs

Lines changed: 50 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ import Internal.RemoteData as RD
2424
import Select as S
2525
import Select.Setters as SS
2626

27-
type Slot =
27+
type Slot =
2828
S.Slot Query ChildSlots Message
2929

3030
type State =
31-
( selections :: Array Location
31+
( selections :: Array Location
3232
, available :: RD.RemoteData String (Array Location)
3333
)
3434

@@ -39,51 +39,51 @@ data Action
3939
data Query a
4040
= GetSelections (Array Location -> a)
4141

42-
data Message
43-
= ItemRemoved Location
42+
data Message
43+
= ItemRemoved Location
4444
| SelectionsChanged (Array Location)
4545

46-
type ChildSlots =
46+
type ChildSlots =
4747
( dropdown :: D.Slot Unit )
4848

49-
-- this typeahead will be opaque; users can just use this pre-built
50-
-- input instead of the usual select one.
51-
input :: S.Input State
52-
input =
53-
{ inputType: S.Text
54-
, debounceTime: Just (Milliseconds 300.0)
55-
, search: Nothing
56-
, getItemCount: maybe 0 length <<< RD.toMaybe <<< _.available
57-
, selections: []
58-
, available: RD.NotAsked
59-
}
60-
61-
spec :: S.Spec State Query Action ChildSlots Message Aff
62-
spec = S.defaultSpec
49+
component :: H.Component HH.HTML (S.Query Query ChildSlots) Unit Message Aff
50+
component = S.component (const input) $ S.defaultSpec
6351
{ render = render
6452
, handleAction = handleAction
6553
, handleQuery = handleQuery
66-
, handleMessage = handleMessage
54+
, handleEvent = handleEvent
6755
}
6856
where
69-
handleMessage
70-
:: S.Message
57+
-- this typeahead will be opaque; users can just use this pre-built
58+
-- input instead of the usual select one.
59+
input :: S.Input State
60+
input =
61+
{ inputType: S.Text
62+
, debounceTime: Just (Milliseconds 300.0)
63+
, search: Nothing
64+
, getItemCount: maybe 0 length <<< RD.toMaybe <<< _.available
65+
, selections: []
66+
, available: RD.NotAsked
67+
}
68+
69+
handleEvent
70+
:: S.Event
7171
-> H.HalogenM (S.State State) (S.Action Action) ChildSlots Message Aff Unit
72-
handleMessage = case _ of
72+
handleEvent = case _ of
7373
S.Selected ix -> do
7474
st <- H.get
7575
for_ st.available \arr ->
7676
for_ (arr !! ix) \item -> do
7777
let newSelections = item : st.selections
78-
H.modify_ _
78+
H.modify_ _
7979
{ selections = item : st.selections
8080
, available = RD.Success (filter (_ /= item) arr)
8181
, search = ""
8282
}
8383
H.raise $ SelectionsChanged newSelections
8484
S.Searched str -> do
8585
st <- H.get
86-
-- we'll use an external api to search locations
86+
-- we'll use an external api to search locations
8787
H.modify_ _ { available = RD.Loading }
8888
items <- H.liftAff $ searchLocations str
8989
H.modify_ _ { available = items <#> \xs -> difference xs st.selections }
@@ -92,13 +92,13 @@ spec = S.defaultSpec
9292
-- You can remove all type signatures except for this one; we need to tell the
9393
-- compiler about the `a` type variable. The minimal necessary signature is below.
9494
handleQuery :: forall a. Query a -> H.HalogenM _ _ _ _ _ (Maybe a)
95-
handleQuery = case _ of
95+
handleQuery = case _ of
9696
GetSelections reply -> do
9797
st <- H.get
9898
pure $ Just $ reply st.selections
9999

100-
handleAction
101-
:: Action
100+
handleAction
101+
:: Action
102102
-> H.HalogenM (S.State State) (S.Action Action) ChildSlots Message Aff Unit
103103
handleAction = case _ of
104104
Remove item -> do
@@ -109,23 +109,23 @@ spec = S.defaultSpec
109109
HandleDropdown msg -> case msg of
110110
D.SelectionChanged oldSelection newSelection -> do
111111
st <- H.get
112-
let
112+
let
113113
mkLocation str = { name: "User Added: " <> str, population: "1" }
114114
newSelections = case oldSelection, newSelection of
115-
Nothing, Nothing ->
115+
Nothing, Nothing ->
116116
Nothing
117-
Nothing, Just str ->
117+
Nothing, Just str ->
118118
Just (mkLocation str : st.selections)
119-
Just str, Nothing ->
119+
Just str, Nothing ->
120120
Just (filter (_ /= mkLocation str) st.selections)
121-
Just old, Just new ->
121+
Just old, Just new ->
122122
Just (mkLocation new : (filter (_ /= mkLocation old) st.selections))
123-
for_ newSelections \selections ->
123+
for_ newSelections \selections ->
124124
H.modify_ _ { selections = selections }
125125

126126
render :: S.State State -> H.ComponentHTML (S.Action Action) ChildSlots Aff
127-
render st =
128-
HH.div
127+
render st =
128+
HH.div
129129
[ class_ "Typeahead" ]
130130
[ renderSelections, renderInput, renderDropdown, renderContainer ]
131131
where
@@ -139,40 +139,40 @@ spec = S.defaultSpec
139139
renderSelectedItem item =
140140
HH.div
141141
[ class_ "Typeahead__item--selected Location" ]
142-
[ HH.span
142+
[ HH.span
143143
[ class_ "Location__name" ]
144144
[ HH.text item.name ]
145145
, closeButton item
146146
]
147147

148148
closeButton item =
149149
HH.span
150-
[ class_ "Location__closeButton"
150+
[ class_ "Location__closeButton"
151151
, HE.onClick \_ -> Just $ S.Action $ Remove item
152152
]
153153
[ HH.text "×" ]
154154

155155
renderInput = HH.input $ SS.setInputProps
156-
[ classes_
156+
[ classes_
157157
[ "Typeahead__input"
158158
, "Typeahead__input--selections" # guard hasSelections
159159
, "Typeahead__input--active" # guard (st.visibility == S.On)
160160
]
161-
, HP.placeholder "Type to search..."
161+
, HP.placeholder "Type to search..."
162162
]
163163

164164
renderDropdown = whenElem (st.visibility == S.On) \_ ->
165-
HH.slot _dropdown unit (S.component D.spec) (D.input dropdownInput) handler
165+
HH.slot _dropdown unit D.component dropdownInput handler
166166
where
167167
_dropdown = SProxy :: SProxy "dropdown"
168168
handler msg = Just $ S.Action $ HandleDropdown msg
169169
dropdownInput = { items: [ "Earth", "Mars" ], buttonLabel: "Human Planets" }
170170

171171
renderContainer = whenElem (st.visibility == S.On) \_ ->
172-
HH.div
173-
(SS.setContainerProps
174-
[ classes_
175-
[ "Typeahead__container"
172+
HH.div
173+
(SS.setContainerProps
174+
[ classes_
175+
[ "Typeahead__container"
176176
, "Typeahead__container--hasItems" # guard hasItems
177177
]
178178
]
@@ -186,14 +186,14 @@ spec = S.defaultSpec
186186
RD.NotAsked -> renderMsg "No search performed..."
187187
RD.Loading -> renderMsg "Loading..."
188188
RD.Failure e -> renderMsg e
189-
RD.Success available
189+
RD.Success available
190190
| hasItems -> renderItem `mapWithIndex` available
191191
| otherwise -> renderMsg "No results found"
192192

193193
renderItem index { name, population } =
194-
HH.div
195-
(SS.setItemProps index [ classes_ [ base, highlight, "Location" ] ])
196-
[ HH.span
194+
HH.div
195+
(SS.setItemProps index [ classes_ [ base, highlight, "Location" ] ])
196+
[ HH.span
197197
[ class_ "Location__name" ]
198198
[ HH.text name ]
199199
, HH.span
@@ -214,7 +214,6 @@ type Location =
214214

215215
searchLocations :: String -> Aff (RD.RemoteData String (Array Location))
216216
searchLocations search = do
217-
res <- AX.get AR.json ("https://swapi.co/api/planets/?search=" <> search)
217+
res <- AX.get AR.json ("https://swapi.co/api/planets/?search=" <> search)
218218
let body = lmap AR.printResponseFormatError res.body
219219
pure $ RD.fromEither $ traverse decodeJson =<< (_ .: "results") =<< decodeJson =<< body
220-

examples/Internal/CSS.purs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,3 @@ classes_ = HP.classes <<< map HH.ClassName
1313

1414
whenElem :: forall p i. Boolean -> (Unit -> HH.HTML i p) -> HH.HTML i p
1515
whenElem cond render = if cond then render unit else HH.text ""
16-

examples/Internal/RemoteData.purs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
-- | Copied over from
1+
-- | Copied over from
22
-- | https://github.com/krisajenkins/purescript-remotedata
33
-- |
44
-- | due to dependency conflicts
@@ -42,4 +42,3 @@ fromMaybe (Just value) = Success value
4242
fromEither :: forall e a. Either e a -> RemoteData e a
4343
fromEither (Left err) = Failure err
4444
fromEither (Right value) = Success value
45-

examples/Main.purs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import Halogen.Aff as HA
1919
import Halogen.HTML as HH
2020
import Halogen.VDom.Driver (runUI)
2121
import Internal.Proxy (ProxyS, proxy)
22-
import Select as Select
2322
import Web.DOM.Element (getAttribute)
2423
import Web.DOM.NodeList (toArray)
2524
import Web.DOM.ParentNode (QuerySelector(..), querySelectorAll)
@@ -48,7 +47,7 @@ type Components
4847
routes :: Components
4948
routes = M.fromFoldable
5049
[ Tuple "typeahead" $ proxy typeahead
51-
, Tuple "dropdown" $ proxy dropdown
50+
, Tuple "dropdown" $ proxy dropdown
5251
]
5352

5453
app :: H.Component HH.HTML (Const Void) String Void Aff
@@ -74,9 +73,9 @@ selectElements
7473
:: { query :: QuerySelector, attr :: String }
7574
-> Aff (Array { element :: HTMLElement, attr :: String })
7675
selectElements { query, attr } = do
77-
nodeArray <- liftEffect do
76+
nodeArray <- liftEffect do
7877
toArray =<< querySelectorAll query <<< toParentNode =<< document =<< window
79-
let
78+
let
8079
elems = fromMaybe [] <<< sequence $ fromNode <$> nodeArray
8180
attrs <- liftEffect $ traverse (getAttribute attr <<< toElement) elems
8281
pure $ zipWith ({ element: _, attr: _ }) elems (fromMaybe "" <$> attrs)
@@ -88,7 +87,7 @@ dropdown :: forall t0 t1 t2. H.Component HH.HTML t0 t1 t2 Aff
8887
dropdown = H.mkComponent
8988
{ initialState: const unit
9089
, render: \_ ->
91-
HH.slot label unit (Select.component Dropdown.spec) (Dropdown.input input) \_ -> Nothing
90+
HH.slot label unit Dropdown.component input \_ -> Nothing
9291
, eval: H.mkEval H.defaultEval
9392
}
9493
where
@@ -99,9 +98,8 @@ typeahead :: forall t0 t1 t2. H.Component HH.HTML t0 t1 t2 Aff
9998
typeahead = H.mkComponent
10099
{ initialState: const unit
101100
, render: \_ ->
102-
HH.slot label unit (Select.component Typeahead.spec) Typeahead.input \_ -> Nothing
101+
HH.slot label unit Typeahead.component unit \_ -> Nothing
103102
, eval: H.mkEval H.defaultEval
104103
}
105104
where
106105
label = SProxy :: SProxy "typeahead"
107-

0 commit comments

Comments
 (0)