Add Selected colors menu

This commit is contained in:
Eva 2022-03-15 11:02:13 +01:00 committed by Alonso Torres
parent 6897c0c3fe
commit 87f5efeadb
11 changed files with 344 additions and 20 deletions

View File

@ -4,6 +4,9 @@
### :boom: Breaking changes ### :boom: Breaking changes
### :sparkles: New features ### :sparkles: New features
- Added selected colors widget in right sidebar [Taiga #2485](https://tree.taiga.io/project/penpot/us/2485)
### :bug: Bugs fixed ### :bug: Bugs fixed
### :arrow_up: Deps updates ### :arrow_up: Deps updates
### :heart: Community contributions by (Thank you!) ### :heart: Community contributions by (Thank you!)

View File

@ -1534,3 +1534,44 @@
margin-right: $size-2; margin-right: $size-2;
} }
} }
.expand-colors {
cursor: pointer;
display: flex;
.text {
color: $color-gray-30;
font-size: 0.75rem;
padding-left: 10px;
}
svg {
width: 16px;
height: 16px;
fill: $color-gray-20;
stroke: $color-gray-20;
}
}
.selected-colors {
.color-data {
margin-bottom: 0;
padding-bottom: 5px;
svg {
visibility: hidden;
}
.percentil {
margin-bottom: 0;
}
}
.color-data:hover {
background-color: $color-gray-60;
svg {
visibility: visible;
}
}
}

View File

@ -390,3 +390,32 @@
(update [_ state] (update [_ state]
(-> state (-> state
(assoc-in [:workspace-global :editing-stop] spot))))) (assoc-in [:workspace-global :editing-stop] spot)))))
(defn color-att->text
[color]
{:fill-color (:color color)
:fill-opacity (:opacity color)
:fill-color-ref-id (:id color)
:fill-color-ref-file (:file-id color)
:fill-color-gradient (:gradient color)})
(defn change-text-color
[old-color new-color index node]
(let [fills (:fills node)
parsed-color (d/without-nils (color-att->text old-color))
parsed-new-color (d/without-nils (color-att->text new-color))
has-color? (d/index-of fills parsed-color)]
(cond-> node
(some? has-color?)
(assoc-in [:fills index] parsed-new-color))))
(defn change-color-in-selected
[new-color shapes-by-color old-color]
(ptk/reify ::change-color-in-selected
ptk/WatchEvent
(watch [_ _ _]
(->> (rx/from shapes-by-color)
(rx/map (fn [shape] (case (:prop shape)
:fill (change-fill [(:shape-id shape)] new-color (:index shape))
:stroke (change-stroke [(:shape-id shape)] new-color (:index shape))
:content (dwt/update-text-with-function (:shape-id shape) (partial change-text-color old-color new-color (:index shape))))))))))

View File

@ -236,5 +236,13 @@
(events/listen globals/window EventType.CLICK on-click)]] (events/listen globals/window EventType.CLICK on-click)]]
#(doseq [key keys] #(doseq [key keys]
(events/unlistenByKey key))))) (events/unlistenByKey key)))))
(mf/use-layout-effect
(mf/deps handle-mouse-wheel)
(fn []
(let [keys [(events/listen (mf/ref-val ref) EventType.WHEEL handle-mouse-wheel #js {:pasive false})]]
#(doseq [key keys]
(events/unlistenByKey key)))))
[:> :input props])) [:> :input props]))

View File

@ -0,0 +1,189 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) UXBOX Labs SL
(ns app.main.ui.workspace.sidebar.options.menus.color-selection
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.text :as txt]
[app.main.data.workspace.colors :as dc]
[app.main.data.workspace.common :as dwc]
[app.main.store :as st]
[app.main.ui.icons :as i]
[app.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row]]
[app.util.i18n :as i18n :refer [tr]]
[rumext.alpha :as mf]))
(defn fill->color-att
[fill]
(let [attrs (d/without-nils {:color (:fill-color fill)
:opacity (:fill-opacity fill)
:id (:fill-color-ref-id fill)
:file-id (:fill-color-ref-file fill)
:gradient (:fill-color-gradient fill)})]
{:attrs attrs
:prop :fill
:shape-id (:shape-id fill)
:index (:index fill)}))
(defn stroke->color-att
[stroke]
(let [attrs (d/without-nils {:color (:stroke-color stroke)
:opacity (:stroke-opacity stroke)
:id (:stroke-color-ref-id stroke)
:file-id (:stroke-color-ref-file stroke)
:gradient (:stroke-color-gradient stroke)})]
{:attrs attrs
:prop :stroke
:shape-id (:shape-id stroke)
:index (:index stroke)}))
(defn text->color-att
[fill]
(let [attrs (d/without-nils {:color (:fill-color fill)
:opacity (:fill-opacity fill)
:id (:fill-color-ref-id fill)
:file-id (:fill-color-ref-file fill)
:gradient (:fill-color-gradient fill)})]
{:attrs attrs
:prop :content
:shape-id (:shape-id fill)
:index (:index fill)}))
(defn treat-node
[node shape-id]
(map-indexed #(assoc %2 :shape-id shape-id :index %1) node))
(defn extract-text-colors
[text]
(let [content (txt/node-seq txt/is-text-node? (:content text))
content-filtered (map :fills content)
indexed (mapcat #(treat-node % (:id text)) content-filtered)]
(map text->color-att indexed)))
(defn- extract-all-colors
[shapes]
(reduce
(fn [list shape]
(let [fill-obj (map-indexed #(assoc %2 :shape-id (:id shape) :index %1) (:fills shape))
stroke-obj (map-indexed #(assoc %2 :shape-id (:id shape) :index %1) (:strokes shape))]
(if (= :text (:type shape))
(-> list
(into (map stroke->color-att) stroke-obj)
(into (extract-text-colors shape)))
(-> list
(into (map fill->color-att) fill-obj)
(into (map stroke->color-att) stroke-obj)))))
[]
shapes))
(defn- prepare-colors
[shapes]
(let [data (extract-all-colors shapes)
grouped-colors (group-by :attrs data)
all-colors (distinct (mapv :attrs data))
tmp (group-by #(some? (:id %)) all-colors)
library-colors (get tmp true)
colors (get tmp false)]
{:grouped-colors grouped-colors
:all-colors all-colors
:colors colors
:library-colors library-colors}))
(mf/defc color-selection-menu
{::mf/wrap [#(mf/memo' % (mf/check-props ["shapes"]))]}
[{:keys [shapes] :as props}]
(let [{:keys [all-colors grouped-colors library-colors colors]} (mf/with-memo [shapes]
(prepare-colors shapes))
expand-lib-color (mf/use-state false)
expand-color (mf/use-state false)
grouped-colors* (mf/use-var nil)
prev-color* (mf/use-var nil)
on-change
(mf/use-fn
(fn [new-color old-color]
(let [old-color (-> (or @prev-color* old-color)
(dissoc :name)
(dissoc :path)
(d/without-nils))
shapes-by-color (get @grouped-colors* old-color)]
(reset! prev-color* new-color)
(st/emit! (dc/change-color-in-selected new-color shapes-by-color old-color)))))
on-open (mf/use-fn
(fn [color]
(reset! prev-color* color)))
on-detach
(mf/use-fn
(fn [color]
(let [shapes-by-color (get @grouped-colors* color)
new-color (assoc color :id nil :file-id nil)]
(st/emit! (dc/change-color-in-selected new-color shapes-by-color color)))))
select-only
(mf/use-fn
(fn [color]
(let [shapes-by-color (get @grouped-colors* color)
ids (into (d/ordered-set) (map :shape-id) shapes-by-color)]
(st/emit! (dwc/select-shapes ids)))))]
(mf/with-effect [grouped-colors]
(reset! grouped-colors* grouped-colors))
(when (> (count all-colors) 1)
[:div.element-set
[:div.element-set-title
[:span (tr "workspace.options.selection-color")]]
[:div.element-set-content
[:div.selected-colors
(for [[index color] (d/enumerate (take 3 library-colors))]
[:& color-row {:key (dm/str "color-" index)
:color color
:index index
:on-detach on-detach
:select-only select-only
:on-change #(on-change % color)
:on-open on-open}])
(when (and (false? @expand-lib-color) (< 3 (count library-colors)))
[:div.expand-colors {:on-click #(reset! expand-lib-color true)}
[:span i/actions]
[:span.text (tr "workspace.options.more-lib-colors")]])
(when @expand-lib-color
(for [[index color] (d/enumerate (drop 3 library-colors))]
[:& color-row {:key (dm/str "color-" index)
:color color
:index index
:on-detach on-detach
:select-only select-only
:on-change #(on-change % color)
:on-open on-open}]))]
[:div.selected-colors
(for [[index color] (d/enumerate (take 3 colors))]
[:& color-row {:key (dm/str "color-" index)
:color color
:index index
:select-only select-only
:on-change #(on-change % color)
:on-open on-open}])
(when (and (false? @expand-color) (< 3 (count colors)))
[:div.expand-colors {:on-click #(reset! expand-color true)}
[:span i/actions]
[:span.text (tr "workspace.options.more-colors")]])
(when @expand-color
(for [[index color] (d/enumerate (drop 3 colors))]
[:& color-row {:key (dm/str "color-" index)
:color color
:index index
:select-only select-only
:on-change #(on-change % color)
:on-open on-open}]))]]])))

View File

@ -14,6 +14,7 @@
[app.main.ui.components.color-input :refer [color-input]] [app.main.ui.components.color-input :refer [color-input]]
[app.main.ui.components.numeric-input :refer [numeric-input]] [app.main.ui.components.numeric-input :refer [numeric-input]]
[app.main.ui.context :as ctx] [app.main.ui.context :as ctx]
[app.main.ui.formats :as fmt]
[app.main.ui.hooks :as h] [app.main.ui.hooks :as h]
[app.main.ui.icons :as i] [app.main.ui.icons :as i]
[app.util.color :as uc] [app.util.color :as uc]
@ -45,7 +46,7 @@
:on-change handle-change-color :on-change handle-change-color
:on-close handle-close :on-close handle-close
:data color}] :data color}]
(handle-open) (handle-open color)
(modal/show! :colorpicker props)))) (modal/show! :colorpicker props))))
(defn opacity->string [opacity] (defn opacity->string [opacity]
@ -53,13 +54,16 @@
"" ""
(str (-> opacity (str (-> opacity
(d/coalesce 1) (d/coalesce 1)
(* 100))))) (* 100)
(fmt/format-number)))))
(defn remove-multiple [v] (defn remove-multiple [v]
(if (= v :multiple) nil v)) (if (= v :multiple) nil v))
(mf/defc color-row (mf/defc color-row
[{:keys [index color disable-gradient disable-opacity on-change on-reorder on-detach on-open on-close title on-remove disable-drag select-all on-blur]}] [{:keys [index color disable-gradient disable-opacity on-change
on-reorder on-detach on-open on-close title on-remove
disable-drag select-all on-blur select-only]}]
(let [current-file-id (mf/use-ctx ctx/current-file-id) (let [current-file-id (mf/use-ctx ctx/current-file-id)
file-colors (mf/deref refs/workspace-file-colors) file-colors (mf/deref refs/workspace-file-colors)
shared-libs (mf/deref refs/workspace-libraries) shared-libs (mf/deref refs/workspace-libraries)
@ -92,7 +96,11 @@
handle-pick-color (fn [color] handle-pick-color (fn [color]
(when on-change (on-change (merge uc/empty-color color)))) (when on-change (on-change (merge uc/empty-color color))))
handle-open (fn [] (when on-open (on-open))) handle-select (fn []
(select-only color))
handle-open (fn [color]
(when on-open (on-open (merge uc/empty-color color))))
handle-close (fn [value opacity id file-id] handle-close (fn [value opacity id file-id]
(when on-close (on-close value opacity id file-id))) (when on-close (on-close value opacity id file-id)))
@ -104,14 +112,12 @@
handle-opacity-change (fn [value] handle-opacity-change (fn [value]
(change-opacity (/ value 100))) (change-opacity (/ value 100)))
handle-click-color (mf/use-callback handle-click-color (color-picker-callback color
(mf/deps color) disable-gradient
(color-picker-callback color disable-opacity
disable-gradient handle-pick-color
disable-opacity handle-open
handle-pick-color handle-close)
handle-open
handle-close))
prev-color (h/use-previous color) prev-color (h/use-previous color)
@ -155,14 +161,23 @@
{:on-mouse-enter #(reset! hover-detach true) {:on-mouse-enter #(reset! hover-detach true)
:on-mouse-leave #(reset! hover-detach false) :on-mouse-leave #(reset! hover-detach false)
:on-click detach-value} :on-click detach-value}
(if @hover-detach i/unchain i/chain)])] (if @hover-detach i/unchain i/chain)])
(when select-only
[:div.element-set-actions-button {:on-click handle-select}
i/pointer-inner])]
;; Rendering a gradient ;; Rendering a gradient
(and (not (uc/multiple? color)) (and (not (uc/multiple? color))
(:gradient color) (:gradient color)
(get-in color [:gradient :type])) (get-in color [:gradient :type]))
[:div.color-info [:*
[:div.color-name (cb/gradient-type->string (get-in color [:gradient :type]))]] [:div.color-info
[:div.color-name (cb/gradient-type->string (get-in color [:gradient :type]))]]
(when select-only
[:div.element-set-actions-button {:on-click handle-select}
i/pointer-inner])]
;; Rendering a plain color/opacity ;; Rendering a plain color/opacity
:else :else
@ -186,7 +201,10 @@
:on-blur on-blur :on-blur on-blur
:on-change handle-opacity-change :on-change handle-opacity-change
:min 0 :min 0
:max 100}]])]) :max 100}]])
(when select-only
[:div.element-set-actions-button {:on-click handle-select}
i/pointer-inner])])
(when (some? on-remove) (when (some? on-remove)
[:div.element-set-actions-button.remove {:on-click on-remove} i/minus])])) [:div.element-set-actions-button.remove {:on-click on-remove} i/minus])]))

View File

@ -8,6 +8,7 @@
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]] [app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
[app.main.ui.workspace.sidebar.options.menus.color-selection :refer [color-selection-menu]]
[app.main.ui.workspace.sidebar.options.menus.component :refer [component-attrs component-menu]] [app.main.ui.workspace.sidebar.options.menus.component :refer [component-attrs component-menu]]
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraints-menu]] [app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraints-menu]]
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-menu]] [app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-menu]]
@ -49,15 +50,19 @@
(when-not (empty? fill-ids) (when-not (empty? fill-ids)
[:& fill-menu {:type type :ids fill-ids :values fill-values}]) [:& fill-menu {:type type :ids fill-ids :values fill-values}])
(when-not (empty? stroke-ids)
[:& stroke-menu {:type type :ids stroke-ids :values stroke-values}])
(when (> (count objects) 2)
[:& color-selection-menu {:type type
:shapes (vals objects)}])
(when-not (empty? shadow-ids) (when-not (empty? shadow-ids)
[:& shadow-menu {:type type :ids shadow-ids :values shadow-values}]) [:& shadow-menu {:type type :ids shadow-ids :values shadow-values}])
(when-not (empty? blur-ids) (when-not (empty? blur-ids)
[:& blur-menu {:type type :ids blur-ids :values blur-values}]) [:& blur-menu {:type type :ids blur-ids :values blur-values}])
(when-not (empty? stroke-ids)
[:& stroke-menu {:type type :ids stroke-ids :values stroke-values}])
(when-not (empty? text-ids) (when-not (empty? text-ids)
[:& ot/text-menu {:type type :ids text-ids :values text-values}]) [:& ot/text-menu {:type type :ids text-ids :values text-values}])

View File

@ -13,6 +13,7 @@
[app.common.text :as txt] [app.common.text :as txt]
[app.main.ui.hooks :as hooks] [app.main.ui.hooks :as hooks]
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-attrs blur-menu]] [app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-attrs blur-menu]]
[app.main.ui.workspace.sidebar.options.menus.color-selection :refer [color-selection-menu]]
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]] [app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]]
[app.main.ui.workspace.sidebar.options.menus.exports :refer [exports-attrs exports-menu]] [app.main.ui.workspace.sidebar.options.menus.exports :refer [exports-attrs exports-menu]]
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]] [app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]]
@ -280,6 +281,9 @@
(when-not (empty? stroke-ids) (when-not (empty? stroke-ids)
[:& stroke-menu {:type type :ids stroke-ids :show-caps show-caps :values stroke-values}]) [:& stroke-menu {:type type :ids stroke-ids :show-caps show-caps :values stroke-values}])
(when-not (empty? shapes)
[:& color-selection-menu {:type type :shapes (vals objects-no-measures)}])
(when-not (empty? shadow-ids) (when-not (empty? shadow-ids)
[:& shadow-menu {:type type :ids shadow-ids :values shadow-values}]) [:& shadow-menu {:type type :ids shadow-ids :values shadow-values}])

View File

@ -10,6 +10,7 @@
[app.main.data.workspace.texts :as dwt] [app.main.data.workspace.texts :as dwt]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]] [app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
[app.main.ui.workspace.sidebar.options.menus.color-selection :refer [color-selection-menu]]
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]] [app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]]
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-menu fill-attrs]] [app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-menu fill-attrs]]
[app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]] [app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]]
@ -58,7 +59,6 @@
:attrs text-attrs}))] :attrs text-attrs}))]
[:* [:*
[:& measures-menu [:& measures-menu
{:ids ids {:ids ids
:type type :type type
@ -82,6 +82,9 @@
:type type :type type
:values stroke-values}] :values stroke-values}]
(when (= :multiple (:fills fill-values))
[:& color-selection-menu {:type type :shapes [shape]}])
[:& shadow-menu [:& shadow-menu
{:ids ids {:ids ids
:values (select-keys shape [:shadow])}] :values (select-keys shape [:shadow])}]

View File

@ -3011,6 +3011,18 @@ msgstr "Selection fill"
msgid "workspace.options.selection-stroke" msgid "workspace.options.selection-stroke"
msgstr "Selection stroke" msgstr "Selection stroke"
#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs
msgid "workspace.options.selection-color"
msgstr "Selected colors"
#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs
msgid "workspace.options.more-colors"
msgstr "More colors"
#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs
msgid "workspace.options.more-lib-colors"
msgstr "More library colors"
#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs #: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs
msgid "workspace.options.shadow-options.blur" msgid "workspace.options.shadow-options.blur"
msgstr "Blur" msgstr "Blur"

View File

@ -3024,6 +3024,18 @@ msgstr "Relleno de selección"
msgid "workspace.options.selection-stroke" msgid "workspace.options.selection-stroke"
msgstr "Borde de selección" msgstr "Borde de selección"
#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs
msgid "workspace.options.selection-color"
msgstr "Colores seleccionados"
#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs
msgid "workspace.options.more-colors"
msgstr "Más colores"
#: src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs
msgid "workspace.options.more-lib-colors"
msgstr "Más colores de la biblioteca"
#: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs #: src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs
msgid "workspace.options.shadow-options.blur" msgid "workspace.options.shadow-options.blur"
msgstr "Desenfoque" msgstr "Desenfoque"