module Comp.CustomFieldMultiInput exposing ( Model , Msg , UpdateResult , ViewSettings , init , initCmd , initWith , isEmpty , nonEmpty , reset , setValues , update , updateSearch , view ) import Api import Api.Model.CustomField exposing (CustomField) 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.Maybe type alias Model = { 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 = let labelThenName cv = Maybe.withDefault cv.name cv.label in Dict.toList model.visibleFields |> List.map (Tuple.second >> .field) |> List.sortBy labelThenName 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 | RemoveField CustomField | CreateNewField | CustomFieldResp (Result Http.Error CustomFieldList) | FieldSelectMsg (Comp.FixedDropdown.Msg CustomField) | SetValues (List ItemFieldValue) nonEmpty : Model -> Bool nonEmpty model = not (isEmpty model) isEmpty : Model -> Bool isEmpty model = List.isEmpty model.allFields initWith : List CustomField -> Model initWith fields = { fieldSelect = mkFieldSelect (currentOptions fields Dict.empty) , visibleFields = Dict.empty , allFields = fields } init : Flags -> ( Model, Cmd Msg ) init flags = ( initWith [] , initCmd flags ) initCmd : Flags -> Cmd Msg initCmd flags = Api.getCustomFields flags "" CustomFieldResp setValues : List ItemFieldValue -> Msg 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 , dropdown = Comp.FixedDropdown.init (List.map mkItem fields) } --- Update type alias UpdateResult = { model : Model , cmd : Cmd Msg , result : CustomFieldChange } mkItem : CustomField -> Comp.FixedDropdown.Item CustomField mkItem f = Comp.FixedDropdown.Item f (Maybe.withDefault f.name f.label) update : Msg -> Model -> UpdateResult update = update1 False updateSearch : Msg -> Model -> UpdateResult updateSearch = update1 True update1 : Bool -> Msg -> Model -> UpdateResult update1 forSearch msg model = case msg of CreateNewField -> UpdateResult model Cmd.none FieldCreateNew CustomFieldResp (Ok list) -> let model_ = { model | allFields = list.items , fieldSelect = mkFieldSelect (currentOptions list.items model.visibleFields) } in UpdateResult model_ Cmd.none NoFieldChange CustomFieldResp (Err _) -> UpdateResult model Cmd.none NoFieldChange FieldSelectMsg lm -> let ( dm_, sel ) = Comp.FixedDropdown.update lm model.fieldSelect.dropdown newF = Util.Maybe.or [ sel, model.fieldSelect.selected ] model_ = { model | fieldSelect = { selected = newF , dropdown = dm_ } } in case sel of Just field -> update (ApplyField field) model Nothing -> UpdateResult model_ Cmd.none NoFieldChange ApplyField f -> let ( fm, fc ) = Comp.CustomFieldInput.init f visible = Dict.insert f.name (VisibleField f fm) model.visibleFields fSelect = mkFieldSelect (currentOptions model.allFields visible) -- have to re-state the open menu when this is invoked -- from a click in the dropdown fSelectDropdown = fSelect.dropdown dropdownOpen = { fSelectDropdown | menuOpen = True } model_ = { model | fieldSelect = { fSelect | dropdown = dropdownOpen } , visibleFields = visible } cmd_ = Cmd.map (CustomFieldInputMsg f) fc in UpdateResult model_ cmd_ NoFieldChange RemoveField f -> let visible = Dict.remove f.name model.visibleFields model_ = { model | visibleFields = visible , fieldSelect = mkFieldSelect (currentOptions model.allFields visible) } in UpdateResult model_ Cmd.none (FieldValueRemove f) CustomFieldInputMsg f lm -> let visibleField = Dict.get f.name model.visibleFields in case visibleField of Just { field, inputModel } -> let res = if forSearch then Comp.CustomFieldInput.updateSearch lm inputModel else Comp.CustomFieldInput.update lm inputModel model_ = { model | visibleFields = Dict.insert field.name (VisibleField field res.model) model.visibleFields } cmd_ = Cmd.map (CustomFieldInputMsg field) res.cmd result = case res.result of Comp.CustomFieldInput.Value str -> FieldValueChange field str Comp.CustomFieldInput.RemoveField -> FieldValueRemove field Comp.CustomFieldInput.NoResult -> NoFieldChange in if res.result == Comp.CustomFieldInput.RemoveField then update (RemoveField field) model_ else UpdateResult model_ cmd_ result Nothing -> UpdateResult model Cmd.none NoFieldChange SetValues values -> let field value = CustomField value.id value.name value.label value.ftype 0 0 merge fv ( dict, cmds ) = let ( fim, fic ) = Comp.CustomFieldInput.initWith fv f = field fv in ( Dict.insert fv.name (VisibleField f fim) dict , Cmd.map (CustomFieldInputMsg f) fic :: cmds ) ( modelDict, cmdList ) = List.foldl merge ( Dict.empty, [] ) values model_ = { model | fieldSelect = mkFieldSelect (currentOptions model.allFields modelDict) , visibleFields = modelDict } in UpdateResult model_ (Cmd.batch cmdList) NoFieldChange --- View type alias ViewSettings = { showAddButton : Bool , classes : String , fieldIcon : CustomField -> Maybe String } view : ViewSettings -> Model -> Html Msg view viewSettings model = div [ class viewSettings.classes ] (viewMenuBar viewSettings model :: List.map (viewCustomField viewSettings model) (visibleFields model) ) viewMenuBar : ViewSettings -> Model -> Html Msg viewMenuBar viewSettings model = let { dropdown, selected } = model.fieldSelect in div [ classList [ ( "field", True ) , ( "ui action input", viewSettings.showAddButton ) ] ] (Html.map FieldSelectMsg (Comp.FixedDropdown.viewStyled "fluid" (Maybe.map mkItem selected) dropdown) :: (if viewSettings.showAddButton then [ addFieldLink "" model ] else [] ) ) viewCustomField : ViewSettings -> Model -> CustomField -> Html Msg viewCustomField viewSettings model field = let visibleField = Dict.get field.name model.visibleFields in case visibleField of Just vf -> Html.map (CustomFieldInputMsg field) (Comp.CustomFieldInput.view "field" (viewSettings.fieldIcon vf.field) vf.inputModel ) Nothing -> span [] [] addFieldLink : String -> Model -> Html Msg addFieldLink classes _ = a [ class ("ui icon button " ++ classes) , href "#" , onClick CreateNewField , title "Create a new custom field" ] [ i [ class "plus link icon" ] [] ]