mirror of
https://github.com/TheAnachronism/docspell.git
synced 2025-06-22 10:28:27 +00:00
Allow providing values for custom fields in item detail
This commit is contained in:
343
modules/webapp/src/main/elm/Comp/CustomFieldInput.elm
Normal file
343
modules/webapp/src/main/elm/Comp/CustomFieldInput.elm
Normal file
@ -0,0 +1,343 @@
|
||||
module Comp.CustomFieldInput exposing
|
||||
( FieldResult(..)
|
||||
, Model
|
||||
, Msg
|
||||
, UpdateResult
|
||||
, init
|
||||
, update
|
||||
, view
|
||||
)
|
||||
|
||||
import Api.Model.CustomField exposing (CustomField)
|
||||
import Comp.DatePicker
|
||||
import Data.CustomFieldType exposing (CustomFieldType)
|
||||
import Date exposing (Date)
|
||||
import DatePicker exposing (DatePicker)
|
||||
import Html exposing (..)
|
||||
import Html.Attributes exposing (..)
|
||||
import Html.Events exposing (onCheck, onClick, onInput)
|
||||
|
||||
|
||||
type alias Model =
|
||||
{ fieldModel : FieldModel
|
||||
, field : CustomField
|
||||
}
|
||||
|
||||
|
||||
type alias FloatModel =
|
||||
{ input : String
|
||||
, result : Result String Float
|
||||
}
|
||||
|
||||
|
||||
type FieldModel
|
||||
= TextField (Maybe String)
|
||||
| NumberField FloatModel
|
||||
| MoneyField FloatModel
|
||||
| BoolField Bool
|
||||
| DateField (Maybe Date) DatePicker
|
||||
|
||||
|
||||
type Msg
|
||||
= NumberMsg String
|
||||
| MoneyMsg String
|
||||
| DateMsg DatePicker.Msg
|
||||
| SetText String
|
||||
| ToggleBool
|
||||
| Remove
|
||||
|
||||
|
||||
fieldType : CustomField -> CustomFieldType
|
||||
fieldType field =
|
||||
Data.CustomFieldType.fromString field.ftype
|
||||
|> Maybe.withDefault Data.CustomFieldType.Text
|
||||
|
||||
|
||||
errorMsg : Model -> Maybe String
|
||||
errorMsg model =
|
||||
let
|
||||
floatModel =
|
||||
case model.fieldModel of
|
||||
NumberField fm ->
|
||||
Just fm
|
||||
|
||||
MoneyField fm ->
|
||||
Just fm
|
||||
|
||||
_ ->
|
||||
Nothing
|
||||
|
||||
getMsg res =
|
||||
case res of
|
||||
Ok _ ->
|
||||
Nothing
|
||||
|
||||
Err m ->
|
||||
Just m
|
||||
in
|
||||
Maybe.andThen getMsg (Maybe.map .result floatModel)
|
||||
|
||||
|
||||
init : CustomField -> ( Model, Cmd Msg )
|
||||
init field =
|
||||
let
|
||||
( dm, dc ) =
|
||||
Comp.DatePicker.init
|
||||
in
|
||||
( { field = field
|
||||
, fieldModel =
|
||||
case fieldType field of
|
||||
Data.CustomFieldType.Text ->
|
||||
TextField Nothing
|
||||
|
||||
Data.CustomFieldType.Numeric ->
|
||||
NumberField (FloatModel "" (Err "No number given"))
|
||||
|
||||
Data.CustomFieldType.Money ->
|
||||
MoneyField (FloatModel "" (Err "No amount given"))
|
||||
|
||||
Data.CustomFieldType.Boolean ->
|
||||
BoolField False
|
||||
|
||||
Data.CustomFieldType.Date ->
|
||||
DateField Nothing dm
|
||||
}
|
||||
, if fieldType field == Data.CustomFieldType.Date then
|
||||
Cmd.map DateMsg dc
|
||||
|
||||
else
|
||||
Cmd.none
|
||||
)
|
||||
|
||||
|
||||
type FieldResult
|
||||
= NoResult
|
||||
| RemoveField
|
||||
| Value String
|
||||
|
||||
|
||||
type alias UpdateResult =
|
||||
{ model : Model
|
||||
, cmd : Cmd Msg
|
||||
, result : FieldResult
|
||||
, subs : Sub Msg
|
||||
}
|
||||
|
||||
|
||||
updateFloatModel : String -> (Float -> Float) -> ( FloatModel, FieldResult )
|
||||
updateFloatModel msg rounding =
|
||||
case String.toFloat msg of
|
||||
Just n ->
|
||||
let
|
||||
fieldVal =
|
||||
if String.endsWith "." msg || String.endsWith ".0" msg then
|
||||
msg
|
||||
|
||||
else
|
||||
String.fromFloat (rounding n)
|
||||
in
|
||||
( { input = fieldVal
|
||||
, result = Ok (rounding n)
|
||||
}
|
||||
, Value (String.fromFloat (rounding n))
|
||||
)
|
||||
|
||||
Nothing ->
|
||||
( { input = msg
|
||||
, result = Err ("Not a number: " ++ msg)
|
||||
}
|
||||
, NoResult
|
||||
)
|
||||
|
||||
|
||||
roundScale2 : Float -> Float
|
||||
roundScale2 input =
|
||||
(round (input * 100) |> toFloat) / 100
|
||||
|
||||
|
||||
update : Msg -> Model -> UpdateResult
|
||||
update msg model =
|
||||
case ( msg, model.fieldModel ) of
|
||||
( SetText str, TextField _ ) ->
|
||||
let
|
||||
model_ =
|
||||
{ model | fieldModel = TextField (Just str) }
|
||||
in
|
||||
UpdateResult model_ Cmd.none (Value str) Sub.none
|
||||
|
||||
( NumberMsg str, NumberField _ ) ->
|
||||
let
|
||||
( fm, res ) =
|
||||
updateFloatModel str identity
|
||||
|
||||
model_ =
|
||||
{ model | fieldModel = NumberField fm }
|
||||
in
|
||||
UpdateResult model_ Cmd.none res Sub.none
|
||||
|
||||
( MoneyMsg str, MoneyField _ ) ->
|
||||
let
|
||||
( fm, res ) =
|
||||
updateFloatModel str roundScale2
|
||||
|
||||
model_ =
|
||||
{ model | fieldModel = MoneyField fm }
|
||||
in
|
||||
UpdateResult model_ Cmd.none res Sub.none
|
||||
|
||||
( ToggleBool, BoolField b ) ->
|
||||
let
|
||||
notb =
|
||||
not b
|
||||
|
||||
model_ =
|
||||
{ model | fieldModel = BoolField notb }
|
||||
|
||||
value =
|
||||
if notb then
|
||||
"true"
|
||||
|
||||
else
|
||||
"false"
|
||||
in
|
||||
UpdateResult model_ Cmd.none (Value value) Sub.none
|
||||
|
||||
( DateMsg lm, DateField _ picker ) ->
|
||||
let
|
||||
( picker_, event ) =
|
||||
Comp.DatePicker.updateDefault lm picker
|
||||
|
||||
( newDate, value ) =
|
||||
case event of
|
||||
DatePicker.Picked date ->
|
||||
( Just date, Value (Date.toIsoString date) )
|
||||
|
||||
DatePicker.None ->
|
||||
( Nothing, NoResult )
|
||||
|
||||
DatePicker.FailedInput _ ->
|
||||
( Nothing, NoResult )
|
||||
|
||||
model_ =
|
||||
{ model | fieldModel = DateField newDate picker_ }
|
||||
in
|
||||
UpdateResult model_ Cmd.none value Sub.none
|
||||
|
||||
( Remove, _ ) ->
|
||||
UpdateResult model Cmd.none RemoveField Sub.none
|
||||
|
||||
-- no other possibilities, not well encoded here
|
||||
_ ->
|
||||
UpdateResult model Cmd.none NoResult Sub.none
|
||||
|
||||
|
||||
mkLabel : Model -> String
|
||||
mkLabel model =
|
||||
Maybe.withDefault model.field.name model.field.label
|
||||
|
||||
|
||||
removeButton : String -> Html Msg
|
||||
removeButton classes =
|
||||
a
|
||||
[ class "ui icon button"
|
||||
, class classes
|
||||
, href "#"
|
||||
, title "Remove this value"
|
||||
, onClick Remove
|
||||
]
|
||||
[ i [ class "trash alternate outline icon" ] []
|
||||
]
|
||||
|
||||
|
||||
view : String -> Maybe String -> Model -> Html Msg
|
||||
view classes icon model =
|
||||
let
|
||||
error =
|
||||
errorMsg model
|
||||
in
|
||||
div
|
||||
[ class classes
|
||||
, classList
|
||||
[ ( "error", error /= Nothing )
|
||||
]
|
||||
]
|
||||
[ label []
|
||||
[ mkLabel model |> text
|
||||
]
|
||||
, makeInput icon model
|
||||
, div
|
||||
[ class "ui red pointing basic label"
|
||||
, classList
|
||||
[ ( "invisible hidden", error == Nothing )
|
||||
]
|
||||
]
|
||||
[ Maybe.withDefault "" error |> text
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
makeInput : Maybe String -> Model -> Html Msg
|
||||
makeInput icon model =
|
||||
let
|
||||
iconOr c =
|
||||
Maybe.withDefault c icon
|
||||
in
|
||||
case model.fieldModel of
|
||||
TextField v ->
|
||||
div [ class "ui action left icon input" ]
|
||||
[ input
|
||||
[ type_ "text"
|
||||
, Maybe.withDefault "" v |> value
|
||||
, onInput SetText
|
||||
]
|
||||
[]
|
||||
, removeButton ""
|
||||
, i [ class (iconOr "pen icon") ] []
|
||||
]
|
||||
|
||||
NumberField nm ->
|
||||
div [ class "ui action left icon input" ]
|
||||
[ input
|
||||
[ type_ "text"
|
||||
, value nm.input
|
||||
, onInput NumberMsg
|
||||
]
|
||||
[]
|
||||
, removeButton ""
|
||||
, i [ class (iconOr "hashtag icon") ] []
|
||||
]
|
||||
|
||||
MoneyField nm ->
|
||||
div [ class "ui action left icon input" ]
|
||||
[ input
|
||||
[ type_ "text"
|
||||
, value nm.input
|
||||
, onInput MoneyMsg
|
||||
]
|
||||
[]
|
||||
, removeButton ""
|
||||
, i [ class (iconOr "money bill icon") ] []
|
||||
]
|
||||
|
||||
BoolField b ->
|
||||
div [ class "ui container" ]
|
||||
[ div [ class "ui checkbox" ]
|
||||
[ input
|
||||
[ type_ "checkbox"
|
||||
, onCheck (\_ -> ToggleBool)
|
||||
, checked b
|
||||
]
|
||||
[]
|
||||
, label []
|
||||
[ text (mkLabel model)
|
||||
]
|
||||
]
|
||||
, removeButton "right floated"
|
||||
]
|
||||
|
||||
DateField v dp ->
|
||||
div [ class "ui action left icon input" ]
|
||||
[ Html.map DateMsg (Comp.DatePicker.view v Comp.DatePicker.defaultSettings dp)
|
||||
, removeButton ""
|
||||
, i [ class (iconOr "calendar icon") ] []
|
||||
]
|
Reference in New Issue
Block a user