0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-13 10:38:13 -05:00

Changes to the colorpicker to support gradients

This commit is contained in:
alonso.torres 2020-10-08 16:08:00 +02:00
parent a7335533bb
commit 381aef77ee
10 changed files with 368 additions and 174 deletions

View file

@ -72,10 +72,16 @@
margin-top: 0.5rem;
margin-bottom: 1rem;
.gradient-background {
.gradient-background-wrapper {
height: 100%;
width: 100%;
border: 1px solid $color-gray-10;
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAADFJREFUOE9jZGBgEAFifOANPknGUQMYhkkYEEgG+NMJKAwIAbwJbdQABnBCIgRoG4gAIF8IsXB/Rs4AAAAASUVORK5CYII=") left center;
}
.gradient-background {
height: 100%;
width: 100%;
}
.gradient-stop-wrapper {
@ -85,16 +91,21 @@
}
.gradient-stop {
display: grid;
grid-template-columns: 50% 50%;
position: absolute;
width: 14px;
height: 14px;
width: 15px;
height: 15px;
border-radius: 2px;
border: 1px solid $color-gray-20;
margin-top: -2px;
margin-left: -7px;
box-shadow: 0 2px 2px rgb(0 0 0 / 15%);
.selected {
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAADFJREFUOE9jZGBgEAFifOANPknGUQMYhkkYEEgG+NMJKAwIAbwJbdQABnBCIgRoG4gAIF8IsXB/Rs4AAAAASUVORK5CYII=") left center;
background-color: $color-white;
&.active {
border-color: $color-primary;
}
}

View file

@ -104,6 +104,34 @@
(assoc-in [:workspace-local :picked-color-select] value)
(assoc-in [:workspace-local :picked-shift?] shift?)))))
(defn change-fill2
([ids color]
(ptk/reify ::change-fill
ptk/WatchEvent
(watch [_ state s]
(let [pid (:current-page-id state)
objects (get-in state [:workspace-data :pages-index pid :objects])
children (mapcat #(cph/get-children % objects) ids)
ids (into ids children)
is-text? #(= :text (:type (get objects %)))
text-ids (filter is-text? ids)
shape-ids (filter (comp not is-text?) ids)
attrs (cond-> {:fill-color (:color color)
:fill-color-ref-id (:id color)
:fill-color-ref-file (:file-id color)
:fill-color-gradient (:gradient color)
:fill-opacity (:opacity color)})
update-fn (fn [shape] (merge shape attrs))
editors (get-in state [:workspace-local :editors])
reduce-fn (fn [state id]
(update-in state [:workspace-data :pages-index pid :objects id] update-fn))]
(rx/from (conj
(map #(dwt/update-text-attrs {:id % :editor (get editors %) :attrs attrs}) text-ids)
(dwc/update-shapes shape-ids update-fn))))))))
(defn change-fill
([ids color id file-id]

View file

@ -43,11 +43,14 @@
(st/emit! (dm/hide)))))
(defn- on-click-outside
[event wrapper-ref allow-click-outside]
[event wrapper-ref type allow-click-outside]
(let [wrapper (mf/ref-val wrapper-ref)
current (dom/get-target event)]
(when (and wrapper (not allow-click-outside) (not (.contains wrapper current)))
(when (and wrapper
(not allow-click-outside)
(not (.contains wrapper current))
(not (= type (keyword (.getAttribute current "data-allow-click-modal")))))
(dom/stop-propagation event)
(dom/prevent-default event)
(st/emit! (dm/hide)))))
@ -61,7 +64,7 @@
handle-click-outside
(fn [event]
(on-click-outside event wrapper-ref (:allow-click-outside data)))]
(on-click-outside event wrapper-ref (:type data) (:allow-click-outside data)))]
(mf/use-layout-effect
(fn []

View file

@ -83,7 +83,7 @@
(math/abs (- unit-value 1.0))
unit-value)
value (+ min-value (* unit-value (- max-value min-value)))]
(on-change value))))]
(on-change (math/precision value 2)))))]
[:div.slider-selector
{:class (str (if vertical? "vertical " "") class)
@ -449,8 +449,7 @@
[:label.blue-label {:for "value-value"} "V"]])
[:label.alpha-label {:for "alpha-value"} "A"]]))
(defn as-color-components [value opacity]
(defn color->components [value opacity]
(let [value (if (uc/hex? value) value "#000000")
[r g b] (uc/hex->rgb value)
[h s v] (uc/hex->hsv value)]
@ -460,13 +459,61 @@
:r r :g g :b b
:h h :s s :v v}))
(mf/defc colorpicker
[{:keys [value opacity on-change on-accept]}]
(let [current-color (mf/use-state (as-color-components value opacity))
(defn data->state [{:keys [color opacity gradient]}]
(let [type (cond
(nil? gradient) :color
(= :linear (:type gradient)) :linear-gradient
(= :radial (:type gradient)) :radial-gradient)
parse-stop (fn [{:keys [offset color opacity]}]
(vector offset (color->components color opacity)))
stops (when gradient
(map parse-stop (:stops gradient)))
current-color (if (nil? gradient)
(color->components color opacity)
(-> stops first second))
gradient-data (select-keys gradient [:start-x :start-y
:end-x :end-y
:width])]
(cond-> {:type type
:current-color current-color}
gradient (assoc :gradient-data gradient-data)
stops (assoc :stops (into {} stops))
stops (assoc :editing-stop (-> stops first first)))))
(defn state->data [{:keys [type current-color stops gradient-data]}]
(if (= type :color)
{:color (:hex current-color)
:opacity (:alpha current-color)}
(let [gradient-type (case type
:linear-gradient :linear
:radial-gradient :radial)
parse-stop (fn [[offset {:keys [hex alpha]}]]
(hash-map :offset offset
:color hex
:opacity alpha))]
{:gradient (-> {:type gradient-type
:stops (mapv parse-stop stops)}
(merge gradient-data))})))
(defn create-gradient-data [type]
{:start-x 0.5
:start-y (if (= type :linear-gradient) 0.0 0.5)
:end-x 0.5
:end-y 1
:width 1.0})
(mf/defc colorpicker
[{:keys [data on-change on-accept]}]
(let [state (mf/use-state (data->state data))
active-tab (mf/use-state :ramp #_:harmony #_:hsva)
selected-library (mf/use-state "recent")
current-library-colors (mf/use-state [])
ref-picker (mf/use-ref)
file-colors (mf/deref refs/workspace-file-colors)
@ -480,38 +527,72 @@
locale (mf/deref i18n/locale)
value-ref (mf/use-var value)
;; data-ref (mf/use-var data)
on-change (or on-change identity)
current-color (:current-color @state)
parse-selected (fn [selected]
(if (#{"recent" "file"} selected)
(keyword selected)
(uuid selected)) )
parse-selected
(fn [selected]
(if (#{"recent" "file"} selected)
(keyword selected)
(uuid selected)) )
change-tab (fn [tab] #(reset! active-tab tab))
change-tab
(fn [tab]
#(reset! active-tab tab))
handle-change-color (fn [changes]
(swap! current-color merge changes)
(when (:hex changes)
(reset! value-ref (:hex changes)))
(on-change (:hex changes (:hex @current-color))
(:alpha changes (:alpha @current-color))))]
handle-change-color
(fn [changes]
(let [editing-stop (:editing-stop @state)]
(swap! state update :current-color merge changes)
(swap! state update-in [:stops editing-stop] merge changes)
#_(when (:hex changes)
(reset! value-ref (:hex changes)))
;; TODO: CHANGE TO SUPPORT GRADIENTS
#_(on-change (:hex changes (:hex current-color))
(:alpha changes (:alpha current-color)))))
handle-change-stop
(fn [offset]
(let [offset-color (get-in @state [:stops offset])]
(swap! state assoc :current-color offset-color)
(swap! state assoc :editing-stop offset)))
on-activate-gradient
(fn [type]
(fn []
(if (= type (:type @state))
(do
(swap! state assoc :type :color)
(swap! state dissoc :editing-stop :stops :gradient-data))
(do
(swap! state assoc :type type)
(when (not (:stops @state))
(swap! state assoc
:editing-stop 0
:gradient-data (create-gradient-data type)
:stops {0 (:current-color @state)
1 (-> (:current-color @state)
(assoc :alpha 0))}))))))]
;; Update state when there is a change in the props upstream
(mf/use-effect
;; TODO: Review for gradients
#_(mf/use-effect
(mf/deps value opacity)
(fn []
(reset! current-color (as-color-components value opacity))))
(swap! state assoc current-color (as-color-components value opacity))))
;; Updates the CSS color variable when there is a change in the color
(mf/use-effect
(mf/deps @current-color)
(mf/deps current-color)
(fn [] (let [node (mf/ref-val ref-picker)
rgb [(:r @current-color) (:g @current-color) (:b @current-color)]
hue-rgb (uc/hsv->rgb [(:h @current-color) 1.0 255])
hsl-from (uc/hsv->hsl [(:h @current-color) 0 (:v @current-color)])
hsl-to (uc/hsv->hsl [(:h @current-color) 1 (:v @current-color)])
{:keys [r g b h s v]} current-color
rgb [r g b]
hue-rgb (uc/hsv->rgb [h 1.0 255])
hsl-from (uc/hsv->hsl [h 0.0 v])
hsl-to (uc/hsv->hsl [h 1.0 v])
format-hsl (fn [[h s l]]
(str/fmt "hsl(%s, %s, %s)"
@ -548,11 +629,10 @@
(reset! current-library-colors (into [] colors))))))
;; When closing the modal we update the recent-color list
(mf/use-effect
#_(mf/use-effect
(fn [] (fn []
(st/emit! (dwc/stop-picker))
(when @value-ref
(st/emit! (dwl/add-recent-color @value-ref))))))
(st/emit! (dwl/add-recent-color (state->data @state))))))
(mf/use-effect
(mf/deps picking-color? picked-color)
@ -560,18 +640,27 @@
(let [[r g b] (or picked-color [0 0 0])
hex (uc/rgb->hex [r g b])
[h s v] (uc/hex->hsv hex)]
(swap! current-color assoc
(swap! update :current-color assoc
:r r :g g :b b
:h h :s s :v v
:hex hex)
(reset! value-ref hex)
(when picked-color-select
(on-change hex (:alpha @current-color) nil nil picked-shift?))))))
(mf/use-effect
;; TODO: UPDATE TO USE GRADIENTS
#_(reset! value-ref hex)
#_(when picked-color-select
(on-change hex (:alpha current-color) nil nil picked-shift?))))))
;; TODO: UPDATE TO USE GRADIENTS
#_(mf/use-effect
(mf/deps picking-color? picked-color-select)
(fn [] (when (and picking-color? picked-color-select)
(on-change (:hex @current-color) (:alpha @current-color) nil nil picked-shift?))))
(on-change (:hex current-color) (:alpha current-color) nil nil picked-shift?))))
(mf/use-effect
(mf/deps @state)
(fn []
(on-change (state->data @state))))
[:div.colorpicker {:ref ref-picker}
[:div.colorpicker-content
@ -584,27 +673,49 @@
i/picker]
[:div.gradients-buttons
[:button.gradient.linear-gradient #_{:class "active"}]
[:button.gradient.radial-gradient]]]
[:button.gradient.linear-gradient
{:on-click (on-activate-gradient :linear-gradient)
:class (when (= :linear-gradient (:type @state)) "active")}]
#_[:div.gradient-stops
[:div.gradient-background {:style {:background "linear-gradient(90deg, #EC0BE5, #CDCDCD)" }}]
[:div.gradient-stop-wrapper
[:div.gradient-stop.start {:style {:background-color "#EC0BE5"}}]
[:div.gradient-stop.end {:style {:background-color "#CDCDCD"
:left "100%"}}]]]
[:button.gradient.radial-gradient
{:on-click (on-activate-gradient :radial-gradient)
:class (when (= :radial-gradient (:type @state)) "active")}]]]
(when (#{:linear-gradient :radial-gradient} (:type @state))
[:div.gradient-stops
(let [format-stop (fn [[offset {:keys [r g b alpha]}]]
(str/fmt "rgba(%s, %s, %s, %s) %s"
r g b alpha
(str (* offset 100) "%")))
gradient-data (str/join "," (map format-stop (:stops @state)))
]
[:div.gradient-background-wrapper
[:div.gradient-background {:style {:background (str/fmt "linear-gradient(90deg, %s)" gradient-data) }}]])
[:div.gradient-stop-wrapper
(for [[offset value] (:stops @state)]
[:div.gradient-stop {:class (when (= (:editing-stop @state) offset) "active")
:on-click (partial handle-change-stop offset)
:style {:left (str (* offset 100) "%")}}
(let [{:keys [hex r g b alpha]} value]
[:*
[:div.gradient-stop-color {:style {:background-color hex}}]
[:div.gradient-stop-alpha {:style {:background-color (str/format "rgba(%s, %s, %s, %s)" r g b alpha)}}]])
])]])
(if picking-color?
[:div.picker-detail-wrapper
[:div.center-circle]
[:canvas#picker-detail {:width 200 :height 160}]]
(case @active-tab
:ramp [:& ramp-selector {:color @current-color :on-change handle-change-color}]
:harmony [:& harmony-selector {:color @current-color :on-change handle-change-color}]
:hsva [:& hsva-selector {:color @current-color :on-change handle-change-color}]
:ramp [:& ramp-selector {:color current-color :on-change handle-change-color}]
:harmony [:& harmony-selector {:color current-color :on-change handle-change-color}]
:hsva [:& hsva-selector {:color current-color :on-change handle-change-color}]
nil))
[:& color-inputs {:type (if (= @active-tab :hsva) :hsv :rgb) :color @current-color :on-change handle-change-color}]
[:& color-inputs {:type (if (= @active-tab :hsva) :hsv :rgb) :color current-color :on-change handle-change-color}]
[:div.libraries
[:select {:on-change (fn [e]
@ -620,7 +731,7 @@
[:div.selected-colors
(when (= "file" @selected-library)
[:div.color-bullet.button.plus-button {:style {:background-color "white"}
:on-click #(st/emit! (dwl/add-color (:hex @current-color)))}
:on-click #(st/emit! (dwl/add-color (:hex current-color)))}
i/plus])
[:div.color-bullet.button {:style {:background-color "white"}
@ -630,14 +741,15 @@
(for [[idx {:keys [id file-id value]}] (map-indexed vector @current-library-colors)]
[:div.color-bullet {:key (str "color-" idx)
:on-click (fn []
(swap! current-color assoc :hex value)
(reset! value-ref value)
(swap! update :current-color assoc :hex value)
#_(reset! value-ref value)
(let [[r g b] (uc/hex->rgb value)
[h s v] (uc/hex->hsv value)]
(swap! current-color assoc
(swap! update current-color assoc
:r r :g g :b b
:h h :s s :v v)
(on-change value (:alpha @current-color) id file-id)))
;; TODO: CHANGE TO SUPPORT GRADIENTS
#_(on-change value (:alpha current-color) id file-id)))
:style {:background-color value}}])]]]
[:div.colorpicker-tabs
[:div.colorpicker-tab {:class (when (= @active-tab :ramp) "active")
@ -650,7 +762,8 @@
[:div.actions
[:button.btn-primary.btn-large
{:on-click (fn []
(on-accept @value-ref)
;; TODO: REVIEW FOR GRADIENTS
#_(on-accept @value-ref)
(modal/hide!))}
(t locale "workspace.libraries.colors.save-color")]])])
)
@ -673,31 +786,35 @@
(mf/defc colorpicker-modal
{::mf/register modal/components
::mf/register-as :colorpicker}
[{:keys [x y default value opacity page on-change on-close disable-opacity position on-accept] :as props}]
[{:keys [x y default data page position on-change on-close on-accept] :as props}]
(let [vport (mf/deref viewport)
dirty? (mf/use-var false)
last-change (mf/use-var nil)
position (or position :left)
style (calculate-position vport position x y)
handle-change (fn [new-value new-opacity id file-id shift-clicked?]
(when (or (not= new-value value) (not= new-opacity opacity))
(reset! dirty? true))
(reset! last-change [new-value new-opacity id file-id])
handle-change (fn [new-data shift-clicked?]
(reset! dirty? (not= data new-data))
(reset! last-change new-data)
(when on-change
(on-change new-value new-opacity id file-id shift-clicked?)))]
(on-change new-data)))
;; handle-change (fn [new-value new-opacity id file-id shift-clicked?]
;; (when (or (not= new-value value) (not= new-opacity opacity))
;; (reset! dirty? true))
;; (reset! last-change [new-value new-opacity id file-id])
;; (when on-change
;; (on-change new-value new-opacity id file-id shift-clicked?)))
]
(mf/use-effect
(fn []
#(when (and @dirty? on-close)
(when-let [[value opacity id file-id] @last-change]
(on-close value opacity id file-id)))))
#(when (and @dirty? @last-change on-close)
(on-close @last-change))))
[:div.colorpicker-tooltip
{:style (clj->js style)}
[:& colorpicker {:value (or value default)
:opacity (or opacity 1)
[:& colorpicker {:data data
:on-change handle-change
:on-accept on-accept
:disable-opacity disable-opacity}]]))
:on-accept on-accept}]]))

View file

@ -13,13 +13,14 @@
[rumext.alpha :as mf]
[cuerdas.core :as str]
[beicon.core :as rx]
[app.main.data.workspace.common :as dwc]
[app.main.store :as st]
[app.main.streams :as ms]
[app.common.math :as mth]
[app.util.dom :as dom]
[app.common.geom.point :as gpt]
[app.common.geom.matrix :as gmt]))
[app.common.geom.matrix :as gmt]
[app.util.dom :as dom]
[app.main.store :as st]
[app.main.refs :as refs]
[app.main.streams :as ms]
[app.main.data.workspace.common :as dwc]))
(def gradient-line-stroke-width 2)
(def gradient-line-stroke-color "white")
@ -114,7 +115,8 @@
:on-mouse-down (partial on-mouse-down :to-p)
:on-mouse-up (partial on-mouse-up :to-p)}]
[:rect {:x (- (:x point) (/ gradient-square-width 2 zoom))
[:rect {:data-allow-click-modal "colorpicker"
:x (- (:x point) (/ gradient-square-width 2 zoom))
:y (- (:y point) (/ gradient-square-width 2 zoom))
:rx (/ gradient-square-radius zoom)
:width (/ gradient-square-width zoom)
@ -220,73 +222,68 @@
:on-mouse-up (partial on-mouse-up :to-p)}]]))
(mf/defc gradient-handlers
[{:keys [shape zoom]}]
(let [{:keys [x y width height] :as sr} (:selrect shape)
state (mf/use-state (:fill-color-gradient shape default-gradient))
[{:keys [id zoom]}]
(let [shape (mf/deref (refs/object-by-id id))
{:keys [x y width height] :as sr} (:selrect shape)
gradient (:fill-color-gradient shape)
[{start-color :color start-opacity :opacity}
{end-color :color end-opacity :opacity}] (:stops @state)
{end-color :color end-opacity :opacity}] (:stops gradient)
from-p (gpt/point (+ x (* width (:start-x @state)))
(+ y (* height (:start-y @state))))
from-p (gpt/point (+ x (* width (:start-x gradient)))
(+ y (* height (:start-y gradient))))
to-p (gpt/point (+ x (* width (:end-x @state)))
(+ y (* height (:end-y @state))))
to-p (gpt/point (+ x (* width (:end-x gradient)))
(+ y (* height (:end-y gradient))))
gradient-vec (gpt/to-vec from-p to-p)
gradient-length (gpt/length gradient-vec)
width-v (-> gradient-vec
(gpt/normal-left)
(gpt/multiply (gpt/point (* (:width @state) (/ gradient-length (/ height 2) ))))
(gpt/multiply (gpt/point (* (:width gradient) (/ gradient-length (/ height 2) ))))
(gpt/multiply (gpt/point (/ width 2))))
width-p (gpt/add from-p width-v)
change! (fn [change]
(st/emit! (dwc/update-shapes
[(:id shape)]
#(update % :fill-color-gradient merge change))))
on-change-start (fn [point]
(let [start-x (/ (- (:x point) x) width)
start-y (/ (- (:y point) y) height)]
(swap! state assoc
:start-x start-x
:start-y start-y )))
start-y (/ (- (:y point) y) height)
start-x (mth/precision start-x 2)
start-y (mth/precision start-y 2)]
(change! {:start-x start-x :start-y start-y})))
on-change-finish (fn [point]
(let [end-x (/ (- (:x point) x) width)
end-y (/ (- (:y point) y) height)]
(swap! state assoc
:end-x end-x
:end-y end-y)))
end-y (/ (- (:y point) y) height)
end-x (mth/precision end-x 2)
end-y (mth/precision end-y 2)]
(change! {:end-x end-x :end-y end-y})))
on-change-width (fn [point]
(let [scale-factor-y (/ gradient-length (/ height 2))
norm-dist (/ (gpt/distance point from-p)
(* (/ width 2) scale-factor-y))]
(swap! state assoc :width norm-dist)))
(change! {:width norm-dist})))
on-change-stop-color (fn [offset color opacity] (println "change-color"))]
(mf/use-effect
(mf/deps shape)
(fn []
(reset! state (:fill-color-gradient shape default-gradient))))
(mf/use-effect
(mf/deps @state)
(fn []
(when (not= (:fill-color-gradient shape) @state)
(st/emit! (dwc/update-shapes
[(:id shape)]
#(assoc % :fill-color-gradient @state))))))
[:& gradient-handler-transformed
{:from-p from-p
:to-p to-p
:width-p (when (= :radial (:type @state)) width-p)
:from-color {:value start-color :opacity start-opacity}
:to-color {:value end-color :opacity end-opacity}
:zoom zoom
:on-change-start on-change-start
:on-change-finish on-change-finish
:on-change-width on-change-width
:on-change-stop-color on-change-stop-color}]))
(when gradient
[:& gradient-handler-transformed
{:from-p from-p
:to-p to-p
:width-p (when (= :radial (:type gradient)) width-p)
:from-color {:value start-color :opacity start-opacity}
:to-color {:value end-color :opacity end-opacity}
:zoom zoom
:on-change-start on-change-start
:on-change-finish on-change-finish
:on-change-width on-change-width
:on-change-stop-color on-change-stop-color}])))

View file

@ -28,8 +28,7 @@
[app.common.geom.point :as gpt]
[app.common.geom.matrix :as gmt]
[app.util.debug :refer [debug?]]
[app.main.ui.workspace.shapes.outline :refer [outline]]
[app.main.ui.workspace.gradients :refer [gradient-handlers]]))
[app.main.ui.workspace.shapes.outline :refer [outline]]))
(def rotation-handler-size 25)
(def resize-point-radius 4)
@ -210,11 +209,7 @@
(case type
:rotation (when (not= :frame (:type shape)) [:> rotation-handler props])
:resize-point [:> resize-point-handler props]
:resize-side [:> resize-side-handler props])))
#_(when (= :rect (:type shape))
[:& gradient-handlers {:shape tr-shape
:zoom zoom}])])))
:resize-side [:> resize-side-handler props])))])))
;; --- Selection Handlers (Component)
(mf/defc path-edition-selection-handlers

View file

@ -21,7 +21,7 @@
[app.util.i18n :as i18n :refer [tr t]]
[app.util.object :as obj]))
(def fill-attrs [:fill-color :fill-opacity :fill-color-ref-id :fill-color-ref-file])
(def fill-attrs [:fill-color :fill-opacity :fill-color-ref-id :fill-color-ref-file :fill-color-gradient])
(defn- fill-menu-props-equals?
[np op]
@ -36,24 +36,28 @@
(= (:fill-color new-values)
(:fill-color old-values))
(= (:fill-opacity new-values)
(:fill-opacity old-values)))))
(:fill-opacity old-values))
(= (:fill-color-gradient new-values)
(:fill-color-gradient old-values)))))
(mf/defc fill-menu
{::mf/wrap [#(mf/memo' % fill-menu-props-equals?)]}
[{:keys [ids type values editor] :as props}]
(let [locale (mf/deref i18n/locale)
show? (not (nil? (:fill-color values)))
show? (or (not (nil? (:fill-color values)))
(not (nil? (:fill-color-gradient values))))
label (case type
:multiple (t locale "workspace.options.selection-fill")
:group (t locale "workspace.options.group-fill")
(t locale "workspace.options.fill"))
color {:value (:fill-color values)
color {:color (:fill-color values)
:opacity (:fill-opacity values)
:id (:fill-color-ref-id values)
:file-id (:fill-color-ref-file values)}
:file-id (:fill-color-ref-file values)
:gradient (:fill-color-gradient values)}
on-add
(mf/use-callback
@ -70,8 +74,8 @@
on-change
(mf/use-callback
(mf/deps ids)
(fn [value opacity id file-id]
(st/emit! (dc/change-fill ids value opacity id file-id))))
(fn [color]
(st/emit! (dc/change-fill2 ids color))))
on-open-picker
(mf/use-callback

View file

@ -10,16 +10,18 @@
(ns app.main.ui.workspace.sidebar.options.rows.color-row
(:require
[rumext.alpha :as mf]
[cuerdas.core :as str]
[app.common.math :as math]
[app.util.dom :as dom]
[app.util.data :refer [classnames]]
[app.util.i18n :as i18n :refer [tr]]
[app.main.data.modal :as modal]
[app.common.data :as d]
[app.main.refs :as refs]))
[app.main.refs :as refs]
[app.util.color :as uc]))
(defn color-picker-callback
[color handle-change-color handle-open handle-close disable-opacity]
[color handle-change-color handle-open handle-close]
(fn [event]
(let [x (.-clientX event)
y (.-clientY event)
@ -27,14 +29,22 @@
:y y
:on-change handle-change-color
:on-close handle-close
:value (:value color)
:opacity (:opacity color)
:disable-opacity disable-opacity}]
:data color}]
(handle-open)
(modal/show! :colorpicker props))))
(defn value-to-background [value]
(if (= value :multiple) "transparent" value))
;; TODO: REMOVE `VALUE` WHEN COLOR IS INTEGRATED
(defn as-background [{:keys [color opacity gradient value] :as tt}]
(cond
(and gradient (not= :multiple gradient))
(uc/gradient->css gradient)
(not= color :multiple)
(let [[r g b] (uc/hex->rgb (or color value))]
(str/fmt "rgba(%s, %s, %s, %s)" r g b opacity))
:else "transparent"))
(defn remove-hash [value]
(if (or (nil? value) (= value :multiple)) "" (subs value 1)))
@ -59,38 +69,39 @@
(if (= v :multiple) nil v))
(mf/defc color-row
[{:keys [color on-change on-open on-close disable-opacity]}]
(let [;;
file-colors (mf/deref refs/workspace-file-colors)
[{:keys [color on-change on-open on-close]}]
(let [file-colors (mf/deref refs/workspace-file-colors)
shared-libs (mf/deref refs/workspace-libraries)
get-color-name (fn [{:keys [id file-id]}]
(let [src-colors (if file-id (get-in shared-libs [file-id :data :colors]) file-colors)]
(get-in src-colors [id :name])))
default-color {:value "#000000" :opacity 1}
parse-color (fn [color]
(-> (merge default-color color)
(update :value #(or % "#000000"))
(update :opacity #(or % 1))))
(-> color
(update :color #(or % (:value color)))))
state (mf/use-state (parse-color color))
value (:value @state)
value (:color @state)
opacity (:opacity @state)
change-value (fn [new-value]
(swap! state assoc :value new-value)
(swap! state assoc :color new-value)
(when on-change (on-change new-value (remove-multiple opacity))))
change-opacity (fn [new-opacity]
(swap! state assoc :opacity new-opacity)
(when on-change (on-change (remove-multiple value) new-opacity)))
handle-pick-color (fn [new-value new-opacity id file-id]
(reset! state {:value new-value :opacity new-opacity})
(when on-change (on-change new-value new-opacity id file-id)))
;;handle-pick-color (fn [new-value new-opacity id file-id]
;; (reset! state {:color new-value :opacity new-opacity})
;; (when on-change (on-change new-value new-opacity id file-id)))
handle-pick-color (fn [color]
(reset! state color)
(when on-change
(on-change color)))
handle-open (fn [] (when on-open (on-open)))
@ -123,28 +134,38 @@
[:div.row-flex.color-data
[:span.color-th
{:class (when (and (:id color) (not= (:id color) :multiple)) "color-name")
:style {:background-color (-> value value-to-background)}
:on-click (color-picker-callback @state handle-pick-color handle-open handle-close disable-opacity)}
:style {:background (as-background color)}
:on-click (color-picker-callback @state handle-pick-color handle-open handle-close)}
(when (= value :multiple) "?")]
(if (:id color)
(cond
;; Rendering a color with ID
(:id color)
[:div.color-info
[:div.color-name (str (get-color-name color))]]
;; Rendering a gradient
(:gradient color)
[:div.color-info
[:input {:value (-> value remove-hash)
:pattern "^[0-9a-fA-F]{0,6}$"
:placeholder (tr "settings.multiple")
:on-click select-all
:on-change handle-value-change}]])
[:div.color-name (str (get-in color [:gradient :type]))]]
(when (not disable-opacity)
[:div.input-element
{:class (classnames :percentail (not= opacity :multiple))}
[:input.input-text {:type "number"
:value (-> opacity opacity->string)
:placeholder (tr "settings.multiple")
:on-click select-all
:on-change handle-opacity-change
:min "0"
:max "100"}]])]))
;; Rendering a plain color/opacity
:else
[:*
[:div.color-info
[:input {:value (-> value remove-hash)
:pattern "^[0-9a-fA-F]{0,6}$"
:placeholder (tr "settings.multiple")
:on-click select-all
:on-change handle-value-change}]]
[:div.input-element
{:class (classnames :percentail (not= opacity :multiple))}
[:input.input-text {:type "number"
:value (-> opacity opacity->string)
:placeholder (tr "settings.multiple")
:on-click select-all
:on-change handle-opacity-change
:min "0"
:max "100"}]]])]))

View file

@ -40,6 +40,7 @@
[app.main.ui.workspace.snap-distances :refer [snap-distances]]
[app.main.ui.workspace.frame-grid :refer [frame-grid]]
[app.main.ui.workspace.shapes.outline :refer [outline]]
[app.main.ui.workspace.gradients :refer [gradient-handlers]]
[app.common.math :as mth]
[app.util.dom :as dom]
[app.util.dom.dnd :as dnd]
@ -642,6 +643,10 @@
:zoom zoom
:edition edition}])
(when (= (count selected) 1)
[:& gradient-handlers {:id (first selected)
:zoom zoom}])
(when drawing-obj
[:& draw-area {:shape drawing-obj
:zoom zoom

View file

@ -77,3 +77,16 @@
(defn hsv->hsl
[hsv]
(hex->hsl (hsv->hex hsv)))
(defn gradient->css [{:keys [type stops]}]
(let [parse-stop
(fn [{:keys [offset color opacity]}]
(let [[r g b] (hex->rgb color)]
(str/fmt "rgba(%s, %s, %s, %s) %s" r g b opacity (str (* offset 100) "%"))))
stops-css (str/join "," (map parse-stop stops))]
(if (= type :linear)
(str/fmt "linear-gradient(to bottom, %s)" stops-css)
(str/fmt "radial-gradient(circle, %s" stops-css))))