git.iko.soy/rowtype-yoga/ry-blocks
on main help sign in
Add more resize hooks
75e29e4Mark Eibes
.vscode/settings.json+2−2
@@ -1,6 +1,6 @@
1{
1{
2 "editor.formatOnSave": true,
2 "editor.formatOnSave": true,
3− "workbench.colorTheme": "Halcyon",
3+ "workbench.colorTheme": "Espresso Soda",
4 "search.useGlobalIgnoreFiles": true,
4 "search.useGlobalIgnoreFiles": true,
5 "files.watcherExclude": {
5 "files.watcherExclude": {
6 "**/.spago/**": true,
6 "**/.spago/**": true,
@@ -9,4 +9,4 @@
9 "**/.storybook/**": true,
9 "**/.storybook/**": true,
10 "**/.github/**": true
10 "**/.github/**": true
11 }
11 }
12−}
12+}
spago.dhall+2
@@ -55,10 +55,12 @@ You can edit this file as you like.
55 , "typelevel-peano"
55 , "typelevel-peano"
56 , "typelevel-prelude"
56 , "typelevel-prelude"
57 , "unsafe-coerce"
57 , "unsafe-coerce"
58+ , "unsafe-reference"
58 , "untagged-union"
59 , "untagged-union"
59 , "web-dom"
60 , "web-dom"
60 , "web-events"
61 , "web-events"
61 , "web-html"
62 , "web-html"
63+ , "web-uievents"
62 ]
64 ]
63, packages = ./packages.dhall
65, packages = ./packages.dhall
64, sources = [ "src/**/*.purs" ]
66, sources = [ "src/**/*.purs" ]
src/Web/DOM/ResizeObserver.js+2−2
@@ -18,8 +18,8 @@ export function _observe(element) {
18 }
18 }
19}
19}
20
20
21−export function unobserve(element) {
21+export function unobserve(observer) {
22− return function (observer) {
22+ return function (element) {
23 return function () {
23 return function () {
24 return observer.unobserve(element)
24 return observer.unobserve(element)
25 }
25 }
src/Web/DOM/ResizeObserver.purs+5−7
@@ -1,5 +1,4 @@
1-- This module provides FFI bindings for the Resize Observer API.
1-- This module provides FFI bindings for the Resize Observer API.
2−-- TODO: Create a PR to make this a part of `purescript-web-dom`.
3-- NOTE: I'm not using the uncurried package as `web-dom` doesn't use it either.
2-- NOTE: I'm not using the uncurried package as `web-dom` doesn't use it either.
4module Web.DOM.ResizeObserver
3module Web.DOM.ResizeObserver
5 ( ResizeObserver
4 ( ResizeObserver
@@ -39,9 +38,7 @@ data ResizeObserverBoxOptions
39
38
40optionsToFFI ∷ ResizeObserverBoxOptions -> String
39optionsToFFI ∷ ResizeObserverBoxOptions -> String
41optionsToFFI BorderBox = "border-box"
40optionsToFFI BorderBox = "border-box"
42−
43optionsToFFI ContentBox = "content-box"
41optionsToFFI ContentBox = "content-box"
44−
45optionsToFFI DevicePixelContentBox = "device-pixel-content-box"
42optionsToFFI DevicePixelContentBox = "device-pixel-content-box"
46
43
47foreign import resizeObserver
44foreign import resizeObserver
@@ -51,12 +48,13 @@ foreign import resizeObserver
51foreign import _observe ∷ ∀ r. Element -> Record r -> ResizeObserver -> Effect Unit
48foreign import _observe ∷ ∀ r. Element -> Record r -> ResizeObserver -> Effect Unit
52
49
53observe
50observe
54− ∷ Element
55− -> ResizeObserverBoxOptions
51+ ∷ ResizeObserverBoxOptions
56 -> ResizeObserver
52 -> ResizeObserver
53+ -> Element
57 -> Effect Unit
54 -> Effect Unit
55+observe options observer element =
58−observe element options = _observe element { box: optionsToFFI options }
56+ _observe element { box: optionsToFFI options } observer
59
57
60−foreign import unobserve ∷ Element -> ResizeObserver -> Effect Unit
58+foreign import unobserve ∷ ResizeObserver -> Element -> Effect Unit
61
59
62foreign import disconnect ∷ ResizeObserver -> Effect Unit
60foreign import disconnect ∷ ResizeObserver -> Effect Unit
src/Yoga/Block/Atom/Button/Style.purs+1−4
@@ -65,10 +65,7 @@ button =
65 { background: str "transparent"
65 { background: str "transparent"
66 , boxShadow: none
66 , boxShadow: none
67 , color: str colour.highlightTextOnBackground
67 , color: str colour.highlightTextOnBackground
68− , "&:active":
69− nest
70− { boxShadow: none
68+ , "&:active": nest { boxShadow: none }
71− }
72 , """&[data-button-type="primary"]""":
69 , """&[data-button-type="primary"]""":
73 nest
70 nest
74 { background: str colour.highlight
71 { background: str colour.highlight
src/Yoga/Block/Atom/Input/View/Container.purs+2−2
@@ -13,7 +13,7 @@ import React.Basic.Emotion (Style)
13import React.Basic.Hooks as React
13import React.Basic.Hooks as React
14import Unsafe.Coerce (unsafeCoerce)
14import Unsafe.Coerce (unsafeCoerce)
15import Yoga.Block.Atom.Input.Style as Style
15import Yoga.Block.Atom.Input.Style as Style
16−import Yoga.Block.Hook.UseResize (useResize)
16+import Yoga.Block.Hook.UseWindowResize (useWindowResize)
17
17
18type PropsF f =
18type PropsF f =
19 ( css ∷ f Style
19 ( css ∷ f Style
@@ -41,7 +41,7 @@ rawContainer ∷ ∀ p. ReactComponent { | p }
41rawContainer =
41rawContainer =
42 mkForwardRefComponent "InputContainer" do
42 mkForwardRefComponent "InputContainer" do
43 \(props ∷ { | PropsOptional }) ref -> React.do
43 \(props ∷ { | PropsOptional }) ref -> React.do
44− sizes <- useResize
44+ sizes <- useWindowResize
45 dimensions /\ setDimensions <- useState' zero
45 dimensions /\ setDimensions <- useState' zero
46 useEffect sizes.innerWidth do
46 useEffect sizes.innerWidth do
47 maybeDimensions <- getOffsetDimensionsFromRef ref
47 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
24import Yoga.Block.Atom.Segmented.View.Types (Item)
24import Yoga.Block.Atom.Segmented.View.Types (Item)
25import Yoga.Block.Hook.Key as Key
25import Yoga.Block.Hook.Key as Key
26import Yoga.Block.Hook.UseKeyDown (useKeyDown)
26import Yoga.Block.Hook.UseKeyDown (useKeyDown)
27−import Yoga.Block.Hook.UseResize (useResize)
27+import Yoga.Block.Hook.UseWindowResize (useWindowResize)
28
28
29type Props =
29type Props =
30 { buttonContents ∷ TwoOrMore Item
30 { buttonContents ∷ TwoOrMore Item
@@ -71,7 +71,7 @@ component =
71 _ -> pure unit
71 _ -> pure unit
72 -------------------------------------------
72 -------------------------------------------
73 -- Ensure redraw on window resize
73 -- Ensure redraw on window resize
74− newWindowSize <- useResize
74+ newWindowSize <- useWindowResize
75 useAff newWindowSize do
75 useAff newWindowSize do
76 let δw = abs (newWindowSize.innerWidth - windowSize.innerWidth)
76 let δw = abs (newWindowSize.innerWidth - windowSize.innerWidth)
77 let δh = abs (newWindowSize.innerHeight - windowSize.innerHeight)
77 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)
23import Yoga.Block.Atom.Segmented.Style as Style
23import Yoga.Block.Atom.Segmented.Style as Style
24import Yoga.Block.Atom.Segmented.View.Types (Item)
24import Yoga.Block.Atom.Segmented.View.Types (Item)
25import Yoga.Block.Hook.Scroll (useScrollPosition)
25import Yoga.Block.Hook.Scroll (useScrollPosition)
26−import Yoga.Block.Hook.UseResize as UseResize
26+import Yoga.Block.Hook.UseWindowResize as UseWindowResize
27
27
28type Props =
28type Props =
29 { activeItemRefs ∷ TwoOrMore (Ref (Nullable Node))
29 { activeItemRefs ∷ TwoOrMore (Ref (Nullable Node))
30 , buttonContents ∷ TwoOrMore Item
30 , buttonContents ∷ TwoOrMore Item
31 , activeItemIndex ∷ Int
31 , activeItemIndex ∷ Int
32 , updateActiveItem ∷ Item -> Int -> Effect Unit
32 , updateActiveItem ∷ Item -> Int -> Effect Unit
33− , windowSize ∷ UseResize.Sizes
33+ , windowSize ∷ UseWindowResize.Sizes
34 }
34 }
35
35
36component ∷ ReactComponent Props
36component ∷ ReactComponent Props
src/Yoga/Block/Container/Style.js+8
@@ -11,3 +11,11 @@ export function getElementStyle(el) {
11export function setStyleProperty(prop) {
11export function setStyleProperty(prop) {
12 return (value) => (style) => () => style.setProperty(prop, value)
12 return (value) => (style) => () => style.setProperty(prop, value)
13}
13}
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)
14import Unsafe.Coerce (unsafeCoerce)
14import Unsafe.Coerce (unsafeCoerce)
15import Web.DOM (Element)
15import Web.DOM (Element)
16import Web.DOM.Document (documentElement)
16import Web.DOM.Document (documentElement)
17−import Web.Event.Internal.Types (EventTarget)
18import Web.HTML (Window, window)
17import Web.HTML (Window, window)
19import Web.HTML.HTMLDocument as HTMLDocument
18import Web.HTML.HTMLDocument as HTMLDocument
20import Web.HTML.Window (document)
19import Web.HTML.Window (document)
@@ -59,6 +58,10 @@ foreign import getElementStyle ∷ Element → Effect ElementStyle
59
58
60foreign import setStyleProperty ∷ String → String → ElementStyle → Effect Unit
59foreign import setStyleProperty ∷ String → String → ElementStyle → Effect Unit
61
60
61+foreign import getStyleProperty :: String -> ElementStyle -> Effect String
62+
63+foreign import removeStyleProperty ∷ String → ElementStyle → Effect Unit
64+
62getDocumentElement ∷ MaybeT Effect Element
65getDocumentElement ∷ MaybeT Effect Element
63getDocumentElement = do
66getDocumentElement = do
64 win ← window # lift
67 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
2
2
3import Prelude
3import Prelude
4
4
@@ -31,12 +31,12 @@ registerListener listener = do
31type Sizes =
31type Sizes =
32 { innerWidth ∷ Number, innerHeight ∷ Number }
32 { innerWidth ∷ Number, innerHeight ∷ Number }
33
33
34−newtype UseResize hooks = UseResize (UseLayoutEffect Unit (UseState Sizes hooks))
34+newtype UseWindowResize hooks = UseWindowResize (UseLayoutEffect Unit (UseState Sizes hooks))
35
35
36−derive instance ntUseResize ∷ Newtype (UseResize hooks) _
36+derive instance Newtype (UseWindowResize hooks) _
37
37
38−useResize ∷ Hook UseResize Sizes
38+useWindowResize ∷ Hook UseWindowResize Sizes
39−useResize =
39+useWindowResize =
40 coerceHook React.do
40 coerceHook React.do
41 size /\ setSize <- React.useState' zero
41 size /\ setSize <- React.useState' zero
42 useLayoutEffect unit do
42 useLayoutEffect unit do
src/Yoga/Block/Internal.purs+14−3
@@ -31,6 +31,7 @@ module Yoga.Block.Internal
31 , unsafeAddProps
31 , unsafeAddProps
32 , createRef
32 , createRef
33 , getBoundingBoxFromRef
33 , getBoundingBoxFromRef
34+ , getClientDimensionsFromRef
34 , getElementFromRef
35 , getElementFromRef
35 , getHTMLElementFromRef
36 , getHTMLElementFromRef
36 , getOffsetHeightFromRef
37 , getOffsetHeightFromRef
@@ -44,6 +45,7 @@ module Yoga.Block.Internal
44 ) where
45 ) where
45
46
46import Prelude
47import Prelude
48+
47import Control.Monad.Cont.Trans (lift)
49import Control.Monad.Cont.Trans (lift)
48import Control.Monad.Maybe.Trans (MaybeT(..), runMaybeT)
50import Control.Monad.Maybe.Trans (MaybeT(..), runMaybeT)
49import Data.Array as Array
51import Data.Array as Array
@@ -64,11 +66,12 @@ import React.Basic.Emotion as E
64import React.Basic.Events (EventHandler)
66import React.Basic.Events (EventHandler)
65import React.Basic.Hooks (JSX, ReactComponent, Ref, Render, readRefMaybe)
67import React.Basic.Hooks (JSX, ReactComponent, Ref, Render, readRefMaybe)
66import Record.Extra (class Keys, keys)
68import Record.Extra (class Keys, keys)
69+import Record.Unsafe.Union (unsafeUnion)
67import Type.Proxy (Proxy(..))
70import Type.Proxy (Proxy(..))
68import Unsafe.Coerce (unsafeCoerce)
71import Unsafe.Coerce (unsafeCoerce)
69import Untagged.Union (UndefinedOr, uorToMaybe)
72import Untagged.Union (UndefinedOr, uorToMaybe)
70import Web.DOM (Element, Node)
73import Web.DOM (Element, Node)
71−import Web.DOM.Element (scrollHeight, scrollWidth, DOMRect, getBoundingClientRect)
74+import Web.DOM.Element (DOMRect, clientHeight, clientWidth, getBoundingClientRect, scrollHeight, scrollWidth)
72import Web.DOM.Element as Element
75import Web.DOM.Element as Element
73import Web.DOM.NonElementParentNode (getElementById)
76import Web.DOM.NonElementParentNode (getElementById)
74import Web.HTML (window)
77import Web.HTML (window)
@@ -78,7 +81,6 @@ import Web.HTML.HTMLElement as HTMLElement
78import Web.HTML.Window (document)
81import Web.HTML.Window (document)
79import Yoga.Block.Internal.CSS (_0)
82import Yoga.Block.Internal.CSS (_0)
80import Yoga.Block.Internal.OptionalProp (OptionalProp(..), Id, appendIfDefined, asOptional, composeHandler, getOr, getOrFlipped, ifTrue, isTruthy, maybeToOp, opToMaybe, setOrDelete, unsafeUnMaybe, unsafeUnOptional, (<>?), (?||))
83import Yoga.Block.Internal.OptionalProp (OptionalProp(..), Id, appendIfDefined, asOptional, composeHandler, getOr, getOrFlipped, ifTrue, isTruthy, maybeToOp, opToMaybe, setOrDelete, unsafeUnMaybe, unsafeUnOptional, (<>?), (?||))
81−import Record.Unsafe.Union (unsafeUnion)
82
84
83unsafeAddProps ∷ ∀ r s. { | r } → { | s } → { | r }
85unsafeAddProps ∷ ∀ r s. { | r } → { | s } → { | r }
84unsafeAddProps = unsafeUnion
86unsafeAddProps = unsafeUnion
@@ -154,6 +156,15 @@ getScrollDimensionsFromRef itemRef =
154 height <- lift $ scrollHeight elem
156 height <- lift $ scrollHeight elem
155 pure { width, height }
157 pure { width, height }
156
158
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+
157getHTMLElementFromRef ∷ Ref (Nullable Node) -> Effect (Maybe HTMLElement)
168getHTMLElementFromRef ∷ Ref (Nullable Node) -> Effect (Maybe HTMLElement)
158getHTMLElementFromRef itemRef =
169getHTMLElementFromRef itemRef =
159 runMaybeT do
170 runMaybeT do
@@ -748,4 +759,4 @@ type ButtonReadablePropsNoChildrenF f more =
748 , vocab ∷ f String
759 , vocab ∷ f String
749 , wmode ∷ f String
760 , wmode ∷ f String
750 | more
761 | more
751− )
762+ )