0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-10 09:08:31 -05:00

Add performance improvements to use-search hook on layers

This commit is contained in:
Andrey Antukh 2023-05-30 19:51:59 +02:00
parent 0102ca1bcf
commit b65452cb73
3 changed files with 328 additions and 291 deletions

View file

@ -15,13 +15,13 @@
(mf/defc search-bar
{::mf/wrap-props false}
[props]
(let [children (unchecked-get props "children")
on-change (unchecked-get props "on-change")
value (unchecked-get props "value")
on-clear (unchecked-get props "clear-action")
(let [children (unchecked-get props "children")
on-change (unchecked-get props "on-change")
value (unchecked-get props "value")
on-clear (unchecked-get props "clear-action")
placeholder (unchecked-get props "placeholder")
icon (unchecked-get props "icon")
icon (unchecked-get props "icon")
handle-change
(mf/use-fn
(mf/deps on-change)
@ -57,4 +57,4 @@
(when (not= "" value)
[:button {:class (dom/classnames (css :clear) true)
:on-click handle-clear}
i/delete-text-refactor])]]))
i/delete-text-refactor])]]))

View file

@ -30,7 +30,8 @@
(mf/defc layer-item
[{:keys [index item selected objects sortable? filtered? recieved-depth parent-size component-child?] :as props}]
{::mf/wrap-props false}
[{:keys [index item selected objects sortable? filtered? recieved-depth parent-size component-child?]}]
(let [id (:id item)
blocked? (:blocked item)
hidden? (:hidden item)

View file

@ -9,6 +9,7 @@
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.pages.helpers :as cph]
[app.common.uuid :as uuid]
[app.main.data.workspace :as dw]
[app.main.refs :as refs]
@ -101,7 +102,6 @@
(defn calc-reparented-objects
[objects]
(let [reparented-objects
(d/mapm (fn [_ val]
(assoc val :parent-id uuid/zero :shapes nil))
@ -116,314 +116,351 @@
;; --- Layers Toolbox
;; FIXME: optimize
(defn- match-filters?
[state [id shape]]
(let [search (:search-text state)
filters (:filters state)
filters (cond-> filters
(contains? filters :shape)
(conj :rect :circle :path :bool))]
(or (= uuid/zero id)
(and (or (str/includes? (str/lower (:name shape)) (str/lower search))
(str/includes? (dm/str (:id shape)) (str/lower search)))
(or (empty? filters)
(and (contains? filters :component)
(contains? shape :component-id))
(let [direct-filters (filter #{:frame :rect :circle :path :bool :image :text} filters)]
(some #{(:type shape)} direct-filters))
(and (contains? filters :group)
(and (cph/group-shape? shape)
(not (contains? shape :component-id))
(or (not (contains? shape :masked-group))
(false? (:masked-group shape)))))
(and (contains? filters :mask)
(true? (:masked-group shape))))))))
(defn use-search
[page objects]
(let [filter-state (mf/use-state {:show-search-box false
:show-filters-menu false
:search-text ""
:active-filters #{}
:num-items 100})
new-css-system (mf/use-ctx ctx/new-css-system)
(let [new-css-system (mf/use-ctx ctx/new-css-system)
state* (mf/use-state
{:show-search false
:show-menu false
:search-text ""
:filters #{}
:num-items 100})
state (deref state*)
current-filters (:filters state)
current-items (:num-items state)
current-search (:search-text state)
show-menu? (:show-menu state)
show-search? (:show-search state)
clear-search-text
(mf/use-callback
(fn []
(swap! filter-state assoc :search-text "" :num-items 100)))
update-search-text
(mf/use-fn
(mf/deps new-css-system)
(fn [event]
;; NOTE: When old-css-system is removed this function will recibe value and event
;; Let won't be necessary any more
(let [value (if new-css-system
event
(dom/get-target-val event))]
(swap! filter-state assoc :search-text value :num-items 100))))
#(swap! state* assoc :search-text "" :num-items 100))
toggle-search
(mf/use-callback
(fn [event]
(let [node (dom/get-current-target event)]
(swap! filter-state assoc :search-text "")
(swap! filter-state assoc :active-filters #{})
(swap! filter-state assoc :show-filters-menu false)
(swap! filter-state assoc :num-items 100)
(swap! filter-state update :show-search-box not)
(dom/blur! node))))
toggle-filters
(mf/use-callback
(fn []
(swap! filter-state update :show-filters-menu not)))
(mf/use-fn
#(swap! state* update :show-menu not))
update-search-text-v1
(mf/use-fn
(fn [event]
(let [value (-> event dom/get-target dom/get-value)]
(swap! state* assoc :search-text value :num-items 100))))
update-search-text-v2
(mf/use-fn
(fn [value _event]
(swap! state* assoc :search-text value :num-items 100)))
toggle-search
(mf/use-fn
(fn [event]
(let [node (dom/get-current-target event)]
(dom/blur! node)
(swap! state* (fn [state]
(-> state
(assoc :search-text "")
(assoc :filters #{})
(assoc :show-menu false)
(assoc :num-items 100)
(update :show-search not)))))))
remove-filter
(mf/use-callback
(mf/deps @filter-state)
(fn [key]
(fn [_]
(swap! filter-state update :active-filters disj key)
(swap! filter-state assoc :num-items 100))))
(mf/use-fn
(fn [event]
(let [fkey (-> (dom/get-current-target event)
(dom/get-data "filter")
(keyword))]
(swap! state* (fn [state]
(-> state
(update :filters disj fkey)
(assoc :num-items 100)))))))
add-filter
(mf/use-callback
(mf/deps @filter-state (:show-filters-menu @filter-state))
(fn [key]
(fn [_]
(swap! filter-state update :active-filters conj key)
(swap! filter-state assoc :num-items 100)
(toggle-filters))))
(mf/use-fn
(fn [event]
(let [key (-> (dom/get-current-target event)
(dom/get-data "filter")
(keyword))]
(swap! state* (fn [state]
(-> state
(update :filters conj key)
(update :show-menu not)
(assoc :num-items 100)))))))
active?
(and
(:show-search-box @filter-state)
(or (d/not-empty? (:search-text @filter-state))
(d/not-empty? (:active-filters @filter-state))))
(and ^boolean show-search?
(or ^boolean (d/not-empty? current-search)
^boolean (d/not-empty? current-filters)))
search-and-filters
(fn [[id shape]]
(let [search (:search-text @filter-state)
filters (:active-filters @filter-state)
filters (cond-> filters
(some #{:shape} filters)
(conj :rect :circle :path :bool))]
(or
(= uuid/zero id)
(and
(or (str/includes? (str/lower (:name shape)) (str/lower search))
(str/includes? (dm/str (:id shape)) (str/lower search)))
(or
(empty? filters)
(and
(some #{:component} filters)
(contains? shape :component-id))
(let [direct_filters (filter #{:frame :rect :circle :path :bool :image :text} filters)]
(some #{(:type shape)} direct_filters))
(and
(some #{:group} filters)
(and (= :group (:type shape))
(not (contains? shape :component-id))
(or (not (contains? shape :masked-group)) (false? (:masked-group shape)))))
(and
(some #{:mask} filters)
(true? (:masked-group shape))))))))
filtered-objects-all
(mf/with-memo [active? objects state]
(when active?
(into [] (filter (partial match-filters? state)) objects)))
filtered-objects-total
(mf/use-memo
(mf/deps objects active? @filter-state)
#(when active?
;; filterv so count is constant time
(filterv search-and-filters objects)))
(count filtered-objects-all)
filtered-objects
(mf/use-memo
(mf/deps filtered-objects-total)
#(when active?
(calc-reparented-objects
(into {}
(take (:num-items @filter-state))
filtered-objects-total))))
(mf/with-memo [active? filtered-objects-all current-items]
(when active?
(->> filtered-objects-all
(into {} (take current-items))
(calc-reparented-objects))))
handle-show-more
(fn []
(when (<= (:num-items @filter-state) (count filtered-objects-total))
(swap! filter-state update :num-items + 100)))
(mf/use-fn
(mf/deps filtered-objects-total current-items)
(fn [_]
(when (<= current-items filtered-objects-total)
(swap! state* update :num-items + 100))))
handle-key-down
(mf/use-callback
(mf/use-fn
(fn [event]
(let [enter? (kbd/enter? event)
esc? (kbd/esc? event)
node (dom/event->target event)]
(when ^boolean enter? (dom/blur! node))
(when ^boolean esc? (dom/blur! node)))))]
(when-let [node (dom/event->target event)]
(when (kbd/enter? event)
(dom/blur! node))
(when (kbd/esc? event)
(dom/blur! node)))))]
[filtered-objects
handle-show-more
(mf/html
(if (:show-search-box @filter-state)
[:*
[:div {:class (if new-css-system
(dom/classnames (css :tool-window-bar) true
(css :search) true)
(dom/classnames :tool-window-bar true
:search true))}
(if new-css-system
[:& search-bar
{:on-change update-search-text
:value (:search-text @filter-state)
:on-clear clear-search-text
:placeholder (tr "workspace.sidebar.layers.search")}
[:button
{:on-click toggle-filters
:class (dom/classnames :active active?
(css :filter-button) true)}
i/filter-refactor]]
#(mf/html
(if show-search?
[:*
[:div {:class (if ^boolean new-css-system
(dom/classnames (css :tool-window-bar) true
(css :search) true)
(dom/classnames :tool-window-bar true
:search true))}
[:span.search-box
[:button.filter
{:on-click toggle-filters
:class (dom/classnames :active active?)}
i/icon-filter]
[:div
[:input {:on-change update-search-text
:value (:search-text @filter-state)
:auto-focus (:show-search-box @filter-state)
:placeholder (tr "workspace.sidebar.layers.search")
:on-key-down handle-key-down}]
(when (not (= "" (:search-text @filter-state)))
[:button.clear {:on-click clear-search-text}
i/exclude])]])
(if ^boolean new-css-system
[:& search-bar
{:on-change update-search-text-v2
:value current-search
:on-clear clear-search-text
:placeholder (tr "workspace.sidebar.layers.search")}
[:button
{:on-click toggle-filters
:class (dom/classnames :active active?
(css :filter-button) true)}
i/filter-refactor]]
[:button {:class (dom/classnames (css :close-search) new-css-system)
:on-click toggle-search}
(if new-css-system
i/close-refactor
i/cross)]]
[:div {:class (if new-css-system
(dom/classnames (css :active-filters) true)
(dom/classnames :active-filters true))}
(for [f (:active-filters @filter-state)]
(let [name (case f
:frame (tr "workspace.sidebar.layers.frames")
:group (tr "workspace.sidebar.layers.groups")
:mask (tr "workspace.sidebar.layers.masks")
:component (tr "workspace.sidebar.layers.components")
:text (tr "workspace.sidebar.layers.texts")
:image (tr "workspace.sidebar.layers.images")
:shape (tr "workspace.sidebar.layers.shapes")
(tr f))]
(if new-css-system
[:button {:class (dom/classnames (css :layer-filter) true)
:on-click (remove-filter f)}
[:span {:class (dom/classnames (css :layer-filter-icon) true)}
[:& sic/element-icon-refactor-by-type {:type f
:main-instance? (= f :component)}]]
[:span {:class (dom/classnames (css :layer-filter-name) true)}
name]
[:span {:class (dom/classnames (css :layer-filter-close) true)}
i/close-small-refactor]]
[:span {:on-click (remove-filter f)}
name i/cross])))]
[:span.search-box
[:button.filter
{:on-click toggle-filters
:class (dom/classnames :active active?)}
i/icon-filter]
[:div
[:input {:on-change update-search-text-v1
:value current-search
:auto-focus show-search?
:placeholder (tr "workspace.sidebar.layers.search")
:on-key-down handle-key-down}]
(when (not (= "" current-search))
[:button.clear {:on-click clear-search-text} i/exclude])]])
(when (:show-filters-menu @filter-state)
(if new-css-system
[:ul {:class (dom/classnames (css :filters-container) true)}
[:li {:key "frames-filter-item"
:class (dom/classnames (css :filter-menu-item) true
(css :selected) (contains? (:active-filters @filter-state) :frame))
:on-click (add-filter :frame)}
[:div {:class (dom/classnames (css :filter-menu-item-name-wrapper) true)}
[:span {:class (dom/classnames (css :filter-menu-item-icon) true)}
i/board-refactor]
[:span {:class (dom/classnames (css :filter-menu-item-name) true)}
(tr "workspace.sidebar.layers.frames")]]
(when (contains? (:active-filters @filter-state) :frame)
[:span {:class (dom/classnames (css :filter-menu-item-tick) true)}
i/tick-refactor])]
[:li {:key "groups-filter-item"
:class (dom/classnames (css :filter-menu-item) true
(css :selected) (contains? (:active-filters @filter-state) :group))
:on-click (add-filter :group)}
[:div {:class (dom/classnames (css :filter-menu-item-name-wrapper) true)}
[:span {:class (dom/classnames (css :filter-menu-item-icon) true)}
i/group-refactor]
[:span {:class (dom/classnames (css :filter-menu-item-name) true)}
(tr "workspace.sidebar.layers.groups")]]
(when (contains? (:active-filters @filter-state) :group)
[:span {:class (dom/classnames (css :filter-menu-item-tick) true)}
i/tick-refactor])]
[:li {:key "masks-filter-item"
:class (dom/classnames (css :filter-menu-item) true
(css :selected) (contains? (:active-filters @filter-state) :mask))
:on-click (add-filter :mask)}
[:div {:class (dom/classnames (css :filter-menu-item-name-wrapper) true)}
[:span {:class (dom/classnames (css :filter-menu-item-icon) true)}
i/mask-refactor]
[:span {:class (dom/classnames (css :filter-menu-item-name) true)}
(tr "workspace.sidebar.layers.masks")]]
(when (contains? (:active-filters @filter-state) :mask)
[:span {:class (dom/classnames (css :filter-menu-item-tick) true)}
i/tick-refactor])]
[:li {:key "components-filter-item"
:class (dom/classnames (css :filter-menu-item) true
(css :selected) (contains? (:active-filters @filter-state) :component))
:on-click (add-filter :component)}
[:div {:class (dom/classnames (css :filter-menu-item-name-wrapper) true)}
[:span {:class (dom/classnames (css :filter-menu-item-icon) true)}
i/component-refactor]
[:span {:class (dom/classnames (css :filter-menu-item-name) true)}
(tr "workspace.sidebar.layers.components")]]
(when (contains? (:active-filters @filter-state) :component)
[:span {:class (dom/classnames (css :filter-menu-item-tick) true)}
i/tick-refactor])]
[:li {:key "texts-filter-item"
:class (dom/classnames (css :filter-menu-item) true
(css :selected) (contains? (:active-filters @filter-state) :text))
:on-click (add-filter :text)}
[:div {:class (dom/classnames (css :filter-menu-item-name-wrapper) true)}
[:span {:class (dom/classnames (css :filter-menu-item-icon) true)}
i/text-refactor]
[:span {:class (dom/classnames (css :filter-menu-item-name) true)}
(tr "workspace.sidebar.layers.texts")]]
(when (contains? (:active-filters @filter-state) :text)
[:span {:class (dom/classnames (css :filter-menu-item-tick) true)}
i/tick-refactor])]
[:li {:key "images-filter-item"
:class (dom/classnames (css :filter-menu-item) true
(css :selected) (contains? (:active-filters @filter-state) :image))
:on-click (add-filter :image)}
[:div {:class (dom/classnames (css :filter-menu-item-name-wrapper) true)}
[:span {:class (dom/classnames (css :filter-menu-item-icon) true)}
i/img-refactor]
[:span {:class (dom/classnames (css :filter-menu-item-name) true)}
(tr "workspace.sidebar.layers.images")]]
(when (contains? (:active-filters @filter-state) :image)
[:span {:class (dom/classnames (css :filter-menu-item-tick) true)}
i/tick-refactor])]
[:li {:key "shapes-filter-item"
:class (dom/classnames (css :filter-menu-item) true
(css :selected) (contains? (:active-filters @filter-state) :shape))
:on-click (add-filter :shape)}
[:div {:class (dom/classnames (css :filter-menu-item-name-wrapper) true)}
[:span {:class (dom/classnames (css :filter-menu-item-icon) true)}
i/path-refactor]
[:span {:class (dom/classnames (css :filter-menu-item-name) true)}
(tr "workspace.sidebar.layers.shapes")]]
(when (contains? (:active-filters @filter-state) :shape)
[:span {:class (dom/classnames (css :filter-menu-item-tick) true)}
i/tick-refactor])]]
[:button {:class (dom/classnames (css :close-search) new-css-system)
:on-click toggle-search}
(if ^boolean new-css-system
i/close-refactor
i/cross)]]
[:div.filters-container
[:span {:on-click (add-filter :frame)} i/artboard (tr "workspace.sidebar.layers.frames")]
[:span {:on-click (add-filter :group)} i/folder (tr "workspace.sidebar.layers.groups")]
[:span {:on-click (add-filter :mask)} i/mask (tr "workspace.sidebar.layers.masks")]
[:span {:on-click (add-filter :component)} i/component (tr "workspace.sidebar.layers.components")]
[:span {:on-click (add-filter :text)} i/text (tr "workspace.sidebar.layers.texts")]
[:span {:on-click (add-filter :image)} i/image (tr "workspace.sidebar.layers.images")]
[:span {:on-click (add-filter :shape)} i/curve (tr "workspace.sidebar.layers.shapes")]]))]
[:div {:class (if ^boolean new-css-system
(dom/classnames (css :active-filters) true)
(dom/classnames :active-filters true))}
(for [fkey current-filters]
(let [fname (d/name fkey)
name (case fkey
:frame (tr "workspace.sidebar.layers.frames")
:group (tr "workspace.sidebar.layers.groups")
:mask (tr "workspace.sidebar.layers.masks")
:component (tr "workspace.sidebar.layers.components")
:text (tr "workspace.sidebar.layers.texts")
:image (tr "workspace.sidebar.layers.images")
:shape (tr "workspace.sidebar.layers.shapes")
(tr fkey))]
(if ^boolean new-css-system
[:button {:class (dom/classnames (css :layer-filter) true)
:key fname
:data-filter fname
:on-click remove-filter}
[:span {:class (dom/classnames (css :layer-filter-icon) true)}
[:& sic/element-icon-refactor-by-type
{:type fkey
:main-instance? (= fkey :component)}]]
[:span {:class (dom/classnames (css :layer-filter-name) true)}
name]
[:span {:class (dom/classnames (css :layer-filter-close) true)}
i/close-small-refactor]]
(if new-css-system
[:div {:class (dom/classnames (css :tool-window-bar) true)}
[:& title-bar {:collapsable? false
:title (:name page)
:on-btn-click toggle-search
:btn-children i/search-refactor}]]
[:div.tool-window-bar
[:span.page-name
(:name page)]
[:button.icon-search {:on-click toggle-search}
i/search]])))]))
[:span {:on-click remove-filter
:data-filter fname
:key fname}
name i/cross])))]
(when ^boolean show-menu?
(if ^boolean new-css-system
[:ul {:class (css :filters-container)}
[:li {:class (dom/classnames
(css :filter-menu-item) true
(css :selected) (contains? current-filters :frame))
:data-filter "frame"
:on-click add-filter}
[:div {:class (dom/classnames (css :filter-menu-item-name-wrapper) true)}
[:span {:class (dom/classnames (css :filter-menu-item-icon) true)} i/board-refactor]
[:span {:class (dom/classnames (css :filter-menu-item-name) true)}
(tr "workspace.sidebar.layers.frames")]]
(when (contains? current-filters :frame)
[:span {:class (dom/classnames (css :filter-menu-item-tick) true)} i/tick-refactor])]
[:li {:class (dom/classnames
(css :filter-menu-item) true
(css :selected) (contains? current-filters :group))
:data-filter "group"
:on-click add-filter}
[:div {:class (dom/classnames (css :filter-menu-item-name-wrapper) true)}
[:span {:class (dom/classnames (css :filter-menu-item-icon) true)} i/group-refactor]
[:span {:class (dom/classnames (css :filter-menu-item-name) true)}
(tr "workspace.sidebar.layers.groups")]]
(when (contains? current-filters :group)
[:span {:class (dom/classnames (css :filter-menu-item-tick) true)} i/tick-refactor])]
[:li {:class (dom/classnames
(css :filter-menu-item) true
(css :selected) (contains? current-filters :mask))
:data-filter "mask"
:on-click add-filter}
[:div {:class (dom/classnames (css :filter-menu-item-name-wrapper) true)}
[:span {:class (dom/classnames (css :filter-menu-item-icon) true)} i/mask-refactor]
[:span {:class (dom/classnames (css :filter-menu-item-name) true)}
(tr "workspace.sidebar.layers.masks")]]
(when (contains? current-filters :mask)
[:span {:class (dom/classnames (css :filter-menu-item-tick) true)} i/tick-refactor])]
[:li {:class (dom/classnames
(css :filter-menu-item) true
(css :selected) (contains? current-filters :component))
:data-filter "component"
:on-click add-filter}
[:div {:class (dom/classnames (css :filter-menu-item-name-wrapper) true)}
[:span {:class (dom/classnames (css :filter-menu-item-icon) true)} i/component-refactor]
[:span {:class (dom/classnames (css :filter-menu-item-name) true)}
(tr "workspace.sidebar.layers.components")]]
(when (contains? current-filters :component)
[:span {:class (dom/classnames (css :filter-menu-item-tick) true)} i/tick-refactor])]
[:li {:class (dom/classnames
(css :filter-menu-item) true
(css :selected) (contains? current-filters :text))
:data-filter "text"
:on-click add-filter}
[:div {:class (dom/classnames (css :filter-menu-item-name-wrapper) true)}
[:span {:class (dom/classnames (css :filter-menu-item-icon) true)} i/text-refactor]
[:span {:class (dom/classnames (css :filter-menu-item-name) true)}
(tr "workspace.sidebar.layers.texts")]]
(when (contains? current-filters :text)
[:span {:class (dom/classnames (css :filter-menu-item-tick) true)} i/tick-refactor])]
[:li {:class (dom/classnames
(css :filter-menu-item) true
(css :selected) (contains? current-filters :image))
:data-filter "image"
:on-click add-filter}
[:div {:class (dom/classnames (css :filter-menu-item-name-wrapper) true)}
[:span {:class (dom/classnames (css :filter-menu-item-icon) true)} i/img-refactor]
[:span {:class (dom/classnames (css :filter-menu-item-name) true)}
(tr "workspace.sidebar.layers.images")]]
(when (contains? current-filters :image)
[:span {:class (dom/classnames (css :filter-menu-item-tick) true)} i/tick-refactor])]
[:li {:class (dom/classnames
(css :filter-menu-item) true
(css :selected) (contains? current-filters :shape))
:data-filter "shape"
:on-click add-filter}
[:div {:class (dom/classnames (css :filter-menu-item-name-wrapper) true)}
[:span {:class (dom/classnames (css :filter-menu-item-icon) true)} i/path-refactor]
[:span {:class (dom/classnames (css :filter-menu-item-name) true)}
(tr "workspace.sidebar.layers.shapes")]]
(when (contains? current-filters :shape)
[:span {:class (dom/classnames (css :filter-menu-item-tick) true)} i/tick-refactor])]]
[:div.filters-container
[:span {:data-filter "frame"
:on-click add-filter}
i/artboard (tr "workspace.sidebar.layers.frames")]
[:span {:data-filter "group"
:on-click add-filter}
i/folder (tr "workspace.sidebar.layers.groups")]
[:span {:data-filter "mask"
:on-click add-filter}
i/mask (tr "workspace.sidebar.layers.masks")]
[:span {:data-filter "component"
:on-click add-filter}
i/component (tr "workspace.sidebar.layers.components")]
[:span {:data-filter "text"
:on-click add-filter}
i/text (tr "workspace.sidebar.layers.texts")]
[:span {:data-filter "image"
:on-click add-filter}
i/image (tr "workspace.sidebar.layers.images")]
[:span {:data-filter "shape"
:on-click add-filter}
i/curve (tr "workspace.sidebar.layers.shapes")]]))]
(if ^boolean new-css-system
[:div {:class (dom/classnames (css :tool-window-bar) true)}
[:& title-bar {:collapsable? false
:title (:name page)
:on-btn-click toggle-search
:btn-children i/search-refactor}]]
[:div.tool-window-bar
[:span.page-name
(:name page)]
[:button.icon-search {:on-click toggle-search}
i/search]])))]))
(mf/defc layers-toolbox
{:wrap [mf/memo]}
[{:keys [size-parent] :as props}]
(let [page (mf/deref refs/workspace-page)
focus (mf/deref refs/workspace-focus-selected)
objects (hooks/with-focus-objects (:objects page) focus)
title (when (= 1 (count focus)) (get-in objects [(first focus) :name]))
{::mf/wrap [mf/memo]
::mf/wrap-props false}
[{:keys [size-parent]}]
(let [page (mf/deref refs/workspace-page)
focus (mf/deref refs/workspace-focus-selected)
objects (hooks/with-focus-objects (:objects page) focus)
title (when (= 1 (count focus))
(dm/get-in objects [(first focus) :name]))
new-css-system (mf/use-ctx ctx/new-css-system)
observer-var (mf/use-var nil)
lazy-load-ref (mf/use-ref nil)
observer-var (mf/use-var nil)
lazy-load-ref (mf/use-ref nil)
[filtered-objects show-more filter-component] (use-search page objects)
@ -434,16 +471,16 @@
on-render-container
(fn [element]
(let [options #js {:root element}
lazy-el (mf/ref-val lazy-load-ref)]
(when-let [lazy-node (mf/ref-val lazy-load-ref)]
(cond
(and (some? element) (not (some? @observer-var)))
(let [observer (js/IntersectionObserver. intersection-callback options)]
(.observe observer lazy-el)
(let [observer (js/IntersectionObserver. intersection-callback
#js {:root element})]
(.observe observer lazy-node)
(reset! observer-var observer))
(and (nil? element) (some? @observer-var))
(do (.disconnect @observer-var)
(do (.disconnect ^js @observer-var)
(reset! observer-var nil)))))
on-scroll
@ -505,18 +542,17 @@
[:div {:class (dom/classnames (css :focus-mode-tag-wrapper) true)}
[:div {:class (dom/classnames (css :focus-mode-tag) true)} (tr "workspace.focus.focus-mode")]]
[:div.focus-mode (tr "workspace.focus.focus-mode")])]]
filter-component)
(filter-component))
(if (some? filtered-objects)
[:*
[:div {:class (if new-css-system
(dom/classnames (css :tool-window-content) true)
(dom/classnames :tool-window-content true))
:ref on-render-container :key "filters"}
:ref on-render-container}
[:& filters-tree {:objects filtered-objects
:key (dm/str (:id page))
:parent-size size-parent}]
[:div.lazy {:ref lazy-load-ref
:key "lazy-load"
:style {:min-height 16}}]]
[:div {:on-scroll on-scroll
:class (if new-css-system