Add performance refactor to component-annotaton react component

This commit is contained in:
Andrey Antukh 2024-02-28 18:08:04 +01:00 committed by Alonso Torres
parent 60962b58fe
commit a21a64aa10
3 changed files with 159 additions and 114 deletions

View file

@ -2210,14 +2210,12 @@
(ptk/reify ::update-component-annotation (ptk/reify ::update-component-annotation
ptk/WatchEvent ptk/WatchEvent
(watch [it state _] (watch [it state _]
(let [data (get state :workspace-data) (let [data (get state :workspace-data)
update-fn update-fn
(fn [component] (fn [component]
;; NOTE: we need to ensure the component exists, ;; NOTE: we need to ensure the component exists,
;; because there are small possibilities of race ;; because there are small possibilities of race
;; conditions with component deletion. ;; conditions with component deletion.
(when component (when component
(if (nil? annotation) (if (nil? annotation)
(dissoc component :annotation) (dissoc component :annotation)
@ -2230,11 +2228,11 @@
(rx/of (dch/commit-changes changes)))))) (rx/of (dch/commit-changes changes))))))
(defn set-annotations-expanded (defn set-annotations-expanded
[expanded?] [expanded]
(ptk/reify ::set-annotations-expanded (ptk/reify ::set-annotations-expanded
ptk/UpdateEvent ptk/UpdateEvent
(update [_ state] (update [_ state]
(assoc-in state [:workspace-annotations :expanded?] expanded?)))) (assoc-in state [:workspace-annotations :expanded] expanded))))
(defn set-annotations-id-for-create (defn set-annotations-id-for-create
[id] [id]
@ -2243,7 +2241,7 @@
(update [_ state] (update [_ state]
(if id (if id
(-> (assoc-in state [:workspace-annotations :id-for-create] id) (-> (assoc-in state [:workspace-annotations :id-for-create] id)
(assoc-in [:workspace-annotations :expanded?] true)) (assoc-in [:workspace-annotations :expanded] true))
(d/dissoc-in state [:workspace-annotations :id-for-create]))))) (d/dissoc-in state [:workspace-annotations :id-for-create])))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View file

@ -574,9 +574,6 @@
[id] [id]
(l/derived #(get % id) workspace-grid-edition)) (l/derived #(get % id) workspace-grid-edition))
(def workspace-annotations
(l/derived #(get % :workspace-annotations {}) st/state))
(def current-file-id (def current-file-id
(l/derived :current-file-id st/state)) (l/derived :current-file-id st/state))

View file

@ -31,144 +31,189 @@
[app.util.i18n :as i18n :refer [tr]] [app.util.i18n :as i18n :refer [tr]]
[app.util.timers :as tm] [app.util.timers :as tm]
[cuerdas.core :as str] [cuerdas.core :as str]
[okulary.core :as l]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(def ref:annotations-state
(l/derived :workspace-annotations st/state))
(mf/defc component-annotation (mf/defc component-annotation
{::mf/props :obj} {::mf/props :obj}
[{:keys [id shape component]}] [{:keys [id shape component]}]
(let [main-instance? (:main-instance shape) (let [main-instance? (:main-instance shape)
component-id (:component-id shape) component-id (:component-id shape)
annotation (:annotation component) annotation (:annotation component)
editing? (mf/use-state false) shape-id (:id shape)
invalid-text? (mf/use-state (or (nil? annotation) (str/blank? annotation)))
size (mf/use-state (count annotation))
textarea-ref (mf/use-ref)
;; hack to create an autogrowing textarea editing* (mf/use-state false)
;; based on https://css-tricks.com/the-cleanest-trick-for-autogrowing-textareas/ editing? (deref editing*)
autogrow #(let [textarea (mf/ref-val textarea-ref)
text (when textarea (.-value textarea))]
(reset! invalid-text? (str/blank? text))
(when textarea
(reset! size (count text))
(aset (.-dataset (.-parentNode textarea)) "replicatedValue" text)))
initialize #(let [textarea (mf/ref-val textarea-ref)]
(when textarea
(aset textarea "value" annotation)
(autogrow)))
discard (fn [event] invalid-text* (mf/use-state #(str/blank? annotation))
(dom/stop-propagation event) invalid-text? (deref invalid-text*)
(let [textarea (mf/ref-val textarea-ref)]
(aset textarea "value" annotation)
(reset! editing? false)
(st/emit! (dw/set-annotations-id-for-create nil))
(autogrow)))
save (fn [event]
(dom/stop-propagation event)
(let [textarea (mf/ref-val textarea-ref)
text (.-value textarea)]
(when-not (str/blank? text)
(reset! editing? false)
(st/emit!
(dw/set-annotations-id-for-create nil)
(dw/update-component-annotation component-id text)))))
workspace-annotations (mf/deref refs/workspace-annotations)
annotations-expanded? (:expanded? workspace-annotations)
creating? (= id (:id-for-create workspace-annotations))
expand #(when-not (or @editing? creating?) size* (mf/use-state #(count annotation))
(st/emit! (dw/set-annotations-expanded %))) size (deref size*)
edit (fn [event]
(dom/stop-propagation event) textarea-ref (mf/use-ref)
(when main-instance?
(let [textarea (mf/ref-val textarea-ref)] state (mf/deref ref:annotations-state)
(reset! editing? true) expanded? (:expanded state)
(dom/focus! textarea)))) create-id (:id-for-create state)
on-delete-annotation creating? (= id create-id)
(mf/use-callback
(mf/deps (:id shape)) ;; hack to create an autogrowing textarea based on
;; https://css-tricks.com/the-cleanest-trick-for-autogrowing-textareas/
adjust-textarea-size
(mf/use-fn
#(when-let [textarea (mf/ref-val textarea-ref)]
(let [text (dom/get-value textarea)]
(reset! invalid-text* (str/blank? text))
(reset! size* (count text))
(let [^js parent (.-parentNode textarea)
^js dataset (.-dataset parent)]
(set! (.-replicatedValue dataset) text)))))
on-toggle-expand
(mf/use-fn
(mf/deps expanded? editing? creating?)
(fn [_]
(st/emit! (dw/set-annotations-expanded (not expanded?)))))
on-discard
(mf/use-fn
(mf/deps adjust-textarea-size creating?)
(fn [event] (fn [event]
(dom/stop-propagation event) (dom/stop-propagation event)
(st/emit! (modal/show (when-let [textarea (mf/ref-val textarea-ref)]
{:type :confirm (dom/set-value! textarea annotation)
:title (tr "modals.delete-component-annotation.title") (reset! editing* false)
:message (tr "modals.delete-component-annotation.message") (when creating?
:accept-label (tr "ds.confirm-ok") (st/emit! (dw/set-annotations-id-for-create nil)))
:on-accept (fn [] (adjust-textarea-size))))
(st/emit!
(dw/set-annotations-id-for-create nil)
(dw/update-component-annotation component-id nil)))}))))]
(mf/use-effect on-edit
(mf/deps (:id shape)) (mf/use-fn
(fn [] (fn [event]
(initialize) (dom/stop-propagation event)
(when (and (not creating?) (:id-for-create workspace-annotations)) ;; cleanup set-annotations-id-for-create if we aren't on the marked component (when ^boolean main-instance?
(st/emit! (dw/set-annotations-id-for-create nil))) (when-let [textarea (mf/ref-val textarea-ref)]
(fn [] (st/emit! (dw/set-annotations-id-for-create nil))))) ;; cleanup set-annotationsid-for-create on unload (reset! editing* true)
(dom/focus! textarea)))))
on-save
(mf/use-fn
(mf/deps creating?)
(fn [event]
(dom/stop-propagation event)
(when-let [textarea (mf/ref-val textarea-ref)]
(let [text (dom/get-value textarea)]
(when-not (str/blank? text)
(reset! editing* false)
(when ^boolean creating?
(st/emit! (dw/set-annotations-id-for-create nil)))
(dw/update-component-annotation component-id text))))))
on-delete-annotation
(mf/use-fn
(mf/deps shape-id component-id creating?)
(fn [event]
(dom/stop-propagation event)
(let [on-accept (fn []
(st/emit!
;; (ptk/data-event {::ev/name "delete-component-annotation"})
(when creating?
(dw/set-annotations-id-for-create nil))
(dw/update-component-annotation component-id nil)))]
(st/emit! (modal/show
{:type :confirm
:title (tr "modals.delete-component-annotation.title")
:message (tr "modals.delete-component-annotation.message")
:accept-label (tr "ds.confirm-ok")
:on-accept on-accept})))))]
(mf/with-effect [shape-id state create-id creating?]
(when-let [textarea (mf/ref-val textarea-ref)]
(dom/set-value! textarea annotation)
(adjust-textarea-size))
;; cleanup set-annotations-id-for-create if we aren't on the marked component
(when (and (not creating?) (some? create-id))
(st/emit! (dw/set-annotations-id-for-create nil)))
;; cleanup set-annotationsid-for-create on unload
(fn []
(when creating?
(st/emit! (dw/set-annotations-id-for-create nil)))))
(when (or creating? annotation) (when (or creating? annotation)
[:div {:class (stl/css-case :component-annotation true [:div {:class (stl/css-case
:editing @editing? :component-annotation true
:creating creating?)} :editing editing?
[:div {:class (stl/css-case :annotation-title true :creating creating?)}
:expandeable (not (or @editing? creating?)) [:div {:class (stl/css-case
:expanded annotations-expanded?) :annotation-title true
:on-click #(expand (not annotations-expanded?))} :expandeable (not (or editing? creating?))
:expanded expanded?)
:on-click on-toggle-expand}
(if (or @editing? creating?) (if (or editing? creating?)
[:span {:class (stl/css :annotation-text)} [:span {:class (stl/css :annotation-text)}
(if @editing? (if editing?
(tr "workspace.options.component.edit-annotation") (tr "workspace.options.component.edit-annotation")
(tr "workspace.options.component.create-annotation"))] (tr "workspace.options.component.create-annotation"))]
[:* [:*
[:span {:class (stl/css-case :icon-arrow true [:span {:class (stl/css-case
:expanded annotations-expanded?)} :icon-arrow true
:expanded expanded?)}
i/arrow-refactor] i/arrow-refactor]
[:span {:class (stl/css :annotation-text)} [:span {:class (stl/css :annotation-text)}
(tr "workspace.options.component.annotation")]]) (tr "workspace.options.component.annotation")]])
[:div {:class (stl/css :icons-wrapper)} [:div {:class (stl/css :icons-wrapper)}
(when (and main-instance? annotations-expanded?) (when (and ^boolean main-instance?
(if (or @editing? creating?) ^boolean expanded?)
(if (or ^boolean editing?
^boolean creating?)
[:* [:*
[:div {:title (if creating? (tr "labels.create") (tr "labels.save")) [:div {:title (if ^boolean creating?
:on-click save (tr "labels.create")
:class (stl/css-case :icon true (tr "labels.save"))
:icon-tick true :on-click on-save
:hidden @invalid-text?)} :class (stl/css-case
:icon true
:icon-tick true
:hidden invalid-text?)}
i/tick-refactor] i/tick-refactor]
[:div {:class (stl/css :icon :icon-cross) [:div {:class (stl/css :icon :icon-cross)
:title (tr "labels.discard") :title (tr "labels.discard")
:on-click discard} :on-click on-discard}
i/close-refactor]] i/close-refactor]]
[:* [:*
[:div {:class (stl/css :icon :icon-edit) [:div {:class (stl/css :icon :icon-edit)
:title (tr "labels.edit") :title (tr "labels.edit")
:on-click edit} :on-click on-edit}
i/curve-refactor] i/curve-refactor]
[:div {:class (stl/css :icon :icon-trash) [:div {:class (stl/css :icon :icon-trash)
:title (tr "labels.delete") :title (tr "labels.delete")
:on-click on-delete-annotation} :on-click on-delete-annotation}
i/delete-refactor]]))]] i/delete-refactor]]))]]
[:div {:class (stl/css-case :hidden (not annotations-expanded?))} [:div {:class (stl/css-case :hidden (not expanded?))}
[:div {:class (stl/css :grow-wrap)} [:div {:class (stl/css :grow-wrap)}
[:div {:class (stl/css :texarea-copy)}] [:div {:class (stl/css :texarea-copy)}]
[:textarea [:textarea
{:ref textarea-ref {:ref textarea-ref
:id "annotation-textarea" :id "annotation-textarea"
:data-debug annotation :data-debug annotation
:auto-focus (or @editing? creating?) :auto-focus (or editing? creating?)
:maxLength 300 :maxLength 300
:on-input autogrow :on-input adjust-textarea-size
:default-value annotation :default-value annotation
:read-only (not (or creating? @editing?))}]] :read-only (not (or creating? editing?))}]]
(when (or @editing? creating?) (when (or editing? creating?)
[:div {:class (stl/css :counter)} (str @size "/300")])]]))) [:div {:class (stl/css :counter)} (str size "/300")])]])))
(mf/defc component-swap-item (mf/defc component-swap-item
{::mf/props :obj} {::mf/props :obj}
@ -204,7 +249,7 @@
path (cfh/butlast-path-with-dots group-name) path (cfh/butlast-path-with-dots group-name)
on-group-click #(on-enter-group group-name)] on-group-click #(on-enter-group group-name)]
[:div {:class (stl/css :component-group) [:div {:class (stl/css :component-group)
:key (uuid/next) :on-click on-group-click :on-click on-group-click
:title group-name} :title group-name}
[:div {:class (stl/css :path-wrapper)} [:div {:class (stl/css :path-wrapper)}
@ -295,7 +340,7 @@
groups (when-not is-search? groups (when-not is-search?
(->> (sort (sequence xform components)) (->> (sort (sequence xform components))
(map #(assoc {} :name %)))) (map (fn [name] {:name name}))))
components (if is-search? components (if is-search?
(filter #(str/includes? (str/lower (:full-name %)) (str/lower (:term filters))) components) (filter #(str/includes? (str/lower (:full-name %)) (str/lower (:term filters))) components)
@ -412,12 +457,12 @@
:component-list (not (:listing-thumbs? filters)))} :component-list (not (:listing-thumbs? filters)))}
(for [item items] (for [item items]
(if (:id item) (if (:id item)
(let [data (get-in libraries [current-library-id :data]) (let [data (dm/get-in libraries [current-library-id :data])
container (ctf/get-component-page data item) container (ctf/get-component-page data item)
root-shape (ctf/get-component-root data item) root-shape (ctf/get-component-root data item)
loop? (or (contains? parent-components (:main-instance-id item)) loop? (or (contains? parent-components (:main-instance-id item))
(contains? parent-components (:id item)))] (contains? parent-components (:id item)))]
[:& component-swap-item {:key (:id item) [:& component-swap-item {:key (dm/str (:id item))
:item item :item item
:loop loop? :loop loop?
:shapes shapes :shapes shapes
@ -427,8 +472,9 @@
:component-id current-comp-id :component-id current-comp-id
:is-search is-search? :is-search is-search?
:listing-thumbs (:listing-thumbs? filters)}]) :listing-thumbs (:listing-thumbs? filters)}])
[:& component-group-item {:item item [:& component-group-item {:item item
:key (:id item) :key (:name item)
:on-enter-group on-enter-group}]))]]]])) :on-enter-group on-enter-group}]))]]]]))
(mf/defc component-ctx-menu (mf/defc component-ctx-menu
@ -442,12 +488,12 @@
[:& dropdown {:show show :on-close on-close} [:& dropdown {:show show :on-close on-close}
[:ul {:class (stl/css-case :custom-select-dropdown true [:ul {:class (stl/css-case :custom-select-dropdown true
:not-main (not main-instance))} :not-main (not main-instance))}
(for [entry menu-entries :when (not (nil? entry))] (for [{:keys [msg] :as entry} menu-entries]
[:li {:key (uuid/next) (when (some? msg)
:class (stl/css :dropdown-element) [:li {:key msg
:on-click (partial do-action (:action entry))} :class (stl/css :dropdown-element)
[:span {:class (stl/css :dropdown-label)} :on-click (partial do-action (:action entry))}
(tr (:msg entry))]])]])) [:span {:class (stl/css :dropdown-label)} (tr msg)]]))]]))
(mf/defc component-menu (mf/defc component-menu
{::mf/props :obj} {::mf/props :obj}
@ -472,7 +518,11 @@
shape (first shapes) shape (first shapes)
id (:id shape) id (:id shape)
shape-name (:name shape) shape-name (:name shape)
component (ctf/resolve-component shape {:id current-file-id :data workspace-data} workspace-libraries {:include-deleted? true}) component (ctf/resolve-component shape
{:id current-file-id
:data workspace-data}
workspace-libraries
{:include-deleted? true})
main-instance? (if components-v2 (ctk/main-instance? shape) true) main-instance? (if components-v2 (ctk/main-instance? shape) true)
toggle-content toggle-content