git.iko.soy/rowtype-yoga/psfp
on 133b502 help sign in
106 lines 3.7 kB history raw
1module Container.Header where
3import Prelude
5import CSS.Safer (cssSafer)
6import Data.Int (toNumber)
7import Data.Maybe (Maybe(..))
8import Data.Monoid (guard)
9import Data.Newtype (class Newtype)
10import Data.Nullable (Nullable, null)
11import Effect (Effect)
12import Effect.Class.Console (log)
13import React.Basic.DOM as R
14import React.Basic.Hooks (type (/\), Hook, ReactComponent, Ref, UseLayoutEffect, UseRef, UseState, coerceHook, component, element, readRefMaybe, useLayoutEffect, useRef, useState, (/\))
15import React.Basic.Hooks as React
16import SVG.Icon (trianglelogoIcon)
17import Theme.Styles (makeStyles)
18import Theme.Types (CSSTheme)
19import Typography.Header (HeadingLevel(..), mkH)
20import Web.DOM (Node)
21import Web.Event.Event (EventType(..))
22import Web.Event.EventTarget (EventListener, addEventListener, eventListener, removeEventListener)
23import Web.HTML (HTMLElement, window)
24import Web.HTML.HTMLElement (offsetTop)
25import Web.HTML.HTMLElement as HTMLElement
26import Web.HTML.Window (scrollY, toEventTarget)
28mkHeader ∷ Effect (ReactComponent {})
29mkHeader = do
30 h <- mkH
31 useStyles <-
32 makeStyles \(theme ∷ CSSTheme) ->
33 { header:
34 cssSafer
35 { backgroundColor: theme.backgroundColour
36 , borderBottom: "0"
37 , fontFamily: theme.headingFontFamily
38 , gridArea: "header"
39 , display: "flex"
40 , alignItems: "center"
41 , justifyContent: "flex-end"
42 , width: "100%"
43 , height: "80px"
44 }
45 , sticky: cssSafer { position: "fixed", top: "0" }
46 , title: cssSafer { paddingRight: "30px"}
47 , logo:
48 cssSafer
49 { width: "70px"
50 , height: "70px"
51 , padding: "5px"
52 , marginTop: "9px"
53 , marginRight: "7px"
54 , fill: theme.textColour
55 }
56 }
57 component "Header" \{} -> React.do
58 classes <- useStyles
59 shouldBeSticky /\ nodeRef <- useShouldBeSticky
60 pure
61 $ R.header
62 { ref: nodeRef
63 , className: classes.header <> " " <> guard shouldBeSticky classes.sticky
64 , children:
65 [ element h { level: H2, text: "Kleislove", className: Just classes.title }
66 , R.div { children: [ element trianglelogoIcon {} ], className: classes.logo }
67 ]
68 }
70newtype UseShouldBeSticky hooks
71 = UseShouldBeSticky (UseLayoutEffect Unit (UseState Boolean (UseRef (Nullable Node) hooks)))
73derive instance ntUseScrollYPosition ∷ Newtype (UseShouldBeSticky hooks) _
75useShouldBeSticky ∷ Hook UseShouldBeSticky (Boolean /\ Ref (Nullable Node))
76useShouldBeSticky =
77 coerceHook React.do
78 nodeRef <- useRef null
79 mustBeSticky /\ modifyMustBeSticky <- useState false
80 let setSticky = modifyMustBeSticky <<< const
81 useLayoutEffect unit do
82 maybeNode <- readRefMaybe nodeRef
83 case maybeNode >>= HTMLElement.fromNode of
84 Nothing -> do
85 log "Could not register listener because there was no node"
86 pure (pure unit)
87 Just element -> do
88 listener <- makeListener setSticky element
89 registerListener listener
90 pure (mustBeSticky /\ nodeRef)
92scrollEventType ∷ EventType
93scrollEventType = EventType "scroll"
95registerListener ∷ EventListener -> Effect (Effect Unit)
96registerListener listener = do
97 target <- window <#> toEventTarget
98 addEventListener scrollEventType listener false target
99 pure $ removeEventListener scrollEventType listener false target
101makeListener ∷ (Boolean -> Effect Unit) -> HTMLElement -> Effect EventListener
102makeListener setSticky element = do
103 nodeStartPos <- offsetTop element
104 eventListener $ const do
105 yPos <- window >>= scrollY <#> toNumber
106 setSticky $ nodeStartPos < yPos