Yes, agree and very true, a lot more can be done than just styling. The issue is, I don’t want to use the default HTML number input, it is not possible to customise it with CSS and therefore I need to write my own logic for it. I could create a function that accepts multiple messages, but then I’d still need to do the logic in the module it’s in, which would also become messy.
Regarding the code, it is a lot more than just styling or moving around HTML parts, as to get a proper number input, a lot of checks need to be done. I don’t want to implement all this logic into every module and it is a lot cleaner to just use a nested TEA approach imo.
The HTML generating part
view : StyleSheet -> Model -> Html Msg
view s m = styled div
[ property "display" "grid"
, property "grid-template-columns" "1fr minmax(10rem, auto) 1fr"
, property "gap" "5rem"
, property "height" "max-content"
, borderRadius defaultBorderRadius
, overflow hidden
]
[ Attr.fromUnstyled <| onWheel Wheel
]
[ arrowButton s m Down arrowLeftDense_
, styled input
( inpDefault s
++
[ border <| rem 0
, padding2 ( rem 4 ) ( rem 0 )
, width <| rem <| calcInputWidth m.value
]
)
[ Attr.value <| String.fromInt m.value
, onInput UpdateValue
, onKeyDown KeyDown
]
[]
, arrowButton s m Up arrowRightDense_
]
arrowButton : StyleSheet -> Model -> Direction -> ( Color -> Html Msg ) -> Html Msg
arrowButton s m d i = styled div
[ displayFlex
, padding2 ( rem 6 ) ( rem 2 )
, opacity
<| Css.num
<| case d of
Down ->
case m.range of
Just range ->
if ( m.value - 1 < 0 && not m.allowNegative )
|| m.value - 1 < range.min then
0.5
else
1
Nothing ->
if ( m.value - 1 < 0 && not m.allowNegative ) then
0.5
else
1
Up ->
case m.range of
Just range ->
if m.value + 1 > range.max then
0.5
else
1
Nothing ->
1
, cursor pointer
, borderRadius defaultBorderRadius
, backgroundColor inherit
, hover
[ backgroundColor s.icon.backgroundHover
]
, transition
[ defaultTransition CssT.backgroundColor3
, defaultTransition CssT.opacity3
]
]
[ onMouseDown <| MouseDown d
, onMouseUp MouseUp
, onMouseLeave MouseUp
]
[ i s.icon.fill
]
The update
function and Msg
type
type Msg
= Decoy
| Decrement
| Increment
| IsMouseDown Posix
| KeyDown Key
| MouseDown Direction
| MouseUp
| SetMouseDownTimestamp Posix
| SetValue Int
| UpdateValue String
| Wheel Event
update : Msg -> Model -> ( Model, Cmd Msg, Maybe Out )
update msg model = case msg of
Decoy ->
( model
, Cmd.none
, Nothing
)
Decrement ->
let
newValue = case model.range of
Just range ->
if ( not model.allowNegative && model.value < 1 )
|| model.value - 1 < range.min then
model.value
else
model.value - 1
Nothing ->
if not model.allowNegative && model.value < 1 then
model.value
else
model.value - 1
in
( { model
| value = newValue
}
, Cmd.none
, if model.activeReport then
Just <| IntInputChange model.id newValue
else
Nothing
)
Increment ->
let
newValue = case model.range of
Just range ->
if model.value + 1 > range.max then
model.value
else
model.value + 1
Nothing ->
model.value + 1
in
( { model
| value = newValue
}
, Cmd.none
, if model.activeReport then
Just <| IntInputChange model.id newValue
else
Nothing
)
IsMouseDown posix ->
if model.mouseDown && model.mouseDownTimestamp == posix then
( model
, Cmd.batch
[ changeValue model.direction
, Process.sleep 50 |> Task.perform ( \ _ -> IsMouseDown posix )
]
, Nothing
)
else
( model
, Cmd.none
, Nothing
)
KeyDown key ->
update
( case key of
Control "ArrowUp" ->
Increment
Control "ArrowDown" ->
Decrement
_ ->
Decoy
)
model
MouseDown direction ->
( { model
| direction = direction
, mouseDown = True
}
, Cmd.batch
[ changeValue direction
, Time.now |> Task.perform SetMouseDownTimestamp
]
, Nothing
)
MouseUp ->
( { model
| mouseDown = False
}
, Cmd.none
, Nothing
)
SetMouseDownTimestamp posix ->
( { model
| mouseDownTimestamp = posix
}
, Process.sleep 500 |> Task.perform ( \ _ -> IsMouseDown posix )
, Nothing
)
SetValue val ->
( { model
| value = val
}
, Cmd.none
, Nothing
)
UpdateValue newValue ->
( { model
| value = case String.toFloat newValue of
Just properValue ->
let
v = truncate properValue
in
case model.range of
Just range ->
if v > range.max then
range.max
else if not model.allowNegative && v < 0 then
0
else if v < range.min then
range.min
else
v
Nothing ->
if not model.allowNegative && v < 0 then
0
else
v
Nothing ->
if String.length newValue == 0 || newValue == "-" then
case model.range of
Just range ->
if range.min > 0 then
range.min
else
0
Nothing ->
0
else
model.value
}
, Cmd.none
, Nothing
)
Wheel e ->
if model.mouseDown then
( model
, Cmd.none
, Nothing
)
else
if e.deltaY < 0 then
update Increment model
else
update Decrement model