mirror of
https://github.com/penpot/penpot.git
synced 2025-02-13 10:38:13 -05:00
Merge pull request #1796 from penpot/fixed-scroll
✨ Add fixed position in viewer
This commit is contained in:
commit
989ff8db7a
10 changed files with 172 additions and 125 deletions
|
@ -6,6 +6,7 @@
|
|||
### :sparkles: New features
|
||||
|
||||
- Added selected colors widget in right sidebar [Taiga #2485](https://tree.taiga.io/project/penpot/us/2485)
|
||||
- Fix elements when scrolling [Taiga #1533](https://tree.taiga.io/project/penpot/us/1533)
|
||||
|
||||
### :bug: Bugs fixed
|
||||
### :arrow_up: Deps updates
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="500" height="500" viewBox="0 0 132.292 132.292">
|
||||
<path d="M78.822.204l-3.3 3.3L56.69 22.335l2.698 9.296-17.315 17.316-18.998-12.4L0 59.624l32.865 32.865-31.33 31.33 6.598 6.599 31.33-31.33 33.001 33 23.076-23.075-12.128-18.726 17.316-17.315 9.432 2.834 22.132-22.13zm0 13.197l40.274 40.274-9.983 10.123-9.369-2.771-28.33 28.33 11.919 18.665-10.869 10.869-59.268-59.269 10.868-10.868 18.926 12.18 28.331-28.33-2.578-9.177z"/>
|
||||
<svg width="12" xmlns="http://www.w3.org/2000/svg" height="12" x="767" viewBox="0 0 500 499.172" y="920.67">
|
||||
<path d="M325.318.012c-21.828-.283-42.138 15.34-48.74 35.798-15.498 31.788-36.62 62.656-67.738 80.85-46.02 26.248-99.73 35.59-152.188 35.65-11.824-.026-24.336 1.564-34 9.065-18.02 12.488-27.52 36.96-20.133 58.034 4.15 13.85 14.838 24.125 25.197 33.582l86.5 86.5C84.488 381.137 30.993 456.543 1.26 498.187c41.623-29.727 117.01-83.215 158.632-112.942 37.542 36.62 68.373 70.025 106.956 105.56 23.195 15.987 59.377 8.335 73.035-16.723 7.6-12.733 8.225-27.804 7.5-42.23 1.366-50.12 11.25-101.404 37.675-144.67 20.658-31.714 53.59-52.978 87.635-68.023 20.972-10.427 31.76-36.413 25.57-58.77-2.664-11.118-9.64-20.31-17.897-27.873-37.25-36.595-73.2-74.495-110.7-110.84-7.227-6.593-13.718-14.767-23.268-17.904-6.682-2.644-13.898-3.908-21.08-3.758zm-.26 36.837c45.35 46.82 90.704 93.64 136.056 140.46-40.437 18.15-79.907 42.182-106.303 78.613-30.216 43.44-43.98 96.178-48.765 148.346-1.852 21.542-2.097 43.256-1.726 64.933-91.546-91.52-183.09-183.044-274.634-274.564 60.078 2.462 121.696-4.117 176.98-28.967 38.773-15.762 70.437-46.032 91.712-81.66 9.637-15.522 17.813-31.905 25.36-48.525z" />
|
||||
</svg>
|
||||
|
||||
|
|
Before Width: | Height: | Size: 481 B After Width: | Height: | Size: 1.2 KiB |
|
@ -1450,7 +1450,8 @@
|
|||
|
||||
.input-select {
|
||||
font-size: $fs12;
|
||||
margin: 0 $size-1;
|
||||
margin: 0 $size-1 $size-2 $size-1;
|
||||
padding: 0 $size-1;
|
||||
}
|
||||
|
||||
svg {
|
||||
|
@ -1471,6 +1472,7 @@
|
|||
.fix-when {
|
||||
font-size: $fs12;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
|
||||
span {
|
||||
margin-left: $size-2;
|
||||
|
|
|
@ -21,3 +21,4 @@
|
|||
(def current-project-id (mf/create-context nil))
|
||||
(def current-page-id (mf/create-context nil))
|
||||
(def current-file-id (mf/create-context nil))
|
||||
(def scroll-ctx (mf/create-context nil))
|
|
@ -20,6 +20,7 @@
|
|||
[props]
|
||||
(let [shape (unchecked-get props "shape")
|
||||
childs (unchecked-get props "childs")
|
||||
objects (unchecked-get props "objects")
|
||||
render-id (mf/use-ctx muc/render-ctx)
|
||||
masked-group? (:masked-group? shape)
|
||||
|
||||
|
@ -41,12 +42,21 @@
|
|||
(if masked-group?
|
||||
["g" (-> (obj/new)
|
||||
(obj/set! "mask" (mask-url render-id mask)))]
|
||||
[mf/Fragment nil])]
|
||||
[mf/Fragment nil])
|
||||
|
||||
;; This factory is generic, it's used for viewer, workspace and handoff.
|
||||
;; These props are generated in viewer wrappers only, in the rest of the
|
||||
;; cases these props will be nil, not affecting the code.
|
||||
delta (unchecked-get props "delta")
|
||||
fixed? (unchecked-get props "fixed?")]
|
||||
|
||||
[:> clip-wrapper clip-props
|
||||
[:> mask-wrapper mask-props
|
||||
(when masked-group?
|
||||
[:> render-mask #js {:mask mask}])
|
||||
[:> render-mask #js {:mask mask
|
||||
:objects objects
|
||||
:delta delta
|
||||
:fixed? fixed?}])
|
||||
|
||||
(for [item childs]
|
||||
[:& shape-wrapper {:shape item
|
||||
|
|
|
@ -47,20 +47,23 @@
|
|||
(mf/fnc mask-shape
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [mask (unchecked-get props "mask")
|
||||
render-id (mf/use-ctx muc/render-ctx)
|
||||
svg-text? (and (= :text (:type mask)) (some? (:position-data mask)))
|
||||
(let [mask (unchecked-get props "mask")
|
||||
render-id (mf/use-ctx muc/render-ctx)
|
||||
svg-text? (and (= :text (:type mask)) (some? (:position-data mask)))
|
||||
mask (cond-> mask svg-text? set-white-fill)
|
||||
|
||||
mask (cond-> mask svg-text? set-white-fill)
|
||||
;; This factory is generic, it's used for viewer, workspace and handoff.
|
||||
;; These props are generated in viewer wrappers only, in the rest of the
|
||||
;; cases these props will be nil, not affecting the code.
|
||||
fixed? (unchecked-get props "fixed?")
|
||||
delta (unchecked-get props "delta")
|
||||
|
||||
mask-bb
|
||||
(cond
|
||||
svg-text?
|
||||
(gst/position-data-points mask)
|
||||
|
||||
:else
|
||||
(-> (gsh/transform-shape mask)
|
||||
(:points)))]
|
||||
mask-for-bb (-> (gsh/transform-shape mask)
|
||||
(cond-> fixed? (gsh/move delta)))
|
||||
|
||||
mask-bb (cond
|
||||
svg-text? (gst/position-data-points mask-for-bb)
|
||||
:else (:points mask-for-bb))]
|
||||
[:*
|
||||
[:g {:opacity 0}
|
||||
[:g {:id (str "shape-" (mask-id render-id mask))}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
[app.main.fonts :as fonts]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.context :as ctx]
|
||||
[app.main.ui.hooks :as hooks]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.main.ui.shapes.filters :as filters]
|
||||
|
@ -69,6 +70,7 @@
|
|||
nav-scroll (:nav-scroll local)
|
||||
orig-viewport-ref (mf/use-ref nil)
|
||||
current-viewport-ref (mf/use-ref nil)
|
||||
viewer-section-ref (mf/use-ref nil)
|
||||
current-animation (:current-animation local)
|
||||
|
||||
page-id (or page-id (-> file :data :pages first))
|
||||
|
@ -89,6 +91,7 @@
|
|||
frame (get frames index)
|
||||
fullscreen? (mf/deref refs/viewer-fullscreen?)
|
||||
overlays (:overlays local)
|
||||
scroll (mf/use-state nil)
|
||||
|
||||
orig-frame
|
||||
(when (:orig-frame-id current-animation)
|
||||
|
@ -126,7 +129,11 @@
|
|||
(fn [_]
|
||||
(let [viewer-section (dom/get-element "viewer-section")
|
||||
size (dom/get-client-size viewer-section)]
|
||||
(st/emit! (dv/set-viewport-size {:size size})))))]
|
||||
(st/emit! (dv/set-viewport-size {:size size})))))
|
||||
|
||||
on-scroll
|
||||
(fn [event]
|
||||
(reset! scroll (dom/get-target-scroll event)))]
|
||||
|
||||
(hooks/use-shortcuts ::viewer sc/shortcuts)
|
||||
|
||||
|
@ -142,9 +149,11 @@
|
|||
|
||||
(mf/use-effect
|
||||
(fn []
|
||||
(let [key1 (events/listen js/window "click" on-click)]
|
||||
(let [key1 (events/listen js/window "click" on-click)
|
||||
key2 (events/listen (mf/ref-val viewer-section-ref) "scroll" on-scroll)]
|
||||
(fn []
|
||||
(events/unlistenByKey key1)))))
|
||||
(events/unlistenByKey key1)
|
||||
(events/unlistenByKey key2)))))
|
||||
|
||||
(mf/use-layout-effect
|
||||
(fn []
|
||||
|
@ -258,6 +267,7 @@
|
|||
:page page
|
||||
:index index}]
|
||||
[:section.viewer-section {:id "viewer-section"
|
||||
:ref viewer-section-ref
|
||||
:class (if fullscreen? "fullscreen" "")}
|
||||
(cond
|
||||
(empty? frames)
|
||||
|
@ -282,79 +292,79 @@
|
|||
[:div.viewer-wrapper
|
||||
{:style {:width (:width wrapper-size)
|
||||
:height (:height wrapper-size)}}
|
||||
[:& (mf/provider ctx/scroll-ctx) {:value @scroll}
|
||||
[:div.viewer-clipper
|
||||
[:*
|
||||
(when orig-frame
|
||||
[:div.viewport-container
|
||||
{:ref orig-viewport-ref
|
||||
:style {:width (:width orig-size)
|
||||
:height (:height orig-size)
|
||||
:position "relative"}}
|
||||
|
||||
[:div.viewer-clipper
|
||||
[:*
|
||||
(when orig-frame
|
||||
[:div.viewport-container
|
||||
{:ref orig-viewport-ref
|
||||
:style {:width (:width orig-size)
|
||||
:height (:height orig-size)
|
||||
:position "relative"}}
|
||||
[:& interactions/viewport
|
||||
{:frame orig-frame
|
||||
:base-frame orig-frame
|
||||
:frame-offset (gpt/point 0 0)
|
||||
:size orig-size
|
||||
:page page
|
||||
:file file
|
||||
:users users
|
||||
:interactions-mode :hide}]])
|
||||
|
||||
[:& interactions/viewport
|
||||
{:frame orig-frame
|
||||
:base-frame orig-frame
|
||||
:frame-offset (gpt/point 0 0)
|
||||
:size orig-size
|
||||
:page page
|
||||
:file file
|
||||
:users users
|
||||
:interactions-mode :hide}]])
|
||||
[:div.viewport-container
|
||||
{:ref current-viewport-ref
|
||||
:style {:width (:width size)
|
||||
:height (:height size)
|
||||
:position "relative"}}
|
||||
|
||||
[:div.viewport-container
|
||||
{:ref current-viewport-ref
|
||||
:style {:width (:width size)
|
||||
:height (:height size)
|
||||
:position "relative"}}
|
||||
[:& interactions/viewport
|
||||
{:frame frame
|
||||
:base-frame frame
|
||||
:frame-offset (gpt/point 0 0)
|
||||
:size size
|
||||
:page page
|
||||
:file file
|
||||
:users users
|
||||
:interactions-mode interactions-mode}]
|
||||
|
||||
[:& interactions/viewport
|
||||
{:frame frame
|
||||
:base-frame frame
|
||||
:frame-offset (gpt/point 0 0)
|
||||
:size size
|
||||
:page page
|
||||
:file file
|
||||
:users users
|
||||
:interactions-mode interactions-mode}]
|
||||
(for [overlay overlays]
|
||||
(let [size-over (calculate-size (:frame overlay) zoom)]
|
||||
[:*
|
||||
(when (or (:close-click-outside overlay)
|
||||
(:background-overlay overlay))
|
||||
[:div.viewer-overlay-background
|
||||
{:class (dom/classnames
|
||||
:visible (:background-overlay overlay))
|
||||
:style {:width (:width wrapper-size)
|
||||
:height (:height wrapper-size)
|
||||
:position "absolute"
|
||||
:left 0
|
||||
:top 0}
|
||||
:on-click #(when (:close-click-outside overlay)
|
||||
(close-overlay (:frame overlay)))}])
|
||||
[:div.viewport-container.viewer-overlay
|
||||
{:id (str "overlay-" (str (:id (:frame overlay))))
|
||||
:style {:width (:width size-over)
|
||||
:height (:height size-over)
|
||||
:left (* (:x (:position overlay)) zoom)
|
||||
:top (* (:y (:position overlay)) zoom)}}
|
||||
[:& interactions/viewport
|
||||
{:frame (:frame overlay)
|
||||
:base-frame frame
|
||||
:frame-offset (:position overlay)
|
||||
:size size-over
|
||||
:page page
|
||||
:file file
|
||||
:users users
|
||||
:interactions-mode interactions-mode}]]]))]]
|
||||
|
||||
(for [overlay overlays]
|
||||
(let [size-over (calculate-size (:frame overlay) zoom)]
|
||||
[:*
|
||||
(when (or (:close-click-outside overlay)
|
||||
(:background-overlay overlay))
|
||||
[:div.viewer-overlay-background
|
||||
{:class (dom/classnames
|
||||
:visible (:background-overlay overlay))
|
||||
:style {:width (:width wrapper-size)
|
||||
:height (:height wrapper-size)
|
||||
:position "absolute"
|
||||
:left 0
|
||||
:top 0}
|
||||
:on-click #(when (:close-click-outside overlay)
|
||||
(close-overlay (:frame overlay)))}])
|
||||
[:div.viewport-container.viewer-overlay
|
||||
{:id (str "overlay-" (str (:id (:frame overlay))))
|
||||
:style {:width (:width size-over)
|
||||
:height (:height size-over)
|
||||
:left (* (:x (:position overlay)) zoom)
|
||||
:top (* (:y (:position overlay)) zoom)}}
|
||||
[:& interactions/viewport
|
||||
{:frame (:frame overlay)
|
||||
:base-frame frame
|
||||
:frame-offset (:position overlay)
|
||||
:size size-over
|
||||
:page page
|
||||
:file file
|
||||
:users users
|
||||
:interactions-mode interactions-mode}]]]))]]
|
||||
|
||||
(when (= section :comments)
|
||||
[:& comments-layer {:file file
|
||||
:users users
|
||||
:frame frame
|
||||
:page page
|
||||
:zoom zoom}])]]]))]]]))
|
||||
(when (= section :comments)
|
||||
[:& comments-layer {:file file
|
||||
:users users
|
||||
:frame frame
|
||||
:page page
|
||||
:zoom zoom}])]]]]))]]]))
|
||||
|
||||
;; --- Component: Viewer Page
|
||||
|
||||
|
|
|
@ -7,12 +7,14 @@
|
|||
(ns app.main.ui.viewer.shapes
|
||||
"The main container for a frame in viewer mode"
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.shapes :as geom]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.spec.interactions :as cti]
|
||||
[app.main.data.viewer :as dv]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.context :as ctx]
|
||||
[app.main.ui.shapes.bool :as bool]
|
||||
[app.main.ui.shapes.circle :as circle]
|
||||
[app.main.ui.shapes.frame :as frame]
|
||||
|
@ -214,7 +216,8 @@
|
|||
childs (unchecked-get props "childs")
|
||||
frame (unchecked-get props "frame")
|
||||
objects (unchecked-get props "objects")
|
||||
|
||||
fixed? (unchecked-get props "fixed?")
|
||||
delta (unchecked-get props "delta")
|
||||
base-frame (mf/use-ctx base-frame-ctx)
|
||||
frame-offset (mf/use-ctx frame-offset-ctx)
|
||||
|
||||
|
@ -241,7 +244,10 @@
|
|||
[:& component {:shape shape
|
||||
:frame frame
|
||||
:childs childs
|
||||
:is-child-selected? true}]
|
||||
:is-child-selected? true
|
||||
:objects objects
|
||||
:fixed? fixed?
|
||||
:delta delta}]
|
||||
|
||||
[:& interaction {:shape shape
|
||||
:interactions interactions
|
||||
|
@ -250,7 +256,8 @@
|
|||
;; Don't wrap svg elements inside a <g> otherwise some can break
|
||||
[:& component {:shape shape
|
||||
:frame frame
|
||||
:childs childs}]))))
|
||||
:childs childs
|
||||
:objects objects}]))))
|
||||
|
||||
(defn frame-wrapper
|
||||
[shape-container]
|
||||
|
@ -313,11 +320,10 @@
|
|||
(mf/fnc group-container
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [shape (unchecked-get props "shape")
|
||||
childs (mapv #(get objects %) (:shapes shape))
|
||||
props (obj/merge! #js {} props
|
||||
#js {:childs childs
|
||||
:objects objects})]
|
||||
(let [childs (mapv #(get objects %) (:shapes (unchecked-get props "shape")))
|
||||
props (obj/merge! #js {} props
|
||||
#js {:childs childs
|
||||
:objects objects})]
|
||||
[:> group-wrapper props]))))
|
||||
|
||||
(defn bool-container-factory
|
||||
|
@ -327,8 +333,7 @@
|
|||
(mf/fnc bool-container
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [shape (unchecked-get props "shape")
|
||||
childs (->> (cph/get-children-ids objects (:id shape))
|
||||
(let [childs (->> (cph/get-children-ids objects (:id (unchecked-get props "shape")))
|
||||
(select-keys objects))
|
||||
props (obj/merge! #js {} props
|
||||
#js {:childs childs
|
||||
|
@ -342,8 +347,7 @@
|
|||
(mf/fnc svg-raw-container
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [shape (unchecked-get props "shape")
|
||||
childs (mapv #(get objects %) (:shapes shape))
|
||||
(let [childs (mapv #(get objects %) (:shapes (unchecked-get props "shape")))
|
||||
props (obj/merge! #js {} props
|
||||
#js {:childs childs
|
||||
:objects objects})]
|
||||
|
@ -359,7 +363,15 @@
|
|||
(mf/fnc shape-container
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [group-container
|
||||
(let [scroll (mf/use-ctx ctx/scroll-ctx)
|
||||
local (mf/deref refs/viewer-local)
|
||||
zoom (:zoom local)
|
||||
shape (unchecked-get props "shape")
|
||||
parents (map (d/getf objects) (cph/get-parent-ids objects (:id shape)))
|
||||
fixed? (or (:fixed-scroll shape) (some :fixed-scroll parents))
|
||||
frame (unchecked-get props "frame")
|
||||
delta {:x (/ (:scroll-left scroll) zoom) :y (/ (:scroll-top scroll) zoom)}
|
||||
group-container
|
||||
(mf/use-memo (mf/deps objects)
|
||||
#(group-container-factory objects))
|
||||
|
||||
|
@ -369,12 +381,12 @@
|
|||
|
||||
svg-raw-container
|
||||
(mf/use-memo (mf/deps objects)
|
||||
#(svg-raw-container-factory objects))
|
||||
shape (unchecked-get props "shape")
|
||||
frame (unchecked-get props "frame")]
|
||||
#(svg-raw-container-factory objects))]
|
||||
(when (and shape (not (:hidden shape)))
|
||||
(let [shape (-> (geom/transform-shape shape)
|
||||
(geom/translate-to-frame frame))
|
||||
(geom/translate-to-frame frame)
|
||||
(cond-> fixed? (geom/move delta)))
|
||||
|
||||
opts #js {:shape shape
|
||||
:objects objects}]
|
||||
(case (:type shape)
|
||||
|
@ -384,7 +396,7 @@
|
|||
:path [:> path-wrapper opts]
|
||||
:image [:> image-wrapper opts]
|
||||
:circle [:> circle-wrapper opts]
|
||||
:group [:> group-container {:shape shape :frame frame :objects objects}]
|
||||
:group [:> group-container {:shape shape :frame frame :objects objects :fixed? fixed? :delta delta}]
|
||||
:bool [:> bool-container {:shape shape :frame frame :objects objects}]
|
||||
:svg-raw [:> svg-raw-container {:shape shape :frame frame :objects objects}])))))))
|
||||
|
||||
|
|
|
@ -46,9 +46,8 @@
|
|||
in-frame? (and (some? ids)
|
||||
(not= (:parent-id values) uuid/zero))
|
||||
|
||||
;; TODO: uncomment when fixed-scroll is fully implemented
|
||||
;; first-level? (and in-frame?
|
||||
;; (= (:parent-id values) (:frame-id values)))
|
||||
first-level? (and in-frame?
|
||||
(= (:parent-id values) (:frame-id values)))
|
||||
|
||||
constraints-h (or (get values :constraints-h) (gsh/default-constraints-h values))
|
||||
constraints-v (or (get values :constraints-v) (gsh/default-constraints-v values))
|
||||
|
@ -104,13 +103,11 @@
|
|||
ids
|
||||
#(assoc % constraint value))))))))
|
||||
|
||||
;; TODO: uncomment when fixed-scroll is fully implemented
|
||||
;; on-fixed-scroll-clicked
|
||||
;; (mf/use-callback
|
||||
;; (mf/deps [ids values])
|
||||
;; (fn [_]
|
||||
;; (st/emit! (dch/update-shapes ids #(update % :fixed-scroll not)))))
|
||||
]
|
||||
on-fixed-scroll-clicked
|
||||
(mf/use-callback
|
||||
(mf/deps [ids values])
|
||||
(fn [_]
|
||||
(st/emit! (dch/update-shapes ids #(update % :fixed-scroll not)))))]
|
||||
|
||||
;; CONSTRAINTS
|
||||
(when in-frame?
|
||||
|
@ -168,12 +165,12 @@
|
|||
[:option {:value "bottom"} (tr "workspace.options.constraints.bottom")]
|
||||
[:option {:value "topbottom"} (tr "workspace.options.constraints.topbottom")]
|
||||
[:option {:value "center"} (tr "workspace.options.constraints.center")]
|
||||
[:option {:value "scale"} (tr "workspace.options.constraints.scale")]
|
||||
;; TODO: uncomment when fixed-scroll is fully implemented
|
||||
;; (when first-level?
|
||||
;; [:div.row-flex
|
||||
;; [:div.fix-when {:class (dom/classnames :active (:fixed-scroll values))
|
||||
;; :on-click on-fixed-scroll-clicked}
|
||||
;; i/pin
|
||||
;; [:span (tr "workspace.options.constraints.fix-when-scrolling")]]])
|
||||
]]]]]])))
|
||||
[:option {:value "scale"} (tr "workspace.options.constraints.scale")]]]
|
||||
(when first-level?
|
||||
[:div.row-flex
|
||||
[:div.fix-when {:class (dom/classnames :active (:fixed-scroll values))
|
||||
:on-click on-fixed-scroll-clicked}
|
||||
(if (:fixed-scroll values)
|
||||
i/pin-fill
|
||||
i/pin)
|
||||
[:span (tr "workspace.options.constraints.fix-when-scrolling")]]])]]]])))
|
||||
|
|
|
@ -127,8 +127,18 @@
|
|||
(when (some? node)
|
||||
(.getAttribute ^js node attr-name)))
|
||||
|
||||
(defn get-scroll-position
|
||||
[^js event]
|
||||
(when (some? event)
|
||||
{:scroll-height (.-scrollHeight event)
|
||||
:scroll-left (.-scrollLeft event)
|
||||
:scroll-top (.-scrollTop event)
|
||||
:scroll-width (.-scrollWidth event)}))
|
||||
|
||||
(def get-target-val (comp get-value get-target))
|
||||
|
||||
(def get-target-scroll (comp get-scroll-position get-target))
|
||||
|
||||
(defn click
|
||||
"Click a node"
|
||||
[^js node]
|
||||
|
|
Loading…
Add table
Reference in a new issue