Optimize layer-item component

This commit is contained in:
Andrey Antukh 2023-05-31 18:02:33 +02:00
parent f3b856b2af
commit 0bc468f434
5 changed files with 184 additions and 156 deletions

View file

@ -190,10 +190,10 @@
(shift-select-shapes id nil)) (shift-select-shapes id nil))
([id objects] ([id objects]
(ptk/reify ::shift-select-shapes-2 (ptk/reify ::shift-select-shapes
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(let [objects (or objects (wsh/lookup-page-objects state)) (let [objects (or objects (wsh/lookup-page-objects state))
selection (-> state selection (-> state
wsh/lookup-selected wsh/lookup-selected
(conj id))] (conj id))]

View file

@ -13,7 +13,8 @@
(mf/defc element-icon-refactor (mf/defc element-icon-refactor
[{:keys [shape main-instance?] :as props}] {::mf/wrap-props false}
[{:keys [shape main-instance?]}]
(if (ctk/instance-head? shape) (if (ctk/instance-head? shape)
(if main-instance? (if main-instance?
i/component-refactor i/component-refactor

View file

@ -248,16 +248,15 @@
(mf/set-ref-val! ref val)) (mf/set-ref-val! ref val))
(mf/ref-val ref))) (mf/ref-val ref)))
;; FIXME: rename to use-focus-objects
(defn with-focus-objects (defn with-focus-objects
([objects] ([objects]
(let [focus (mf/deref refs/workspace-focus-selected)] (let [focus (mf/deref refs/workspace-focus-selected)]
(with-focus-objects objects focus))) (with-focus-objects objects focus)))
([objects focus] ([objects focus]
(let [objects (mf/use-memo (mf/with-memo [focus objects]
(mf/deps focus objects) (cpf/focus-objects objects focus))))
#(cpf/focus-objects objects focus))]
objects)))
(defn use-debounce (defn use-debounce
[ms value] [ms value]

View file

@ -5,9 +5,10 @@
;; Copyright (c) KALEIDOS INC ;; Copyright (c) KALEIDOS INC
(ns app.main.ui.workspace.sidebar.layer-item (ns app.main.ui.workspace.sidebar.layer-item
(:require-macros [app.main.style :refer [css]]) (:require-macros [app.main.style :as stl])
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.pages.helpers :as cph] [app.common.pages.helpers :as cph]
[app.common.types.shape.layout :as ctl] [app.common.types.shape.layout :as ctl]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
@ -28,34 +29,38 @@
[okulary.core :as l] [okulary.core :as l]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(mf/defc layer-item (mf/defc layer-item
{::mf/wrap-props false} {::mf/wrap-props false}
[{:keys [index item selected objects sortable? filtered? recieved-depth parent-size component-child?]}] [{:keys [index item selected objects sortable? filtered? depth parent-size component-child?]}]
(let [id (:id item) (let [id (:id item)
blocked? (:blocked item) name (:name item)
hidden? (:hidden item) blocked? (:blocked item)
hidden? (:hidden item)
touched? (-> item :touched seq boolean)
has-shapes? (-> item :shapes seq boolean)
disable-drag (mf/use-state false) drag-disabled* (mf/use-state false)
scroll-to-middle? (mf/use-var true) drag-disabled? (deref drag-disabled*)
expanded-iref (mf/with-memo [id]
(-> (l/in [:expanded id])
(l/derived refs/workspace-local)))
expanded? (mf/deref expanded-iref) scroll-to-middle? (mf/use-var true)
selected? (contains? selected id) expanded-iref (mf/with-memo [id]
container? (or (cph/frame-shape? item) (-> (l/in [:expanded id])
(cph/group-shape? item)) (l/derived refs/workspace-local)))
absolute? (ctl/layout-absolute? item) expanded? (mf/deref expanded-iref)
components-v2 (mf/use-ctx ctx/components-v2) selected? (contains? selected id)
workspace-read-only? (mf/use-ctx ctx/workspace-read-only?) container? (or (cph/frame-shape? item)
new-css-system (mf/use-ctx ctx/new-css-system) (cph/group-shape? item))
main-instance? (if components-v2 absolute? (ctl/layout-absolute? item)
(:main-instance item)
true) components-v2 (mf/use-ctx ctx/components-v2)
parent-board? (and (= :frame (:type item)) read-only? (mf/use-ctx ctx/workspace-read-only?)
(= uuid/zero (:parent-id item))) new-css-system (mf/use-ctx ctx/new-css-system)
main-instance? (if components-v2
(:main-instance item)
true)
parent-board? (and (cph/frame-shape? item)
(= uuid/zero (:parent-id item)))
toggle-collapse toggle-collapse
(mf/use-fn (mf/use-fn
(mf/deps expanded?) (mf/deps expanded?)
@ -108,22 +113,22 @@
on-pointer-enter on-pointer-enter
(mf/use-fn (mf/use-fn
(mf/deps id) (mf/deps id)
(fn [_event] (fn [_]
(st/emit! (dw/highlight-shape id)))) (st/emit! (dw/highlight-shape id))))
on-pointer-leave on-pointer-leave
(mf/use-fn (mf/use-fn
(mf/deps id) (mf/deps id)
(fn [_event] (fn [_]
(st/emit! (dw/dehighlight-shape id)))) (st/emit! (dw/dehighlight-shape id))))
on-context-menu on-context-menu
(mf/use-fn (mf/use-fn
(mf/deps item workspace-read-only?) (mf/deps item read-only?)
(fn [event] (fn [event]
(dom/prevent-default event) (dom/prevent-default event)
(dom/stop-propagation event) (dom/stop-propagation event)
(when-not workspace-read-only? (when-not read-only?
(let [pos (dom/get-client-position event)] (let [pos (dom/get-client-position event)]
(st/emit! (dw/show-shape-context-menu {:position pos :shape item})))))) (st/emit! (dw/show-shape-context-menu {:position pos :shape item}))))))
@ -151,27 +156,37 @@
(when-not expanded? (when-not expanded?
(st/emit! (dwc/toggle-collapse id))))) (st/emit! (dwc/toggle-collapse id)))))
zoom-to-selected
(mf/use-fn
(fn [event]
(dom/stop-propagation event)
(dom/prevent-default event)
(st/emit! dw/zoom-to-selected-shape)))
[dprops dref] [dprops dref]
(hooks/use-sortable (hooks/use-sortable
:data-type "penpot/layer" :data-type "penpot/layer"
:on-drop on-drop :on-drop on-drop
:on-drag on-drag :on-drag on-drag
:on-hold on-hold :on-hold on-hold
:disabled @disable-drag :disabled drag-disabled?
:detect-center? container? :detect-center? container?
:data {:id (:id item) :data {:id (:id item)
:index index :index index
:name (:name item)} :name (:name item)}
:draggable? (and sortable? (not workspace-read-only?))) :draggable? (and sortable? (not read-only?)))
ref (mf/use-ref) ref (mf/use-ref)
depth (+ recieved-depth 1) depth (+ depth 1)
component-tree? (or component-child? (:component-root item))] component-tree? (or component-child? (:component-root item))
enable-drag (mf/use-fn #(reset! drag-disabled* true))
disable-drag (mf/use-fn #(reset! drag-disabled* false))]
(mf/with-effect [selected? selected] (mf/with-effect [selected? selected]
(let [single? (= (count selected) 1) (let [single? (= (count selected) 1)
node (mf/ref-val ref) node (mf/ref-val ref)
parent-node (dom/get-parent (dom/get-parent node)) parent (dom/get-parent (dom/get-parent node))
subid subid
(when (and single? selected?) (when (and single? selected?)
@ -179,9 +194,9 @@
(ts/schedule (ts/schedule
100 100
#(if scroll-to #(if scroll-to
(dom/scroll-into-view! parent-node #js {:block "center" :behavior "smooth" :inline "start"}) (dom/scroll-into-view! parent {:block "center" :behavior "smooth" :inline "start"})
(do (do
(dom/scroll-into-view-if-needed! parent-node #js {:block "center" :behavior "smooth" :inline "start"}) (dom/scroll-into-view-if-needed! parent {:block "center" :behavior "smooth" :inline "start"})
(reset! scroll-to-middle? true))))))] (reset! scroll-to-middle? true))))))]
#(when (some? subid) #(when (some? subid)
@ -193,89 +208,98 @@
:ref dref :ref dref
:on-click select-shape :on-click select-shape
:id id :id id
:class (dom/classnames :class (stl/css-case
(css :layer-row) true :layer-row true
(css :component) (not (nil? (:component-id item))) :component (some? (:component-id item))
(css :masked) (:masked-group item) :masked (:masked-group item)
(css :selected) selected? :selected selected?
(css :type-frame) (= :frame (:type item)) :type-frame (cph/frame-shape? item)
(css :type-bool) (= :bool (:type item)) :type-bool (cph/bool-shape? item)
(css :type-comp) component-tree? :type-comp component-tree?
(css :hidden) (:hidden item) :hidden hidden?
:dnd-over (= (:over dprops) :center) :dnd-over (= (:over dprops) :center)
:dnd-over-top (= (:over dprops) :top) :dnd-over-top (= (:over dprops) :top)
:dnd-over-bot (= (:over dprops) :bot) :dnd-over-bot (= (:over dprops) :bot)
:root-board parent-board?)} :root-board parent-board?)}
[:span {:class (dom/classnames (css :tab-indentation) true [:span {:class (stl/css-case
(css :filtered) filtered?) :tab-indentation true
:style #js {"--depth" depth}}] :filtered filtered?)
[:div {:class (dom/classnames (css :element-list-body) true :style {"--depth" depth}}]
(css :filtered) filtered? [:div {:class (stl/css-case
(css :selected) selected? :element-list-body true
(css :icon-layer) (= (:type item) :icon)) :filtered filtered?
:style #js {"--depth" depth} :selected selected?
:icon-layer (= (:type item) :icon))
:style {"--depth" depth}
:on-pointer-enter on-pointer-enter :on-pointer-enter on-pointer-enter
:on-pointer-leave on-pointer-leave :on-pointer-leave on-pointer-leave
:on-double-click #(dom/stop-propagation %)} :on-double-click dom/stop-propagation}
(if (< 0 (count (:shapes item))) (if (< 0 (count (:shapes item)))
[:div {:class (dom/classnames (css :button-content) true)} [:div {:class (stl/css :button-content)}
(when (not filtered?) (when (not filtered?)
[:button {:class (dom/classnames (css :toggle-content) true [:button {:class (stl/css-case
(css :inverse) expanded?) :toggle-content true
:inverse expanded?)
:on-click toggle-collapse} :on-click toggle-collapse}
i/arrow-refactor]) i/arrow-refactor])
[:div {:class (dom/classnames (css :icon-shape) true) [:div {:class (stl/css :icon-shape)
:on-double-click #(do (dom/stop-propagation %) :on-double-click zoom-to-selected}
(dom/prevent-default %)
(st/emit! dw/zoom-to-selected-shape))}
(when absolute? (when absolute?
[:div {:class (dom/classnames (css :absolute) true)} ]) [:div {:class (stl/css :absolute)}])
[:& sic/element-icon-refactor {:shape item
:main-instance? main-instance?}]]] [:& sic/element-icon-refactor
[:div {:class (dom/classnames (css :button-content) true)} {:shape item
(when (not filtered?) :main-instance? main-instance?}]]]
[:span {:class (dom/classnames (css :toggle-content) true)}])
[:div {:class (dom/classnames (css :icon-shape) true) [:div {:class (stl/css :button-content)}
:on-double-click #(do (dom/stop-propagation %) (when (not ^boolean filtered?)
(dom/prevent-default %) [:span {:class (stl/css :toggle-content)}])
(st/emit! dw/zoom-to-selected-shape))} [:div {:class (stl/css :icon-shape)
(when absolute? :on-double-click zoom-to-selected}
[:div {:class (dom/classnames (css :absolute) true)} ]) (when ^boolean absolute?
[:& sic/element-icon-refactor {:shape item [:div {:class (stl/css :absolute)}])
:main-instance? main-instance?}]]]) [:& sic/element-icon-refactor
{:shape item
:main-instance? main-instance?}]]])
[:& layer-name {:ref ref [:& layer-name {:ref ref
:shape-id (:id item) :shape-id id
:shape-name (:name item) :shape-name name
:shape-touched? (boolean (seq (:touched item))) :shape-touched? touched?
:disabled-double-click workspace-read-only? :disabled-double-click read-only?
:on-start-edit #(reset! disable-drag true) :on-start-edit disable-drag
:on-stop-edit #(reset! disable-drag false) :on-stop-edit enable-drag
:depth depth :depth depth
:parent-size parent-size :parent-size parent-size
:selected? selected? :selected? selected?
:type-comp component-tree? :type-comp component-tree?
:type-frame (= :frame (:type item)) :type-frame (cph/frame-shape? item)
:hidden? (:hidden item)}] :hidden? hidden?}]
[:div {:class (dom/classnames (css :element-actions) true [:div {:class (stl/css-case
(css :is-parent) (:shapes item) :element-actions true
(css :selected) (:hidden item) :is-parent has-shapes?
(css :selected) (:blocked item))} :selected hidden?
[:button {:class (dom/classnames (css :toggle-element) true :selected blocked?)}
(css :selected) (:hidden item)) [:button {:class (stl/css-case
:toggle-element true
:selected hidden?)
:on-click toggle-visibility} :on-click toggle-visibility}
(if (:hidden item) i/hide-refactor i/shown-refactor)] (if ^boolean hidden? i/hide-refactor i/shown-refactor)]
[:button {:class (dom/classnames (css :block-element) true [:button {:class (stl/css-case
(css :selected) (:blocked item)) :block-element true
:selected blocked?)
:on-click toggle-blocking} :on-click toggle-blocking}
(if (:blocked item) i/lock-refactor i/unlock-refactor)]]]] (if ^boolean blocked? i/lock-refactor i/unlock-refactor)]]]]
(when (and (:shapes item) expanded?) (when (and (:shapes item) expanded?)
[:div {:class (dom/classnames (css :element-children) true [:div {:class (stl/css-case
(css :parent-selected) selected? :element-children true
:sticky-children parent-board?) :parent-selected selected?
:data-id (when parent-board? (:id item))} :sticky-children parent-board?)
:data-id (when ^boolean parent-board? id)}
(for [[index id] (reverse (d/enumerate (:shapes item)))] (for [[index id] (reverse (d/enumerate (:shapes item)))]
(when-let [item (get objects id)] (when-let [item (get objects id)]
[:& layer-item [:& layer-item
@ -283,63 +307,68 @@
:selected selected :selected selected
:index index :index index
:objects objects :objects objects
:key (:id item) :key (dm/str id)
:sortable? sortable? :sortable? sortable?
:recieved-depth depth :depth depth
:parent-size parent-size :parent-size parent-size
:component-child? component-tree?}]))])] :component-child? component-tree?}]))])]
;; ---- OLD CSS
[:li {:on-context-menu on-context-menu [:li {:on-context-menu on-context-menu
:ref dref :ref dref
:class (dom/classnames :class (stl/css-case*
:component (not (nil? (:component-id item))) :component (some? (:component-id item))
:masked (:masked-group item) :masked (:masked-group item)
:dnd-over (= (:over dprops) :center) :dnd-over (= (:over dprops) :center)
:dnd-over-top (= (:over dprops) :top) :dnd-over-top (= (:over dprops) :top)
:dnd-over-bot (= (:over dprops) :bot) :dnd-over-bot (= (:over dprops) :bot)
:selected selected? :selected selected?
:type-frame (= :frame (:type item)))} :type-frame (cph/frame-shape? item))}
[:div.element-list-body {:class (dom/classnames :selected selected? [:div.element-list-body {:class (stl/css-case*
:icon-layer (= (:type item) :icon)) :selected selected?
:icon-layer (= (:type item) :icon))
:on-click select-shape :on-click select-shape
:on-pointer-enter on-pointer-enter :on-pointer-enter on-pointer-enter
:on-pointer-leave on-pointer-leave :on-pointer-leave on-pointer-leave
:on-double-click #(dom/stop-propagation %)} :on-double-click dom/stop-propagation}
[:div.icon {:on-double-click #(do (dom/stop-propagation %) [:div.icon {:on-double-click zoom-to-selected}
(dom/prevent-default %) (when ^boolean absolute?
(st/emit! dw/zoom-to-selected-shape))}
(when absolute?
[:div.absolute i/position-absolute]) [:div.absolute i/position-absolute])
[:& si/element-icon {:shape item [:& si/element-icon
:main-instance? main-instance?}]] {:shape item
:main-instance? main-instance?}]]
[:& layer-name {:ref ref [:& layer-name {:ref ref
:parent-size parent-size :parent-size parent-size
:shape-id (:id item) :shape-id id
:shape-name (:name item) :shape-name name
:shape-touched? (boolean (seq (:touched item))) :shape-touched? touched?
:on-start-edit #(reset! disable-drag true) :on-start-edit disable-drag
:on-stop-edit #(reset! disable-drag false) :on-stop-edit enable-drag
:disabled-double-click workspace-read-only? :disabled-double-click read-only?
:selected? selected? :selected? selected?
:type-comp component-tree? :type-comp component-tree?
:type-frame (= :frame (:type item)) :type-frame (cph/frame-shape? item)
:hidden? (:hidden item)}] :hidden? hidden?}]
[:div.element-actions {:class (when (:shapes item) "is-parent")} [:div.element-actions {:class (when ^boolean has-shapes? "is-parent")}
[:div.toggle-element {:class (when (:hidden item) "selected") [:div.toggle-element {:class (when ^boolean hidden? "selected")
:on-click toggle-visibility} :on-click toggle-visibility}
(if (:hidden item) i/eye-closed i/eye)] (if ^boolean hidden? i/eye-closed i/eye)]
[:div.block-element {:class (when (:blocked item) "selected") [:div.block-element {:class (when ^boolean blocked? "selected")
:on-click toggle-blocking} :on-click toggle-blocking}
(if (:blocked item) i/lock i/unlock)]] (if ^boolean blocked? i/lock i/unlock)]]
(when (:shapes item) (when ^boolean has-shapes?
(when (not filtered?) [:span.toggle-content (when (not ^boolean filtered?)
{:on-click toggle-collapse [:span.toggle-content
:class (when expanded? "inverse")} {:on-click toggle-collapse
i/arrow-slide]))] :class (when ^boolean expanded? "inverse")}
(when (and (:shapes item) expanded?) i/arrow-slide]))]
(when (and ^boolean has-shapes?
^boolean expanded?)
[:ul.element-children [:ul.element-children
(for [[index id] (reverse (d/enumerate (:shapes item)))] (for [[index id] (reverse (d/enumerate (:shapes item)))]
(when-let [item (get objects id)] (when-let [item (get objects id)]
@ -348,5 +377,5 @@
:selected selected :selected selected
:index index :index index
:objects objects :objects objects
:key (:id item) :key (dm/str id)
:sortable? sortable?}]))])]))) :sortable? sortable?}]))])])))

View file

@ -5,7 +5,7 @@
;; Copyright (c) KALEIDOS INC ;; Copyright (c) KALEIDOS INC
(ns app.main.ui.workspace.sidebar.layers (ns app.main.ui.workspace.sidebar.layers
(:require-macros [app.main.style :as stl :refer [css]]) (:require-macros [app.main.style :as stl])
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
@ -34,8 +34,7 @@
;; affected by the selected set. ;; affected by the selected set.
(mf/defc frame-wrapper (mf/defc frame-wrapper
{::mf/wrap-props false {::mf/wrap-props false
::mf/wrap [mf/memo ::mf/wrap [mf/memo #(mf/deferred % ts/idle-then-raf)]}
#(mf/deferred % ts/idle-then-raf)]}
[props] [props]
[:> layer-item props]) [:> layer-item props])
@ -43,16 +42,16 @@
{::mf/wrap [#(mf/memo % =) {::mf/wrap [#(mf/memo % =)
#(mf/throttle % 200)]} #(mf/throttle % 200)]}
[{:keys [objects filtered? parent-size] :as props}] [{:keys [objects filtered? parent-size] :as props}]
(let [selected (mf/deref refs/selected-shapes) (let [selected (mf/deref refs/selected-shapes)
selected (hooks/use-equal-memo selected) selected (hooks/use-equal-memo selected)
root (get objects uuid/zero) root (get objects uuid/zero)
new-css-system (mf/use-ctx ctx/new-css-system)] new-css-system (mf/use-ctx ctx/new-css-system)]
[:ul [:ul
{:class (stl/css new-css-system ::stl/element-list)} {:class (stl/css new-css-system ::stl/element-list)}
[:& hooks/sortable-container {} [:& hooks/sortable-container {}
(for [[index id] (reverse (d/enumerate (:shapes root)))] (for [[index id] (reverse (d/enumerate (:shapes root)))]
(when-let [obj (get objects id)] (when-let [obj (get objects id)]
(if (= (:type obj) :frame) (if (cph/frame-shape? obj)
[:& frame-wrapper [:& frame-wrapper
{:item obj {:item obj
:selected selected :selected selected
@ -62,7 +61,7 @@
:sortable? true :sortable? true
:filtered? filtered? :filtered? filtered?
:parent-size parent-size :parent-size parent-size
:recieved-depth -1}] :depth -1}]
[:& layer-item [:& layer-item
{:item obj {:item obj
:selected selected :selected selected
@ -71,16 +70,16 @@
:key id :key id
:sortable? true :sortable? true
:filtered? filtered? :filtered? filtered?
:recieved-depth -1 :depth -1
:parent-size parent-size}])))]])) :parent-size parent-size}])))]]))
(mf/defc filters-tree (mf/defc filters-tree
{::mf/wrap [#(mf/memo % =) {::mf/wrap [#(mf/memo % =)
#(mf/throttle % 200)]} #(mf/throttle % 200)]}
[{:keys [objects parent-size] :as props}] [{:keys [objects parent-size]}]
(let [selected (mf/deref refs/selected-shapes) (let [selected (mf/deref refs/selected-shapes)
selected (hooks/use-equal-memo selected) selected (hooks/use-equal-memo selected)
root (get objects uuid/zero) root (get objects uuid/zero)
new-css-system (mf/use-ctx ctx/new-css-system)] new-css-system (mf/use-ctx ctx/new-css-system)]
[:ul {:class (stl/css new-css-system ::stl/element-list)} [:ul {:class (stl/css new-css-system ::stl/element-list)}
(for [[index id] (d/enumerate (:shapes root))] (for [[index id] (d/enumerate (:shapes root))]
@ -93,7 +92,7 @@
:key id :key id
:sortable? false :sortable? false
:filtered? true :filtered? true
:recieved-depth -1 :depth -1
:parent-size parent-size}]))])) :parent-size parent-size}]))]))
(defn calc-reparented-objects (defn calc-reparented-objects