@@ -13,7 +13,7 @@ import Data.Array (delete, elem, find, fold, length, mapMaybe, null, snoc)
13
13
import Data.Array as Array
14
14
import Data.Either (either )
15
15
import Data.Foldable (for_ )
16
- import Data.Maybe (Maybe (..), fromMaybe , isJust , maybe )
16
+ import Data.Maybe (Maybe (..), fromMaybe , isJust , isNothing , maybe )
17
17
import Data.Monoid (guard )
18
18
import Data.Newtype (class Newtype , un )
19
19
import Data.Nullable (Nullable , toMaybe )
@@ -92,6 +92,23 @@ type TableProps row =
92
92
, hidden :: Boolean
93
93
, sticky :: Boolean
94
94
}
95
+ , onColumnChange ::
96
+ Nullable
97
+ ( EffectFn1
98
+ ( Array
99
+ { required :: Boolean
100
+ , name :: ColumnName
101
+ , label :: Nullable String
102
+ , filterLabel :: Nullable String
103
+ , sortBy :: Nullable ColumnName
104
+ , style :: R.CSS
105
+ , renderCell :: row -> JSX
106
+ , hidden :: Boolean
107
+ , sticky :: Boolean
108
+ }
109
+ )
110
+ Unit
111
+ )
95
112
}
96
113
97
114
component :: forall row . Component (TableProps row )
@@ -108,7 +125,7 @@ data Action
108
125
table :: forall a . TableProps a -> JSX
109
126
table = make component
110
127
{ initialState
111
- , didMount
128
+ , didMount: syncProps
112
129
, didUpdate
113
130
, render
114
131
}
@@ -123,19 +140,16 @@ table = make component
123
140
, menuStyle: { top: " 0px" , left: " 0px" }
124
141
}
125
142
126
- didMount self = do
127
- maybeSavedColumnSort <- loadColumnState $ columnSaveKey self.props.name
128
- syncProps self maybeSavedColumnSort
129
-
130
143
didUpdate self _ = do
131
144
case toMaybe self.props.selected of
132
145
Nothing -> pure unit
133
146
Just selected ->
134
147
when (selected /= self.state.selected) do
135
- syncProps self Nothing
148
+ syncProps self
136
149
137
- syncProps self maybeSavedColumnSort = do
138
- when (null self.state.columns) do
150
+ syncProps self = do
151
+ when (isNothing (toMaybe self.props.onColumnChange) && null self.state.columns) do
152
+ maybeSavedColumnSort <- loadColumnState $ columnSaveKey self.props.name
139
153
self.setState \state -> state
140
154
{ columns =
141
155
case maybeSavedColumnSort of
@@ -157,21 +171,21 @@ table = make component
157
171
self.setState \state -> state { showMenu = false }
158
172
159
173
setColumnSort self newColumnOrder = do
160
- self.setStateThen (\state ->
161
- let
162
- columnsSorted = sortColumnsBy state.columns $
163
- newColumnOrder <#> \{ name, hidden } ->
164
- { name: ColumnName name
165
- , hidden
166
- }
167
- in
168
- state { columns = columnsSorted })
169
- do
170
- props <- readProps self
171
- state <- readState self
172
- saveColumnState
173
- (columnSaveKey props.name)
174
- (getColumnSortFields <$> state. columns)
174
+ case toMaybe self.props.onColumnChange of
175
+ Nothing ->
176
+ self.setStateThen (\ state ->
177
+ let
178
+ columnsSorted = state.columns `sortColumnsBy` newColumnOrder
179
+ in
180
+ state { columns = columnsSorted })
181
+ do
182
+ props <- readProps self
183
+ state <- readState self
184
+ saveColumnState
185
+ (columnSaveKey props.name)
186
+ (getColumnSortFields <$> state.columns)
187
+ Just onColumnChange ->
188
+ runEffectFn1 onColumnChange $ self.props. columns `sortColumnsBy` newColumnOrder
175
189
176
190
onSelect self { shift, key, checked } = do
177
191
self.setStateThen (\state ->
@@ -225,79 +239,97 @@ table = make component
225
239
runEffectFn1 props.onSelect state.selected
226
240
227
241
sortColumnsBy columns newColumnOrder =
228
- newColumnOrder `flip mapMaybe` \newCol -> do
229
- matchedCol <- columns `flip find` \c -> c.name == newCol.name
230
- pure $ matchedCol { hidden = newCol.hidden }
242
+ let
243
+ matches =
244
+ newColumnOrder `flip Array.mapMaybe` \newCol -> do
245
+ matchedCol <- columns # find (\c -> c.name == newCol.name)
246
+ pure $ matchedCol { hidden = newCol.hidden }
247
+ newColumnOrderNames = map _.name newColumnOrder
248
+ nonMatches =
249
+ columns
250
+ # Array .filter (\c -> c.name `not elem` newColumnOrderNames)
251
+ # map _ { hidden = true }
252
+ in
253
+ matches <> nonMatches
231
254
232
255
getColumnSortFields { name, hidden } = { name, hidden }
233
256
234
257
render self =
235
- renderLumiTable \tableRef ->
236
- [ if not self.state.showMenu
237
- then empty
238
- else renderFilterDropdown
239
- { close: closeMenu self
240
- , reorderItems: setColumnSort self
241
- , items: self.state.columns <#> \{ name, label, filterLabel, hidden } ->
242
- { name: un ColumnName name
243
- , label
244
- , filterLabel
245
- , hidden
246
- }
247
- , style: R .css self.state.menuStyle
258
+ let
259
+ columns =
260
+ if isNothing $ toMaybe self.props.onColumnChange
261
+ then self.state.columns
262
+ else self.props.columns
263
+ in
264
+ renderLumiTable \tableRef ->
265
+ [ if not self.state.showMenu
266
+ then empty
267
+ else renderFilterDropdown
268
+ { close: closeMenu self
269
+ , reorderItems: setColumnSort self <<< map \{ name, hidden } ->
270
+ { name: ColumnName name
271
+ , hidden
272
+ }
273
+ , items: columns <#> \{ name, label, filterLabel, hidden } ->
274
+ { name: un ColumnName name
275
+ , label
276
+ , filterLabel
277
+ , hidden
278
+ }
279
+ , style: R .css self.state.menuStyle
280
+ }
281
+ , element scrollObserver
282
+ { node: tableRef
283
+ , render: \{ hasScrolledY, hasScrolledX } ->
284
+ R .table
285
+ { className:
286
+ let
287
+ isCompact = contains (Pattern " compact" ) (show self.props.variant)
288
+ isFixed = contains (Pattern " fixed" ) (show self.props.variant)
289
+ in
290
+ joinWith " "
291
+ $ [ " lumi" ]
292
+ <> guard isCompact [ " compact" ]
293
+ <> guard isFixed [ " fixed" ]
294
+ <> guard hasScrolledX [ " has-scrolled-x" ]
295
+ <> guard hasScrolledY [ " has-scrolled-y" ]
296
+ <> guard self.props.selectable [ " selectable" ]
297
+ , children:
298
+ [ renderTableHead columns tableRef
299
+ , R .tbody_
300
+ let
301
+ tableProps =
302
+ { columns
303
+ , primaryColumn
304
+ , selectable: self.props.selectable
305
+ , getRowKey: self.props.getRowKey
306
+ , rowEq: self.props.rowEq
307
+ , onNavigate: self.props.onNavigate
308
+ , onSelect: onSelect self
309
+ }
310
+ in
311
+ self.props.rows # map \row ->
312
+ keyed
313
+ (tableProps.getRowKey row)
314
+ (tableRow
315
+ { tableProps
316
+ , row
317
+ , isSelected:
318
+ tableProps.selectable &&
319
+ tableProps.getRowKey row `elem` selected
320
+ })
321
+ ]
322
+ }
248
323
}
249
- , element scrollObserver
250
- { node: tableRef
251
- , render: \{ hasScrolledY, hasScrolledX } ->
252
- R .table
253
- { className:
254
- let
255
- isCompact = contains (Pattern " compact" ) (show self.props.variant)
256
- isFixed = contains (Pattern " fixed" ) (show self.props.variant)
257
- in
258
- joinWith " "
259
- $ [ " lumi" ]
260
- <> guard isCompact [ " compact" ]
261
- <> guard isFixed [ " fixed" ]
262
- <> guard hasScrolledX [ " has-scrolled-x" ]
263
- <> guard hasScrolledY [ " has-scrolled-y" ]
264
- <> guard self.props.selectable [ " selectable" ]
265
- , children:
266
- [ renderTableHead tableRef
267
- , R .tbody_
268
- let
269
- tableProps =
270
- { columns: self.state.columns
271
- , primaryColumn
272
- , selectable: self.props.selectable
273
- , getRowKey: self.props.getRowKey
274
- , rowEq: self.props.rowEq
275
- , onNavigate: self.props.onNavigate
276
- , onSelect: onSelect self
277
- }
278
- in
279
- self.props.rows # map \row ->
280
- keyed
281
- (tableProps.getRowKey row)
282
- (tableRow
283
- { tableProps
284
- , row
285
- , isSelected:
286
- tableProps.selectable &&
287
- tableProps.getRowKey row `elem` selected
288
- })
289
- ]
290
- }
291
- }
292
- ]
324
+ ]
293
325
where
294
326
selected = fromMaybe self.state.selected (toMaybe self.props.selected)
295
327
296
328
primaryColumn = toMaybe self.props.primaryColumn
297
329
298
- renderTableHead tableRef =
330
+ renderTableHead columns tableRef =
299
331
element (R .unsafeCreateDOMComponent " thead" )
300
- { onContextMenu: Events .handler preventDefault \e -> do
332
+ { onContextMenu: Events .handler ( preventDefault >>> stopPropagation) \e -> do
301
333
{ x, y } <- runEffectFn2 getMouseEventPositionWithOffset tableRef e
302
334
openMenu self
303
335
{ top: show (y - 2.0 ) <> " px"
@@ -307,7 +339,7 @@ table = make component
307
339
[ R .tr_ $
308
340
[ renderHeadCheckbox ]
309
341
<> (maybe [] (pure <<< renderHeadPrimaryCell) primaryColumn)
310
- <> (map renderHeadCell self.state. columns)
342
+ <> (map renderHeadCell columns)
311
343
]
312
344
}
313
345
@@ -320,7 +352,7 @@ table = make component
320
352
then empty
321
353
else R .th
322
354
{ style: R .css { width: " 2rem" }
323
- , onClick: Events .handler stopPropagation (const (pure unit))
355
+ , onClick: Events .handler ( stopPropagation) (const (pure unit))
324
356
, children:
325
357
[ input checkbox
326
358
{ checked =
@@ -409,8 +441,9 @@ table = make component
409
441
, handler: case maybeMenuRef of
410
442
Nothing -> \e -> pure unit
411
443
Just menuRef -> \e -> do
444
+
412
445
isEventTargetInTree <- runEffectFn2 checkIsEventTargetInTree menuRef e
413
- when (not isEventTargetInTree) close
446
+ when (not isRightClick e && not isEventTargetInTree) close
414
447
, options: { capture: false , once: false , passive: false }
415
448
}
416
449
$ filterDropdown
@@ -561,6 +594,8 @@ foreign import getMouseEventPositionWithOffset :: EffectFn2 Node SyntheticEvent
561
594
562
595
foreign import checkIsEventTargetInTree :: EffectFn2 Node Event Boolean
563
596
597
+ foreign import isRightClick :: Event -> Boolean
598
+
564
599
foreign import hasWindowSelection :: Effect Boolean
565
600
566
601
foreign import scrollObserver
0 commit comments