-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathRender.elm
214 lines (187 loc) · 9.87 KB
/
Render.elm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
module Render exposing (viewAsList, Context)
import Date exposing (Date)
import Date.Extra.Config.Config_en_au exposing (config)
import Date.Extra.Compare exposing (Compare2(..), is)
import Date.Extra.Format exposing (formatUtc, isoDateFormat)
import Html exposing (div, Html, ol, li, span, text, button, select, option)
import Html.Attributes exposing (class, value, id, title, disabled, style, src)
import Html.Events exposing (onWithOptions, onInput, onClick)
import List exposing (length, head)
import List.Extra exposing ((!!), dropWhile, last)
import Maybe exposing (map, map2, map3, withDefault)
import Maybe.Extra exposing (isJust, (?), join)
import Tuple exposing (..)
import Icons exposing (..)
import Model exposing (..)
import Rdap
import TimelineWidget
-- Rendering happens inside a context
type alias Context = {
history : History,
fromVersion : Maybe Version,
toVersion : Maybe Version,
versions : List Version, -- TODO: rename this since it can be mistakely taken as all versions
navigationLocks : (LockerState, LockerState),
versionDateDetail : Maybe Date,
today: Date,
timelineWidgetZoom : TimelineZoom,
timelineWidgetZoomDate : Maybe Date
}
type DateFormat = Short | Long
mkCtx : Date -> History -> Model -> Context
mkCtx today h m =
let (fromVersion, toVersion) = m.displayedVersions
versions = Maybe.Extra.values [fromVersion, toVersion]
in Context h fromVersion toVersion versions m.navigationLocks m.versionDateDetail today m.timelineWidgetZoom m.timelineWidgetZoomDate
viewAsList : Response -> Model -> List (Html Msg)
viewAsList response m =
let history = response.history !! m.selected
in case history of
Nothing -> [] -- TODO: will never happen. deal with it differently?
Just h -> let ctx = mkCtx response.stamp h m
in [div [class "historyPane"] ((objectListPanel response.history m.selected) ++
(objectListMobile response.history m.selected) ++
(detailPanel ctx))]
-- Selection Panel
objectListPanel : List History -> Int -> List (Html Msg)
objectListPanel history idx =
if length history <= 1
then []
else [ol [ class "objectList" ] <| List.indexedMap (viewSummary idx) history]
viewSummary : Int -> Int -> History -> Html Msg
viewSummary sel idx h = li
[ class (if sel == idx then "selected" else "" )
, onClick (Select idx) ]
[ span [ class "handle" ] [ text h.identifier.handle ] ]
objectListMobile : List History -> Int -> List (Html Msg)
objectListMobile historyList idx =
if length historyList <= 1
then []
else [div[class "objectListMobile"] [text "Search results: ", dropdownResultsMobile historyList idx]]
dropdownResultsMobile : List History -> Int -> Html Msg
dropdownResultsMobile historyList idx =
let clickedIndex s = Result.withDefault 0 (String.toInt s)
in select [Html.Events.onInput (\s -> Select <| clickedIndex s)] <| List.indexedMap (\x h -> option [Html.Attributes.value <| toString x] [text h.identifier.handle]) historyList
-- Detail Panel
detailPanel : Context -> List (Html Msg)
detailPanel ctx =
[ div [class "detailPanel"] [
timelineWidgetPanel ctx,
div [class "detailPanelMain"] [
navPanel ctx Bkwd,
div [class "detailCenterPanel"] ([versionDatesPanel ctx] ++ versionDateDetailPanels ctx ++ [diffPanel ctx]),
navPanel ctx Fwd,
navBottomPanel ctx
]
]]
diffPanel : Context -> Html Msg
diffPanel ctx =
case ctx.toVersion of
Nothing -> text "Nothing to show"
Just v -> div [class "diffPanel"] (viewDiff ctx ctx.fromVersion v)
versionDatesPanel : Context -> Html Msg
versionDatesPanel ctx =
case ctx.versions of
[] -> text ""
v :: [] -> div [class "versionDatesPanel"] [
div [class "versionDateLeft"] [span [] ((createDateLabel (Just v.from) ctx.versionDateDetail) ++ [text ">"])],
div [class "versionDateRight"] [span []([text "<"] ++ createDateLabel v.until ctx.versionDateDetail)]
]
v1 :: v2 :: _ ->
let versionsInBetween = (+) (-1) <| withDefault 0 <| getDistance v1 v2 ctx.history.versions
middleLabel = if versionsInBetween == 0
then createDateLabel (Just v2.from) ctx.versionDateDetail
else [text <| (toString versionsInBetween) ++
" version" ++ (if versionsInBetween > 1 then "s" else "")]
in div [class "versionDatesPanel"] [
div [class "versionDateLeft"]
[span [] ((createDateLabel (Just v1.from) ctx.versionDateDetail) ++ [text ">"])],
div [class "versionDateCenter"] [span [] ([text "<"] ++ middleLabel ++ [text ">"])],
div [class "versionDateRight"] [span [] ([text "<"] ++ createDateLabel v2.until ctx.versionDateDetail)]
]
versionDateDetailPanels : Context -> List (Html a)
versionDateDetailPanels ctx =
let createDiv pos (panelClass, dateString) = div [class <| "versionDateDetailPanel " ++ pos ++ " " ++ panelClass]
[text dateString]
process date = if date /= Nothing && ctx.versionDateDetail == date
then ("showPanel", Maybe.withDefault "" <| Maybe.map toString date)
else ("hidePanel", "")
in [createDiv "left" <| process <| Maybe.map .from ctx.fromVersion,
createDiv "center" <| process <| Maybe.map .from ctx.toVersion,
createDiv "right" <| process <| Maybe.Extra.join <| Maybe.map .until ctx.toVersion]
timelineWidgetPanel : Context -> Html Msg
timelineWidgetPanel ctx =
TimelineWidget.render <| mkTimelineModel ctx
navPanel : Context -> NavigationDirection -> Html Msg
navPanel ctx direction =
div [class "navPanel"] [
div [class "versionDatesPanel"] [],
div [class "navPanelButtons"] [arrowButton ctx direction,
lockButton ctx direction <| "navPanel" ++ toString direction]
]
navBottomPanel : Context -> Html Msg
navBottomPanel ctx =
div [class "navBottomPanel"]
[
div [class "navBottomPanelItem"] [lockButton ctx Bkwd "navBottomBkwd", arrowButton ctx Bkwd],
div [class "navBottomPanelItem"] [arrowButton ctx Fwd, lockButton ctx Fwd "navBottomFwd"]
]
arrowButton : Context -> NavigationDirection -> Html Msg
arrowButton ctx direction =
case direction of
Bkwd -> button [class "arrowButton", onClick (NavigateDiff Bkwd),
disabled <| checkNavDisabled ctx direction] [arrow "leftArrow"]
Fwd -> button [class "arrowButton", onClick (NavigateDiff Fwd),
disabled <| checkNavDisabled ctx direction] [arrow "rightArrow"]
lockButton : Context -> NavigationDirection -> String -> Html Msg
lockButton ctx dir id =
let f = if dir == Fwd then second else first
state = f ctx.navigationLocks
buttonClass = if state == Locked then "lockedButton" else "unlockedButton"
in button [class buttonClass, onClick (FlipNavLock dir)] [lockerIcon state "lockerIcon" id]
viewDiff : Context -> Maybe Version -> Version -> List (Html Msg)
viewDiff ctx was is =
let rWas = map (Rdap.render ctx.history.identifier << .object) was
rIs = Rdap.render ctx.history.identifier is.object
diff = Rdap.diff rWas rIs
diffOutput = Rdap.output diff
mobileDiffOutput = Rdap.mobileOutput diff
in case was of
Nothing -> [ div [class "diffPanelItem"] [div [class "rdap-is"] [diffOutput] ],
div [class "rdap-mobile"] [mobileDiffOutput]]
_ -> [ div [class "diffPanelItem rdap-was"] [diffOutput],
div [class "diffPanelItem rdap-is"] [diffOutput],
div [class "rdap-mobile"] [mobileDiffOutput]
]
prettifyDate : Date -> DateFormat -> Bool -> Html a
prettifyDate d df active =
let pattern = case df of
Short -> "%d/%m/%y"
Long -> "%d/%m/%Y %H:%M"
spanClass = case df of
Short -> "dateShort"
Long -> "dateLong"
activeClass = if active then " active" else ""
in span [class <| spanClass ++ activeClass] [text <| formatUtc config pattern d]
createDateLabel : Maybe Date -> Maybe Date -> List (Html Msg)
createDateLabel md versionDateDetail =
case md of
Nothing -> [text "Present"]
Just d -> let flipTo = case versionDateDetail of
Nothing -> md
Just vd -> if (is Same d vd) then Nothing else md
(buttonClass, tooltipText, active) = if flipTo == Nothing
then ("pressedFlipShowVersionButton", "Hide date detail", True)
else ("flipShowVersionButton", "Show date detail", False)
in [ prettifyDate d Short active, prettifyDate d Long active,
button [class buttonClass, onClick (FlipShowVersionDateDetail flipTo), title tooltipText]
[expandIcon "moreIconSvg"]
]
-- Utility methods
checkNavDisabled : Context -> NavigationDirection -> Bool
checkNavDisabled ctx dir =
not <| Model.canNavigate ctx.history.versions (ctx.fromVersion, ctx.toVersion) dir ctx.navigationLocks
mkTimelineModel : Context -> TimelineWidget.Model
mkTimelineModel ctx =
TimelineWidget.Model ctx.timelineWidgetZoom ctx.timelineWidgetZoomDate (ctx.fromVersion, ctx.toVersion)
ctx.history.versions ctx.today