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