module InputTable.Update exposing (update, updateFilterText) {-| Handles messages sent to InputTable @docs update -} import InputTable.Messages exposing (..) import InputTable.Model exposing (..) import List.Extra exposing (updateIf, findIndex, elemIndex, (!!)) import Set import InputTable.Updates.SortRows as SortRows import InputTable.ExportTableRows as ExportTableRows import Navigation {-| When sent a TableMsg, this function will update the TableState appropriately -} getVisibleColumnIdsForUrlQuery columns = columns |> List.filter (\c -> c.visible) |> List.map (\c -> c.id) |> String.join "," update : TableMsg rowData focussedData -> TableState rowData sideRowData -> ( TableState rowData sideRowData, Cmd (TableMsg rowData focussedData) ) update msg tableState = case msg of SetCellValue columnId setter rowId multipleValues value -> ( { tableState | rows = setCellData tableState.rows setter rowId value }, Cmd.none ) ToggleSideBar rowId -> let newRowId = case tableState.sideBarRowId of Just currentRowId -> if currentRowId == (Maybe.withDefault "" rowId) then Nothing else rowId Nothing -> rowId in ( { tableState | sideBarRowId = newRowId, focussedRow = Nothing }, Cmd.none ) -- naaz SetSideBarTinyMceValue tinyMceResponse -> let value = tinyMceResponse.response rowId = case tableState.sideBarRowId of Just sideBarRowId -> sideBarRowId Nothing -> "" columnId = tinyMceResponse.columnId sideBarHeaders = Maybe.withDefault [] tableState.sideBarHeaders editedHeader = sideBarHeaders |> List.Extra.find (\h -> h.id == columnId) newRows = case editedHeader of Just header -> case header.subType of SideBarMceInputType1 props -> setCellData tableState.rows props.set rowId value SideBarMceInputType2 props -> setCellData tableState.rows props.set rowId value SideBarMceInputType3 props -> setCellData tableState.rows props.set rowId value SideBarMceInputType4 props -> setCellData tableState.rows props.set rowId value _ -> tableState.rows Nothing -> tableState.rows in ( { tableState | rows = newRows }, Cmd.none ) SetBoolCellValue columnId setter rowId value -> ( { tableState | rows = setCellData tableState.rows setter rowId value }, Cmd.none ) ToggleCellDropdown rowId columnId -> let update column = case column.subType of SubDropdownColumn props -> let newProps = (case props.focussedRowId of Just currentFocussedRowId -> if currentFocussedRowId == rowId then { props | focussedRowId = Nothing , focussedOption = Nothing } else { props | focussedRowId = Just rowId , focussedOption = Nothing } Nothing -> { props | focussedRowId = Just rowId , focussedOption = Nothing } ) in { column | subType = SubDropdownColumn newProps } _ -> column in ( { tableState | columns = updateIfHasId columnId update tableState.columns } , Cmd.none ) ViewDropdownChildren rowId columnId choice set -> let updateColumn column = case column.subType of SubDropdownColumn props -> let newProps = { props | focussedRowId = Just rowId , focussedOption = Just choice } in { column | subType = SubDropdownColumn newProps } _ -> column in ( { tableState | columns = updateIfHasId columnId updateColumn tableState.columns } , Cmd.none ) SelectDropdownParent rowId columnId choice set -> let updateColumn column = case column.subType of SubDropdownColumn props -> let newProps = { props | focussedRowId = Nothing , focussedOption = Nothing } in { column | subType = SubDropdownColumn newProps } _ -> column updateRow row = { row | data = set row.data ( choice, Nothing ) } in ( { tableState | columns = updateIfHasId columnId updateColumn tableState.columns , rows = updateIfHasId rowId updateRow tableState.rows } , Cmd.none ) SelectDropdownChild rowId columnId choice subChoice set -> let updateColumn column = case column.subType of SubDropdownColumn props -> let newProps = { props | focussedRowId = Nothing , focussedOption = Nothing } in { column | subType = SubDropdownColumn newProps } _ -> column updateRow row = { row | data = set row.data ( choice, Just subChoice ) } in ( { tableState | columns = updateIfHasId columnId updateColumn tableState.columns , rows = updateIfHasId rowId updateRow tableState.rows } , Cmd.none ) SetSearchText value -> ( { tableState | searchText = value, currentPage = 1 } , Cmd.none ) SetColumnFilterText columnId value -> let command = if not (String.isEmpty value) then Navigation.modifyUrl ("?filterText=" ++ columnId ++ "=" ++ value) else Navigation.modifyUrl ("?") in ( { tableState | columns = updateFilterText columnId value tableState.columns , currentPage = 1 } , command ) SwitchColumnCheckboxFilter columnId newFilterState -> ( { tableState | columns = switchCheckboxFilter columnId newFilterState tableState.columns } , Cmd.none ) ToggleRowCheckbox rowId -> ( { tableState | rows = updateIfHasId rowId (\r -> { r | checked = not r.checked }) tableState.rows } , Cmd.none ) ToggleSideRowCheckbox rowId -> ( { tableState | sideRows = updateIfHasId rowId (\r -> { r | checked = not r.checked }) tableState.sideRows } , Cmd.none ) ToggleVisibleRowsCheckboxes visibleRows -> let enabledVisibleRows = List.filter (.checkboxDisabledMsg >> (==) Nothing) visibleRows allChecked = List.all .checked enabledVisibleRows visibleRowsIds = enabledVisibleRows |> List.map .id |> Set.fromList newRows = List.map setChecked tableState.rows setChecked row = if Set.member row.id visibleRowsIds then { row | checked = not allChecked } else row in ( { tableState | rows = newRows }, Cmd.none ) ToggleVisibleRowsExpansion visibleRows -> let allExpanded = List.all .accordionExpanded visibleRows visibleRowsIds = visibleRows |> List.map .id |> Set.fromList newRows = List.map setExpanded tableState.rows setExpanded row = if Set.member row.id visibleRowsIds then { row | accordionExpanded = not allExpanded } else row in ( { tableState | rows = newRows }, Cmd.none ) ToggleVisibleSideRowsCheckboxes visibleRowsIds -> let visibleRows = List.filter (.id >> ((flip Set.member) visibleRowsIds)) tableState.sideRows allChecked = List.all .checked visibleRows newRows = List.map setChecked tableState.sideRows setChecked row = if Set.member row.id visibleRowsIds then { row | checked = not allChecked } else row in ( { tableState | sideRows = newRows }, Cmd.none ) ToggleChooseVisibleColumnsUi -> ( { tableState | showVisibleColumnsUi = not tableState.showVisibleColumnsUi }, Cmd.none ) ToggleColumnVisibility columndId -> let newColumns = updateIfHasId columndId (\c -> { c | visible = not c.visible }) tableState.columns in ( { tableState | columns = newColumns } , Cmd.none ) SortRows { id, subType, hasNumericalValues } -> let { rows, sorting } = SortRows.run { rows = tableState.rows , sorting = tableState.sorting } { id = id , subType = subType , hasNumericalValues = hasNumericalValues } sortingQuery = case sorting of Asc col -> "?sorting=" ++ col ++ "=asc" Desc col -> "?sorting=" ++ col ++ "=desc" _ -> "" in ( { tableState | rows = rows , sorting = sorting } , Cmd.none ) TableClick -> let removeFocus column = case column.subType of SubDropdownColumn props -> let newProps = { props | focussedRowId = Nothing , focussedOption = Nothing } in { column | subType = SubDropdownColumn newProps } _ -> column in ( { tableState | columns = List.map removeFocus tableState.columns }, Cmd.none ) PreviousPage -> ( { tableState | currentPage = max (tableState.currentPage - 1) 1 }, Cmd.none ) NextPage rowCount -> let -- the tableState.currentPage can be 0 if no currentPage is indicated in the URL -- so we want to change it to 1 currentPage = max tableState.currentPage 1 maxPage = ((rowCount - 1) // (Maybe.withDefault 1 tableState.pageSize)) + 1 in ( { tableState | currentPage = min (currentPage + 1) maxPage } , Cmd.none ) SetPage pageIndex -> ( { tableState | currentPage = (pageIndex |> String.toInt |> Result.toMaybe |> Maybe.withDefault tableState.currentPage ) } , Cmd.none ) ExportRows -> ( tableState, ExportTableRows.run tableState ) ToggleSideTable -> ( { tableState | showSideTable = not tableState.showSideTable }, Cmd.none ) UpdateSideSearchText input -> ( { tableState | sideSearchText = input }, Cmd.none ) PostSideRows -> let anyChecked = List.any .checked ableToPostFromSideRow = anyChecked tableState.rows && anyChecked tableState.sideRows cannotAssignSideRowsReason = if ableToPostFromSideRow then Nothing else Just "You must select submissions and reviewers before assigning." in ( { tableState | cannotAssignSideRowsReason = cannotAssignSideRowsReason , sideTableIsAssigning = ableToPostFromSideRow } , Cmd.none ) ToggleAccordion rowId -> ( { tableState | rows = updateIfHasId rowId (\r -> { r | accordionExpanded = not r.accordionExpanded }) tableState.rows } , Cmd.none ) FocusRow row -> let ( focussedRow, sideBarRowId ) = case row.rowFocusUrl of Just url -> ( Just row, Just row.id ) _ -> ( Nothing, Nothing ) in ( { tableState | focussedRow = focussedRow, sideBarRowId = sideBarRowId, showVisibleColumnsUi = False }, Cmd.none ) BlurRow -> ( { tableState | focussedRow = Nothing, sideBarRowId = Nothing, showVisibleColumnsUi = False }, Cmd.none ) FocusOnPrevious -> let getPrevious list item = list |> List.Extra.findIndex (.id >> (==) item.id) |> Maybe.map (\i -> i - 1) |> Maybe.andThen (\i -> list !! (i % (List.length list))) previous = Maybe.andThen (getPrevious tableState.rows) tableState.focussedRow sideBarRowId = Maybe.map .id previous in ( { tableState | focussedRow = previous, sideBarRowId = sideBarRowId }, Cmd.none ) FocusOnNext -> let getNext list item = list |> List.Extra.findIndex (.id >> (==) item.id) |> Maybe.map ((+) 1) |> Maybe.andThen (\i -> list !! (i % (List.length list))) next = Maybe.andThen (getNext tableState.rows) tableState.focussedRow sideBarRowId = Maybe.map .id next in ( { tableState | focussedRow = next, sideBarRowId = sideBarRowId }, Cmd.none ) DoNothing -> ( tableState, Cmd.none ) setCellData : List (Row rowData) -> (rowData -> a -> rowData) -> RowId -> a -> List (Row rowData) setCellData rows setter rowId value = let update row = { row | data = setter row.data value } in updateIfHasId rowId update rows updateFilterText : ColumnId -> String -> List (Column rowData) -> List (Column rowData) updateFilterText columnId value columns = let updateIfText column = case column.subType of DisplayColumn props -> DisplayColumn (update props) LinkColumn props -> LinkColumn (update props) TextColumn props -> TextColumn (update props) DropdownColumn props -> DropdownColumn (update props) MenuDropdownColumn props -> MenuDropdownColumn (update props) SubDropdownColumn props -> SubDropdownColumn (update props) CheckboxColumn props -> CheckboxColumn props SideBarButtonColumn props -> SideBarButtonColumn props update props = { props | filter = value } in updateIfHasId columnId (\c -> { c | subType = updateIfText c }) columns switchCheckboxFilter : ColumnId -> Maybe Bool -> List (Column rowData) -> List (Column rowData) switchCheckboxFilter columnId newFilterState columns = let updateIfText column = case column.subType of DisplayColumn props -> DisplayColumn props LinkColumn props -> LinkColumn props TextColumn props -> TextColumn props DropdownColumn props -> DropdownColumn props MenuDropdownColumn props -> MenuDropdownColumn props SubDropdownColumn props -> SubDropdownColumn props CheckboxColumn props -> CheckboxColumn (update props) SideBarButtonColumn props -> SideBarButtonColumn (props) update props = { props | filter = newFilterState } in updateIfHasId columnId (\c -> { c | subType = updateIfText c }) columns updateIfHasId : a -> ({ b | id : a } -> { b | id : a }) -> List { b | id : a } -> List { b | id : a } updateIfHasId id list = updateIf (\a -> a.id == id) list