@@ -13,7 +13,7 @@ import Data.Array (delete, elem, find, fold, length, mapMaybe, null, snoc)
1313import Data.Array as Array
1414import Data.Either (either )
1515import Data.Foldable (for_ )
16- import Data.Maybe (Maybe (..), fromMaybe , isJust , maybe )
16+ import Data.Maybe (Maybe (..), fromMaybe , isJust , isNothing , maybe )
1717import Data.Monoid (guard )
1818import Data.Newtype (class Newtype , un )
1919import Data.Nullable (Nullable , toMaybe )
@@ -92,6 +92,23 @@ type TableProps row =
9292 , hidden :: Boolean
9393 , sticky :: Boolean
9494 }
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+ )
95112 }
96113
97114component :: forall row . Component (TableProps row )
@@ -108,7 +125,7 @@ data Action
108125table :: forall a . TableProps a -> JSX
109126table = make component
110127 { initialState
111- , didMount
128+ , didMount: syncProps
112129 , didUpdate
113130 , render
114131 }
@@ -123,19 +140,16 @@ table = make component
123140 , menuStyle: { top: " 0px" , left: " 0px" }
124141 }
125142
126- didMount self = do
127- maybeSavedColumnSort <- loadColumnState $ columnSaveKey self.props.name
128- syncProps self maybeSavedColumnSort
129-
130143 didUpdate self _ = do
131144 case toMaybe self.props.selected of
132145 Nothing -> pure unit
133146 Just selected ->
134147 when (selected /= self.state.selected) do
135- syncProps self Nothing
148+ syncProps self
136149
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
139153 self.setState \state -> state
140154 { columns =
141155 case maybeSavedColumnSort of
@@ -157,21 +171,21 @@ table = make component
157171 self.setState \state -> state { showMenu = false }
158172
159173 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
175189
176190 onSelect self { shift, key, checked } = do
177191 self.setStateThen (\state ->
@@ -225,79 +239,97 @@ table = make component
225239 runEffectFn1 props.onSelect state.selected
226240
227241 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
231254
232255 getColumnSortFields { name, hidden } = { name, hidden }
233256
234257 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+ }
248323 }
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+ ]
293325 where
294326 selected = fromMaybe self.state.selected (toMaybe self.props.selected)
295327
296328 primaryColumn = toMaybe self.props.primaryColumn
297329
298- renderTableHead tableRef =
330+ renderTableHead columns tableRef =
299331 element (R .unsafeCreateDOMComponent " thead" )
300- { onContextMenu: Events .handler preventDefault \e -> do
332+ { onContextMenu: Events .handler ( preventDefault >>> stopPropagation) \e -> do
301333 { x, y } <- runEffectFn2 getMouseEventPositionWithOffset tableRef e
302334 openMenu self
303335 { top: show (y - 2.0 ) <> " px"
@@ -307,7 +339,7 @@ table = make component
307339 [ R .tr_ $
308340 [ renderHeadCheckbox ]
309341 <> (maybe [] (pure <<< renderHeadPrimaryCell) primaryColumn)
310- <> (map renderHeadCell self.state. columns)
342+ <> (map renderHeadCell columns)
311343 ]
312344 }
313345
@@ -320,7 +352,7 @@ table = make component
320352 then empty
321353 else R .th
322354 { style: R .css { width: " 2rem" }
323- , onClick: Events .handler stopPropagation (const (pure unit))
355+ , onClick: Events .handler ( stopPropagation) (const (pure unit))
324356 , children:
325357 [ input checkbox
326358 { checked =
@@ -409,8 +441,9 @@ table = make component
409441 , handler: case maybeMenuRef of
410442 Nothing -> \e -> pure unit
411443 Just menuRef -> \e -> do
444+
412445 isEventTargetInTree <- runEffectFn2 checkIsEventTargetInTree menuRef e
413- when (not isEventTargetInTree) close
446+ when (not isRightClick e && not isEventTargetInTree) close
414447 , options: { capture: false , once: false , passive: false }
415448 }
416449 $ filterDropdown
@@ -561,6 +594,8 @@ foreign import getMouseEventPositionWithOffset :: EffectFn2 Node SyntheticEvent
561594
562595foreign import checkIsEventTargetInTree :: EffectFn2 Node Event Boolean
563596
597+ foreign import isRightClick :: Event -> Boolean
598+
564599foreign import hasWindowSelection :: Effect Boolean
565600
566601foreign import scrollObserver
0 commit comments