[Chinese]关于在[Elm] Alertmanager中引入DatePicker的故事
最近在制作网页界面时,日期选择器是非常需要的一个功能。
最近因为接触到Elm的DatePicker,所以想着在Purometheus的Alertmanager中加入一个datepicker不是挺好的吗?
于是我试着加了进去。
Elm的日期选择器 (Elm de qì)
关于DatePicker,在Elm中处理日期的话,首先推荐使用这些包。这篇文章总结得很清楚,我参考了它。
在Elm中,几乎没有自由定制的DatePicker可用,或者可以说几乎没有。它可能是由Elm-CSS嵌入的,或者只能使用输入字段。将其集成到现有系统中肯定非常麻烦。
CurrySoftware/elm-datepicker – 咖喱软件/榆木日期选择器
这个链接是我调查后发现最好的选项。
外观非常简洁,而且易于自定义。
你可以自己定义属性和日期格式,因此几乎可以按照自己的想法来显示DatePicker。
type alias Settings =
{ placeholder : String
, classNamespace : String
, containerClassList : List ( String, Bool )
, inputClassList : List ( String, Bool )
, inputName : Maybe String
, inputId : Maybe String
, inputAttributes : List (Attribute Msg)
, isDisabled : Date -> Bool
, parser : String -> Result String Date
, dateFormatter : Date -> String
, dayFormatter : Weekday -> String
, monthFormatter : Month -> String
, yearFormatter : Int -> String
, cellFormatter : String -> Html Msg
, firstDayOfWeek : Weekday
, changeYear : YearRange
}
最近,如同在Elm 0.19处理日期的文章所提到的,一个相当重要的事情是所有的日期都是由Posix标准管理的。因此,在尽可能避免依赖来源不明的软件包的意义上,这是最佳选择。
基本上,elm/time和time-extra就足夠使用了,但是有很多不同的日期包可供選擇,搞得人有點不知道該用哪一個。
Elmer的人好像討厭引入Date包。他們似乎更傾向於使用自己獨有的日期類型,並全部使用Posix來管理時間(我想的。至於這方面,說實話不太清楚)。
就这样,我将 “CurrySoftware/elm-datepicker” 添加到 alertmanager,并提交了一个 PR。
https://github.com/prometheus/alertmanager/pull/2262
然而,很快就有人回复说需要一个TimePicker。
我猜也许支持时间也会很好,但可能并不那么容易(至少该组件只支持日期)?
时间选择器真的不是那么必要吗?
我个人认为,当到达时间时,手动输入就足够了。
但是,毫无疑问,有一个DateTimePicker会更方便,所以现在我们要寻找一个DateTimePicker。
Elm 的日期时间选择器
当谈及DateTimePicker时,选择变得更加有限。
虽然没有“最佳”选择,但有一些看起来不错的选项。
水星媒体/榆树日期时间选择器
最初我是被w0rm推荐了这个网址的:
https://package.elm-lang.org/packages/mercurymedia/elm-datetime-picker/latest/
作为一个DateTimePicker,它的设计无可挑剔。非常棒。我想我会使用它。
但是,选择日期时会清零时间的规定,
每个人对此可能有不同的看法,但在我个人看来,安排好的时间在更改日期时被清零是无法接受的。
我会继续寻找其他的。
潘纳吉奥蒂斯乔治亚斯/榆树日期选择器
看起来次点的选择是PanagiotisGeorgiadis/elm-datepicker。
外观也很酷,而且拥有持续时间选择器,所以可能适合Alertmanager。
我们决定使用它,并将其用作持续时间选择器,但存在一个致命的缺陷…
在持续时间选择器中,不能选择相同的开始日期/结束日期。
这是规定吗?这种行为令人怀疑。对于Alertmanager来说,这是不可接受的。
因为有很多事情发生,最终我决定自己动手做会更快,是这样的。
制作一个DateTimePicker
代码在这里
https://github.com/prometheus/alertmanager/pull/2262/files
以上就是全部内容了,
因为意外地容易,所以做了个备忘录。
日期选择器
以下是一种汉语本地化的表达方式:
下面是更新内容之一,与日期选择器相关的事项几乎都包含在内。
-- UPDATE
....
MouseOverDay time ->
{ dateTimePicker | mouseOverDay = Just time }
ClearMouseOverDay ->
{ dateTimePicker | mouseOverDay = Nothing }
OnClickDay ->
let
addDateTime_ : Posix -> Maybe Posix -> Posix
addDateTime_ date maybeTime =
case maybeTime of
Just time ->
floorDate date
|> Time.posixToMillis
|> (\d ->
trimTime time
|> Time.posixToMillis
|> (\t -> d + t)
)
|> Time.millisToPosix
Nothing ->
floorDate date
updateTime_ : Maybe Posix -> Maybe Posix -> Maybe Posix
updateTime_ maybeDate maybeTime =
case maybeDate of
Just date ->
Just <| addDateTime_ date maybeTime
Nothing ->
maybeTime
( startDate, endDate ) =
case dateTimePicker.mouseOverDay of
Just m ->
case ( dateTimePicker.startDate, dateTimePicker.endDate ) of
( Nothing, Nothing ) ->
( Just m
, Nothing
)
( Just start, Nothing ) ->
case
compare (floorDate m |> Time.posixToMillis)
(floorDate start |> Time.posixToMillis)
of
LT ->
( Just m
, Just start
)
_ ->
( Just start
, Just m
)
( Nothing, Just end ) ->
( Just m
, Just end
)
( Just start, Just end ) ->
( Just m
, Nothing
)
_ ->
( dateTimePicker.startDate
, dateTimePicker.endDate
)
in
{ dateTimePicker
| startDate = startDate
, endDate = endDate
, startTime = updateTime_ startDate dateTimePicker.startTime
, endTime = updateTime_ endDate dateTimePicker.endTime
}
-- VIEW
-- カレンダー1マス分のviewを抜粋
[ div
[ class ("date front" ++ mouseoverClass ++ startClass ++ endClass ++ thisMonthClass)
, onMouseOver <| MouseOverDay day
, onClick OnClickDay
]
[ text (Time.toDay utc day |> String.fromInt) ]
]
当鼠标悬停在MouseOverDay时间的单元格(日期)上时,将其记录在mouseOverDay模型中。
当点击日期时(设置为MouseOverDay日期),将其设置为已选取的日期(起始日期,结束日期)。
然后,由于持续时间选择器,我们需要判断所点击的两个日期,是将其设为开始日期还是结束日期,这个过程会有很多决定。
时间选择器
为了明确区分日期,TimePicker组件定义了startTime和endTime作为容器。
-- UPDATE
....
SetInputTime startOrEnd inputHourOrMinute num ->
let
limit_ : Int -> Int -> Int
limit_ limit n =
if n < 0 then
0
else
modBy limit n
updateHourOrMinute_ : InputHourOrMinute -> Posix -> Posix
updateHourOrMinute_ ihom s =
case ihom of
InputHour ->
updateHour (limit_ 24 num) s
InputMinute ->
updateMinute (limit_ 60 num) s
( startTime, endTime ) =
setTime_ startOrEnd inputHourOrMinute updateHourOrMinute_
in
{ dateTimePicker | startTime = startTime, endTime = endTime }
IncrementTime startOrEnd inputHourOrMinute num ->
let
updateHourOrMinute_ : InputHourOrMinute -> Posix -> Posix
updateHourOrMinute_ ihom s =
let
compare_ : Posix -> Posix
compare_ a =
if
(floorDate s |> Time.posixToMillis)
== (floorDate a |> Time.posixToMillis)
then
a
else
s
in
case ihom of
InputHour ->
addHour num s
|> compare_
InputMinute ->
addMinute num s
|> compare_
( startTime, endTime ) =
setTime_ startOrEnd inputHourOrMinute updateHourOrMinute_
in
{ dateTimePicker | startTime = startTime, endTime = endTime }
-- VIEW
, div [ class "minute" ]
[ button
[ class "up-button d-flex-center"
, onClick <| IncrementTime startOrEnd InputMinute 1
]
[ i
[ class "fa fa-angle-up" ]
[]
]
, input
[ on "blur" <| Decode.map (SetInputTime startOrEnd InputMinute) targetValueIntParse
, value
(case startOrEnd of
Start ->
case dateTimePicker.startTime of
Just t ->
Time.toMinute utc t |> String.fromInt
Nothing ->
"0"
End ->
case dateTimePicker.endTime of
Just t ->
Time.toMinute utc t |> String.fromInt
Nothing ->
"0"
)
, maxlength 2
, class "view"
]
[]
如果要在按钮点击时增加时间,请使用IncrementTime;如果要在文本输入中指定时间,请使用SetInputTime。我稍微花了些心思,所以这段话比较长。
为了统一管理全部以Posix为单位的时间,需要经常使用Time.floor、Time.posixToMillis等函数来进行日期和时间的加法操作。
由于这些操作会使代码变得冗长,所以我将它们全部整理到了另一个名为Utils.elm的文件中。
个人感受
Elm的DateTimePicker有很多外部库可供选择,看起来还算好用。虽然有一些可以自由定制的选项,但数量相对较少。
由于它们都有一些特殊之处,所以在选择之前必须明确需求,否则会迷失方向。
(上述情况并不仅限于Elm,因为任何库都不是简单导入即可使用,因此选择是一个繁琐的试错过程,需要耗费大量精力)
使用Elm创建DatePicker非常简单。如果没有喜欢的库,我认为自己创建是一个选择。我想要一个叫做DatePickerSDK或者DatePickerHelper的东西。