0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-04-01 09:31:26 -05:00

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
### :sparkles: New features
- Added selected colors widget in right sidebar [Taiga #2485](https://tree.taiga.io/project/penpot/us/2485)
### :bug: Bugs fixed
### :arrow_up: Deps updates
### :heart: Community contributions by (Thank you!)

View file

@ -1534,3 +1534,44 @@
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]
(-> state
(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)]]
#(doseq [key keys]
(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]))

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.numeric-input :refer [numeric-input]]
[app.main.ui.context :as ctx]
[app.main.ui.formats :as fmt]
[app.main.ui.hooks :as h]
[app.main.ui.icons :as i]
[app.util.color :as uc]
@ -45,7 +46,7 @@
:on-change handle-change-color
:on-close handle-close
:data color}]
(handle-open)
(handle-open color)
(modal/show! :colorpicker props))))
(defn opacity->string [opacity]
@ -53,13 +54,16 @@
""
(str (-> opacity
(d/coalesce 1)
(* 100)))))
(* 100)
(fmt/format-number)))))
(defn remove-multiple [v]
(if (= v :multiple) nil v))
(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)
file-colors (mf/deref refs/workspace-file-colors)
shared-libs (mf/deref refs/workspace-libraries)
@ -92,7 +96,11 @@
handle-pick-color (fn [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]
(when on-close (on-close value opacity id file-id)))
@ -104,14 +112,12 @@
handle-opacity-change (fn [value]
(change-opacity (/ value 100)))
handle-click-color (mf/use-callback
(mf/deps color)
(color-picker-callback color
disable-gradient
disable-opacity
handle-pick-color
handle-open
handle-close))
handle-click-color (color-picker-callback color
disable-gradient
disable-opacity
handle-pick-color
handle-open
handle-close)
prev-color (h/use-previous color)
@ -155,14 +161,23 @@
{:on-mouse-enter #(reset! hover-detach true)
:on-mouse-leave #(reset! hover-detach false)
: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
(and (not (uc/multiple? color))
(:gradient color)
(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
:else
@ -186,7 +201,10 @@
:on-blur on-blur
:on-change handle-opacity-change
: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)
[:div.element-set-actions-button.remove {:on-click on-remove} i/minus])]))

View file

@ -8,6 +8,7 @@
(:require
[app.common.data :as d]
[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.constraints :refer [constraints-menu]]
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-menu]]
@ -49,15 +50,19 @@
(when-not (empty? fill-ids)
[:& 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)
[:& shadow-menu {:type type :ids shadow-ids :values shadow-values}])
(when-not (empty? blur-ids)
[:& 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)
[:& ot/text-menu {:type type :ids text-ids :values text-values}])

View file

@ -13,6 +13,7 @@
[app.common.text :as txt]
[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.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.exports :refer [exports-attrs exports-menu]]
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]]
@ -280,6 +281,9 @@
(when-not (empty? stroke-ids)
[:& 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)
[:& 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.refs :as refs]
[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.fill :refer [fill-menu fill-attrs]]
[app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]]
@ -58,7 +59,6 @@
:attrs text-attrs}))]
[:*
[:& measures-menu
{:ids ids
:type type
@ -82,6 +82,9 @@
:type type
:values stroke-values}]
(when (= :multiple (:fills fill-values))
[:& color-selection-menu {:type type :shapes [shape]}])
[:& shadow-menu
{:ids ids
:values (select-keys shape [:shadow])}]

View file

@ -3011,6 +3011,18 @@ msgstr "Selection fill"
msgid "workspace.options.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
msgid "workspace.options.shadow-options.blur"
msgstr "Blur"

View file

@ -3024,6 +3024,18 @@ msgstr "Relleno de selección"
msgid "workspace.options.selection-stroke"
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
msgid "workspace.options.shadow-options.blur"
msgstr "Desenfoque"