docspell/modules/webapp/src/main/elm/Comp/CustomFieldMultiInput.elm
2021-03-09 20:40:49 +01:00

395 lines
10 KiB
Elm

module Comp.CustomFieldMultiInput exposing
( Model
, Msg
, UpdateResult
, ViewSettings
, init
, initCmd
, initWith
, isEmpty
, nonEmpty
, reset
, setValues
, update
, updateSearch
, view2
)
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.DropdownStyle as DS
import Data.Flags exposing (Flags)
import Dict exposing (Dict)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick)
import Http
import Styles as S
import Util.CustomField
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.sortBy Util.CustomField.nameOrLabel all
|> List.filter (\e -> not <| Dict.member e.name visible)
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 : Flags -> Msg -> Model -> UpdateResult
update =
update1 False
updateSearch : Flags -> Msg -> Model -> UpdateResult
updateSearch =
update1 True
update1 : Bool -> Flags -> Msg -> Model -> UpdateResult
update1 forSearch flags 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 flags (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)
model_ =
{ model
| fieldSelect = fSelect
, 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 flags (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
--- View2
type alias ViewSettings =
{ showAddButton : Bool
, classes : String
, fieldIcon : CustomField -> Maybe String
}
view2 : DS.DropdownStyle -> ViewSettings -> Model -> Html Msg
view2 ddstyle viewSettings model =
div [ class viewSettings.classes ]
(viewMenuBar2 ddstyle viewSettings model
:: List.map (viewCustomField2 viewSettings model) (visibleFields model)
)
viewMenuBar2 : DS.DropdownStyle -> ViewSettings -> Model -> Html Msg
viewMenuBar2 ddstyle viewSettings model =
let
{ dropdown, selected } =
model.fieldSelect
ddstyleFlex =
{ ddstyle | root = ddstyle.root ++ " flex-grow" }
in
div
[ classList
[ ( "", viewSettings.showAddButton )
]
, class " flex flex-row"
]
(Html.map FieldSelectMsg
(Comp.FixedDropdown.viewStyled2
ddstyleFlex
False
(Maybe.map mkItem selected)
dropdown
)
:: (if viewSettings.showAddButton then
[ addFieldLink2 "ml-1" model
]
else
[]
)
)
viewCustomField2 : ViewSettings -> Model -> CustomField -> Html Msg
viewCustomField2 viewSettings model field =
let
visibleField =
Dict.get field.name model.visibleFields
in
case visibleField of
Just vf ->
Html.map (CustomFieldInputMsg field)
(Comp.CustomFieldInput.view2 "mt-2"
(viewSettings.fieldIcon vf.field)
vf.inputModel
)
Nothing ->
span [] []
addFieldLink2 : String -> Model -> Html Msg
addFieldLink2 classes _ =
a
[ class classes
, class S.secondaryButton
-- , class "absolute -right-12 top-0"
, href "#"
, onClick CreateNewField
, title "Create a new custom field"
]
[ i [ class "fa fa-plus" ] []
]