♻️ Rework assets selection to have a single selected set

This commit is contained in:
Andrés Moya 2021-04-23 17:33:46 +02:00 committed by Alonso Torres
parent 4c93ef4bb3
commit da8a32047c
2 changed files with 268 additions and 274 deletions

View file

@ -45,38 +45,6 @@
[okulary.core :as l] [okulary.core :as l]
[rumext.alpha :as mf])) [rumext.alpha :as mf]))
;; ---- Assets selection management
(def empty-selection #{})
(defn toggle-select
[selected asset-id]
(if (contains? selected asset-id)
(disj selected asset-id)
(conj selected asset-id)))
(defn replace-select
[selected asset-id]
#{asset-id})
(defn extend-select
[selected asset-id groups]
(let [assets (->> groups vals flatten)
clicked-idx (d/index-of-pred assets #(= (:id %) asset-id))
selected-idx (->> selected
(map (fn [id] (d/index-of-pred assets
#(= (:id %) id)))))
min-idx (apply min (conj selected-idx clicked-idx))
max-idx (apply max (conj selected-idx clicked-idx))]
(->> assets
d/enumerate
(filter #(<= min-idx (first %) max-idx))
(map #(-> % second :id))
set)))
;; ---- Group assets management ---- ;; ---- Group assets management ----
(s/def ::asset-name ::us/not-empty-string) (s/def ::asset-name ::us/not-empty-string)
@ -149,40 +117,42 @@
;; ---- Components box ---- ;; ---- Components box ----
(mf/defc components-box (mf/defc components-box
[{:keys [file-id local? components listing-thumbs? open? change-selected] :as props}] [{:keys [file-id local? components listing-thumbs? open? selected-assets
on-asset-click on-assets-delete on-clear-selection] :as props}]
(let [state (mf/use-state {:menu-open false (let [state (mf/use-state {:menu-open false
:renaming nil :renaming nil
:top nil :top nil
:left nil :left nil
:component-id nil :component-id nil
:selected empty-selection
:folded-groups empty-folded-groups}) :folded-groups empty-folded-groups})
selected-components (:components selected-assets)
multi-components? (> (count selected-components) 1)
multi-assets? (or (not (empty? (:graphics selected-assets)))
(not (empty? (:colors selected-assets)))
(not (empty? (:typographies selected-assets))))
groups (group-assets components) groups (group-assets components)
selected (:selected @state)
folded-groups (:folded-groups @state) folded-groups (:folded-groups @state)
on-duplicate on-duplicate
(mf/use-callback (mf/use-callback
(mf/deps @state) (mf/deps @state)
(fn [] (fn []
(if (empty? selected) (if (empty? selected-components)
(st/emit! (dwl/duplicate-component {:id (:component-id @state)})) (st/emit! (dwl/duplicate-component {:id (:component-id @state)}))
(do (do
(st/emit! (dwc/start-undo-transaction)) (st/emit! (dwc/start-undo-transaction))
(apply st/emit! (map #(dwl/duplicate-component {:id %}) selected)) (apply st/emit! (map #(dwl/duplicate-component {:id %}) selected-components))
(st/emit! (dwc/commit-undo-transaction)))))) (st/emit! (dwc/commit-undo-transaction))))))
on-delete on-delete
(mf/use-callback (mf/use-callback
(mf/deps @state) (mf/deps @state file-id multi-components? multi-assets?)
(fn [] (fn []
(if (empty? selected) (if (or multi-components? multi-assets?)
(st/emit! (dwl/delete-component {:id (:component-id @state)})) (on-assets-delete)
(do (st/emit! (dwl/delete-component {:id (:component-id @state)})))
(st/emit! (dwc/start-undo-transaction))
(apply st/emit! (map #(dwl/delete-component {:id %}) selected))
(st/emit! (dwc/commit-undo-transaction))))
(st/emit! (dwl/sync-file file-id file-id)))) (st/emit! (dwl/sync-file file-id file-id))))
on-rename on-rename
@ -205,6 +175,7 @@
on-context-menu on-context-menu
(mf/use-callback (mf/use-callback
(mf/deps selected-components on-clear-selection)
(fn [component-id] (fn [component-id]
(fn [event] (fn [event]
(when local? (when local?
@ -212,39 +183,22 @@
top (:y pos) top (:y pos)
left (- (:x pos) 20)] left (- (:x pos) 20)]
(dom/prevent-default event) (dom/prevent-default event)
(when-not (contains? selected-components component-id)
(on-clear-selection))
(swap! state assoc :menu-open true (swap! state assoc :menu-open true
:top top :top top
:left left :left left
:component-id component-id)))))) :component-id component-id))))))
unselect-all
(mf/use-callback
(fn [event]
(swap! state assoc :selected empty-selection)))
on-select
(mf/use-callback
(fn [component-id]
(fn [event]
(dom/stop-propagation event)
(swap! state update :selected
(fn [selected]
(cond
(kbd/ctrl? event)
(toggle-select selected component-id)
(kbd/shift? event)
(extend-select selected component-id groups)))))))
create-group create-group
(mf/use-callback (mf/use-callback
(mf/deps components selected) (mf/deps components selected-components on-clear-selection)
(fn [name] (fn [name]
(swap! state assoc :selected empty-selection) (on-clear-selection)
(st/emit! (dwc/start-undo-transaction)) (st/emit! (dwc/start-undo-transaction))
(apply st/emit! (apply st/emit!
(->> components (->> components
(filter #(contains? selected (:id %))) (filter #(contains? selected-components (:id %)))
(map #(dwl/rename-component (map #(dwl/rename-component
(:id %) (:id %)
(str name " / " (str name " / "
@ -262,7 +216,7 @@
on-group on-group
(mf/use-callback (mf/use-callback
(mf/deps components selected) (mf/deps components selected-components)
(fn [event] (fn [event]
(dom/stop-propagation event) (dom/stop-propagation event)
(modal/show! :create-group-dialog {:create create-group}))) (modal/show! :create-group-dialog {:create create-group})))
@ -274,11 +228,7 @@
:component component}) :component component})
(dnd/set-allowed-effect! event "move")))] (dnd/set-allowed-effect! event "move")))]
(mf/use-effect [:div.asset-section
(mf/deps [change-selected selected])
#(change-selected (count selected)))
[:div.asset-section {:on-click unselect-all}
[:div.asset-title {:class (when (not open?) "closed")} [:div.asset-title {:class (when (not open?) "closed")}
[:span {:on-click (st/emitf (dwl/set-assets-box-open file-id :components (not open?)))} [:span {:on-click (st/emitf (dwl/set-assets-box-open file-id :components (not open?)))}
i/arrow-slide (tr "workspace.assets.components")] i/arrow-slide (tr "workspace.assets.components")]
@ -308,11 +258,11 @@
(let [renaming? (= (:renaming @state)(:id component))] (let [renaming? (= (:renaming @state)(:id component))]
[:div {:key (:id component) [:div {:key (:id component)
:class-name (dom/classnames :class-name (dom/classnames
:selected (contains? selected (:id component)) :selected (contains? selected-components (:id component))
:grid-cell @listing-thumbs? :grid-cell @listing-thumbs?
:enum-item (not @listing-thumbs?)) :enum-item (not @listing-thumbs?))
:draggable true :draggable true
:on-click (on-select (:id component)) :on-click #(on-asset-click % (:id component) groups nil)
:on-context-menu (on-context-menu (:id component)) :on-context-menu (on-context-menu (:id component))
:on-drag-start (partial on-drag-start component)} :on-drag-start (partial on-drag-start component)}
[:& exports/component-svg {:group (get-in component [:objects (:id component)]) [:& exports/component-svg {:group (get-in component [:objects (:id component)])
@ -340,28 +290,35 @@
:on-close #(swap! state assoc :menu-open false) :on-close #(swap! state assoc :menu-open false)
:top (:top @state) :top (:top @state)
:left (:left @state) :left (:left @state)
:options [(when (<= (count selected) 1) :options [(when-not (or multi-components? multi-assets?)
[(tr "workspace.assets.rename") on-rename]) [(tr "workspace.assets.rename") on-rename])
[(tr "workspace.assets.duplicate") on-duplicate] (when-not multi-assets?
[(tr "workspace.assets.duplicate") on-duplicate])
[(tr "workspace.assets.delete") on-delete] [(tr "workspace.assets.delete") on-delete]
[(tr "workspace.assets.group") on-group]]}])])) (when-not multi-assets?
[(tr "workspace.assets.group") on-group])]}])]))
;; ---- Graphics box ---- ;; ---- Graphics box ----
(mf/defc graphics-box (mf/defc graphics-box
[{:keys [file-id local? objects listing-thumbs? open? change-selected] :as props}] [{:keys [file-id local? objects listing-thumbs? open? selected-assets
on-asset-click on-assets-delete on-clear-selection] :as props}]
(let [input-ref (mf/use-ref nil) (let [input-ref (mf/use-ref nil)
state (mf/use-state {:menu-open false state (mf/use-state {:menu-open false
:renaming nil :renaming nil
:top nil :top nil
:left nil :left nil
:object-id nil :object-id nil
:selected empty-selection
:folded-groups empty-folded-groups}) :folded-groups empty-folded-groups})
selected-objects (:graphics selected-assets)
multi-objects? (> (count selected-objects) 1)
multi-assets? (or (not (empty? (:components selected-assets)))
(not (empty? (:colors selected-assets)))
(not (empty? (:typographies selected-assets))))
groups (group-assets objects) groups (group-assets objects)
selected (:selected @state)
folded-groups (:folded-groups @state) folded-groups (:folded-groups @state)
add-graphic add-graphic
@ -370,7 +327,7 @@
(st/emitf (dwl/set-assets-box-open file-id :graphics true)) (st/emitf (dwl/set-assets-box-open file-id :graphics true))
(dom/click (mf/ref-val input-ref)))) (dom/click (mf/ref-val input-ref))))
on-selected on-file-selected
(mf/use-callback (mf/use-callback
(mf/deps file-id) (mf/deps file-id)
(fn [blobs] (fn [blobs]
@ -380,14 +337,11 @@
on-delete on-delete
(mf/use-callback (mf/use-callback
(mf/deps @state) (mf/deps @state multi-objects? multi-assets?)
(fn [] (fn []
(if (empty? selected) (if (or multi-objects? multi-assets?)
(st/emit! (dwl/delete-media {:id (:object-id @state)})) (on-assets-delete)
(do (st/emit! (dwl/delete-media {:id (:object-id @state)})))))
(st/emit! (dwc/start-undo-transaction))
(apply st/emit! (map #(dwl/delete-media {:id %}) selected))
(st/emit! (dwc/commit-undo-transaction))))))
on-rename on-rename
(mf/use-callback (mf/use-callback
@ -409,6 +363,7 @@
on-context-menu on-context-menu
(mf/use-callback (mf/use-callback
(mf/deps selected-objects on-clear-selection)
(fn [object-id] (fn [object-id]
(fn [event] (fn [event]
(when local? (when local?
@ -416,39 +371,22 @@
top (:y pos) top (:y pos)
left (- (:x pos) 20)] left (- (:x pos) 20)]
(dom/prevent-default event) (dom/prevent-default event)
(when-not (contains? selected-objects object-id)
(on-clear-selection))
(swap! state assoc :menu-open true (swap! state assoc :menu-open true
:top top :top top
:left left :left left
:object-id object-id)))))) :object-id object-id))))))
unselect-all
(mf/use-callback
(fn [event]
(swap! state assoc :selected empty-selection)))
on-select
(mf/use-callback
(fn [object-id]
(fn [event]
(dom/stop-propagation event)
(swap! state update :selected
(fn [selected]
(cond
(kbd/ctrl? event)
(toggle-select selected object-id)
(kbd/shift? event)
(extend-select selected object-id groups)))))))
create-group create-group
(mf/use-callback (mf/use-callback
(mf/deps objects selected) (mf/deps objects selected-objects on-clear-selection)
(fn [name] (fn [name]
(swap! state assoc :selected empty-selection) (on-clear-selection)
(st/emit! (dwc/start-undo-transaction)) (st/emit! (dwc/start-undo-transaction))
(apply st/emit! (apply st/emit!
(->> objects (->> objects
(filter #(contains? selected (:id %))) (filter #(contains? selected-objects (:id %)))
(map #(dwl/rename-media (map #(dwl/rename-media
(:id %) (:id %)
(str name " / " (str name " / "
@ -466,7 +404,7 @@
on-group on-group
(mf/use-callback (mf/use-callback
(mf/deps objects selected) (mf/deps objects selected-objects)
(fn [event] (fn [event]
(dom/stop-propagation event) (dom/stop-propagation event)
(modal/show! :create-group-dialog {:create create-group}))) (modal/show! :create-group-dialog {:create create-group})))
@ -479,11 +417,7 @@
(dnd/set-data! event "text/asset-type" mtype) (dnd/set-data! event "text/asset-type" mtype)
(dnd/set-allowed-effect! event "move")))] (dnd/set-allowed-effect! event "move")))]
(mf/use-effect [:div.asset-section
(mf/deps [change-selected selected])
#(change-selected (count selected)))
[:div.asset-section {:on-click unselect-all}
[:div.asset-title {:class (when (not open?) "closed")} [:div.asset-title {:class (when (not open?) "closed")}
[:span {:on-click (st/emitf (dwl/set-assets-box-open file-id :graphics (not open?)))} [:span {:on-click (st/emitf (dwl/set-assets-box-open file-id :graphics (not open?)))}
i/arrow-slide (tr "workspace.assets.graphics")] i/arrow-slide (tr "workspace.assets.graphics")]
@ -494,7 +428,7 @@
[:& file-uploader {:accept cm/str-media-types [:& file-uploader {:accept cm/str-media-types
:multi true :multi true
:input-ref input-ref :input-ref input-ref
:on-selected on-selected}]])] :on-selected on-file-selected}]])]
(when open? (when open?
(for [group groups] (for [group groups]
(let [path (first group) (let [path (first group)
@ -518,11 +452,11 @@
(for [object objects] (for [object objects]
[:div {:key (:id object) [:div {:key (:id object)
:class-name (dom/classnames :class-name (dom/classnames
:selected (contains? selected (:id object)) :selected (contains? selected-objects (:id object))
:grid-cell @listing-thumbs? :grid-cell @listing-thumbs?
:enum-item (not @listing-thumbs?)) :enum-item (not @listing-thumbs?))
:draggable true :draggable true
:on-click (on-select (:id object)) :on-click #(on-asset-click % (:id object) groups nil)
:on-context-menu (on-context-menu (:id object)) :on-context-menu (on-context-menu (:id object))
:on-drag-start (partial on-drag-start object)} :on-drag-start (partial on-drag-start object)}
[:img {:src (cfg/resolve-file-media object true) [:img {:src (cfg/resolve-file-media object true)
@ -552,16 +486,18 @@
:on-close #(swap! state assoc :menu-open false) :on-close #(swap! state assoc :menu-open false)
:top (:top @state) :top (:top @state)
:left (:left @state) :left (:left @state)
:options [(when (<= (count selected) 1) :options [(when-not (or multi-objects? multi-assets?)
[(tr "workspace.assets.rename") on-rename]) [(tr "workspace.assets.rename") on-rename])
[(tr "workspace.assets.delete") on-delete] [(tr "workspace.assets.delete") on-delete]
[(tr "workspace.assets.group") on-group]]}])])) (when-not multi-assets?
[(tr "workspace.assets.group") on-group])]}])]))
;; ---- Colors box ---- ;; ---- Colors box ----
(mf/defc color-item (mf/defc color-item
[{:keys [color local? file-id selected on-select locale] :as props}] [{:keys [color local? file-id selected-colors multi-colors? multi-assets?
on-asset-click on-assets-delete on-clear-selection colors locale] :as props}]
(let [rename? (= (:color-for-rename @refs/workspace-local) (:id color)) (let [rename? (= (:color-for-rename @refs/workspace-local) (:id color))
id (:id color) id (:id color)
input-ref (mf/use-ref) input-ref (mf/use-ref)
@ -575,10 +511,8 @@
(:color color) (:color color) (:color color) (:color color)
:else (:value color)) :else (:value color))
click-color apply-color
(fn [event] (fn [color-id event]
(when on-select
((on-select (:id color)) event))
(let [ids (get-in @st/state [:workspace-local :selected])] (let [ids (get-in @st/state [:workspace-local :selected])]
(if (kbd/shift? event) (if (kbd/shift? event)
(st/emit! (dc/change-stroke ids color)) (st/emit! (dc/change-stroke ids color))
@ -594,13 +528,12 @@
(st/emit! (dwl/update-color updated-color file-id)))) (st/emit! (dwl/update-color updated-color file-id))))
delete-color delete-color
(mf/use-callback
(mf/deps @state multi-colors? multi-assets?)
(fn [] (fn []
(if (empty? selected) (if (or multi-colors? multi-assets?)
(st/emit! (dwl/delete-color color)) (on-assets-delete)
(do (st/emit! (dwl/delete-color color)))))
(st/emit! (dwc/start-undo-transaction))
(apply st/emit! (map #(dwl/delete-color {:id %}) selected))
(st/emit! (dwc/commit-undo-transaction)))))
rename-color-clicked rename-color-clicked
(fn [event] (fn [event]
@ -634,16 +567,20 @@
:position :right})) :position :right}))
on-context-menu on-context-menu
(mf/use-callback
(mf/deps color selected-colors on-clear-selection)
(fn [event] (fn [event]
(when local? (when local?
(let [pos (dom/get-client-position event) (let [pos (dom/get-client-position event)
top (:y pos) top (:y pos)
left (+ 10 (:x pos))] left (+ 10 (:x pos))]
(dom/prevent-default event) (dom/prevent-default event)
(when-not (contains? selected-colors (:id color))
(on-clear-selection))
(swap! state assoc (swap! state assoc
:menu-open true :menu-open true
:top top :top top
:left left))))] :left left)))))]
(mf/use-effect (mf/use-effect
(mf/deps (:editing @state)) (mf/deps (:editing @state))
@ -653,10 +590,11 @@
nil)) nil))
[:div.asset-list-item {:class-name (dom/classnames [:div.asset-list-item {:class-name (dom/classnames
:selected (contains? selected (:id color))) :selected (contains? selected-colors (:id color)))
:on-context-menu on-context-menu :on-context-menu on-context-menu
:on-click (when-not (:editing @state) :on-click (when-not (:editing @state)
click-color)} #(on-asset-click % (:id color) {"" colors}
(partial apply-color (:id color))))}
[:& bc/color-bullet {:color color}] [:& bc/color-bullet {:color color}]
(if (:editing @state) (if (:editing @state)
@ -679,17 +617,20 @@
:on-close #(swap! state assoc :menu-open false) :on-close #(swap! state assoc :menu-open false)
:top (:top @state) :top (:top @state)
:left (:left @state) :left (:left @state)
:options [(when (<= (count selected) 1) :options [(when-not (or multi-colors? multi-assets?)
[(t locale "workspace.assets.rename") rename-color-clicked]) [(t locale "workspace.assets.rename") rename-color-clicked])
(when (<= (count selected) 1) (when-not (or multi-colors? multi-assets?)
[(t locale "workspace.assets.edit") edit-color-clicked]) [(t locale "workspace.assets.edit") edit-color-clicked])
[(t locale "workspace.assets.delete") delete-color]]}])])) [(t locale "workspace.assets.delete") delete-color]]}])]))
(mf/defc colors-box (mf/defc colors-box
[{:keys [file-id local? colors locale open? change-selected] :as props}] [{:keys [file-id local? colors locale open? selected-assets
(let [state (mf/use-state {:selected empty-selection}) on-asset-click on-assets-delete on-clear-selection] :as props}]
(let [selected-colors (:colors selected-assets)
selected (:selected @state) multi-colors? (> (count selected-colors) 1)
multi-assets? (or (not (empty? (:components selected-assets)))
(not (empty? (:graphics selected-assets)))
(not (empty? (:typographies selected-assets))))
add-color add-color
(mf/use-callback (mf/use-callback
@ -708,32 +649,9 @@
:on-accept add-color :on-accept add-color
:data {:color "#406280" :data {:color "#406280"
:opacity 1} :opacity 1}
:position :right}))) :position :right})))]
unselect-all [:div.asset-section
(mf/use-callback
(fn [event]
(swap! state assoc :selected empty-selection)))
on-select
(mf/use-callback
(fn [color-id]
(fn [event]
(dom/stop-propagation event)
(swap! state update :selected
(fn [selected]
(cond
(kbd/ctrl? event)
(toggle-select selected color-id)
(kbd/shift? event)
(extend-select selected color-id {"" colors})))))))]
(mf/use-effect
(mf/deps [change-selected selected])
#(change-selected (count selected)))
[:div.asset-section {:on-click unselect-all}
[:div.asset-title {:class (when (not open?) "closed")} [:div.asset-title {:class (when (not open?) "closed")}
[:span {:on-click (st/emitf (dwl/set-assets-box-open file-id :colors (not open?)))} [:span {:on-click (st/emitf (dwl/set-assets-box-open file-id :colors (not open?)))}
i/arrow-slide (t locale "workspace.assets.colors")] i/arrow-slide (t locale "workspace.assets.colors")]
@ -751,24 +669,34 @@
:color color :color color
:file-id file-id :file-id file-id
:local? local? :local? local?
:selected selected :selected-colors selected-colors
:on-select on-select :multi-colors? multi-colors?
:multi-assets? multi-assets?
:on-asset-click on-asset-click
:on-assets-delete on-assets-delete
:on-clear-selection on-clear-selection
:colors colors
:locale locale}]))])])) :locale locale}]))])]))
;; ---- Typography box ---- ;; ---- Typography box ----
(mf/defc typography-box (mf/defc typography-box
[{:keys [file file-id local? typographies locale open? change-selected] :as props}] [{:keys [file file-id local? typographies locale open? selected-assets
on-asset-click on-assets-delete on-clear-selection] :as props}]
(let [state (mf/use-state {:detail-open? false (let [state (mf/use-state {:detail-open? false
:menu-open? false :menu-open? false
:top nil :top nil
:left nil :left nil
:selected empty-selection}) :id nil})
local (deref refs/workspace-local) local (deref refs/workspace-local)
selected (:selected @state)
selected-typographies (:typographies selected-assets)
multi-typographies? (> (count selected-typographies) 1)
multi-assets? (or (not (empty? (:graphics selected-assets)))
(not (empty? (:colors selected-assets)))
(not (empty? (:typographies selected-assets))))
add-typography add-typography
(mf/use-callback (mf/use-callback
@ -782,19 +710,8 @@
(fn [typography changes] (fn [typography changes]
(st/emit! (dwl/update-typography (merge typography changes) file-id)))) (st/emit! (dwl/update-typography (merge typography changes) file-id))))
handle-typography-selection apply-typography
(fn [typography event] (fn [typography event]
(dom/stop-propagation event)
(swap! state update :selected
(fn [selected]
(cond
(kbd/ctrl? event)
(toggle-select selected (:id typography))
(kbd/shift? event)
(extend-select selected (:id typography) {"" typographies}))))
(let [ids (get-in @st/state [:workspace-local :selected]) (let [ids (get-in @st/state [:workspace-local :selected])
attrs (merge attrs (merge
{:typography-ref-file file-id {:typography-ref-file file-id
@ -804,23 +721,21 @@
ids))) ids)))
on-context-menu on-context-menu
(mf/use-callback
(mf/deps selected-typographies on-clear-selection)
(fn [id event] (fn [id event]
(when local? (when local?
(let [pos (dom/get-client-position event) (let [pos (dom/get-client-position event)
top (:y pos) top (:y pos)
left (- (:x pos) 20)] left (- (:x pos) 20)]
(dom/prevent-default event) (dom/prevent-default event)
(when-not (contains? selected-typographies id)
(on-clear-selection))
(swap! state assoc (swap! state assoc
:menu-open? true :menu-open? true
:top top :top top
:left left :left left
:id id)))) :id id)))))
unselect-all
(mf/use-callback
(fn [event]
(swap! state assoc :selected empty-selection)))
closed-typography-edit closed-typography-edit
(mf/use-callback (mf/use-callback
@ -836,13 +751,12 @@
(st/emit! #(assoc-in % [:workspace-local :edit-typography] (:id @state)))) (st/emit! #(assoc-in % [:workspace-local :edit-typography] (:id @state))))
handle-delete-typography handle-delete-typography
(mf/use-callback
(mf/deps @state multi-typographies? multi-assets?)
(fn [] (fn []
(if (empty? selected) (if (or multi-typographies? multi-assets?)
(st/emit! (dwl/delete-typography (:id @state))) (on-assets-delete)
(do (st/emit! (dwl/delete-typography (:id @state))))))
(st/emit! (dwc/start-undo-transaction))
(apply st/emit! (map #(dwl/delete-typography %) selected))
(st/emit! (dwc/commit-undo-transaction)))))
editting-id (or (:rename-typography local) (:edit-typography local))] editting-id (or (:rename-typography local) (:edit-typography local))]
@ -854,11 +768,7 @@
(when (:edit-typography local) (when (:edit-typography local)
(st/emit! #(update % :workspace-local dissoc :edit-typography))))) (st/emit! #(update % :workspace-local dissoc :edit-typography)))))
(mf/use-effect [:div.asset-section
(mf/deps [change-selected selected])
#(change-selected (count selected)))
[:div.asset-section {:on-click unselect-all}
[:div.asset-title {:class (when (not open?) "closed")} [:div.asset-title {:class (when (not open?) "closed")}
[:span {:on-click (st/emitf (dwl/set-assets-box-open file-id :typographies (not open?)))} [:span {:on-click (st/emitf (dwl/set-assets-box-open file-id :typographies (not open?)))}
i/arrow-slide (t locale "workspace.assets.typography")] i/arrow-slide (t locale "workspace.assets.typography")]
@ -872,9 +782,9 @@
:on-close #(swap! state assoc :menu-open? false) :on-close #(swap! state assoc :menu-open? false)
:top (:top @state) :top (:top @state)
:left (:left @state) :left (:left @state)
:options [(when (<= (count selected) 1) :options [(when-not (or multi-typographies? multi-assets?)
[(t locale "workspace.assets.rename") handle-rename-typography-clicked]) [(t locale "workspace.assets.rename") handle-rename-typography-clicked])
(when (<= (count selected) 1) (when-not (or multi-typographies? multi-assets?)
[(t locale "workspace.assets.edit") handle-edit-typography-clicked]) [(t locale "workspace.assets.edit") handle-edit-typography-clicked])
[(t locale "workspace.assets.delete") handle-delete-typography]]}] [(t locale "workspace.assets.delete") handle-delete-typography]]}]
(when open? (when open?
@ -887,8 +797,9 @@
:read-only? (not local?) :read-only? (not local?)
:on-context-menu #(on-context-menu (:id typography) %) :on-context-menu #(on-context-menu (:id typography) %)
:on-change #(handle-change typography %) :on-change #(handle-change typography %)
:selected? (contains? selected (:id typography)) :selected? (contains? selected-typographies (:id typography))
:on-select #(handle-typography-selection typography %) :on-click #(on-asset-click % (:id typography) {"" typographies}
(partial apply-typography typography))
:editting? (= editting-id (:id typography)) :editting? (= editting-id (:id typography))
:focus-name? (= (:rename-typography local) (:id typography))}])])])) :focus-name? (= (:rename-typography local) (:id typography))}])])]))
@ -961,16 +872,19 @@
reverse-sort? (mf/use-state false) reverse-sort? (mf/use-state false)
listing-thumbs? (mf/use-state true) listing-thumbs? (mf/use-state true)
selected-count (mf/use-state {:components 0
:graphics 0 selected-assets (mf/use-state {:components #{}
:colors 0 :graphics #{}
:typographies 0}) :colors #{}
:typographies #{}})
selected-count (+ (count (:components @selected-assets))
(count (:graphics @selected-assets))
(count (:colors @selected-assets))
(count (:typographies @selected-assets)))
toggle-open (st/emitf (dwl/set-assets-box-open (:id file) :library (not open?))) toggle-open (st/emitf (dwl/set-assets-box-open (:id file) :library (not open?)))
change-selected-count (fn [asset-type cnt]
(swap! selected-count assoc asset-type cnt))
url (rt/resolve router :workspace url (rt/resolve router :workspace
{:project-id (:project-id file) {:project-id (:project-id file)
:file-id (:id file)} :file-id (:id file)}
@ -996,9 +910,84 @@
toggle-listing toggle-listing
(mf/use-callback (mf/use-callback
(fn [event] (fn [event]
(swap! listing-thumbs? not)))] (swap! listing-thumbs? not)))
[:div.tool-window {:on-context-menu #(dom/prevent-default %)} toggle-selected-asset
(mf/use-callback
(mf/deps @selected-assets)
(fn [asset-type asset-id]
(swap! selected-assets update asset-type
(fn [selected]
(if (contains? selected asset-id)
(disj selected asset-id)
(conj selected asset-id))))))
extend-selected-assets
(mf/use-callback
(mf/deps @selected-assets)
(fn [asset-type asset-id asset-groups]
(swap! selected-assets update asset-type
(fn [selected]
(let [all-assets (-> asset-groups vals flatten)
clicked-idx (d/index-of-pred all-assets #(= (:id %) asset-id))
selected-idx (->> selected
(map (fn [id]
(d/index-of-pred all-assets
#(= (:id %) id)))))
min-idx (apply min (conj selected-idx clicked-idx))
max-idx (apply max (conj selected-idx clicked-idx))]
(->> all-assets
d/enumerate
(filter #(<= min-idx (first %) max-idx))
(map #(-> % second :id))
set))))))
unselect-all
(mf/use-callback
(fn []
(swap! selected-assets {:components #{}
:graphics #{}
:colors #{}
:typographies #{}})))
on-asset-click
(mf/use-callback
(mf/deps toggle-selected-asset extend-selected-assets)
(fn [asset-type event asset-id all-assets default-click]
(cond
(kbd/ctrl? event)
(do
(dom/stop-propagation event)
(toggle-selected-asset asset-type asset-id))
(kbd/shift? event)
(do
(dom/stop-propagation event)
(extend-selected-assets asset-type asset-id all-assets))
:else
(when default-click
(default-click event)))))
on-assets-delete
(mf/use-callback
(mf/deps @selected-assets)
(fn []
(do
(st/emit! (dwc/start-undo-transaction))
(apply st/emit! (map #(dwl/delete-component {:id %})
(:components @selected-assets)))
(apply st/emit! (map #(dwl/delete-media {:id %})
(:graphics @selected-assets)))
(apply st/emit! (map #(dwl/delete-color {:id %})
(:colors @selected-assets)))
(apply st/emit! (map #(dwl/delete-typography %)
(:typographies @selected-assets)))
(st/emit! (dwc/commit-undo-transaction)))))]
[:div.tool-window {:on-context-menu #(dom/prevent-default %)
:on-click unselect-all}
[:div.tool-window-bar.library-bar [:div.tool-window-bar.library-bar
{:on-click toggle-open} {:on-click toggle-open}
[:div.collapse-library [:div.collapse-library
@ -1037,14 +1026,9 @@
(str/empty? (:term filters))))] (str/empty? (:term filters))))]
[:div.tool-window-content [:div.tool-window-content
[:div.listing-options [:div.listing-options
(when (> selected-count 0)
[:span.selected-count [:span.selected-count
(let [selected-count @selected-count (tr "workspace.assets.selected-count" (i18n/c selected-count))])
total (+ (:components selected-count)
(:graphics selected-count)
(:colors selected-count)
(:typographies selected-count))]
(when (> total 0)
(tr "workspace.assets.selected-count" (i18n/c total))))]
[:div.listing-option-btn.first {:on-click toggle-sort} [:div.listing-option-btn.first {:on-click toggle-sort}
(if @reverse-sort? (if @reverse-sort?
i/sort-descending i/sort-descending
@ -1053,30 +1037,38 @@
(if @listing-thumbs? (if @listing-thumbs?
i/listing-enum i/listing-enum
i/listing-thumbs)]] i/listing-thumbs)]]
(when show-components? (when show-components?
[:& components-box {:file-id (:id file) [:& components-box {:file-id (:id file)
:local? local? :local? local?
:components components :components components
:listing-thumbs? listing-thumbs? :listing-thumbs? listing-thumbs?
:change-selected (partial change-selected-count :open? (open-box? :components)
:components) :selected-assets @selected-assets
:open? (open-box? :components)}]) :on-asset-click (partial on-asset-click :components)
:on-assets-delete on-assets-delete
:on-clear-selection unselect-all}])
(when show-graphics? (when show-graphics?
[:& graphics-box {:file-id (:id file) [:& graphics-box {:file-id (:id file)
:local? local? :local? local?
:objects media :objects media
:listing-thumbs? listing-thumbs? :listing-thumbs? listing-thumbs?
:change-selected (partial change-selected-count :open? (open-box? :graphics)
:graphics) :selected-assets @selected-assets
:open? (open-box? :graphics)}]) :on-asset-click (partial on-asset-click :graphics)
:on-assets-delete on-assets-delete
:on-clear-selection unselect-all}])
(when show-colors? (when show-colors?
[:& colors-box {:file-id (:id file) [:& colors-box {:file-id (:id file)
:local? local? :local? local?
:locale locale :locale locale
:colors colors :colors colors
:open? (open-box? :colors) :open? (open-box? :colors)
:change-selected (partial change-selected-count :selected-assets @selected-assets
:colors)}]) :on-asset-click (partial on-asset-click :colors)
:on-assets-delete on-assets-delete
:on-clear-selection unselect-all}])
(when show-typography? (when show-typography?
[:& typography-box {:file file [:& typography-box {:file file
@ -1085,8 +1077,10 @@
:locale locale :locale locale
:typographies typographies :typographies typographies
:open? (open-box? :typographies) :open? (open-box? :typographies)
:change-selected (partial change-selected-count :selected-assets @selected-assets
:typographies)}]) :on-asset-click (partial on-asset-click :typographies)
:on-assets-delete on-assets-delete
:on-clear-selection unselect-all}])
(when (and (not show-components?) (not show-graphics?) (not show-colors?)) (when (and (not show-components?) (not show-graphics?) (not show-colors?))
[:div.asset-section [:div.asset-section

View file

@ -207,7 +207,7 @@
(mf/defc typography-entry (mf/defc typography-entry
[{:keys [typography read-only? selected? on-select on-change on-detach on-context-menu editting? focus-name? file]}] [{:keys [typography read-only? selected? on-click on-change on-detach on-context-menu editting? focus-name? file]}]
(let [locale (mf/deref i18n/locale) (let [locale (mf/deref i18n/locale)
open? (mf/use-state editting?) open? (mf/use-state editting?)
hover-detach (mf/use-state false) hover-detach (mf/use-state false)
@ -241,8 +241,8 @@
[:div.element-set-options-group.typography-entry [:div.element-set-options-group.typography-entry
{:class (when selected? "selected")} {:class (when selected? "selected")}
[:div.typography-selection-wrapper [:div.typography-selection-wrapper
{:class (when on-select "is-selectable") {:class (when on-click "is-selectable")
:on-click on-select :on-click on-click
:on-context-menu on-context-menu} :on-context-menu on-context-menu}
[:div.typography-sample [:div.typography-sample
{:style {:font-family (:font-family typography) {:style {:font-family (:font-family typography)