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:
parent
a7335533bb
commit
381aef77ee
10 changed files with 368 additions and 174 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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 []
|
||||
|
|
|
@ -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}]]))
|
||||
|
||||
|
|
|
@ -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}])))
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"}]]])]))
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))))
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue