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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
module Gargantext.Components.Forest.Tree.Node.Tools where
import Gargantext.Prelude
import Data.Array as A
import Data.Maybe (fromMaybe, Maybe(..))
import Data.Set (Set)
import Data.Set as Set
import Data.String as S
import Data.Tuple.Nested ((/\))
import Effect (Effect)
import Effect.Aff (Aff, launchAff, launchAff_)
import Gargantext.Components.Bootstrap as B
import Gargantext.Components.Bootstrap.Types (Elevation(..), Variant(..))
import Gargantext.Components.Forest.Tree.Node.Action (icon, text)
import Gargantext.Components.Forest.Tree.Node.Action.Types (Action)
import Gargantext.Components.InputWithEnter (inputWithEnter)
import Gargantext.Types as GT
import Gargantext.Utils (toggleSet)
import Gargantext.Utils.Reactix as R2
import Gargantext.Utils.Show as GUS
import Reactix as R
import Reactix.DOM.HTML as H
import Record.Extra as RX
import Toestand as T
here :: R2.Here
here = R2.here "Gargantext.Components.Forest.Tree.Node.Tools"
fragmentPT :: String -> R.Element
fragmentPT text = H.div { style: { margin: "10px" } } [ H.text text ]
type PanelProps =
(mError :: Maybe String)
-- | Last element of panel's children goes to footer, all others go to body
panel :: R2.Component PanelProps
panel = R.createElement panelCpt
panelCpt :: R.Component PanelProps
panelCpt = here.component "panel" cpt
where
cpt { mError } children = do
let
errorCpt =
R2.fromMaybe mError $
\err ->
R2.row
[ R2.col 12
[ H.div { className: "alert alert-danger" } [ H.text err ]
]
]
pure $ R.fragment
[ H.div { className: "card-body" }
[ H.div { className: "card-text" }
[ R2.row
-- TODO add type for text or form here [ H.form {className: "form-horizontal"} bodies ]
[ R2.col 12 bodies ]
, errorCpt
]
]
, H.div { className: "card-footer" }
[ R2.row
[ H.div { className: "mx-auto" } [ footer ] ]
]
]
where
bodies /\ footer =
case A.unsnoc children of
Nothing -> [] /\ (H.div {} [])
Just { init, last } -> init /\ last
-- | A panel without a footer
panelNoFooter :: R2.Component PanelProps
panelNoFooter = R.createElement panelNoFooterCpt
panelNoFooterCpt :: R.Component PanelProps
panelNoFooterCpt = here.component "panelNoFooter" cpt
where
cpt props children =
pure $ panel props (children <> [ H.div {} [] ])
type PanelWithSubmitButtonProps =
( action :: Action
, dispatch :: Action -> Aff Unit
| PanelProps
)
-- | A panel with 'submitButton { action, dispatch }'
panelWithSubmitButton :: R2.Component PanelWithSubmitButtonProps
panelWithSubmitButton = R.createElement panelWithSubmitButtonCpt
panelWithSubmitButtonCpt :: R.Component PanelWithSubmitButtonProps
panelWithSubmitButtonCpt = here.component "panelWithSubmitButton" cpt
where
cpt props@{ action, dispatch } children = do
let pProps = (RX.pick props :: Record PanelProps)
pure $ panel pProps
( children
-- footer
<> [ submitButton { action, dispatch } ]
)
type PanelWithSubmitButtonHrefProps =
( action :: Action
, href :: String
| PanelProps
)
-- | A panel with 'submitButtonHref { action, href }'
panelWithSubmitButtonHref :: R2.Component PanelWithSubmitButtonHrefProps
panelWithSubmitButtonHref = R.createElement panelWithSubmitButtonHrefCpt
panelWithSubmitButtonHrefCpt :: R.Component PanelWithSubmitButtonHrefProps
panelWithSubmitButtonHrefCpt = here.component "panelWithSubmitButtonHref" cpt
where
cpt props@{ action, href } children = do
let pProps = (RX.pick props :: Record PanelProps)
pure $ panel pProps
( children
-- footer
<> [ submitButtonHref { action, href } ]
)
type TextInputBoxProps =
( id :: GT.ID
, dispatch :: Action -> Aff Unit
, text :: String
, isOpen :: T.Box Boolean
, boxName :: String
, boxAction :: String -> Action
)
textInputBox :: R2.Component TextInputBoxProps
textInputBox = R.createElement textInputBoxCpt
textInputBoxCpt :: R.Component TextInputBoxProps
textInputBoxCpt = here.component "textInputBox" cpt
where
cpt { boxAction, boxName, dispatch, isOpen, text } _ =
content <$> T.useLive T.unequal isOpen <*> R.useRef text
where
content false _ = (R.fragment [])
content true renameNodeNameRef =
H.div
{ className: "d-flex align-items-center justify-content-space-between" }
[ textInput renameNodeNameRef
, submitBtn renameNodeNameRef
, cancelBtn
]
textInput renameNodeNameRef =
H.div
{ className: "w-10/12" }
[ inputWithEnter
{ autoFocus: true
, className: "form-control"
, defaultValue: text
, onBlur: R.setRef renameNodeNameRef
, onEnter: submit renameNodeNameRef
, onValueChanged: R.setRef renameNodeNameRef
, placeholder: (boxName <> " Node")
, type: "text"
, required: false
}
]
submitBtn renameNodeNameRef =
B.iconButton
{ callback: submit renameNodeNameRef
, title: "Submit"
, name: "floppy-o"
, elevation: Level1
}
cancelBtn =
B.iconButton
{ callback: const $ T.write_ false isOpen
, variant: Danger
, title: "Cancel"
, name: "times"
}
submit ref _ = do
launchAff_ $ dispatch (boxAction $ R.readRef ref)
T.write_ false isOpen
type InviteInputBoxProps =
( id :: GT.ID
, dispatch :: Action -> Aff Unit
, text :: String
, boxName :: String
, boxAction :: String -> Action
, username :: T.Box String
)
inviteInputBox :: R2.Component InviteInputBoxProps
inviteInputBox = R.createElement inviteInputBoxCpt
inviteInputBoxCpt :: R.Component InviteInputBoxProps
inviteInputBoxCpt = here.component "textInputBox" cpt
where
cpt { boxAction, boxName, dispatch, text, username } _ =
content <$> R.useRef text
where
content renameNodeNameRef =
H.div
{ className: "d-flex align-items-center" }
[ textInput renameNodeNameRef
, B.wad_ [ "d-inline-block", "w-3" ]
, submitBtn renameNodeNameRef
]
textInput renameNodeNameRef =
H.div
{}
[ inputWithEnter
{ autoFocus: true
, className: "form-control"
, defaultValue: text
, onBlur: R.setRef renameNodeNameRef
, onEnter: submit renameNodeNameRef
, onValueChanged: R.setRef renameNodeNameRef
, placeholder: (boxName <> " Node")
, type: "text"
, required: false
}
]
submitBtn renameNodeNameRef =
B.iconButton
{ callback: submit renameNodeNameRef
, title: "Submit"
, name: "plus"
, elevation: Level1
}
submit ref _ = do
T.write_ ("Invited " <> R.readRef ref <> " to the team") username
launchAff_ $ dispatch (boxAction $ R.readRef ref)
-- type DefaultText = String
-- formEdit :: forall prev next
-- . DefaultText -> ((prev -> String) -> Effect next) -> R.Element
-- formEdit defaultValue setter =
-- H.div { className: "form-group" }
-- [ H.input { defaultValue, type: "text", on: { input }
-- , placeholder: defaultValue, className: "form-control" }
-- ] where input = setter <<< const <<< R.unsafeEventValue
type FormChoiceSafeProps item m =
( items :: Array item
, default :: item
, callback :: item -> Effect m
, print :: item -> String
)
-- | Form Choice input
-- if the list of options is not big enough, a button is used instead
formChoiceSafe
:: forall item m
. Show item
=> R2.Component (FormChoiceSafeProps item m)
formChoiceSafe = R.createElement formChoiceSafeCpt
formChoiceSafeCpt :: forall item m. Show item => R.Component (FormChoiceSafeProps item m)
formChoiceSafeCpt = here.component "formChoiceSafe" cpt
where
cpt { items, default, callback, print } _ = do
pure $ case items of
[] -> H.div {} []
[ n ] -> formButton { item: n, callback, print } []
_ -> formChoice { items, default, callback, print } []
type FormChoiceProps item m =
( items :: Array item
, default :: item
, callback :: item -> Effect m
, print :: item -> String
)
-- | List Form
formChoice
:: forall item m
. Show item
=> R2.Component (FormChoiceProps item m)
formChoice = R.createElement formChoiceCpt
formChoiceCpt :: forall item m. Show item => R.Component (FormChoiceProps item m)
formChoiceCpt = here.component "formChoice" cpt
where
cpt { items, callback, default, print } _ = do
pure $ H.div { className: "form-group" }
[ R2.select
{ className: "form-control with-icon-font"
, defaultValue: show default
, on: { change }
} $ map option items
]
where
change e = callback $ fromMaybe default $ reader $ R.unsafeEventValue e
option opt = H.option { value: show opt } [ H.text $ print opt ]
reader = GUS.reader items
type FormButtonProps item m =
( item :: item
, callback :: item -> Effect m
, print :: item -> String
)
-- | Button Form
-- FIXME: currently needs a click from the user (by default, we could avoid such click)
formButton :: forall item m. R2.Component (FormButtonProps item m)
formButton = R.createElement formButtonCpt
formButtonCpt :: forall item m. R.Component (FormButtonProps item m)
formButtonCpt = here.component "formButton" cpt
where
cpt { item, callback, print } _ = do
pure $ H.div {}
[ H.text $ "Confirm the selection of: " <> print item
, cta
]
where
cta =
H.button
{ className: "cold-md-5 btn btn-primary center"
, type: "button"
, title: "Form Button"
, style: { width: "100%" }
, on: { click: \_ -> callback item }
}
[ H.text "Confirmation" ]
------------------------------------------------------------------------
------------------------------------------------------------------------
type SubmitButtonProps =
( action :: Action
, dispatch :: Action -> Aff Unit
)
submitButton :: R2.Leaf SubmitButtonProps
submitButton = R2.leaf submitButtonCpt
submitButtonCpt :: R.Component SubmitButtonProps
submitButtonCpt = here.component "submitButton" cpt
where
cpt
{ action
, dispatch
}
_ = do
pure $ H.button
{ className: "btn btn-primary fa fa-" <> icon action
, type: "button"
, id: S.toLower $ show action
, title: show action
, on: { click: \_ -> launchAff $ dispatch action }
}
[ H.span { className: "font-family-theme mx-1" } [ H.text $ " " <> text action ] ]
type Href = String
type SubmitButtonHrefProps =
( action :: Action
, href :: Href
)
submitButtonHref :: R2.Leaf SubmitButtonHrefProps
submitButtonHref = R2.leaf submitButtonHrefCpt
submitButtonHrefCpt :: R.Component SubmitButtonHrefProps
submitButtonHrefCpt = here.component "submitButtonHref" cpt
where
cpt { action, href } _ = do
pure $
H.a { className, href, target: "_blank" }
[ H.span { className: "font-family-theme mx-1" } [ H.text $ " " <> text action ] ]
where
className = "btn btn-primary fa fa-" <> icon action
------------------------------------------------------------------------
-- | CheckBox tools
-- checkboxes: Array of boolean values (basic: without pending option)
-- checkbox : One boolean value only
type CheckboxProps =
(value :: T.Box Boolean)
checkbox :: R2.Leaf CheckboxProps
checkbox = R2.leaf checkboxCpt
checkboxCpt :: R.Component CheckboxProps
checkboxCpt = here.component "checkbox" cpt
where
cpt { value } _ = do
value' <- T.useLive T.unequal value
pure $ H.input
{ className: "form-check-input"
, on: { click }
, type: "checkbox"
, value: value'
}
where
click _ = T.modify_ not value
data CheckBoxes = Multiple | Uniq
type CheckboxesListGroup a =
( groups :: Array a
, options :: T.Box (Set a)
)
checkboxesListGroup :: forall a. Ord a => Show a => R2.Component (CheckboxesListGroup a)
checkboxesListGroup = R.createElement checkboxesListGroupCpt
checkboxesListGroupCpt :: forall a. Ord a => Show a => R.Component (CheckboxesListGroup a)
checkboxesListGroupCpt = here.component "checkboxesListGroup" cpt
where
cpt { options } _ = do
options' <- T.useLive T.unequal options
let
one a =
H.li { className: "list-group-item" }
[ H.div { className: "form-check" }
[ H.input
{ defaultChecked: Set.member a options'
, on:
{ click: \_ -> T.write_ (toggleSet a options') options
, type: "checkbox"
}
, className: "form-check-input"
}
, H.label { className: "form-check-label" } [ H.text (show a) ]
]
]
pure $ R.fragment $ map one $ Set.toUnfoldable options'