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