From 23b343649c7565c834174c415365e8a581739ee3 Mon Sep 17 00:00:00 2001 From: Eike Kettner Date: Sun, 22 Nov 2020 20:07:35 +0100 Subject: [PATCH] Fix and enhance custom-multi-input field --- .../main/elm/Comp/CustomFieldMultiInput.elm | 180 +++++++++--------- .../src/main/elm/Comp/ItemDetail/EditMenu.elm | 5 +- .../src/main/elm/Comp/ItemDetail/Update.elm | 5 +- .../src/main/elm/Data/CustomFieldChange.elm | 77 ++++++++ 4 files changed, 175 insertions(+), 92 deletions(-) create mode 100644 modules/webapp/src/main/elm/Data/CustomFieldChange.elm diff --git a/modules/webapp/src/main/elm/Comp/CustomFieldMultiInput.elm b/modules/webapp/src/main/elm/Comp/CustomFieldMultiInput.elm index b5f7f57f..e0aadf4e 100644 --- a/modules/webapp/src/main/elm/Comp/CustomFieldMultiInput.elm +++ b/modules/webapp/src/main/elm/Comp/CustomFieldMultiInput.elm @@ -1,13 +1,14 @@ module Comp.CustomFieldMultiInput exposing - ( FieldResult(..) - , Model + ( Model , Msg , UpdateResult , ViewSettings , init , initCmd , initWith + , isEmpty , nonEmpty + , reset , setValues , update , view @@ -19,24 +20,49 @@ import Api.Model.CustomFieldList exposing (CustomFieldList) import Api.Model.ItemFieldValue exposing (ItemFieldValue) import Comp.CustomFieldInput import Comp.FixedDropdown +import Data.CustomFieldChange exposing (CustomFieldChange(..)) import Data.Flags exposing (Flags) import Dict exposing (Dict) import Html exposing (..) import Html.Attributes exposing (..) import Html.Events exposing (onClick) import Http -import Util.List import Util.Maybe type alias Model = - { fieldModels : Dict String Comp.CustomFieldInput.Model - , fieldSelect : FieldSelect - , visibleFields : List CustomField - , availableFields : List CustomField + { fieldSelect : FieldSelect + , visibleFields : Dict String VisibleField + , allFields : List CustomField } +type alias FieldSelect = + { selected : Maybe CustomField + , dropdown : Comp.FixedDropdown.Model CustomField + } + + +type alias VisibleField = + { field : CustomField + , inputModel : Comp.CustomFieldInput.Model + } + + +visibleFields : Model -> List CustomField +visibleFields model = + Dict.toList model.visibleFields + |> List.map (Tuple.second >> .field) + |> List.sortBy .name + + +currentOptions : List CustomField -> Dict String VisibleField -> List CustomField +currentOptions all visible = + List.filter + (\e -> not <| Dict.member e.name visible) + all + + type Msg = CustomFieldInputMsg CustomField Comp.CustomFieldInput.Msg | ApplyField CustomField @@ -47,30 +73,21 @@ type Msg | SetValues (List ItemFieldValue) -type FieldResult - = NoResult - | FieldValueRemove CustomField - | FieldValueChange CustomField String - | FieldCreateNew - - -type alias FieldSelect = - { selected : Maybe CustomField - , dropdown : Comp.FixedDropdown.Model CustomField - } - - nonEmpty : Model -> Bool nonEmpty model = - not (List.isEmpty model.availableFields && List.isEmpty model.visibleFields) + not (isEmpty model) + + +isEmpty : Model -> Bool +isEmpty model = + List.isEmpty model.allFields initWith : List CustomField -> Model initWith fields = - { fieldModels = Dict.empty - , fieldSelect = mkFieldSelect fields - , visibleFields = [] - , availableFields = fields + { fieldSelect = mkFieldSelect (currentOptions fields Dict.empty) + , visibleFields = Dict.empty + , allFields = fields } @@ -91,6 +108,18 @@ setValues values = SetValues values +reset : Model -> Model +reset model = + let + opts = + currentOptions model.allFields Dict.empty + in + { model + | fieldSelect = mkFieldSelect opts + , visibleFields = Dict.empty + } + + mkFieldSelect : List CustomField -> FieldSelect mkFieldSelect fields = { selected = Nothing @@ -106,7 +135,7 @@ type alias UpdateResult = { model : Model , cmd : Cmd Msg , subs : Sub Msg - , result : FieldResult + , result : CustomFieldChange } @@ -123,21 +152,16 @@ update msg model = CustomFieldResp (Ok list) -> let - avail = - List.filter - (\e -> not <| Dict.member e.name model.fieldModels) - list.items - model_ = { model - | availableFields = avail - , fieldSelect = mkFieldSelect avail + | allFields = list.items + , fieldSelect = mkFieldSelect (currentOptions list.items model.visibleFields) } in - UpdateResult model_ Cmd.none Sub.none NoResult + UpdateResult model_ Cmd.none Sub.none NoFieldChange CustomFieldResp (Err _) -> - UpdateResult model Cmd.none Sub.none NoResult + UpdateResult model Cmd.none Sub.none NoFieldChange FieldSelectMsg lm -> let @@ -160,26 +184,18 @@ update msg model = update (ApplyField field) model Nothing -> - UpdateResult model_ Cmd.none Sub.none NoResult + UpdateResult model_ Cmd.none Sub.none NoFieldChange ApplyField f -> let - notSelected e = - e /= f && (not <| Dict.member e.name model.fieldModels) - ( fm, fc ) = Comp.CustomFieldInput.init f - avail = - List.filter notSelected model.availableFields - visible = - f - :: model.visibleFields - |> List.sortBy .name + Dict.insert f.name (VisibleField f fm) model.visibleFields fSelect = - mkFieldSelect avail + mkFieldSelect (currentOptions model.allFields visible) -- have to re-state the open menu when this is invoked -- from a click in the dropdown @@ -192,46 +208,43 @@ update msg model = model_ = { model | fieldSelect = { fSelect | dropdown = dropdownOpen } - , availableFields = avail , visibleFields = visible - , fieldModels = Dict.insert f.name fm model.fieldModels } cmd_ = Cmd.map (CustomFieldInputMsg f) fc in - UpdateResult model_ cmd_ Sub.none NoResult + UpdateResult model_ cmd_ Sub.none NoFieldChange RemoveField f -> let - avail = - f :: model.availableFields - visible = - List.filter (\e -> e /= f) model.visibleFields + Dict.remove f.name model.visibleFields model_ = { model - | availableFields = avail - , visibleFields = visible - , fieldSelect = mkFieldSelect avail + | visibleFields = visible + , fieldSelect = mkFieldSelect (currentOptions model.allFields visible) } in UpdateResult model_ Cmd.none Sub.none (FieldValueRemove f) - CustomFieldInputMsg field lm -> + CustomFieldInputMsg f lm -> let - fieldModel = - Dict.get field.name model.fieldModels + visibleField = + Dict.get f.name model.visibleFields in - case fieldModel of - Just fm -> + case visibleField of + Just { field, inputModel } -> let res = - Comp.CustomFieldInput.update lm fm + Comp.CustomFieldInput.update lm inputModel model_ = - { model | fieldModels = Dict.insert field.name res.model model.fieldModels } + { model + | visibleFields = + Dict.insert field.name (VisibleField field res.model) model.visibleFields + } cmd_ = Cmd.map (CustomFieldInputMsg field) res.cmd @@ -245,7 +258,7 @@ update msg model = FieldValueRemove field Comp.CustomFieldInput.NoResult -> - NoResult + NoFieldChange in if res.result == Comp.CustomFieldInput.RemoveField then update (RemoveField field) model_ @@ -254,7 +267,7 @@ update msg model = UpdateResult model_ cmd_ Sub.none result Nothing -> - UpdateResult model Cmd.none Sub.none NoResult + UpdateResult model Cmd.none Sub.none NoFieldChange SetValues values -> let @@ -265,33 +278,24 @@ update msg model = let ( fim, fic ) = Comp.CustomFieldInput.initWith fv + + f = + field fv in - ( Dict.insert fv.name fim dict - , Cmd.map (CustomFieldInputMsg (field fv)) fic :: cmds + ( Dict.insert fv.name (VisibleField f fim) dict + , Cmd.map (CustomFieldInputMsg f) fic :: cmds ) ( modelDict, cmdList ) = List.foldl merge ( Dict.empty, [] ) values - avail = - List.filter - (\e -> not <| Dict.member e.name modelDict) - (model.availableFields ++ model.visibleFields) - model_ = { model - | fieldModels = modelDict - , availableFields = avail - , fieldSelect = mkFieldSelect avail - , visibleFields = - model.visibleFields - ++ model.availableFields - |> List.filter (\e -> Dict.member e.name modelDict) - |> Util.List.distinct - |> List.sortBy .name + | fieldSelect = mkFieldSelect (currentOptions model.allFields modelDict) + , visibleFields = modelDict } in - UpdateResult model_ (Cmd.batch cmdList) Sub.none NoResult + UpdateResult model_ (Cmd.batch cmdList) Sub.none NoFieldChange @@ -308,7 +312,7 @@ view : ViewSettings -> Model -> Html Msg view viewSettings model = div [ class viewSettings.classes ] (viewMenuBar viewSettings model - :: List.map (viewCustomField model) model.visibleFields + :: List.map (viewCustomField model) (visibleFields model) ) @@ -339,13 +343,13 @@ viewMenuBar viewSettings model = viewCustomField : Model -> CustomField -> Html Msg viewCustomField model field = let - fieldModel = - Dict.get field.name model.fieldModels + visibleField = + Dict.get field.name model.visibleFields in - case fieldModel of - Just fm -> + case visibleField of + Just vf -> Html.map (CustomFieldInputMsg field) - (Comp.CustomFieldInput.view "field" Nothing fm) + (Comp.CustomFieldInput.view "field" Nothing vf.inputModel) Nothing -> span [] [] diff --git a/modules/webapp/src/main/elm/Comp/ItemDetail/EditMenu.elm b/modules/webapp/src/main/elm/Comp/ItemDetail/EditMenu.elm index c919fe92..37d1316f 100644 --- a/modules/webapp/src/main/elm/Comp/ItemDetail/EditMenu.elm +++ b/modules/webapp/src/main/elm/Comp/ItemDetail/EditMenu.elm @@ -18,11 +18,12 @@ import Api.Model.ItemProposals exposing (ItemProposals) import Api.Model.ReferenceList exposing (ReferenceList) import Api.Model.Tag exposing (Tag) import Api.Model.TagList exposing (TagList) -import Comp.CustomFieldMultiInput exposing (FieldResult(..)) +import Comp.CustomFieldMultiInput import Comp.DatePicker import Comp.DetailEdit import Comp.Dropdown exposing (isDropdownChangeMsg) import Comp.ItemDetail.FormChange exposing (FormChange(..)) +import Data.CustomFieldChange exposing (CustomFieldChange(..)) import Data.Direction exposing (Direction) import Data.Fields import Data.Flags exposing (Flags) @@ -568,7 +569,7 @@ update flags msg model = change = case res.result of - NoResult -> + NoFieldChange -> NoFormChange FieldValueRemove cf -> diff --git a/modules/webapp/src/main/elm/Comp/ItemDetail/Update.elm b/modules/webapp/src/main/elm/Comp/ItemDetail/Update.elm index cdca4c34..f610f1ce 100644 --- a/modules/webapp/src/main/elm/Comp/ItemDetail/Update.elm +++ b/modules/webapp/src/main/elm/Comp/ItemDetail/Update.elm @@ -14,7 +14,7 @@ import Api.Model.ReferenceList exposing (ReferenceList) import Api.Model.Tag exposing (Tag) import Browser.Navigation as Nav import Comp.AttachmentMeta -import Comp.CustomFieldMultiInput exposing (FieldResult(..)) +import Comp.CustomFieldMultiInput import Comp.DatePicker import Comp.DetailEdit import Comp.Dropdown exposing (isDropdownChangeMsg) @@ -41,6 +41,7 @@ import Comp.OrgForm import Comp.PersonForm import Comp.SentMails import Comp.YesNoDimmer +import Data.CustomFieldChange exposing (CustomFieldChange(..)) import Data.Direction import Data.Fields exposing (Field) import Data.Flags exposing (Flags) @@ -1301,7 +1302,7 @@ update key flags inav settings msg model = action = case result.result of - NoResult -> + NoFieldChange -> Cmd.none FieldValueRemove field -> diff --git a/modules/webapp/src/main/elm/Data/CustomFieldChange.elm b/modules/webapp/src/main/elm/Data/CustomFieldChange.elm new file mode 100644 index 00000000..7a12dbc2 --- /dev/null +++ b/modules/webapp/src/main/elm/Data/CustomFieldChange.elm @@ -0,0 +1,77 @@ +module Data.CustomFieldChange exposing + ( CustomFieldChange(..) + , CustomFieldValueCollect + , collectValues + , emptyCollect + , isValueChange + , toFieldValues + ) + +import Api.Model.CustomField exposing (CustomField) +import Api.Model.CustomFieldValue exposing (CustomFieldValue) +import Dict exposing (Dict) + + +type CustomFieldChange + = NoFieldChange + | FieldValueRemove CustomField + | FieldValueChange CustomField String + | FieldCreateNew + + +type CustomFieldValueCollect + = CustomFieldValueCollect (Dict String String) + + +emptyCollect : CustomFieldValueCollect +emptyCollect = + CustomFieldValueCollect Dict.empty + + +collectValues : + CustomFieldChange + -> CustomFieldValueCollect + -> CustomFieldValueCollect +collectValues change collector = + let + dict = + case collector of + CustomFieldValueCollect d -> + d + in + case change of + NoFieldChange -> + collector + + FieldValueRemove f -> + CustomFieldValueCollect (Dict.remove f.id dict) + + FieldValueChange f v -> + CustomFieldValueCollect (Dict.insert f.id v dict) + + FieldCreateNew -> + collector + + +toFieldValues : CustomFieldValueCollect -> List CustomFieldValue +toFieldValues dict = + case dict of + CustomFieldValueCollect d -> + Dict.toList d + |> List.map (\( k, v ) -> CustomFieldValue k v) + + +isValueChange : CustomFieldChange -> Bool +isValueChange change = + case change of + NoFieldChange -> + False + + FieldValueRemove _ -> + True + + FieldValueChange _ _ -> + True + + FieldCreateNew -> + False