git.iko.soy/rowtype-yoga/ry-blocks
on main help sign in
Add more resize hooks
commit75e29e4f90b24760c68481cb18c7360753d20c03 authorMark Eibes <mark.eibes@gmail.com> dateMon 1 Aug 2022, 10:20 (4 years ago) parentb34e248
14 files changed+155−30
.vscode/settings.json+2−2 spago.dhall+2 src/Web/DOM/ResizeObserver.js+2−2 src/Web/DOM/ResizeObserver.purs+5−7 src/Yoga/Block/Atom/Button/Style.purs+1−4 src/Yoga/Block/Atom/Input/View/Container.purs+2−2 src/Yoga/Block/Atom/Segmented/View.purs+2−2 src/Yoga/Block/Atom/Segmented/View/ActiveIndicator.purs+2−2 src/Yoga/Block/Container/Style.js+8 src/Yoga/Block/Container/Style.purs+4−1 src/Yoga/Block/Hook/UseElementResize.purs+43 src/Yoga/Block/Hook/UseOverflows.purs+63 src/Yoga/Block/Hook/UseResize.purs → src/Yoga/Block/Hook/UseWindowResize.purs+5−5 src/Yoga/Block/Internal.purs+14−3
.vscode/settings.json+2−2
@@ -1,6 +1,6 @@
11{
22 "editor.formatOnSave": true,
3− "workbench.colorTheme": "Halcyon",
3+ "workbench.colorTheme": "Espresso Soda",
44 "search.useGlobalIgnoreFiles": true,
55 "files.watcherExclude": {
66 "**/.spago/**": true,
@@ -9,4 +9,4 @@
99 "**/.storybook/**": true,
1010 "**/.github/**": true
1111 }
12−}
12+}
spago.dhall+2
@@ -55,10 +55,12 @@ You can edit this file as you like.
5555 , "typelevel-peano"
5656 , "typelevel-prelude"
5757 , "unsafe-coerce"
58+ , "unsafe-reference"
5859 , "untagged-union"
5960 , "web-dom"
6061 , "web-events"
6162 , "web-html"
63+ , "web-uievents"
6264 ]
6365, packages = ./packages.dhall
6466, sources = [ "src/**/*.purs" ]
src/Web/DOM/ResizeObserver.js+2−2
@@ -18,8 +18,8 @@ export function _observe(element) {
1818 }
1919}
2020
21−export function unobserve(element) {
22− return function (observer) {
21+export function unobserve(observer) {
22+ return function (element) {
2323 return function () {
2424 return observer.unobserve(element)
2525 }
src/Web/DOM/ResizeObserver.purs+5−7
@@ -1,5 +1,4 @@
11-- This module provides FFI bindings for the Resize Observer API.
2−-- TODO: Create a PR to make this a part of `purescript-web-dom`.
32-- NOTE: I'm not using the uncurried package as `web-dom` doesn't use it either.
43module Web.DOM.ResizeObserver
54 ( ResizeObserver
@@ -39,9 +38,7 @@ data ResizeObserverBoxOptions
3938
4039optionsToFFI ∷ ResizeObserverBoxOptions -> String
4140optionsToFFI BorderBox = "border-box"
42−
4341optionsToFFI ContentBox = "content-box"
44−
4542optionsToFFI DevicePixelContentBox = "device-pixel-content-box"
4643
4744foreign import resizeObserver
@@ -51,12 +48,13 @@ foreign import resizeObserver
5148foreign import _observe ∷ ∀ r. Element -> Record r -> ResizeObserver -> Effect Unit
5249
5350observe
54− ∷ Element
55− -> ResizeObserverBoxOptions
51+ ∷ ResizeObserverBoxOptions
5652 -> ResizeObserver
53+ -> Element
5754 -> Effect Unit
58−observe element options = _observe element { box: optionsToFFI options }
55+observe options observer element =
56+ _observe element { box: optionsToFFI options } observer
5957
60−foreign import unobserve ∷ Element -> ResizeObserver -> Effect Unit
58+foreign import unobserve ∷ ResizeObserver -> Element -> Effect Unit
6159
6260foreign import disconnect ∷ ResizeObserver -> Effect Unit
src/Yoga/Block/Atom/Button/Style.purs+1−4
@@ -65,10 +65,7 @@ button =
6565 { background: str "transparent"
6666 , boxShadow: none
6767 , color: str colour.highlightTextOnBackground
68− , "&:active":
69− nest
70− { boxShadow: none
71− }
68+ , "&:active": nest { boxShadow: none }
7269 , """&[data-button-type="primary"]""":
7370 nest
7471 { background: str colour.highlight
src/Yoga/Block/Atom/Input/View/Container.purs+2−2
@@ -13,7 +13,7 @@ import React.Basic.Emotion (Style)
1313import React.Basic.Hooks as React
1414import Unsafe.Coerce (unsafeCoerce)
1515import Yoga.Block.Atom.Input.Style as Style
16−import Yoga.Block.Hook.UseResize (useResize)
16+import Yoga.Block.Hook.UseWindowResize (useWindowResize)
1717
1818type PropsF f =
1919 ( css ∷ f Style
@@ -41,7 +41,7 @@ rawContainer ∷ ∀ p. ReactComponent { | p }
4141rawContainer =
4242 mkForwardRefComponent "InputContainer" do
4343 \(props ∷ { | PropsOptional }) ref -> React.do
44− sizes <- useResize
44+ sizes <- useWindowResize
4545 dimensions /\ setDimensions <- useState' zero
4646 useEffect sizes.innerWidth do
4747 maybeDimensions <- getOffsetDimensionsFromRef ref
src/Yoga/Block/Atom/Segmented/View.purs+2−2
@@ -24,7 +24,7 @@ import Yoga.Block.Atom.Segmented.View.ActiveIndicator as ActiveIndicator
2424import Yoga.Block.Atom.Segmented.View.Types (Item)
2525import Yoga.Block.Hook.Key as Key
2626import Yoga.Block.Hook.UseKeyDown (useKeyDown)
27−import Yoga.Block.Hook.UseResize (useResize)
27+import Yoga.Block.Hook.UseWindowResize (useWindowResize)
2828
2929type Props =
3030 { buttonContents ∷ TwoOrMore Item
@@ -71,7 +71,7 @@ component =
7171 _ -> pure unit
7272 -------------------------------------------
7373 -- Ensure redraw on window resize
74− newWindowSize <- useResize
74+ newWindowSize <- useWindowResize
7575 useAff newWindowSize do
7676 let δw = abs (newWindowSize.innerWidth - windowSize.innerWidth)
7777 let δh = abs (newWindowSize.innerHeight - windowSize.innerHeight)
src/Yoga/Block/Atom/Segmented/View/ActiveIndicator.purs+2−2
@@ -23,14 +23,14 @@ import Unsafe.Coerce (unsafeCoerce)
2323import Yoga.Block.Atom.Segmented.Style as Style
2424import Yoga.Block.Atom.Segmented.View.Types (Item)
2525import Yoga.Block.Hook.Scroll (useScrollPosition)
26−import Yoga.Block.Hook.UseResize as UseResize
26+import Yoga.Block.Hook.UseWindowResize as UseWindowResize
2727
2828type Props =
2929 { activeItemRefs ∷ TwoOrMore (Ref (Nullable Node))
3030 , buttonContents ∷ TwoOrMore Item
3131 , activeItemIndex ∷ Int
3232 , updateActiveItem ∷ Item -> Int -> Effect Unit
33− , windowSize ∷ UseResize.Sizes
33+ , windowSize ∷ UseWindowResize.Sizes
3434 }
3535
3636component ∷ ReactComponent Props
src/Yoga/Block/Container/Style.js+8
@@ -11,3 +11,11 @@ export function getElementStyle(el) {
1111export function setStyleProperty(prop) {
1212 return (value) => (style) => () => style.setProperty(prop, value)
1313}
14+
15+export function getStyleProperty(prop) {
16+ return (style) => () => style[prop]
17+}
18+
19+export function removeStyleProperty(prop) {
20+ return (style) => () => delete style[prop]
21+}
src/Yoga/Block/Container/Style.purs+4−1
@@ -14,7 +14,6 @@ import Type.Proxy (Proxy)
1414import Unsafe.Coerce (unsafeCoerce)
1515import Web.DOM (Element)
1616import Web.DOM.Document (documentElement)
17−import Web.Event.Internal.Types (EventTarget)
1817import Web.HTML (Window, window)
1918import Web.HTML.HTMLDocument as HTMLDocument
2019import Web.HTML.Window (document)
@@ -59,6 +58,10 @@ foreign import getElementStyle ∷ Element → Effect ElementStyle
5958
6059foreign import setStyleProperty ∷ String → String → ElementStyle → Effect Unit
6160
61+foreign import getStyleProperty :: String -> ElementStyle -> Effect String
62+
63+foreign import removeStyleProperty ∷ String → ElementStyle → Effect Unit
64+
6265getDocumentElement ∷ MaybeT Effect Element
6366getDocumentElement = do
6467 win ← window # lift
src/Yoga/Block/Hook/UseElementResize.pursnew file+43
@@ -0,0 +1,43 @@
1+module Yoga.Block.Hook.UseOnElementResize where
2+
3+import Yoga.Prelude.View
4+
5+import Data.Array as Array
6+import Data.Newtype (class Newtype)
7+import React.Basic.Hooks as React
8+import Type.Function (type (#))
9+import Web.DOM.ResizeObserver (ResizeObserverBoxOptions(..), observe, resizeObserver, unobserve)
10+import Yoga.Block.Internal (getElementFromRef)
11+
12+newtype UseOnElementResizeWithRef hooks = UseOnElementResizeWithRef
13+ (hooks # UseLayoutEffect Unit)
14+
15+derive instance Newtype (UseOnElementResizeWithRef hooks) _
16+
17+type OnResize = { old :: DOMRect, new :: DOMRect } -> Effect Unit
18+
19+useOnElementResizeWithRef ∷ NodeRef -> OnResize -> Hook UseOnElementResizeWithRef Unit
20+useOnElementResizeWithRef ref onResize =
21+ coerceHook React.do
22+ useLayoutEffectOnce do
23+ elʔ <- getElementFromRef ref
24+ case elʔ of
25+ Nothing -> mempty
26+ Just elem -> do
27+ observer <- resizeObserver \entries _ ->
28+ Array.head entries # traverse_ \{ contentRect: new } -> do
29+ old <- getBoundingClientRect elem
30+ onResize { old, new }
31+ elem # observe BorderBox observer
32+ pure (elem # unobserve observer)
33+
34+newtype UseOnElementResize hooks = UseOnElementResize
35+ (hooks # UseRef (Nullable Node) # UseOnElementResizeWithRef)
36+
37+derive instance Newtype (UseOnElementResize hooks) _
38+
39+useOnElementResize ∷ OnResize -> Hook UseOnElementResize NodeRef
40+useOnElementResize onResize = coerceHook React.do
41+ ref <- React.useRef null
42+ useOnElementResizeWithRef ref onResize
43+ pure ref
src/Yoga/Block/Hook/UseOverflows.pursnew file+63
@@ -0,0 +1,63 @@
1+module Yoga.Block.Hook.UseOverflows where
2+
3+import Debug
4+import Yoga.Prelude.View
5+
6+import Data.Array as Array
7+import Data.Newtype (class Newtype)
8+import Data.Traversable (traverse)
9+import React.Basic.Hooks (type (&))
10+import React.Basic.Hooks as React
11+import Web.DOM.Element (clientHeight, clientWidth, scrollHeight, scrollWidth)
12+import Web.DOM.ResizeObserver (ResizeObserverBoxOptions(..), observe, resizeObserver, unobserve)
13+import Web.HTML.HTMLElement (offsetTop)
14+import Web.HTML.HTMLElement as HTMLElement
15+import Yoga.Block.Container.Style (getElementStyle, getStyleProperty, removeStyleProperty, setStyleProperty)
16+import Yoga.Block.Hook.UseStateEq (UseStateEq, useStateEq')
17+import Yoga.Block.Internal (getElementFromRef)
18+
19+newtype UseOverflows hooks = UseOverflows
20+ ( hooks
21+ & UseStateEq { x :: Boolean, y :: Boolean }
22+ & UseRef (Nullable Node)
23+ & UseLayoutEffect Unit
24+ )
25+
26+derive instance Newtype (UseOverflows hooks) _
27+
28+useOverflows ∷ Hook UseOverflows { ref :: NodeRef, xOverflows :: Boolean, yOverflows :: Boolean }
29+useOverflows = coerceHook React.do
30+ overflowState /\ setOverflowState <- useStateEq' { x: false, y: false }
31+ ref <- React.useRef null
32+ useLayoutEffectOnce do
33+ elʔ <- getElementFromRef ref
34+ case elʔ of
35+ Nothing -> mempty
36+ Just element -> do
37+ observer <- resizeObserver \entries _ ->
38+ Array.head entries # traverse_ \{ target } -> do
39+ -- get old style
40+ styleʔ <- elʔ # traverse getElementStyle
41+ let _ = spy "setting style visible" styleʔ
42+ oldOverflowʔ <- styleʔ # traverse (getStyleProperty "overflow")
43+ for_ styleʔ \style -> do
44+ setStyleProperty "overflow" "scroll" style
45+ -- force reflow: https://gist.github.com/paulirish/5d52fb081b3570c81e3a
46+ -- (elʔ >>= HTMLElement.fromElement) # traverse_ (offsetTop)
47+ let _ = spy "setting style visible" style
48+ pure unit
49+ -- get sizes
50+ sw <- scrollWidth target
51+ sh <- scrollHeight target
52+ cw <- clientWidth target
53+ ch <- clientHeight target
54+ setOverflowState { x: sw > cw, y: sh > ch }
55+ -- reset old style
56+ for_ styleʔ \s ->
57+ case oldOverflowʔ of
58+ Nothing -> removeStyleProperty "overflow" s
59+ Just oo -> setStyleProperty "overflow" oo s
60+ element # observe BorderBox observer
61+ pure (element # unobserve observer)
62+
63+ pure { ref, xOverflows: overflowState.x, yOverflows: overflowState.y }
src/Yoga/Block/Hook/UseResize.purs → src/Yoga/Block/Hook/UseWindowResize.pursrenamed+5−5
@@ -1,4 +1,4 @@
1−module Yoga.Block.Hook.UseResize where
1+module Yoga.Block.Hook.UseWindowResize where
22
33import Prelude
44
@@ -31,12 +31,12 @@ registerListener listener = do
3131type Sizes =
3232 { innerWidth ∷ Number, innerHeight ∷ Number }
3333
34−newtype UseResize hooks = UseResize (UseLayoutEffect Unit (UseState Sizes hooks))
34+newtype UseWindowResize hooks = UseWindowResize (UseLayoutEffect Unit (UseState Sizes hooks))
3535
36−derive instance ntUseResize ∷ Newtype (UseResize hooks) _
36+derive instance Newtype (UseWindowResize hooks) _
3737
38−useResize ∷ Hook UseResize Sizes
39−useResize =
38+useWindowResize ∷ Hook UseWindowResize Sizes
39+useWindowResize =
4040 coerceHook React.do
4141 size /\ setSize <- React.useState' zero
4242 useLayoutEffect unit do
src/Yoga/Block/Internal.purs+14−3
@@ -31,6 +31,7 @@ module Yoga.Block.Internal
3131 , unsafeAddProps
3232 , createRef
3333 , getBoundingBoxFromRef
34+ , getClientDimensionsFromRef
3435 , getElementFromRef
3536 , getHTMLElementFromRef
3637 , getOffsetHeightFromRef
@@ -44,6 +45,7 @@ module Yoga.Block.Internal
4445 ) where
4546
4647import Prelude
48+
4749import Control.Monad.Cont.Trans (lift)
4850import Control.Monad.Maybe.Trans (MaybeT(..), runMaybeT)
4951import Data.Array as Array
@@ -64,11 +66,12 @@ import React.Basic.Emotion as E
6466import React.Basic.Events (EventHandler)
6567import React.Basic.Hooks (JSX, ReactComponent, Ref, Render, readRefMaybe)
6668import Record.Extra (class Keys, keys)
69+import Record.Unsafe.Union (unsafeUnion)
6770import Type.Proxy (Proxy(..))
6871import Unsafe.Coerce (unsafeCoerce)
6972import Untagged.Union (UndefinedOr, uorToMaybe)
7073import Web.DOM (Element, Node)
71−import Web.DOM.Element (scrollHeight, scrollWidth, DOMRect, getBoundingClientRect)
74+import Web.DOM.Element (DOMRect, clientHeight, clientWidth, getBoundingClientRect, scrollHeight, scrollWidth)
7275import Web.DOM.Element as Element
7376import Web.DOM.NonElementParentNode (getElementById)
7477import Web.HTML (window)
@@ -78,7 +81,6 @@ import Web.HTML.HTMLElement as HTMLElement
7881import Web.HTML.Window (document)
7982import Yoga.Block.Internal.CSS (_0)
8083import Yoga.Block.Internal.OptionalProp (OptionalProp(..), Id, appendIfDefined, asOptional, composeHandler, getOr, getOrFlipped, ifTrue, isTruthy, maybeToOp, opToMaybe, setOrDelete, unsafeUnMaybe, unsafeUnOptional, (<>?), (?||))
81−import Record.Unsafe.Union (unsafeUnion)
8284
8385unsafeAddProps ∷ ∀ r s. { | r } → { | s } → { | r }
8486unsafeAddProps = unsafeUnion
@@ -154,6 +156,15 @@ getScrollDimensionsFromRef itemRef =
154156 height <- lift $ scrollHeight elem
155157 pure { width, height }
156158
159+getClientDimensionsFromRef ∷ NodeRef -> Effect (Maybe { height ∷ Number, width ∷ Number })
160+getClientDimensionsFromRef itemRef =
161+ runMaybeT do
162+ node <- MaybeT $ readRefMaybe itemRef
163+ elem <- MaybeT $ pure $ Element.fromNode node
164+ width <- lift $ clientWidth elem
165+ height <- lift $ clientHeight elem
166+ pure { width, height }
167+
157168getHTMLElementFromRef ∷ Ref (Nullable Node) -> Effect (Maybe HTMLElement)
158169getHTMLElementFromRef itemRef =
159170 runMaybeT do
@@ -748,4 +759,4 @@ type ButtonReadablePropsNoChildrenF f more =
748759 , vocab ∷ f String
749760 , wmode ∷ f String
750761 | more
751− )
762+ )