git.iko.soy/rowtype-yoga/psfp
on 8b4e766 help sign in
97 lines 3.4 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 Scroll.Listener as Scroll
18import Theme.Styles (makeStyles)
19import Theme.Types (CSSTheme)
20import Typography.Header (HeadingLevel(..), mkH)
21import Web.DOM (Node)
22import Web.Event.EventTarget (EventListener, eventListener)
23import Web.HTML (HTMLElement, window)
24import Web.HTML.HTMLElement (offsetTop)
25import Web.HTML.HTMLElement as HTMLElement
26import Web.HTML.Window (scrollY)
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 ntUseShouldBeSticky ∷ 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 Scroll.registerListener listener
90 pure (mustBeSticky /\ nodeRef)
92makeListener ∷ (Boolean -> Effect Unit) -> HTMLElement -> Effect EventListener
93makeListener setSticky element = do
94 nodeStartPos <- offsetTop element
95 eventListener $ const do
96 yPos <- window >>= scrollY <#> toNumber
97 setSticky $ nodeStartPos < yPos