From 381aef77ee74e249cf93d0d4d01d09c1a4250288 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 8 Oct 2020 16:08:00 +0200 Subject: [PATCH 01/14] :sparkles: Changes to the colorpicker to support gradients --- .../styles/main/partials/colorpicker.scss | 19 +- frontend/src/app/main/data/colors.cljs | 28 ++ frontend/src/app/main/ui/modal.cljs | 9 +- .../app/main/ui/workspace/colorpicker.cljs | 249 +++++++++++++----- .../src/app/main/ui/workspace/gradients.cljs | 95 ++++--- .../src/app/main/ui/workspace/selection.cljs | 9 +- .../ui/workspace/sidebar/options/fill.cljs | 18 +- .../sidebar/options/rows/color_row.cljs | 97 ++++--- .../src/app/main/ui/workspace/viewport.cljs | 5 + frontend/src/app/util/color.cljs | 13 + 10 files changed, 368 insertions(+), 174 deletions(-) diff --git a/frontend/resources/styles/main/partials/colorpicker.scss b/frontend/resources/styles/main/partials/colorpicker.scss index 9b3d268e44..fc3f6c457d 100644 --- a/frontend/resources/styles/main/partials/colorpicker.scss +++ b/frontend/resources/styles/main/partials/colorpicker.scss @@ -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; } } diff --git a/frontend/src/app/main/data/colors.cljs b/frontend/src/app/main/data/colors.cljs index 217f28aad1..0153f03348 100644 --- a/frontend/src/app/main/data/colors.cljs +++ b/frontend/src/app/main/data/colors.cljs @@ -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] diff --git a/frontend/src/app/main/ui/modal.cljs b/frontend/src/app/main/ui/modal.cljs index e3894e042b..ac6a3c9aa0 100644 --- a/frontend/src/app/main/ui/modal.cljs +++ b/frontend/src/app/main/ui/modal.cljs @@ -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 [] diff --git a/frontend/src/app/main/ui/workspace/colorpicker.cljs b/frontend/src/app/main/ui/workspace/colorpicker.cljs index a8901ba2e1..cdacccd709 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker.cljs @@ -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}]])) diff --git a/frontend/src/app/main/ui/workspace/gradients.cljs b/frontend/src/app/main/ui/workspace/gradients.cljs index 0fc1e92a8c..739b29666a 100644 --- a/frontend/src/app/main/ui/workspace/gradients.cljs +++ b/frontend/src/app/main/ui/workspace/gradients.cljs @@ -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}]))) diff --git a/frontend/src/app/main/ui/workspace/selection.cljs b/frontend/src/app/main/ui/workspace/selection.cljs index b5e281510b..eda6c18880 100644 --- a/frontend/src/app/main/ui/workspace/selection.cljs +++ b/frontend/src/app/main/ui/workspace/selection.cljs @@ -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 diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/fill.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/fill.cljs index 511ebcdfa1..409b101abc 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/fill.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/fill.cljs @@ -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 diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs index 021fb8bc18..c6462d3b25 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs @@ -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"}]]])])) diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index ce0ef64f9e..e69c427d36 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -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 diff --git a/frontend/src/app/util/color.cljs b/frontend/src/app/util/color.cljs index d80f4436c0..b1163563cf 100644 --- a/frontend/src/app/util/color.cljs +++ b/frontend/src/app/util/color.cljs @@ -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)))) + From c266f78d1ef8bb23724145130f0174bbfd7720c1 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Fri, 9 Oct 2020 12:29:35 +0200 Subject: [PATCH 02/14] :sparkles: Gradients support in shapes --- frontend/src/app/main/data/colors.cljs | 7 ++ frontend/src/app/main/data/modal.cljs | 12 ++++ frontend/src/app/main/ui/shapes/attrs.cljs | 49 +++++++++----- .../src/app/main/ui/shapes/gradients.cljs | 21 +++--- frontend/src/app/main/ui/shapes/rect.cljs | 16 ++--- frontend/src/app/main/ui/shapes/text.cljs | 19 +++++- .../app/main/ui/workspace/colorpicker.cljs | 29 ++++++-- .../src/app/main/ui/workspace/gradients.cljs | 66 +++++++++++-------- .../app/main/ui/workspace/shapes/common.cljs | 13 ++++ .../app/main/ui/workspace/shapes/path.cljs | 13 +++- .../sidebar/options/rows/color_row.cljs | 4 +- .../ui/workspace/sidebar/options/text.cljs | 2 +- frontend/src/app/util/color.cljs | 2 +- frontend/src/app/util/object.cljs | 2 + 14 files changed, 176 insertions(+), 79 deletions(-) diff --git a/frontend/src/app/main/data/colors.cljs b/frontend/src/app/main/data/colors.cljs index 0153f03348..4a6a7c7c57 100644 --- a/frontend/src/app/main/data/colors.cljs +++ b/frontend/src/app/main/data/colors.cljs @@ -202,3 +202,10 @@ :type :colorpicker :props {:on-change handle-change-color} :allow-click-outside true})))))) + +(defn select-gradient-stop [spot] + (ptk/reify ::start-picker + ptk/UpdateEvent + (update [_ state] + (-> state + (assoc-in [:workspace-local :editing-stop] spot))))) diff --git a/frontend/src/app/main/data/modal.cljs b/frontend/src/app/main/data/modal.cljs index 0b12b21f65..976ecff65d 100644 --- a/frontend/src/app/main/data/modal.cljs +++ b/frontend/src/app/main/data/modal.cljs @@ -29,6 +29,14 @@ :type type :props props :allow-click-outside false}))))) +(defn update-props + ([type props] + (ptk/reify ::show-modal + ptk/UpdateEvent + (update [_ state] + (cond-> state + (::modal state) + (update-in [::modal :props] merge props)))))) (defn hide [] @@ -48,6 +56,10 @@ [type props] (st/emit! (show type props))) +(defn update-props! + [type props] + (st/emit! (update-props type props))) + (defn allow-click-outside! [] (st/emit! (update {:allow-click-outside true}))) diff --git a/frontend/src/app/main/ui/shapes/attrs.cljs b/frontend/src/app/main/ui/shapes/attrs.cljs index 185c5e1f49..4c8a4d9e7d 100644 --- a/frontend/src/app/main/ui/shapes/attrs.cljs +++ b/frontend/src/app/main/ui/shapes/attrs.cljs @@ -20,21 +20,36 @@ :dashed "10,10" nil)) +(defn add-border-radius [attrs shape] + (obj/merge! attrs #js {:rx (:rx shape) + :ry (:ry shape)})) + +(defn add-fill [attrs shape] + (let [fill-color-gradient-id (str "fill-color-gradient_" (:id shape))] + (if (:fill-color-gradient shape) + (obj/merge! attrs #js {:fill (str/format "url(#%s)" fill-color-gradient-id)}) + (obj/merge! attrs #js {:fill (or (:fill-color shape) "transparent") + :fillOpacity (:fill-opacity shape nil)})))) + +(defn add-stroke [attrs shape] + (let [stroke-style (:stroke-style shape :none) + stroke-color-gradient-id (str "stroke-color-gradient_" (:id shape))] + (if (not= stroke-style :none) + (if (:stroke-color-gradient shape) + (obj/merge! attrs + #js {:stroke (str/format "url(#%s)" stroke-color-gradient-id) + :strokeWidth (:stroke-width shape 1) + :strokeDasharray (stroke-type->dasharray stroke-style)}) + (obj/merge! attrs + #js {:stroke (:stroke-color shape nil) + :strokeWidth (:stroke-width shape 1) + :strokeOpacity (:stroke-opacity shape nil) + :strokeDasharray (stroke-type->dasharray stroke-style)})))) + attrs) + (defn extract-style-attrs - ([shape] (extract-style-attrs shape nil)) - ([shape gradient-id] - (let [stroke-style (:stroke-style shape :none) - attrs #js {:rx (:rx shape nil) - :ry (:ry shape nil)} - attrs (obj/merge! attrs - (if gradient-id - #js {:fill (str/format "url(#%s)" gradient-id)} - #js {:fill (or (:fill-color shape) "transparent") - :fillOpacity (:fill-opacity shape nil)}))] - (when (not= stroke-style :none) - (obj/merge! attrs - #js {:stroke (:stroke-color shape nil) - :strokeWidth (:stroke-width shape 1) - :strokeOpacity (:stroke-opacity shape nil) - :strokeDasharray (stroke-type->dasharray stroke-style)})) - attrs))) + ([shape] + (-> (obj/new) + (add-border-radius shape) + (add-fill shape) + (add-stroke shape)))) diff --git a/frontend/src/app/main/ui/shapes/gradients.cljs b/frontend/src/app/main/ui/shapes/gradients.cljs index f86b025b0e..621f7e278c 100644 --- a/frontend/src/app/main/ui/shapes/gradients.cljs +++ b/frontend/src/app/main/ui/shapes/gradients.cljs @@ -11,11 +11,11 @@ (:require [rumext.alpha :as mf] [cuerdas.core :as str] - [goog.object :as gobj] + [app.util.object :as obj] [app.common.uuid :as uuid] [app.common.geom.point :as gpt])) -(mf/defc linear-gradient [{:keys [id shape gradient]}] +(mf/defc linear-gradient [{:keys [id gradient shape]}] (let [{:keys [x y width height]} shape] [:defs [:linearGradient {:id id @@ -29,12 +29,11 @@ :stop-color color :stop-opacity opacity}])]])) -(mf/defc radial-gradient [{:keys [id shape gradient]}] +(mf/defc radial-gradient [{:keys [id gradient shape]}] (let [{:keys [x y width height]} shape] [:defs (let [translate-vec (gpt/point (+ x (* width (:start-x gradient))) (+ y (* height (:start-y gradient)))) - gradient-vec (gpt/to-vec (gpt/point (* width (:start-x gradient)) (* height (:start-y gradient))) @@ -71,8 +70,14 @@ (mf/defc gradient {::mf/wrap-props false} [props] - (let [gradient (gobj/get props "gradient")] + (let [attr (obj/get props "attr") + shape (obj/get props "shape") + + id (str (name attr) "_" (:id shape)) + gradient (get shape attr) + gradient-props #js {:id id + :gradient gradient + :shape shape}] (case (:type gradient) - :linear [:> linear-gradient props] - :radial [:> radial-gradient props] - nil))) + :linear [:> linear-gradient gradient-props] + :radial [:> radial-gradient gradient-props]))) diff --git a/frontend/src/app/main/ui/shapes/rect.cljs b/frontend/src/app/main/ui/shapes/rect.cljs index 5cac4b6b38..f2c8e98e97 100644 --- a/frontend/src/app/main/ui/shapes/rect.cljs +++ b/frontend/src/app/main/ui/shapes/rect.cljs @@ -27,9 +27,7 @@ {:keys [id x y width height]} shape transform (geom/transform-matrix shape) - gradient-id (when (:fill-color-gradient shape) (str (uuid/next))) - - props (-> (attrs/extract-style-attrs shape gradient-id) + props (-> (attrs/extract-style-attrs shape) (obj/merge! #js {:x x :y y @@ -38,12 +36,6 @@ :width width :height height}))] - - [:* - (when gradient-id - [:& gradient {:id gradient-id - :shape shape - :gradient (:fill-color-gradient shape)}]) - [:& shape-custom-stroke {:shape shape - :base-props props - :elem-name "rect"}]])) + [:& shape-custom-stroke {:shape shape + :base-props props + :elem-name "rect"}])) diff --git a/frontend/src/app/main/ui/shapes/text.cljs b/frontend/src/app/main/ui/shapes/text.cljs index 05707f50b9..205a6817b1 100644 --- a/frontend/src/app/main/ui/shapes/text.cljs +++ b/frontend/src/app/main/ui/shapes/text.cljs @@ -68,17 +68,29 @@ fill-color (obj/get data "fill-color" fill) fill-opacity (obj/get data "fill-opacity" opacity) + fill-color-gradient (obj/get data "fill-color-gradient" opacity) + fill-color-gradient (-> (js->clj fill-color-gradient :keywordize-keys true) + (update :type keyword)) + fill-color-ref-id (obj/get data "fill-color-ref-id") fill-color-ref-file (obj/get data "fill-color-ref-file") [r g b a] (uc/hex->rgba fill-color fill-opacity) + background (if fill-color-gradient + (uc/gradient->css (js->clj fill-color-gradient)) + (str/format "rgba(%s, %s, %s, %s)" r g b a)) fontsdb (deref fonts/fontsdb) base #js {:textDecoration text-decoration - :color (str/format "rgba(%s, %s, %s, %s)" r g b a) + ;:color (str/format "rgba(%s, %s, %s, %s)" r g b a) :textTransform text-transform - :lineHeight (or line-height "inherit")}] + :lineHeight (or line-height "inherit") + + :background background + :WebkitTextFillColor "transparent" + :WebkitBackgroundClip "text" + }] (when (and (string? letter-spacing) (pos? (alength letter-spacing))) @@ -167,7 +179,8 @@ (if (string? text) (let [style (generate-text-styles (clj->js node))] - [:span {:style style :key index} (if (= text "") "\u00A0" text)]) + [:span {:style style + :key (str index "-" (:fill-color node))} (if (= text "") "\u00A0" text)]) (let [children (map-indexed (fn [index node] (mf/element text-node {:index index :node node :key index})) children)] diff --git a/frontend/src/app/main/ui/workspace/colorpicker.cljs b/frontend/src/app/main/ui/workspace/colorpicker.cljs index cdacccd709..f5823017ae 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker.cljs @@ -21,7 +21,7 @@ [app.main.store :as st] [app.main.refs :as refs] [app.main.data.workspace.libraries :as dwl] - [app.main.data.colors :as dwc] + [app.main.data.colors :as dc] [app.main.data.modal :as modal] [app.main.ui.icons :as i] [app.util.i18n :as i18n :refer [t]])) @@ -43,6 +43,8 @@ (def viewport (l/derived (l/in [:workspace-local :vport]) st/state)) +(def editing-spot-state-ref + (l/derived (l/in [:workspace-local :editing-stop]) st/state)) ;; --- Color Picker Modal @@ -525,6 +527,8 @@ picked-color-select (mf/deref picked-color-select) picked-shift? (mf/deref picked-shift?) + editing-spot-state (mf/deref editing-spot-state-ref) + locale (mf/deref i18n/locale) ;; data-ref (mf/use-var data) @@ -558,7 +562,8 @@ (fn [offset] (let [offset-color (get-in @state [:stops offset])] (swap! state assoc :current-color offset-color) - (swap! state assoc :editing-stop offset))) + (swap! state assoc :editing-stop offset) + (st/emit! (dc/select-gradient-stop offset)))) on-activate-gradient (fn [type] @@ -568,11 +573,11 @@ (swap! state assoc :type :color) (swap! state dissoc :editing-stop :stops :gradient-data)) (do - (swap! state assoc :type type) + (swap! state assoc :type type + :gradient-data (create-gradient-data 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))}))))))] @@ -631,7 +636,7 @@ ;; When closing the modal we update the recent-color list #_(mf/use-effect (fn [] (fn [] - (st/emit! (dwc/stop-picker)) + (st/emit! (dc/stop-picker)) (st/emit! (dwl/add-recent-color (state->data @state)))))) (mf/use-effect @@ -657,6 +662,16 @@ (fn [] (when (and picking-color? picked-color-select) (on-change (:hex current-color) (:alpha current-color) nil nil picked-shift?)))) + (mf/use-effect + (mf/deps editing-spot-state) + #(when (not= editing-spot-state (:editing-stop @state)) + (handle-change-stop (or editing-spot-state 0)))) + + (mf/use-effect + (mf/deps data) + #(let [gradient-data (-> data data->state :gradient-data)] + (swap! state assoc :gradient-data gradient-data))) + (mf/use-effect (mf/deps @state) (fn [] @@ -669,7 +684,7 @@ {:class (when picking-color? "active") :on-click (fn [] (modal/allow-click-outside!) - (st/emit! (dwc/start-picker)))} + (st/emit! (dc/start-picker)))} i/picker] [:div.gradients-buttons @@ -735,7 +750,7 @@ i/plus]) [:div.color-bullet.button {:style {:background-color "white"} - :on-click #(st/emit! (dwc/show-palette (parse-selected @selected-library)))} + :on-click #(st/emit! (dc/show-palette (parse-selected @selected-library)))} i/palette] (for [[idx {:keys [id file-id value]}] (map-indexed vector @current-library-colors)] diff --git a/frontend/src/app/main/ui/workspace/gradients.cljs b/frontend/src/app/main/ui/workspace/gradients.cljs index 739b29666a..38dc28471a 100644 --- a/frontend/src/app/main/ui/workspace/gradients.cljs +++ b/frontend/src/app/main/ui/workspace/gradients.cljs @@ -13,6 +13,7 @@ [rumext.alpha :as mf] [cuerdas.core :as str] [beicon.core :as rx] + [okulary.core :as l] [app.common.math :as mth] [app.common.geom.point :as gpt] [app.common.geom.matrix :as gmt] @@ -20,7 +21,9 @@ [app.main.store :as st] [app.main.refs :as refs] [app.main.streams :as ms] - [app.main.data.workspace.common :as dwc])) + [app.main.data.modal :as modal] + [app.main.data.workspace.common :as dwc] + [app.main.data.colors :as dc])) (def gradient-line-stroke-width 2) (def gradient-line-stroke-color "white") @@ -32,6 +35,9 @@ (def gradient-square-stroke-color "white") (def gradient-square-stroke-color-selected "#1FDEA7") +(def editing-spot-ref + (l/derived (l/in [:workspace-local :editing-stop]) st/state)) + (mf/defc shadow [{:keys [id x y width height offset]}] [:filter {:id id :x x @@ -78,24 +84,13 @@ :height (+ (/ (* 2 gradient-width-handler-radius) zoom) (/ 2 zoom) 4) :offset (/ 2 zoom)}]) -(def default-gradient - {:type :linear - :start-x 0.5 :start-y 0.5 - :end-x 0.5 :end-y 1 - :width 1.0 - :stops [{:offset 0 - :color "#FF0000" - :opacity 1} - {:offset 1 - :color "#FF0000" - :opacity 0.2}]}) - (def checkboard "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAIAAAC0tAIdAAACvUlEQVQoFQGyAk39AeLi4gAAAAAAAB0dHQAAAAAAAOPj4wAAAAAAAB0dHQAAAAAAAOPj4wAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB////AAAAAAAA4+PjAAAAAAAAHR0dAAAAAAAA4+PjAAAAAAAAHR0dAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATj4+MAAAAAAAAdHR0AAAAAAADj4+MAAAAAAAAdHR0AAAAAAADj4+MAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjScaa0cU7nIAAAAASUVORK5CYII=") #_(def checkboard "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAADFJREFUOE9jZGBgEAFifOANPknGUQMYhkkYEEgG+NMJKAwIAbwJbdQABnBCIgRoG4gAIF8IsXB/Rs4AAAAASUVORK5CYII=") (mf/defc gradient-color-handler - [{:keys [filter-id zoom point color angle on-click on-mouse-down on-mouse-up]}] + [{:keys [filter-id zoom point color angle selected + on-click on-mouse-down on-mouse-up]}] [:g {:filter (str/fmt "url(#%s)" filter-id) :transform (gmt/rotate-matrix angle point)} @@ -121,7 +116,7 @@ :rx (/ gradient-square-radius zoom) :width (/ gradient-square-width zoom) :height (/ gradient-square-width zoom) - :stroke "white" + :stroke (if selected "#31EFB8" "white") :stroke-width (/ gradient-square-stroke-width zoom) :fill (:value color) :fill-opacity (:opacity color) @@ -130,18 +125,27 @@ :on-mouse-up on-mouse-up}]]) (mf/defc gradient-handler-transformed - [{:keys [from-p to-p width-p from-color to-color zoom on-change-start on-change-finish on-change-width on-change-stop-color]}] + [{:keys [from-p to-p width-p from-color to-color zoom editing + on-change-start on-change-finish on-change-width on-change-stop-color]}] (let [moving-point (mf/use-var nil) angle (+ 90 (gpt/angle from-p to-p)) on-click (fn [position event] (dom/stop-propagation event) - (dom/prevent-default event)) + (dom/prevent-default event) + (when (#{:from-p :to-p} position) + (st/emit! (dc/select-gradient-stop (case position + :from-p 0 + :to-p 1))))) on-mouse-down (fn [position event] (dom/stop-propagation event) (dom/prevent-default event) - (reset! moving-point position)) + (reset! moving-point position) + (when (#{:from-p :to-p} position) + (st/emit! (dc/select-gradient-stop (case position + :from-p 0 + :to-p 1))))) on-mouse-up (fn [position event] (dom/stop-propagation event) @@ -194,7 +198,8 @@ (when width-p [:g {:filter "url(#gradient_width_handler_drop_shadow)"} - [:circle {:cx (:x width-p) + [:circle {:data-allow-click-modal "colorpicker" + :cx (:x width-p) :cy (:y width-p) :r (/ gradient-width-handler-radius zoom) :fill gradient-width-handler-color @@ -202,7 +207,8 @@ :on-mouse-up (partial on-mouse-up :width-p)}]]) [:& gradient-color-handler - {:filter-id "gradient_square_from_drop_shadow" + {:selected (or (not editing) (= editing 0)) + :filter-id "gradient_square_from_drop_shadow" :zoom zoom :point from-p :color from-color @@ -212,7 +218,8 @@ :on-mouse-up (partial on-mouse-up :from-p)}] [:& gradient-color-handler - {:filter-id "gradient_square_to_drop_shadow" + {:selected (= editing 1) + :filter-id "gradient_square_to_drop_shadow" :zoom zoom :point to-p :color to-color @@ -221,11 +228,16 @@ :on-mouse-down (partial on-mouse-down :to-p) :on-mouse-up (partial on-mouse-up :to-p)}]])) +(def modal-type-ref + (l/derived (comp :type ::modal/modal) st/state)) + (mf/defc gradient-handlers [{: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) + modal (mf/deref modal-type-ref) + editing-spot (mf/deref editing-spot-ref) [{start-color :color start-opacity :opacity} {end-color :color end-opacity :opacity}] (:stops gradient) @@ -271,13 +283,12 @@ norm-dist (/ (gpt/distance point from-p) (* (/ width 2) scale-factor-y))] - (change! {:width norm-dist}))) + (change! {:width norm-dist})))] - on-change-stop-color (fn [offset color opacity] (println "change-color"))] - - (when gradient + (when (and gradient (= modal :colorpicker)) [:& gradient-handler-transformed - {:from-p from-p + {:editing editing-spot + :from-p from-p :to-p to-p :width-p (when (= :radial (:type gradient)) width-p) :from-color {:value start-color :opacity start-opacity} @@ -285,5 +296,4 @@ :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}]))) + :on-change-width on-change-width}]))) diff --git a/frontend/src/app/main/ui/workspace/shapes/common.cljs b/frontend/src/app/main/ui/workspace/shapes/common.cljs index 2df882f9e9..762469b14c 100644 --- a/frontend/src/app/main/ui/workspace/shapes/common.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/common.cljs @@ -15,7 +15,9 @@ [app.main.store :as st] [app.main.ui.keyboard :as kbd] [app.main.ui.shapes.filters :as filters] + [app.main.ui.shapes.gradients :as grad] [app.util.dom :as dom] + [app.common.uuid :as uuid] [app.common.geom.matrix :as gmt] [app.common.geom.point :as gpt] [app.common.geom.shapes :as geom])) @@ -72,10 +74,21 @@ (mf/deps shape) #(on-context-menu % shape)) filter-id (mf/use-memo filters/get-filter-id)] + [:g.shape {:on-mouse-down on-mouse-down :on-context-menu on-context-menu :filter (filters/filter-str filter-id shape)} + [:& filters/filters {:filter-id filter-id :shape shape}] + + (when (:fill-color-gradient shape) + [:& grad/gradient {:attr :fill-color-gradient + :shape shape}]) + + (when (:stroke-color-gradient shape) + [:& grad/gradient {:attr :stroke-color-gradient + :shape shape}]) + [:& component {:shape shape}]]))) diff --git a/frontend/src/app/main/ui/workspace/shapes/path.cljs b/frontend/src/app/main/ui/workspace/shapes/path.cljs index d128509aab..bf19a128e7 100644 --- a/frontend/src/app/main/ui/workspace/shapes/path.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/path.cljs @@ -18,6 +18,7 @@ [app.main.ui.keyboard :as kbd] [app.main.ui.shapes.path :as path] [app.main.ui.shapes.filters :as filters] + [app.main.ui.shapes.gradients :as grad] [app.main.ui.workspace.shapes.common :as common] [app.main.data.workspace.drawing :as dr] [app.util.dom :as dom] @@ -43,6 +44,7 @@ (dom/stop-propagation event) (dom/prevent-default event) (st/emit! (dw/start-edition-mode (:id shape))))))) + filter-id (mf/use-memo filters/get-filter-id)] [:g.shape {:on-double-click on-double-click @@ -50,5 +52,14 @@ :on-context-menu on-context-menu :filter (filters/filter-str filter-id shape)} [:& filters/filters {:filter-id filter-id :shape shape}] - [:& path/path-shape {:shape shape :background? true}]])) + + (when (:fill-color-gradient shape) + [:& grad/gradient {:attr :fill-color-gradient + :shape shape}]) + + (when (:stroke-color-gradient shape) + [:& grad/gradient {:attr :stroke-color-gradient + :shape shape}]) + [:& path/path-shape {:shape shape + :background? true}]])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs index c6462d3b25..98f3f3cbc3 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs @@ -129,7 +129,9 @@ (mf/use-effect (mf/deps color) - #(reset! state (parse-color color))) + (fn [] + (modal/update-props! :colorpicker {:data (parse-color color)}) + (reset! state (parse-color color)))) [:div.row-flex.color-data [:span.color-th diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/text.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/text.cljs index 5a324a0f62..4c1a219966 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/text.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/text.cljs @@ -32,7 +32,7 @@ ["slate" :refer [Transforms]])) (def text-typography-attrs [:typography-ref-id :typography-ref-file]) -(def text-fill-attrs [:fill-color :fill-opacity :fill-color-ref-id :fill-color-ref-file :fill :opacity ]) +(def text-fill-attrs [:fill-color :fill-opacity :fill-color-ref-id :fill-color-ref-file :fill-color-gradient :fill :opacity ]) (def text-font-attrs [:font-id :font-family :font-variant-id :font-size :font-weight :font-style]) (def text-align-attrs [:text-align]) (def text-spacing-attrs [:line-height :letter-spacing]) diff --git a/frontend/src/app/util/color.cljs b/frontend/src/app/util/color.cljs index b1163563cf..7075bc3d48 100644 --- a/frontend/src/app/util/color.cljs +++ b/frontend/src/app/util/color.cljs @@ -88,5 +88,5 @@ (if (= type :linear) (str/fmt "linear-gradient(to bottom, %s)" stops-css) - (str/fmt "radial-gradient(circle, %s" stops-css)))) + (str/fmt "radial-gradient(circle, %s)" stops-css)))) diff --git a/frontend/src/app/util/object.cljs b/frontend/src/app/util/object.cljs index def453015e..a867cc6463 100644 --- a/frontend/src/app/util/object.cljs +++ b/frontend/src/app/util/object.cljs @@ -15,6 +15,8 @@ [goog.object :as gobj] ["lodash/omit" :as omit])) +(defn new [] #js {}) + (defn get ([obj k] (when-not (nil? obj) From a412fc113dff76aedc787778a2627affadd86553 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Fri, 9 Oct 2020 15:35:44 +0200 Subject: [PATCH 03/14] :sparkles: Color picker refactor --- frontend/src/app/main/data/colors.cljs | 36 +- frontend/src/app/main/data/workspace.cljs | 9 +- .../app/main/ui/workspace/colorpicker.cljs | 526 +----------------- .../workspace/colorpicker/color_inputs.cljs | 172 ++++++ .../ui/workspace/colorpicker/gradients.cljs | 54 ++ .../ui/workspace/colorpicker/harmony.cljs | 154 +++++ .../main/ui/workspace/colorpicker/hsva.cljs | 57 ++ .../ui/workspace/colorpicker/libraries.cljs | 100 ++++ .../workspace/colorpicker/pixel_overlay.cljs | 29 + .../workspace/colorpicker/pixel_picker.cljs | 29 + .../main/ui/workspace/colorpicker/ramp.cljs | 92 +++ .../colorpicker/slider_selector.cljs | 68 +++ .../sidebar/options/rows/color_row.cljs | 20 +- frontend/src/app/util/color.cljs | 13 + 14 files changed, 831 insertions(+), 528 deletions(-) create mode 100644 frontend/src/app/main/ui/workspace/colorpicker/color_inputs.cljs create mode 100644 frontend/src/app/main/ui/workspace/colorpicker/gradients.cljs create mode 100644 frontend/src/app/main/ui/workspace/colorpicker/harmony.cljs create mode 100644 frontend/src/app/main/ui/workspace/colorpicker/hsva.cljs create mode 100644 frontend/src/app/main/ui/workspace/colorpicker/libraries.cljs create mode 100644 frontend/src/app/main/ui/workspace/colorpicker/pixel_overlay.cljs create mode 100644 frontend/src/app/main/ui/workspace/colorpicker/pixel_picker.cljs create mode 100644 frontend/src/app/main/ui/workspace/colorpicker/ramp.cljs create mode 100644 frontend/src/app/main/ui/workspace/colorpicker/slider_selector.cljs diff --git a/frontend/src/app/main/data/colors.cljs b/frontend/src/app/main/data/colors.cljs index 4a6a7c7c57..038ec1ed73 100644 --- a/frontend/src/app/main/data/colors.cljs +++ b/frontend/src/app/main/data/colors.cljs @@ -163,6 +163,27 @@ (map #(dwt/update-text-attrs {:id % :editor (get editors %) :attrs attrs}) text-ids) (dwc/update-shapes shape-ids update-fn)))))))) +(defn change-stroke2 [ids color] + (ptk/reify ::change-stroke + ptk/WatchEvent + (watch [_ state s] + (let [objects (get-in state [:workspace-data :pages-index (:current-page-id state) :objects]) + children (mapcat #(cph/get-children % objects) ids) + ids (into ids children) + + update-fn (fn [s] + (cond-> s + true + (assoc :stroke-color (:color color) + :stroke-color-gradient (:gradient color) + :stroke-color-ref-id (:id color) + :stroke-color-ref-file (:file-id color)) + + (= (:stroke-style s) :none) + (assoc :stroke-style :solid + :stroke-width 1 + :stroke-opacity 1)))] + (rx/of (dwc/update-shapes ids update-fn)))))) (defn change-stroke [ids color id file-id] (ptk/reify ::change-stroke ptk/WatchEvent @@ -186,13 +207,14 @@ (defn picker-for-selected-shape [] ;; TODO: replace st/emit! by a subject push and set that in the WatchEvent - (let [handle-change-color (fn [color opacity id file-id shift?] - (let [ids (get-in @st/state [:workspace-local :selected])] - (st/emit! - (if shift? - (change-stroke ids color nil nil) - (change-fill ids color nil nil)) - (md/hide))))] + (let [handle-change-color + (fn [color shift?] + (let [ids (get-in @st/state [:workspace-local :selected])] + (st/emit! + (if shift? + (change-stroke2 ids color) + (change-fill2 ids color)) + (md/hide))))] (ptk/reify ::start-picker ptk/UpdateEvent (update [_ state] diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 5fcd337cd8..e62276f982 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -1444,22 +1444,23 @@ (defn change-canvas-color [color] - (s/assert string? color) + ;; TODO: Create a color spec + #_(s/assert string? color) (ptk/reify ::change-canvas-color ptk/WatchEvent (watch [_ state stream] (let [page-id (get state :current-page-id) options (dwc/lookup-page-options state page-id) - ccolor (:background options)] + previus-color (:background options)] (rx/of (dwc/commit-changes [{:type :set-option :page-id page-id :option :background - :value color}] + :value (:color color)}] [{:type :set-option :page-id page-id :option :background - :value ccolor}] + :value previus-color}] {:commit-local? true})))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/frontend/src/app/main/ui/workspace/colorpicker.cljs b/frontend/src/app/main/ui/workspace/colorpicker.cljs index f5823017ae..99c2c45abe 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker.cljs @@ -24,7 +24,13 @@ [app.main.data.colors :as dc] [app.main.data.modal :as modal] [app.main.ui.icons :as i] - [app.util.i18n :as i18n :refer [t]])) + [app.util.i18n :as i18n :refer [t]] + [app.main.ui.workspace.colorpicker.gradients :refer [gradients]] + [app.main.ui.workspace.colorpicker.harmony :refer [harmony-selector]] + [app.main.ui.workspace.colorpicker.hsva :refer [hsva-selector]] + [app.main.ui.workspace.colorpicker.ramp :refer [ramp-selector]] + [app.main.ui.workspace.colorpicker.color-inputs :refer [color-inputs]] + [app.main.ui.workspace.colorpicker.libraries :refer [libraries]])) ;; --- Refs @@ -48,409 +54,6 @@ ;; --- Color Picker Modal -(mf/defc value-saturation-selector [{:keys [hue saturation value on-change]}] - (let [dragging? (mf/use-state false) - calculate-pos - (fn [ev] - (let [{:keys [left right top bottom]} (-> ev dom/get-target dom/get-bounding-rect) - {:keys [x y]} (-> ev dom/get-client-position) - px (math/clamp (/ (- x left) (- right left)) 0 1) - py (* 255 (- 1 (math/clamp (/ (- y top) (- bottom top)) 0 1)))] - (on-change px py)))] - [:div.value-saturation-selector - {:on-mouse-down #(reset! dragging? true) - :on-mouse-up #(reset! dragging? false) - :on-pointer-down (partial dom/capture-pointer) - :on-pointer-up (partial dom/release-pointer) - :on-click calculate-pos - :on-mouse-move #(when @dragging? (calculate-pos %))} - [:div.handler {:style {:pointer-events "none" - :left (str (* 100 saturation) "%") - :top (str (* 100 (- 1 (/ value 255))) "%")}}]])) - - -(mf/defc slider-selector [{:keys [value class min-value max-value vertical? reverse? on-change]}] - (let [min-value (or min-value 0) - max-value (or max-value 1) - dragging? (mf/use-state false) - calculate-pos - (fn [ev] - (when on-change - (let [{:keys [left right top bottom]} (-> ev dom/get-target dom/get-bounding-rect) - {:keys [x y]} (-> ev dom/get-client-position) - unit-value (if vertical? - (math/clamp (/ (- bottom y) (- bottom top)) 0 1) - (math/clamp (/ (- x left) (- right left)) 0 1)) - unit-value (if reverse? - (math/abs (- unit-value 1.0)) - unit-value) - value (+ min-value (* unit-value (- max-value min-value)))] - (on-change (math/precision value 2)))))] - - [:div.slider-selector - {:class (str (if vertical? "vertical " "") class) - :on-mouse-down #(reset! dragging? true) - :on-mouse-up #(reset! dragging? false) - :on-pointer-down (partial dom/capture-pointer) - :on-pointer-up (partial dom/release-pointer) - :on-click calculate-pos - :on-mouse-move #(when @dragging? (calculate-pos %))} - - (let [value-percent (* (/ (- value min-value) - (- max-value min-value)) 100) - - value-percent (if reverse? - (math/abs (- value-percent 100)) - value-percent) - value-percent-str (str value-percent "%") - - style-common #js {:pointerEvents "none"} - style-horizontal (obj/merge! #js {:left value-percent-str} style-common) - style-vertical (obj/merge! #js {:bottom value-percent-str} style-common)] - [:div.handler {:style (if vertical? style-vertical style-horizontal)}])])) - - -(defn create-color-wheel - [canvas-node] - (let [ctx (.getContext canvas-node "2d") - width (obj/get canvas-node "width") - height (obj/get canvas-node "height") - radius (/ width 2) - cx (/ width 2) - cy (/ width 2) - step 0.2] - - (.clearRect ctx 0 0 width height) - - (doseq [degrees (range 0 360 step)] - (let [degrees-rad (math/radians degrees) - x (* radius (math/cos (- degrees-rad))) - y (* radius (math/sin (- degrees-rad)))] - (obj/set! ctx "strokeStyle" (str/format "hsl(%s, 100%, 50%)" degrees)) - (.beginPath ctx) - (.moveTo ctx cx cy) - (.lineTo ctx (+ cx x) (+ cy y)) - (.stroke ctx))) - - (let [grd (.createRadialGradient ctx cx cy 0 cx cx radius)] - (.addColorStop grd 0 "white") - (.addColorStop grd 1 "rgba(255, 255, 255, 0") - (obj/set! ctx "fillStyle" grd) - - (.beginPath ctx) - (.arc ctx cx cy radius 0 (* 2 math/PI) true) - (.closePath ctx) - (.fill ctx)))) - -(mf/defc ramp-selector [{:keys [color on-change]}] - (let [{hue :h saturation :s value :v alpha :alpha} color - - on-change-value-saturation - (fn [new-saturation new-value] - (let [hex (uc/hsv->hex [hue new-saturation new-value]) - [r g b] (uc/hex->rgb hex)] - (on-change {:hex hex - :r r :g g :b b - :s new-saturation - :v new-value}))) - - on-change-hue - (fn [new-hue] - (let [hex (uc/hsv->hex [new-hue saturation value]) - [r g b] (uc/hex->rgb hex)] - (on-change {:hex hex - :r r :g g :b b - :h new-hue} ))) - - on-change-opacity - (fn [new-opacity] - (on-change {:alpha new-opacity} ))] - [:* - [:& value-saturation-selector - {:hue hue - :saturation saturation - :value value - :on-change on-change-value-saturation}] - - [:div.shade-selector - [:div.color-bullet] - [:& slider-selector {:class "hue" - :max-value 360 - :value hue - :on-change on-change-hue}] - - [:& slider-selector {:class "opacity" - :max-value 1 - :value alpha - :on-change on-change-opacity}]]])) - -(defn color->point - [canvas-side hue saturation] - (let [hue-rad (math/radians (- hue)) - comp-x (* saturation (math/cos hue-rad)) - comp-y (* saturation (math/sin hue-rad)) - x (+ (/ canvas-side 2) (* comp-x (/ canvas-side 2))) - y (+ (/ canvas-side 2) (* comp-y (/ canvas-side 2)))] - (gpt/point x y))) - -(mf/defc harmony-selector [{:keys [color on-change]}] - (let [canvas-ref (mf/use-ref nil) - {hue :h saturation :s value :v alpha :alpha} color - - canvas-side 152 - pos-current (color->point canvas-side hue saturation) - pos-complement (color->point canvas-side (mod (+ hue 180) 360) saturation) - dragging? (mf/use-state false) - - calculate-pos (fn [ev] - (let [{:keys [left right top bottom]} (-> ev dom/get-target dom/get-bounding-rect) - {:keys [x y]} (-> ev dom/get-client-position) - px (math/clamp (/ (- x left) (- right left)) 0 1) - py (math/clamp (/ (- y top) (- bottom top)) 0 1) - - px (- (* 2 px) 1) - py (- (* 2 py) 1) - - angle (math/degrees (math/atan2 px py)) - new-hue (math/precision (mod (- angle 90 ) 360) 2) - new-saturation (math/clamp (math/distance [px py] [0 0]) 0 1) - hex (uc/hsv->hex [new-hue new-saturation value]) - [r g b] (uc/hex->rgb hex)] - (on-change {:hex hex - :r r :g g :b b - :h new-hue - :s new-saturation}))) - - on-change-value (fn [new-value] - (let [hex (uc/hsv->hex [hue saturation new-value]) - [r g b] (uc/hex->rgb hex)] - (on-change {:hex hex - :r r :g g :b b - :v new-value}))) - on-complement-click (fn [ev] - (let [new-hue (mod (+ hue 180) 360) - hex (uc/hsv->hex [new-hue saturation value]) - [r g b] (uc/hex->rgb hex)] - (on-change {:hex hex - :r r :g g :b b - :h new-hue - :s saturation}))) - - on-change-opacity (fn [new-alpha] (on-change {:alpha new-alpha}))] - - (mf/use-effect - (mf/deps canvas-ref) - (fn [] (when canvas-ref - (create-color-wheel (mf/ref-val canvas-ref))))) - - [:div.harmony-selector - [:div.hue-wheel-wrapper - [:canvas.hue-wheel - {:ref canvas-ref - :width canvas-side - :height canvas-side - :on-mouse-down #(reset! dragging? true) - :on-mouse-up #(reset! dragging? false) - :on-pointer-down (partial dom/capture-pointer) - :on-pointer-up (partial dom/release-pointer) - :on-click calculate-pos - :on-mouse-move #(when @dragging? (calculate-pos %))}] - [:div.handler {:style {:pointer-events "none" - :left (:x pos-current) - :top (:y pos-current)}}] - [:div.handler.complement {:style {:left (:x pos-complement) - :top (:y pos-complement) - :cursor "pointer"} - :on-click on-complement-click}]] - [:div.handlers-wrapper - [:& slider-selector {:class "value" - :vertical? true - :reverse? true - :value value - :max-value 255 - :vertical true - :on-change on-change-value}] - [:& slider-selector {:class "opacity" - :vertical? true - :value alpha - :max-value 1 - :vertical true - :on-change on-change-opacity}]]])) - -(mf/defc hsva-selector [{:keys [color on-change]}] - (let [{hue :h saturation :s value :v alpha :alpha} color - handle-change-slider (fn [key] - (fn [new-value] - (let [change (hash-map key new-value) - {:keys [h s v]} (merge color change) - hex (uc/hsv->hex [h s v]) - [r g b] (uc/hex->rgb hex)] - (on-change (merge change - {:hex hex - :r r :g g :b b}))))) - on-change-opacity (fn [new-alpha] (on-change {:alpha new-alpha}))] - [:div.hsva-selector - [:span.hsva-selector-label "H"] - [:& slider-selector - {:class "hue" :max-value 360 :value hue :on-change (handle-change-slider :h)}] - - [:span.hsva-selector-label "S"] - [:& slider-selector - {:class "saturation" :max-value 1 :value saturation :on-change (handle-change-slider :s)}] - - [:span.hsva-selector-label "V"] - [:& slider-selector - {:class "value" :reverse? true :max-value 255 :value value :on-change (handle-change-slider :v)}] - - [:span.hsva-selector-label "A"] - [:& slider-selector - {:class "opacity" :max-value 1 :value alpha :on-change on-change-opacity}]])) - -(mf/defc color-inputs [{:keys [type color on-change]}] - (let [{red :r green :g blue :b - hue :h saturation :s value :v - hex :hex alpha :alpha} color - - parse-hex (fn [val] (if (= (first val) \#) val (str \# val))) - - refs {:hex (mf/use-ref nil) - :r (mf/use-ref nil) - :g (mf/use-ref nil) - :b (mf/use-ref nil) - :h (mf/use-ref nil) - :s (mf/use-ref nil) - :v (mf/use-ref nil) - :alpha (mf/use-ref nil)} - - on-change-hex - (fn [e] - (let [val (-> e dom/get-target-val parse-hex)] - (when (uc/hex? val) - (let [[r g b] (uc/hex->rgb val) - [h s v] (uc/hex->hsv hex)] - (on-change {:hex val - :h h :s s :v v - :r r :g g :b b}))))) - - on-change-property - (fn [property max-value] - (fn [e] - (let [val (-> e dom/get-target-val (math/clamp 0 max-value)) - val (if (#{:s} property) (/ val 100) val)] - (when (not (nil? val)) - (if (#{:r :g :b} property) - (let [{:keys [r g b]} (merge color (hash-map property val)) - hex (uc/rgb->hex [r g b]) - [h s v] (uc/hex->hsv hex)] - (on-change {:hex hex - :h h :s s :v v - :r r :g g :b b})) - - (let [{:keys [h s v]} (merge color (hash-map property val)) - hex (uc/hsv->hex [h s v]) - [r g b] (uc/hex->rgb hex)] - (on-change {:hex hex - :h h :s s :v v - :r r :g g :b b}))))))) - - on-change-opacity - (fn [e] - (when-let [new-alpha (-> e dom/get-target-val (math/clamp 0 100) (/ 100))] - (on-change {:alpha new-alpha})))] - - - ;; Updates the inputs values when a property is changed in the parent - (mf/use-effect - (mf/deps color type) - (fn [] - (doseq [ref-key (keys refs)] - (let [property-val (get color ref-key) - property-ref (get refs ref-key)] - (when (and property-val property-ref) - (when-let [node (mf/ref-val property-ref)] - (case ref-key - (:s :alpha) (dom/set-value! node (math/round (* property-val 100))) - :hex (dom/set-value! node property-val) - (dom/set-value! node (math/round property-val))))))))) - - [:div.color-values - [:input {:id "hex-value" - :ref (:hex refs) - :default-value hex - :on-change on-change-hex}] - - (if (= type :rgb) - [:* - [:input {:id "red-value" - :ref (:r refs) - :type "number" - :min 0 - :max 255 - :default-value red - :on-change (on-change-property :r 255)}] - - [:input {:id "green-value" - :ref (:g refs) - :type "number" - :min 0 - :max 255 - :default-value green - :on-change (on-change-property :g 255)}] - - [:input {:id "blue-value" - :ref (:b refs) - :type "number" - :min 0 - :max 255 - :default-value blue - :on-change (on-change-property :b 255)}]] - [:* - [:input {:id "hue-value" - :ref (:h refs) - :type "number" - :min 0 - :max 360 - :default-value hue - :on-change (on-change-property :h 360)}] - - [:input {:id "saturation-value" - :ref (:s refs) - :type "number" - :min 0 - :max 100 - :step 1 - :default-value saturation - :on-change (on-change-property :s 100)}] - - [:input {:id "value-value" - :ref (:v refs) - :type "number" - :min 0 - :max 255 - :default-value value - :on-change (on-change-property :v 255)}]]) - - [:input.alpha-value {:id "alpha-value" - :ref (:alpha refs) - :type "number" - :min 0 - :step 1 - :max 100 - :default-value (if (= alpha :multiple) "" (math/precision alpha 2)) - :on-change on-change-opacity}] - - [:label.hex-label {:for "hex-value"} "HEX"] - (if (= type :rgb) - [:* - [:label.red-label {:for "red-value"} "R"] - [:label.green-label {:for "green-value"} "G"] - [:label.blue-label {:for "blue-value"} "B"]] - [:* - [:label.red-label {:for "hue-value"} "H"] - [:label.green-label {:for "saturation-value"} "S"] - [:label.blue-label {:for "value-value"} "V"]]) - [:label.alpha-label {:for "alpha-value"} "A"]])) - (defn color->components [value opacity] (let [value (if (uc/hex? value) value "#000000") [r g b] (uc/hex->rgb value) @@ -513,15 +116,10 @@ [{: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 []) + locale (mf/deref i18n/locale) ref-picker (mf/use-ref) - file-colors (mf/deref refs/workspace-file-colors) - shared-libs (mf/deref refs/workspace-libraries) - recent-colors (mf/deref refs/workspace-recent-colors) - picking-color? (mf/deref picking-color?) picked-color (mf/deref picked-color) picked-color-select (mf/deref picked-color-select) @@ -529,18 +127,10 @@ editing-spot-state (mf/deref editing-spot-state-ref) - locale (mf/deref i18n/locale) - ;; data-ref (mf/use-var data) current-color (:current-color @state) - parse-selected - (fn [selected] - (if (#{"recent" "file"} selected) - (keyword selected) - (uuid selected)) ) - change-tab (fn [tab] #(reset! active-tab tab)) @@ -565,6 +155,9 @@ (swap! state assoc :editing-stop offset) (st/emit! (dc/select-gradient-stop offset)))) + on-select-library-color + (fn [color] (prn "color" color)) + on-activate-gradient (fn [type] (fn [] @@ -609,30 +202,6 @@ (dom/set-css-property node "--saturation-grad-from" (format-hsl hsl-from)) (dom/set-css-property node "--saturation-grad-to" (format-hsl hsl-to))))) - ;; Load library colors when the select is changed - (mf/use-effect - (mf/deps @selected-library) - (fn [] - (let [mapped-colors - (cond - (= @selected-library "recent") - (map #(hash-map :value %) (reverse (or recent-colors []))) - - (= @selected-library "file") - (map #(select-keys % [:id :value]) (vals file-colors)) - - :else ;; Library UUID - (map #(merge {:file-id (uuid @selected-library)} (select-keys % [:id :value])) - (vals (get-in shared-libs [(uuid @selected-library) :data :colors]))))] - (reset! current-library-colors (into [] mapped-colors))))) - - ;; If the file colors change and the file option is selected updates the state - (mf/use-effect - (mf/deps file-colors) - (fn [] (when (= @selected-library "file") - (let [colors (map #(select-keys % [:id :value]) (vals file-colors))] - (reset! current-library-colors (into [] colors)))))) - ;; When closing the modal we update the recent-color list #_(mf/use-effect (fn [] (fn [] @@ -646,7 +215,7 @@ hex (uc/rgb->hex [r g b]) [h s v] (uc/hex->hsv hex)] - (swap! update :current-color assoc + (swap! state update :current-color assoc :r r :g g :b b :h h :s s :v v :hex hex) @@ -669,8 +238,8 @@ (mf/use-effect (mf/deps data) - #(let [gradient-data (-> data data->state :gradient-data)] - (swap! state assoc :gradient-data gradient-data))) + #(if-let [gradient-data (-> data data->state :gradient-data)] + (swap! state assoc :gradient-data gradient-data))) (mf/use-effect (mf/deps @state) @@ -696,29 +265,10 @@ {: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)}}]]) - - ])]]) + [:& gradients {:type (:type @state) + :stops (:stops @state) + :editing-stop (:editing-stop @state) + :on-select-stop handle-change-stop}] (if picking-color? [:div.picker-detail-wrapper @@ -730,42 +280,12 @@ :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] - (let [val (-> e dom/get-target dom/get-value)] - (reset! selected-library val))) - :value @selected-library} - [:option {:value "recent"} (t locale "workspace.libraries.colors.recent-colors")] - [:option {:value "file"} (t locale "workspace.libraries.colors.file-library")] - (for [[_ {:keys [name id]}] shared-libs] - [:option {:key id - :value id} name])] - - [: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)))} - i/plus]) - - [:div.color-bullet.button {:style {:background-color "white"} - :on-click #(st/emit! (dc/show-palette (parse-selected @selected-library)))} - i/palette] - - (for [[idx {:keys [id file-id value]}] (map-indexed vector @current-library-colors)] - [:div.color-bullet {:key (str "color-" idx) - :on-click (fn [] - (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! update current-color assoc - :r r :g g :b b - :h h :s s :v v) - ;; TODO: CHANGE TO SUPPORT GRADIENTS - #_(on-change value (:alpha current-color) id file-id))) - :style {:background-color value}}])]]] + [:& libraries {:current-color current-color + :on-select-color on-select-library-color}]] [:div.colorpicker-tabs [:div.colorpicker-tab {:class (when (= @active-tab :ramp) "active") :on-click (change-tab :ramp)} i/picker-ramp] diff --git a/frontend/src/app/main/ui/workspace/colorpicker/color_inputs.cljs b/frontend/src/app/main/ui/workspace/colorpicker/color_inputs.cljs new file mode 100644 index 0000000000..699762ad46 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/colorpicker/color_inputs.cljs @@ -0,0 +1,172 @@ +;; 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/. +;; +;; This Source Code Form is "Incompatible With Secondary Licenses", as +;; defined by the Mozilla Public License, v. 2.0. +;; +;; Copyright (c) 2020 UXBOX Labs SL + +(ns app.main.ui.workspace.colorpicker.color-inputs + (:require + [rumext.alpha :as mf] + [okulary.core :as l] + [cuerdas.core :as str] + [app.common.geom.point :as gpt] + [app.common.math :as math] + [app.common.uuid :refer [uuid]] + [app.util.dom :as dom] + [app.util.color :as uc] + [app.util.object :as obj] + [app.main.store :as st] + [app.main.refs :as refs] + [app.main.data.workspace.libraries :as dwl] + [app.main.data.colors :as dc] + [app.main.data.modal :as modal] + [app.main.ui.icons :as i] + [app.util.i18n :as i18n :refer [t]])) + +(mf/defc color-inputs [{:keys [type color on-change]}] + (let [{red :r green :g blue :b + hue :h saturation :s value :v + hex :hex alpha :alpha} color + + parse-hex (fn [val] (if (= (first val) \#) val (str \# val))) + + refs {:hex (mf/use-ref nil) + :r (mf/use-ref nil) + :g (mf/use-ref nil) + :b (mf/use-ref nil) + :h (mf/use-ref nil) + :s (mf/use-ref nil) + :v (mf/use-ref nil) + :alpha (mf/use-ref nil)} + + on-change-hex + (fn [e] + (let [val (-> e dom/get-target-val parse-hex)] + (when (uc/hex? val) + (let [[r g b] (uc/hex->rgb val) + [h s v] (uc/hex->hsv hex)] + (on-change {:hex val + :h h :s s :v v + :r r :g g :b b}))))) + + on-change-property + (fn [property max-value] + (fn [e] + (let [val (-> e dom/get-target-val (math/clamp 0 max-value)) + val (if (#{:s} property) (/ val 100) val)] + (when (not (nil? val)) + (if (#{:r :g :b} property) + (let [{:keys [r g b]} (merge color (hash-map property val)) + hex (uc/rgb->hex [r g b]) + [h s v] (uc/hex->hsv hex)] + (on-change {:hex hex + :h h :s s :v v + :r r :g g :b b})) + + (let [{:keys [h s v]} (merge color (hash-map property val)) + hex (uc/hsv->hex [h s v]) + [r g b] (uc/hex->rgb hex)] + (on-change {:hex hex + :h h :s s :v v + :r r :g g :b b}))))))) + + on-change-opacity + (fn [e] + (when-let [new-alpha (-> e dom/get-target-val (math/clamp 0 100) (/ 100))] + (on-change {:alpha new-alpha})))] + + + ;; Updates the inputs values when a property is changed in the parent + (mf/use-effect + (mf/deps color type) + (fn [] + (doseq [ref-key (keys refs)] + (let [property-val (get color ref-key) + property-ref (get refs ref-key)] + (when (and property-val property-ref) + (when-let [node (mf/ref-val property-ref)] + (case ref-key + (:s :alpha) (dom/set-value! node (math/round (* property-val 100))) + :hex (dom/set-value! node property-val) + (dom/set-value! node (math/round property-val))))))))) + + [:div.color-values + [:input {:id "hex-value" + :ref (:hex refs) + :default-value hex + :on-change on-change-hex}] + + (if (= type :rgb) + [:* + [:input {:id "red-value" + :ref (:r refs) + :type "number" + :min 0 + :max 255 + :default-value red + :on-change (on-change-property :r 255)}] + + [:input {:id "green-value" + :ref (:g refs) + :type "number" + :min 0 + :max 255 + :default-value green + :on-change (on-change-property :g 255)}] + + [:input {:id "blue-value" + :ref (:b refs) + :type "number" + :min 0 + :max 255 + :default-value blue + :on-change (on-change-property :b 255)}]] + [:* + [:input {:id "hue-value" + :ref (:h refs) + :type "number" + :min 0 + :max 360 + :default-value hue + :on-change (on-change-property :h 360)}] + + [:input {:id "saturation-value" + :ref (:s refs) + :type "number" + :min 0 + :max 100 + :step 1 + :default-value saturation + :on-change (on-change-property :s 100)}] + + [:input {:id "value-value" + :ref (:v refs) + :type "number" + :min 0 + :max 255 + :default-value value + :on-change (on-change-property :v 255)}]]) + + [:input.alpha-value {:id "alpha-value" + :ref (:alpha refs) + :type "number" + :min 0 + :step 1 + :max 100 + :default-value (if (= alpha :multiple) "" (math/precision alpha 2)) + :on-change on-change-opacity}] + + [:label.hex-label {:for "hex-value"} "HEX"] + (if (= type :rgb) + [:* + [:label.red-label {:for "red-value"} "R"] + [:label.green-label {:for "green-value"} "G"] + [:label.blue-label {:for "blue-value"} "B"]] + [:* + [:label.red-label {:for "hue-value"} "H"] + [:label.green-label {:for "saturation-value"} "S"] + [:label.blue-label {:for "value-value"} "V"]]) + [:label.alpha-label {:for "alpha-value"} "A"]])) diff --git a/frontend/src/app/main/ui/workspace/colorpicker/gradients.cljs b/frontend/src/app/main/ui/workspace/colorpicker/gradients.cljs new file mode 100644 index 0000000000..459274b99d --- /dev/null +++ b/frontend/src/app/main/ui/workspace/colorpicker/gradients.cljs @@ -0,0 +1,54 @@ +;; 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/. +;; +;; This Source Code Form is "Incompatible With Secondary Licenses", as +;; defined by the Mozilla Public License, v. 2.0. +;; +;; Copyright (c) 2020 UXBOX Labs SL + +(ns app.main.ui.workspace.colorpicker.gradients + (:require + [rumext.alpha :as mf] + [okulary.core :as l] + [cuerdas.core :as str] + [app.common.geom.point :as gpt] + [app.common.math :as math] + [app.common.uuid :refer [uuid]] + [app.util.dom :as dom] + [app.util.color :as uc] + [app.util.object :as obj] + [app.main.store :as st] + [app.main.refs :as refs] + [app.main.data.workspace.libraries :as dwl] + [app.main.data.colors :as dc] + [app.main.data.modal :as modal] + [app.main.ui.icons :as i] + [app.util.i18n :as i18n :refer [t]])) + +(defn gradient->string [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-css (str/join "," (map format-stop stops))] + (str/fmt "linear-gradient(90deg, %s)" gradient-css))) + +(mf/defc gradients [{:keys [type stops editing-stop on-select-stop]}] + (when (#{:linear-gradient :radial-gradient} type) + [:div.gradient-stops + [:div.gradient-background-wrapper + [:div.gradient-background {:style {:background (gradient->string stops)}}]] + + [:div.gradient-stop-wrapper + (for [[offset value] stops] + [:div.gradient-stop + {:class (when (= editing-stop offset) "active") + :on-click (partial on-select-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)}}]])])]])) diff --git a/frontend/src/app/main/ui/workspace/colorpicker/harmony.cljs b/frontend/src/app/main/ui/workspace/colorpicker/harmony.cljs new file mode 100644 index 0000000000..79588ac9fb --- /dev/null +++ b/frontend/src/app/main/ui/workspace/colorpicker/harmony.cljs @@ -0,0 +1,154 @@ +;; 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/. +;; +;; This Source Code Form is "Incompatible With Secondary Licenses", as +;; defined by the Mozilla Public License, v. 2.0. +;; +;; Copyright (c) 2020 UXBOX Labs SL + +(ns app.main.ui.workspace.colorpicker.harmony + (:require + [rumext.alpha :as mf] + [okulary.core :as l] + [cuerdas.core :as str] + [app.common.geom.point :as gpt] + [app.common.math :as math] + [app.common.uuid :refer [uuid]] + [app.util.dom :as dom] + [app.util.color :as uc] + [app.util.object :as obj] + [app.main.store :as st] + [app.main.refs :as refs] + [app.main.data.workspace.libraries :as dwl] + [app.main.data.colors :as dc] + [app.main.data.modal :as modal] + [app.main.ui.icons :as i] + [app.util.i18n :as i18n :refer [t]] + [app.main.ui.workspace.colorpicker.slider-selector :refer [slider-selector]])) + + +(defn create-color-wheel + [canvas-node] + (let [ctx (.getContext canvas-node "2d") + width (obj/get canvas-node "width") + height (obj/get canvas-node "height") + radius (/ width 2) + cx (/ width 2) + cy (/ width 2) + step 0.2] + + (.clearRect ctx 0 0 width height) + + (doseq [degrees (range 0 360 step)] + (let [degrees-rad (math/radians degrees) + x (* radius (math/cos (- degrees-rad))) + y (* radius (math/sin (- degrees-rad)))] + (obj/set! ctx "strokeStyle" (str/format "hsl(%s, 100%, 50%)" degrees)) + (.beginPath ctx) + (.moveTo ctx cx cy) + (.lineTo ctx (+ cx x) (+ cy y)) + (.stroke ctx))) + + (let [grd (.createRadialGradient ctx cx cy 0 cx cx radius)] + (.addColorStop grd 0 "white") + (.addColorStop grd 1 "rgba(255, 255, 255, 0") + (obj/set! ctx "fillStyle" grd) + + (.beginPath ctx) + (.arc ctx cx cy radius 0 (* 2 math/PI) true) + (.closePath ctx) + (.fill ctx)))) + +(defn color->point + [canvas-side hue saturation] + (let [hue-rad (math/radians (- hue)) + comp-x (* saturation (math/cos hue-rad)) + comp-y (* saturation (math/sin hue-rad)) + x (+ (/ canvas-side 2) (* comp-x (/ canvas-side 2))) + y (+ (/ canvas-side 2) (* comp-y (/ canvas-side 2)))] + (gpt/point x y))) + +(mf/defc harmony-selector [{:keys [color on-change]}] + (let [canvas-ref (mf/use-ref nil) + {hue :h saturation :s value :v alpha :alpha} color + + canvas-side 152 + pos-current (color->point canvas-side hue saturation) + pos-complement (color->point canvas-side (mod (+ hue 180) 360) saturation) + dragging? (mf/use-state false) + + calculate-pos (fn [ev] + (let [{:keys [left right top bottom]} (-> ev dom/get-target dom/get-bounding-rect) + {:keys [x y]} (-> ev dom/get-client-position) + px (math/clamp (/ (- x left) (- right left)) 0 1) + py (math/clamp (/ (- y top) (- bottom top)) 0 1) + + px (- (* 2 px) 1) + py (- (* 2 py) 1) + + angle (math/degrees (math/atan2 px py)) + new-hue (math/precision (mod (- angle 90 ) 360) 2) + new-saturation (math/clamp (math/distance [px py] [0 0]) 0 1) + hex (uc/hsv->hex [new-hue new-saturation value]) + [r g b] (uc/hex->rgb hex)] + (on-change {:hex hex + :r r :g g :b b + :h new-hue + :s new-saturation}))) + + on-change-value (fn [new-value] + (let [hex (uc/hsv->hex [hue saturation new-value]) + [r g b] (uc/hex->rgb hex)] + (on-change {:hex hex + :r r :g g :b b + :v new-value}))) + on-complement-click (fn [ev] + (let [new-hue (mod (+ hue 180) 360) + hex (uc/hsv->hex [new-hue saturation value]) + [r g b] (uc/hex->rgb hex)] + (on-change {:hex hex + :r r :g g :b b + :h new-hue + :s saturation}))) + + on-change-opacity (fn [new-alpha] (on-change {:alpha new-alpha}))] + + (mf/use-effect + (mf/deps canvas-ref) + (fn [] (when canvas-ref + (create-color-wheel (mf/ref-val canvas-ref))))) + + [:div.harmony-selector + [:div.hue-wheel-wrapper + [:canvas.hue-wheel + {:ref canvas-ref + :width canvas-side + :height canvas-side + :on-mouse-down #(reset! dragging? true) + :on-mouse-up #(reset! dragging? false) + :on-pointer-down (partial dom/capture-pointer) + :on-pointer-up (partial dom/release-pointer) + :on-click calculate-pos + :on-mouse-move #(when @dragging? (calculate-pos %))}] + [:div.handler {:style {:pointer-events "none" + :left (:x pos-current) + :top (:y pos-current)}}] + [:div.handler.complement {:style {:left (:x pos-complement) + :top (:y pos-complement) + :cursor "pointer"} + :on-click on-complement-click}]] + [:div.handlers-wrapper + [:& slider-selector {:class "value" + :vertical? true + :reverse? true + :value value + :max-value 255 + :vertical true + :on-change on-change-value}] + [:& slider-selector {:class "opacity" + :vertical? true + :value alpha + :max-value 1 + :vertical true + :on-change on-change-opacity}]]])) diff --git a/frontend/src/app/main/ui/workspace/colorpicker/hsva.cljs b/frontend/src/app/main/ui/workspace/colorpicker/hsva.cljs new file mode 100644 index 0000000000..c7cbfde794 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/colorpicker/hsva.cljs @@ -0,0 +1,57 @@ +;; 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/. +;; +;; This Source Code Form is "Incompatible With Secondary Licenses", as +;; defined by the Mozilla Public License, v. 2.0. +;; +;; Copyright (c) 2020 UXBOX Labs SL + +(ns app.main.ui.workspace.colorpicker.hsva + (:require + [rumext.alpha :as mf] + [okulary.core :as l] + [cuerdas.core :as str] + [app.common.geom.point :as gpt] + [app.common.math :as math] + [app.common.uuid :refer [uuid]] + [app.util.dom :as dom] + [app.util.color :as uc] + [app.util.object :as obj] + [app.main.store :as st] + [app.main.refs :as refs] + [app.main.data.workspace.libraries :as dwl] + [app.main.data.colors :as dc] + [app.main.data.modal :as modal] + [app.main.ui.icons :as i] + [app.util.i18n :as i18n :refer [t]] + [app.main.ui.workspace.colorpicker.slider-selector :refer [slider-selector]])) + +(mf/defc hsva-selector [{:keys [color on-change]}] + (let [{hue :h saturation :s value :v alpha :alpha} color + handle-change-slider (fn [key] + (fn [new-value] + (let [change (hash-map key new-value) + {:keys [h s v]} (merge color change) + hex (uc/hsv->hex [h s v]) + [r g b] (uc/hex->rgb hex)] + (on-change (merge change + {:hex hex + :r r :g g :b b}))))) + on-change-opacity (fn [new-alpha] (on-change {:alpha new-alpha}))] + [:div.hsva-selector + [:span.hsva-selector-label "H"] + [:& slider-selector + {:class "hue" :max-value 360 :value hue :on-change (handle-change-slider :h)}] + + [:span.hsva-selector-label "S"] + [:& slider-selector + {:class "saturation" :max-value 1 :value saturation :on-change (handle-change-slider :s)}] + + [:span.hsva-selector-label "V"] + [:& slider-selector + {:class "value" :reverse? true :max-value 255 :value value :on-change (handle-change-slider :v)}] + + [:span.hsva-selector-label "A"] + [:& slider-selector + {:class "opacity" :max-value 1 :value alpha :on-change on-change-opacity}]])) diff --git a/frontend/src/app/main/ui/workspace/colorpicker/libraries.cljs b/frontend/src/app/main/ui/workspace/colorpicker/libraries.cljs new file mode 100644 index 0000000000..56d1fe0a8f --- /dev/null +++ b/frontend/src/app/main/ui/workspace/colorpicker/libraries.cljs @@ -0,0 +1,100 @@ +;; 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/. +;; +;; This Source Code Form is "Incompatible With Secondary Licenses", as +;; defined by the Mozilla Public License, v. 2.0. +;; +;; Copyright (c) 2020 UXBOX Labs SL + +(ns app.main.ui.workspace.colorpicker.libraries + (:require + [rumext.alpha :as mf] + [okulary.core :as l] + [cuerdas.core :as str] + [app.common.geom.point :as gpt] + [app.common.math :as math] + [app.common.uuid :refer [uuid]] + [app.util.dom :as dom] + [app.util.color :as uc] + [app.util.object :as obj] + [app.main.store :as st] + [app.main.refs :as refs] + [app.main.data.workspace.libraries :as dwl] + [app.main.data.colors :as dc] + [app.main.data.modal :as modal] + [app.main.ui.icons :as i] + [app.util.i18n :as i18n :refer [t]] + [app.main.ui.workspace.colorpicker.gradients :refer [gradients]] + [app.main.ui.workspace.colorpicker.harmony :refer [harmony-selector]] + [app.main.ui.workspace.colorpicker.hsva :refer [hsva-selector]] + [app.main.ui.workspace.colorpicker.ramp :refer [ramp-selector]] + [app.main.ui.workspace.colorpicker.color-inputs :refer [color-inputs]])) + +(mf/defc libraries [{:keys [current-color on-select-color]}] + (let [selected-library (mf/use-state "recent") + current-library-colors (mf/use-state []) + + shared-libs (mf/deref refs/workspace-libraries) + file-colors (mf/deref refs/workspace-file-colors) + recent-colors (mf/deref refs/workspace-recent-colors) + locale (mf/deref i18n/locale) + + parse-selected + (fn [selected] + (if (#{"recent" "file"} selected) + (keyword selected) + (uuid selected)) )] + + ;; Load library colors when the select is changed + (mf/use-effect + (mf/deps @selected-library) + (fn [] + (let [mapped-colors + (cond + (= @selected-library "recent") + (map #(hash-map :value %) (reverse (or recent-colors []))) + + (= @selected-library "file") + (map #(select-keys % [:id :value]) (vals file-colors)) + + :else ;; Library UUID + (map #(merge {:file-id (uuid @selected-library)} (select-keys % [:id :value])) + (vals (get-in shared-libs [(uuid @selected-library) :data :colors]))))] + (reset! current-library-colors (into [] mapped-colors))))) + + ;; If the file colors change and the file option is selected updates the state + (mf/use-effect + (mf/deps file-colors) + (fn [] (when (= @selected-library "file") + (let [colors (map #(select-keys % [:id :value]) (vals file-colors))] + (reset! current-library-colors (into [] colors)))))) + + + [:div.libraries + [:select {:on-change (fn [e] + (when-let [val (dom/get-target-val e)] + (reset! selected-library val))) + :value @selected-library} + [:option {:value "recent"} (t locale "workspace.libraries.colors.recent-colors")] + [:option {:value "file"} (t locale "workspace.libraries.colors.file-library")] + + (for [[_ {:keys [name id]}] shared-libs] + [:option {:key id + :value id} name])] + + [:div.selected-colors + (when (= "file" @selected-library) + [:div.color-bullet.button.plus-button {:style {:background-color "white"} + :on-click #(st/emit! (dwl/add-color current-color))} + i/plus]) + + [:div.color-bullet.button {:style {:background-color "white"} + :on-click #(st/emit! (dc/show-palette (parse-selected @selected-library)))} + i/palette] + + (for [[idx color] (map-indexed vector @current-library-colors)] + [:div.color-bullet + {:key (str "color-" idx) + :on-click #(on-select-color color) + :style {:background (uc/color->background color)}}])]])) diff --git a/frontend/src/app/main/ui/workspace/colorpicker/pixel_overlay.cljs b/frontend/src/app/main/ui/workspace/colorpicker/pixel_overlay.cljs new file mode 100644 index 0000000000..faa61fbe58 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/colorpicker/pixel_overlay.cljs @@ -0,0 +1,29 @@ +;; 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/. +;; +;; This Source Code Form is "Incompatible With Secondary Licenses", as +;; defined by the Mozilla Public License, v. 2.0. +;; +;; Copyright (c) 2020 UXBOX Labs SL + +(ns app.main.ui.workspace.colorpicker.pixel-overlay + (:require + [rumext.alpha :as mf] + [okulary.core :as l] + [cuerdas.core :as str] + [app.common.geom.point :as gpt] + [app.common.math :as math] + [app.common.uuid :refer [uuid]] + [app.util.dom :as dom] + [app.util.color :as uc] + [app.util.object :as obj] + [app.main.store :as st] + [app.main.refs :as refs] + [app.main.data.workspace.libraries :as dwl] + [app.main.data.colors :as dc] + [app.main.data.modal :as modal] + [app.main.ui.icons :as i] + [app.util.i18n :as i18n :refer [t]])) + + diff --git a/frontend/src/app/main/ui/workspace/colorpicker/pixel_picker.cljs b/frontend/src/app/main/ui/workspace/colorpicker/pixel_picker.cljs new file mode 100644 index 0000000000..21a35a0fc5 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/colorpicker/pixel_picker.cljs @@ -0,0 +1,29 @@ +;; 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/. +;; +;; This Source Code Form is "Incompatible With Secondary Licenses", as +;; defined by the Mozilla Public License, v. 2.0. +;; +;; Copyright (c) 2020 UXBOX Labs SL + +(ns app.main.ui.workspace.colorpicker.pixel-picker + (:require + [rumext.alpha :as mf] + [okulary.core :as l] + [cuerdas.core :as str] + [app.common.geom.point :as gpt] + [app.common.math :as math] + [app.common.uuid :refer [uuid]] + [app.util.dom :as dom] + [app.util.color :as uc] + [app.util.object :as obj] + [app.main.store :as st] + [app.main.refs :as refs] + [app.main.data.workspace.libraries :as dwl] + [app.main.data.colors :as dc] + [app.main.data.modal :as modal] + [app.main.ui.icons :as i] + [app.util.i18n :as i18n :refer [t]])) + + diff --git a/frontend/src/app/main/ui/workspace/colorpicker/ramp.cljs b/frontend/src/app/main/ui/workspace/colorpicker/ramp.cljs new file mode 100644 index 0000000000..8cab703620 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/colorpicker/ramp.cljs @@ -0,0 +1,92 @@ +;; 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/. +;; +;; This Source Code Form is "Incompatible With Secondary Licenses", as +;; defined by the Mozilla Public License, v. 2.0. +;; +;; Copyright (c) 2020 UXBOX Labs SL + +(ns app.main.ui.workspace.colorpicker.ramp + (:require + [rumext.alpha :as mf] + [okulary.core :as l] + [cuerdas.core :as str] + [app.common.geom.point :as gpt] + [app.common.math :as math] + [app.common.uuid :refer [uuid]] + [app.util.dom :as dom] + [app.util.color :as uc] + [app.util.object :as obj] + [app.main.store :as st] + [app.main.refs :as refs] + [app.main.data.workspace.libraries :as dwl] + [app.main.data.colors :as dc] + [app.main.data.modal :as modal] + [app.main.ui.icons :as i] + [app.util.i18n :as i18n :refer [t]] + [app.main.ui.workspace.colorpicker.slider-selector :refer [slider-selector]])) + + +(mf/defc value-saturation-selector [{:keys [hue saturation value on-change]}] + (let [dragging? (mf/use-state false) + calculate-pos + (fn [ev] + (let [{:keys [left right top bottom]} (-> ev dom/get-target dom/get-bounding-rect) + {:keys [x y]} (-> ev dom/get-client-position) + px (math/clamp (/ (- x left) (- right left)) 0 1) + py (* 255 (- 1 (math/clamp (/ (- y top) (- bottom top)) 0 1)))] + (on-change px py)))] + [:div.value-saturation-selector + {:on-mouse-down #(reset! dragging? true) + :on-mouse-up #(reset! dragging? false) + :on-pointer-down (partial dom/capture-pointer) + :on-pointer-up (partial dom/release-pointer) + :on-click calculate-pos + :on-mouse-move #(when @dragging? (calculate-pos %))} + [:div.handler {:style {:pointer-events "none" + :left (str (* 100 saturation) "%") + :top (str (* 100 (- 1 (/ value 255))) "%")}}]])) + + +(mf/defc ramp-selector [{:keys [color on-change]}] + (let [{hue :h saturation :s value :v alpha :alpha} color + + on-change-value-saturation + (fn [new-saturation new-value] + (let [hex (uc/hsv->hex [hue new-saturation new-value]) + [r g b] (uc/hex->rgb hex)] + (on-change {:hex hex + :r r :g g :b b + :s new-saturation + :v new-value}))) + + on-change-hue + (fn [new-hue] + (let [hex (uc/hsv->hex [new-hue saturation value]) + [r g b] (uc/hex->rgb hex)] + (on-change {:hex hex + :r r :g g :b b + :h new-hue} ))) + + on-change-opacity + (fn [new-opacity] + (on-change {:alpha new-opacity} ))] + [:* + [:& value-saturation-selector + {:hue hue + :saturation saturation + :value value + :on-change on-change-value-saturation}] + + [:div.shade-selector + [:div.color-bullet] + [:& slider-selector {:class "hue" + :max-value 360 + :value hue + :on-change on-change-hue}] + + [:& slider-selector {:class "opacity" + :max-value 1 + :value alpha + :on-change on-change-opacity}]]])) diff --git a/frontend/src/app/main/ui/workspace/colorpicker/slider_selector.cljs b/frontend/src/app/main/ui/workspace/colorpicker/slider_selector.cljs new file mode 100644 index 0000000000..e6b4e0c4fa --- /dev/null +++ b/frontend/src/app/main/ui/workspace/colorpicker/slider_selector.cljs @@ -0,0 +1,68 @@ +;; 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/. +;; +;; This Source Code Form is "Incompatible With Secondary Licenses", as +;; defined by the Mozilla Public License, v. 2.0. +;; +;; Copyright (c) 2020 UXBOX Labs SL + +(ns app.main.ui.workspace.colorpicker.slider-selector + (:require + [rumext.alpha :as mf] + [okulary.core :as l] + [cuerdas.core :as str] + [app.common.geom.point :as gpt] + [app.common.math :as math] + [app.common.uuid :refer [uuid]] + [app.util.dom :as dom] + [app.util.color :as uc] + [app.util.object :as obj] + [app.main.store :as st] + [app.main.refs :as refs] + [app.main.data.workspace.libraries :as dwl] + [app.main.data.colors :as dc] + [app.main.data.modal :as modal] + [app.main.ui.icons :as i] + [app.util.i18n :as i18n :refer [t]])) + +(mf/defc slider-selector + [{:keys [value class min-value max-value vertical? reverse? on-change]}] + (let [min-value (or min-value 0) + max-value (or max-value 1) + dragging? (mf/use-state false) + calculate-pos + (fn [ev] + (when on-change + (let [{:keys [left right top bottom]} (-> ev dom/get-target dom/get-bounding-rect) + {:keys [x y]} (-> ev dom/get-client-position) + unit-value (if vertical? + (math/clamp (/ (- bottom y) (- bottom top)) 0 1) + (math/clamp (/ (- x left) (- right left)) 0 1)) + unit-value (if reverse? + (math/abs (- unit-value 1.0)) + unit-value) + value (+ min-value (* unit-value (- max-value min-value)))] + (on-change (math/precision value 2)))))] + + [:div.slider-selector + {:class (str (if vertical? "vertical " "") class) + :on-mouse-down #(reset! dragging? true) + :on-mouse-up #(reset! dragging? false) + :on-pointer-down (partial dom/capture-pointer) + :on-pointer-up (partial dom/release-pointer) + :on-click calculate-pos + :on-mouse-move #(when @dragging? (calculate-pos %))} + + (let [value-percent (* (/ (- value min-value) + (- max-value min-value)) 100) + + value-percent (if reverse? + (math/abs (- value-percent 100)) + value-percent) + value-percent-str (str value-percent "%") + + style-common #js {:pointerEvents "none"} + style-horizontal (obj/merge! #js {:left value-percent-str} style-common) + style-vertical (obj/merge! #js {:bottom value-percent-str} style-common)] + [:div.handler {:style (if vertical? style-vertical style-horizontal)}])])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs index 98f3f3cbc3..2f86c55904 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs @@ -34,18 +34,6 @@ (modal/show! :colorpicker props)))) -;; 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))) @@ -99,6 +87,7 @@ ;; (when on-change (on-change new-value new-opacity id file-id))) handle-pick-color (fn [color] + (prn "handle-pick-color" color) (reset! state color) (when on-change (on-change color))) @@ -136,7 +125,7 @@ [:div.row-flex.color-data [:span.color-th {:class (when (and (:id color) (not= (:id color) :multiple)) "color-name") - :style {:background (as-background color)} + :style {:background (uc/color->background color)} :on-click (color-picker-callback @state handle-pick-color handle-open handle-close)} (when (= value :multiple) "?")] @@ -149,7 +138,10 @@ ;; Rendering a gradient (:gradient color) [:div.color-info - [:div.color-name (str (get-in color [:gradient :type]))]] + [:div.color-name + (case (get-in color [:gradient :type]) + :linear "Linear gradient" + :radial "Radial gradient")]] ;; Rendering a plain color/opacity :else diff --git a/frontend/src/app/util/color.cljs b/frontend/src/app/util/color.cljs index 7075bc3d48..a77a63d0fa 100644 --- a/frontend/src/app/util/color.cljs +++ b/frontend/src/app/util/color.cljs @@ -90,3 +90,16 @@ (str/fmt "linear-gradient(to bottom, %s)" stops-css) (str/fmt "radial-gradient(circle, %s)" stops-css)))) +;; TODO: REMOVE `VALUE` WHEN COLOR IS INTEGRATED +(defn color->background [{:keys [color opacity gradient value]}] + (let [color (or color value) + opacity (or opacity 1)] + (cond + (and gradient (not= :multiple gradient)) + (gradient->css gradient) + + (not= color :multiple) + (let [[r g b] (hex->rgb (or color value))] + (str/fmt "rgba(%s, %s, %s, %s)" r g b opacity)) + + :else "transparent"))) From 69fb1426d437bbd0df3ae931d94079230cf78c85 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 13 Oct 2020 09:31:22 +0200 Subject: [PATCH 04/14] :sparkles: Changes to library model --- common/app/common/pages.cljc | 32 +++++++- .../styles/main/partials/colorpicker.scss | 4 + .../app/main/ui/workspace/colorpicker.cljs | 80 ++++++++++++------- .../workspace/colorpicker/color_inputs.cljs | 23 +++--- .../ui/workspace/colorpicker/harmony.cljs | 15 ++-- .../main/ui/workspace/colorpicker/hsva.cljs | 10 ++- .../main/ui/workspace/colorpicker/ramp.cljs | 11 +-- .../app/main/ui/workspace/shapes/frame.cljs | 11 +++ .../main/ui/workspace/sidebar/options.cljs | 16 ++-- .../ui/workspace/sidebar/options/fill.cljs | 5 +- .../ui/workspace/sidebar/options/page.cljs | 5 +- .../sidebar/options/rows/color_row.cljs | 63 +++++++-------- 12 files changed, 170 insertions(+), 105 deletions(-) diff --git a/common/app/common/pages.cljc b/common/app/common/pages.cljc index 81791d6520..b0229d42ec 100644 --- a/common/app/common/pages.cljc +++ b/common/app/common/pages.cljc @@ -55,6 +55,33 @@ (>= % min-safe-int) (<= % max-safe-int))) + +(s/def :internal.page.gradient.stop/color ::string) +(s/def :internal.page.gradient.stop/opacity ::safe-number) +(s/def :internal.page.gradient.stop/offset ::safe-number) + +(s/def :internal.page.gradient/start-x ::safe-number) +(s/def :internal.page.gradient/start-y ::safe-number) +(s/def :internal.page.gradient/end-x ::safe-number) +(s/def :internal.page.gradient/end-y ::safe-number) +(s/def :internal.page.gradient/width ::safe-number) + +(s/def :internal.page.gradient/stop + (s/keys :req-un [:internal.page.gradient.stop/color + :internal.page.gradient.stop/opacity + :internal.page.gradient.stop/offset])) + +(s/def :internal.page.gradient/stops + (s/map-of ::safe-number :internal.page.gradient/stop)) + +(s/def ::gradient + (s/keys :req-un [:internal.page.gradient/start-x + :internal.page.gradient/start-y + :internal.page.gradient/end-x + :internal.page.gradient/end-y + :internal.page.gradient/width + :internal.page.gradient/stops])) + ;; Page Options (s/def :internal.page.grid.color/value string?) (s/def :internal.page.grid.color/opacity ::safe-number) @@ -110,10 +137,13 @@ (s/def :internal.shape/blocked boolean?) (s/def :internal.shape/collapsed boolean?) (s/def :internal.shape/content any?) + (s/def :internal.shape/fill-color string?) +(s/def :internal.shape/fill-opacity ::safe-number) +(s/def :internal.shape/fill-gradient (s/nilable ::gradient)) (s/def :internal.shape/fill-color-ref-file (s/nilable uuid?)) (s/def :internal.shape/fill-color-ref-id (s/nilable uuid?)) -(s/def :internal.shape/fill-opacity ::safe-number) + (s/def :internal.shape/font-family string?) (s/def :internal.shape/font-size ::safe-integer) (s/def :internal.shape/font-style string?) diff --git a/frontend/resources/styles/main/partials/colorpicker.scss b/frontend/resources/styles/main/partials/colorpicker.scss index fc3f6c457d..45a62c6c0d 100644 --- a/frontend/resources/styles/main/partials/colorpicker.scss +++ b/frontend/resources/styles/main/partials/colorpicker.scss @@ -280,6 +280,10 @@ justify-items: center; grid-column-gap: 0.25rem; + &.disable-opacity { + grid-template-columns: 3.5rem repeat(3, 1fr); + } + input { width: 100%; margin: 0; diff --git a/frontend/src/app/main/ui/workspace/colorpicker.cljs b/frontend/src/app/main/ui/workspace/colorpicker.cljs index 99c2c45abe..d8f7936566 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker.cljs @@ -83,6 +83,7 @@ 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) @@ -113,7 +114,7 @@ :width 1.0}) (mf/defc colorpicker - [{:keys [data on-change on-accept]}] + [{:keys [data disable-gradient disable-opacity on-change on-accept]}] (let [state (mf/use-state (data->state data)) active-tab (mf/use-state :ramp #_:harmony #_:hsva) locale (mf/deref i18n/locale) @@ -138,8 +139,9 @@ 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) + (swap! state #(cond-> % + true (update :current-color merge changes) + editing-stop (update-in [:stops editing-stop] merge changes))) #_(when (:hex changes) (reset! value-ref (:hex changes))) @@ -150,10 +152,12 @@ 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) - (st/emit! (dc/select-gradient-stop offset)))) + (when-let [offset-color (get-in @state [:stops offset])] + (do (swap! state assoc + :current-color offset-color + :editing-stop offset) + + (st/emit! (dc/select-gradient-stop offset))))) on-select-library-color (fn [color] (prn "color" color)) @@ -210,20 +214,21 @@ (mf/use-effect (mf/deps picking-color? picked-color) - (fn [] (when picking-color? - (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! state update :current-color assoc - :r r :g g :b b - :h h :s s :v v - :hex hex) + (fn [] + (when picking-color? + (let [[r g b] (or picked-color [0 0 0]) + hex (uc/rgb->hex [r g b]) + [h s v] (uc/hex->hsv hex)] - ;; TODO: UPDATE TO USE GRADIENTS - #_(reset! value-ref hex) - #_(when picked-color-select - (on-change hex (:alpha current-color) nil nil picked-shift?)))))) + (swap! state update :current-color assoc + :r r :g g :b b + :h h :s s :v v + :hex hex) + + ;; 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 @@ -256,14 +261,15 @@ (st/emit! (dc/start-picker)))} i/picker] - [:div.gradients-buttons - [:button.gradient.linear-gradient - {:on-click (on-activate-gradient :linear-gradient) - :class (when (= :linear-gradient (:type @state)) "active")}] + (when (not disable-gradient) + [:div.gradients-buttons + [:button.gradient.linear-gradient + {:on-click (on-activate-gradient :linear-gradient) + :class (when (= :linear-gradient (:type @state)) "active")}] - [:button.gradient.radial-gradient - {:on-click (on-activate-gradient :radial-gradient) - :class (when (= :radial-gradient (:type @state)) "active")}]]] + [:button.gradient.radial-gradient + {:on-click (on-activate-gradient :radial-gradient) + :class (when (= :radial-gradient (:type @state)) "active")}]])] [:& gradients {:type (:type @state) :stops (:stops @state) @@ -275,12 +281,19 @@ [: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 + :disable-opacity disable-opacity + :on-change handle-change-color}] + :harmony [:& harmony-selector {:color current-color + :disable-opacity disable-opacity + :on-change handle-change-color}] + :hsva [:& hsva-selector {:color current-color + :disable-opacity disable-opacity + :on-change handle-change-color}] nil)) [:& color-inputs {:type (if (= @active-tab :hsva) :hsv :rgb) + :disable-opacity disable-opacity :color current-color :on-change handle-change-color}] @@ -321,7 +334,10 @@ (mf/defc colorpicker-modal {::mf/register modal/components ::mf/register-as :colorpicker} - [{:keys [x y default data page position on-change on-close on-accept] :as props}] + [{:keys [x y default data page position + disable-gradient + disable-opacity + on-change on-close on-accept] :as props}] (let [vport (mf/deref viewport) dirty? (mf/use-var false) last-change (mf/use-var nil) @@ -350,6 +366,8 @@ [:div.colorpicker-tooltip {:style (clj->js style)} [:& colorpicker {:data data + :disable-gradient disable-gradient + :disable-opacity disable-opacity :on-change handle-change :on-accept on-accept}]])) diff --git a/frontend/src/app/main/ui/workspace/colorpicker/color_inputs.cljs b/frontend/src/app/main/ui/workspace/colorpicker/color_inputs.cljs index 699762ad46..72a6341bb7 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/color_inputs.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker/color_inputs.cljs @@ -26,7 +26,7 @@ [app.main.ui.icons :as i] [app.util.i18n :as i18n :refer [t]])) -(mf/defc color-inputs [{:keys [type color on-change]}] +(mf/defc color-inputs [{:keys [type color disable-opacity on-change]}] (let [{red :r green :g blue :b hue :h saturation :s value :v hex :hex alpha :alpha} color @@ -94,6 +94,7 @@ (dom/set-value! node (math/round property-val))))))))) [:div.color-values + {:class (when disable-opacity "disable-opacity")} [:input {:id "hex-value" :ref (:hex refs) :default-value hex @@ -150,14 +151,15 @@ :default-value value :on-change (on-change-property :v 255)}]]) - [:input.alpha-value {:id "alpha-value" - :ref (:alpha refs) - :type "number" - :min 0 - :step 1 - :max 100 - :default-value (if (= alpha :multiple) "" (math/precision alpha 2)) - :on-change on-change-opacity}] + (when (not disable-opacity) + [:input.alpha-value {:id "alpha-value" + :ref (:alpha refs) + :type "number" + :min 0 + :step 1 + :max 100 + :default-value (if (= alpha :multiple) "" (math/precision alpha 2)) + :on-change on-change-opacity}]) [:label.hex-label {:for "hex-value"} "HEX"] (if (= type :rgb) @@ -169,4 +171,5 @@ [:label.red-label {:for "hue-value"} "H"] [:label.green-label {:for "saturation-value"} "S"] [:label.blue-label {:for "value-value"} "V"]]) - [:label.alpha-label {:for "alpha-value"} "A"]])) + (when (not disable-opacity) + [:label.alpha-label {:for "alpha-value"} "A"])])) diff --git a/frontend/src/app/main/ui/workspace/colorpicker/harmony.cljs b/frontend/src/app/main/ui/workspace/colorpicker/harmony.cljs index 79588ac9fb..a17a510061 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/harmony.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker/harmony.cljs @@ -69,7 +69,7 @@ y (+ (/ canvas-side 2) (* comp-y (/ canvas-side 2)))] (gpt/point x y))) -(mf/defc harmony-selector [{:keys [color on-change]}] +(mf/defc harmony-selector [{:keys [color disable-opacity on-change]}] (let [canvas-ref (mf/use-ref nil) {hue :h saturation :s value :v alpha :alpha} color @@ -146,9 +146,10 @@ :max-value 255 :vertical true :on-change on-change-value}] - [:& slider-selector {:class "opacity" - :vertical? true - :value alpha - :max-value 1 - :vertical true - :on-change on-change-opacity}]]])) + (when (not disable-opacity) + [:& slider-selector {:class "opacity" + :vertical? true + :value alpha + :max-value 1 + :vertical true + :on-change on-change-opacity}])]])) diff --git a/frontend/src/app/main/ui/workspace/colorpicker/hsva.cljs b/frontend/src/app/main/ui/workspace/colorpicker/hsva.cljs index c7cbfde794..5112f74732 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/hsva.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker/hsva.cljs @@ -27,7 +27,7 @@ [app.util.i18n :as i18n :refer [t]] [app.main.ui.workspace.colorpicker.slider-selector :refer [slider-selector]])) -(mf/defc hsva-selector [{:keys [color on-change]}] +(mf/defc hsva-selector [{:keys [color disable-opacity on-change]}] (let [{hue :h saturation :s value :v alpha :alpha} color handle-change-slider (fn [key] (fn [new-value] @@ -52,6 +52,8 @@ [:& slider-selector {:class "value" :reverse? true :max-value 255 :value value :on-change (handle-change-slider :v)}] - [:span.hsva-selector-label "A"] - [:& slider-selector - {:class "opacity" :max-value 1 :value alpha :on-change on-change-opacity}]])) + (when (not disable-opacity) + [:* + [:span.hsva-selector-label "A"] + [:& slider-selector + {:class "opacity" :max-value 1 :value alpha :on-change on-change-opacity}]])])) diff --git a/frontend/src/app/main/ui/workspace/colorpicker/ramp.cljs b/frontend/src/app/main/ui/workspace/colorpicker/ramp.cljs index 8cab703620..ab68a5e578 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/ramp.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker/ramp.cljs @@ -49,7 +49,7 @@ :top (str (* 100 (- 1 (/ value 255))) "%")}}]])) -(mf/defc ramp-selector [{:keys [color on-change]}] +(mf/defc ramp-selector [{:keys [color disable-opacity on-change]}] (let [{hue :h saturation :s value :v alpha :alpha} color on-change-value-saturation @@ -86,7 +86,8 @@ :value hue :on-change on-change-hue}] - [:& slider-selector {:class "opacity" - :max-value 1 - :value alpha - :on-change on-change-opacity}]]])) + (when (not disable-opacity) + [:& slider-selector {:class "opacity" + :max-value 1 + :value alpha + :on-change on-change-opacity}])]])) diff --git a/frontend/src/app/main/ui/workspace/shapes/frame.cljs b/frontend/src/app/main/ui/workspace/shapes/frame.cljs index 1b3b469654..0882bf6498 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame.cljs @@ -19,6 +19,7 @@ [app.main.ui.workspace.shapes.common :as common] [app.main.data.workspace.selection :as dws] [app.main.ui.shapes.frame :as frame] + [app.main.ui.shapes.gradients :as grad] [app.main.ui.shapes.filters :as filters] [app.common.geom.matrix :as gmt] [app.common.geom.point :as gpt] @@ -126,8 +127,18 @@ :on-context-menu on-context-menu :on-double-click on-double-click :on-mouse-down on-mouse-down}] + [:g.frame {:filter (filters/filter-str filter-id shape)} [:& filters/filters {:filter-id filter-id :shape shape}] + + (when (:fill-color-gradient shape) + [:& grad/gradient {:attr :fill-color-gradient + :shape shape}]) + + (when (:stroke-color-gradient shape) + [:& grad/gradient {:attr :stroke-color-gradient + :shape shape}]) + [:& frame-shape {:shape shape :childs children}]]]))))) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options.cljs b/frontend/src/app/main/ui/workspace/sidebar/options.cljs index 2bfe53b193..bca5a51de8 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options.cljs @@ -40,15 +40,15 @@ [{:keys [shape shapes-with-children page-id file-id]}] [:* (case (:type shape) - :frame [:& frame/options {:shape shape}] - :group [:& group/options {:shape shape :shape-with-children shapes-with-children}] - :text [:& text/options {:shape shape}] - :rect [:& rect/options {:shape shape}] - :icon [:& icon/options {:shape shape}] + :frame [:& frame/options {:shape shape}] + :group [:& group/options {:shape shape :shape-with-children shapes-with-children}] + :text [:& text/options {:shape shape}] + :rect [:& rect/options {:shape shape}] + :icon [:& icon/options {:shape shape}] :circle [:& circle/options {:shape shape}] - :path [:& path/options {:shape shape}] - :curve [:& path/options {:shape shape}] - :image [:& image/options {:shape shape}] + :path [:& path/options {:shape shape}] + :curve [:& path/options {:shape shape}] + :image [:& image/options {:shape shape}] nil) [:& exports-menu {:shape shape diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/fill.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/fill.cljs index 409b101abc..005c6333ab 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/fill.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/fill.cljs @@ -63,13 +63,14 @@ (mf/use-callback (mf/deps ids) (fn [event] - (st/emit! (dc/change-fill ids cp/default-color nil nil)))) + (st/emit! (dc/change-fill2 ids {:color cp/default-color + :opacity 1})))) on-delete (mf/use-callback (mf/deps ids) (fn [event] - (st/emit! (dc/change-fill ids nil nil nil)))) + (st/emit! (dc/change-fill2 ids nil)))) on-change (mf/use-callback diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/page.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/page.cljs index 04e6fe5e9e..31381c3a08 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/page.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/page.cljs @@ -44,8 +44,9 @@ [:div.element-set [:div.element-set-title (t locale "workspace.options.canvas-background")] [:div.element-set-content - [:& color-row {:disable-opacity true - :color {:value (get options :background "#E8E9EA") + [:& color-row {:disable-gradient true + :disable-opacity true + :color {:color (get options :background "#E8E9EA") :opacity 1} :on-change handle-change-color :on-open on-open diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs index 2f86c55904..93439b626d 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs @@ -21,12 +21,14 @@ [app.util.color :as uc])) (defn color-picker-callback - [color handle-change-color handle-open handle-close] + [color disable-gradient disable-opacity handle-change-color handle-open handle-close] (fn [event] (let [x (.-clientX event) y (.-clientY event) props {:x x :y y + :disable-gradient disable-gradient + :disable-opacity disable-opacity :on-change handle-change-color :on-close handle-close :data color}] @@ -57,7 +59,7 @@ (if (= v :multiple) nil v)) (mf/defc color-row - [{:keys [color on-change on-open on-close]}] + [{:keys [color disable-gradient disable-opacity on-change on-open on-close]}] (let [file-colors (mf/deref refs/workspace-file-colors) shared-libs (mf/deref refs/workspace-libraries) @@ -69,28 +71,14 @@ (-> color (update :color #(or % (:value color))))) - state (mf/use-state (parse-color color)) - - value (:color @state) - opacity (:opacity @state) - change-value (fn [new-value] - (swap! state assoc :color new-value) - (when on-change (on-change new-value (remove-multiple opacity)))) + (when on-change (on-change (assoc color :color new-value)))) 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 {:color new-value :opacity new-opacity}) - ;; (when on-change (on-change new-value new-opacity id file-id))) + (when on-change (on-change (assoc color :opacity new-opacity)))) handle-pick-color (fn [color] - (prn "handle-pick-color" color) - (reset! state color) - (when on-change - (on-change color))) + (when on-change (on-change color))) handle-open (fn [] (when on-open (on-open))) @@ -114,20 +102,24 @@ change-opacity)))) select-all (fn [event] - (dom/select-text! (dom/get-target event)))] + (dom/select-text! (dom/get-target event))) + + handle-click-color (mf/use-callback + (mf/deps color) + (color-picker-callback color disable-gradient disable-opacity + handle-pick-color handle-open handle-close))] (mf/use-effect (mf/deps color) (fn [] - (modal/update-props! :colorpicker {:data (parse-color color)}) - (reset! state (parse-color color)))) + (modal/update-props! :colorpicker {:data (parse-color color)}))) [:div.row-flex.color-data [:span.color-th {:class (when (and (:id color) (not= (:id color) :multiple)) "color-name") :style {:background (uc/color->background color)} - :on-click (color-picker-callback @state handle-pick-color handle-open handle-close)} - (when (= value :multiple) "?")] + :on-click handle-click-color} + (when (= (:color color) :multiple) "?")] (cond ;; Rendering a color with ID @@ -136,7 +128,7 @@ [:div.color-name (str (get-color-name color))]] ;; Rendering a gradient - (:gradient color) + (and (:gradient color) (get-in color [:gradient :type])) [:div.color-info [:div.color-name (case (get-in color [:gradient :type]) @@ -147,19 +139,20 @@ :else [:* [:div.color-info - [:input {:value (-> value remove-hash) + [:input {:value (-> color :color 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"}]]])])) + (when (not disable-opacity) + [:div.input-element + {:class (classnames :percentail (not= (:opacity color) :multiple))} + [:input.input-text {:type "number" + :value (-> color :opacity opacity->string) + :placeholder (tr "settings.multiple") + :on-click select-all + :on-change handle-opacity-change + :min "0" + :max "100"}]])])])) From 7d7008d405922450557894696848a84284629785 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 13 Oct 2020 16:17:37 +0200 Subject: [PATCH 05/14] :sparkles: Styles changes to color picker --- common/app/common/pages.cljc | 66 ++++--- frontend/resources/styles/main-default.scss | 1 + .../styles/main/partials/color-bullet.scss | 173 ++++++++++++++++++ .../styles/main/partials/color-palette.scss | 57 +----- .../styles/main/partials/colorpicker.scss | 37 ---- .../styles/main/partials/sidebar-assets.scss | 7 - .../partials/sidebar-element-options.scss | 41 ----- frontend/src/app/main/data/colors.cljs | 13 +- .../app/main/data/workspace/libraries.cljs | 27 ++- .../app/main/ui/components/color_bullet.cljs | 52 ++++++ .../app/main/ui/workspace/colorpalette.cljs | 44 ++--- .../app/main/ui/workspace/colorpicker.cljs | 18 +- .../ui/workspace/colorpicker/libraries.cljs | 15 +- .../app/main/ui/workspace/sidebar/assets.cljs | 23 ++- .../ui/workspace/sidebar/options/fill.cljs | 6 +- .../sidebar/options/rows/color_row.cljs | 5 +- .../ui/workspace/sidebar/options/stroke.cljs | 20 +- 17 files changed, 361 insertions(+), 244 deletions(-) create mode 100644 frontend/resources/styles/main/partials/color-bullet.scss create mode 100644 frontend/src/app/main/ui/components/color_bullet.cljs diff --git a/common/app/common/pages.cljc b/common/app/common/pages.cljc index b0229d42ec..f9b0c8e5cd 100644 --- a/common/app/common/pages.cljc +++ b/common/app/common/pages.cljc @@ -56,31 +56,34 @@ (<= % max-safe-int))) -(s/def :internal.page.gradient.stop/color ::string) -(s/def :internal.page.gradient.stop/opacity ::safe-number) -(s/def :internal.page.gradient.stop/offset ::safe-number) -(s/def :internal.page.gradient/start-x ::safe-number) -(s/def :internal.page.gradient/start-y ::safe-number) -(s/def :internal.page.gradient/end-x ::safe-number) -(s/def :internal.page.gradient/end-y ::safe-number) -(s/def :internal.page.gradient/width ::safe-number) +(s/def :internal.gradient.stop/color ::string) +(s/def :internal.gradient.stop/opacity ::safe-number) +(s/def :internal.gradient.stop/offset ::safe-number) -(s/def :internal.page.gradient/stop - (s/keys :req-un [:internal.page.gradient.stop/color - :internal.page.gradient.stop/opacity - :internal.page.gradient.stop/offset])) +(s/def :internal.gradient/type #{:linear :radial}) +(s/def :internal.gradient/start-x ::safe-number) +(s/def :internal.gradient/start-y ::safe-number) +(s/def :internal.gradient/end-x ::safe-number) +(s/def :internal.gradient/end-y ::safe-number) +(s/def :internal.gradient/width ::safe-number) -(s/def :internal.page.gradient/stops - (s/map-of ::safe-number :internal.page.gradient/stop)) +(s/def :internal.gradient/stop + (s/keys :req-un [:internal.gradient.stop/color + :internal.gradient.stop/opacity + :internal.gradient.stop/offset])) + +(s/def :internal.gradient/stops + (s/coll-of :internal.gradient/stop :kind vector?)) (s/def ::gradient - (s/keys :req-un [:internal.page.gradient/start-x - :internal.page.gradient/start-y - :internal.page.gradient/end-x - :internal.page.gradient/end-y - :internal.page.gradient/width - :internal.page.gradient/stops])) + (s/keys :req-un [:internal.gradient/type + :internal.gradient/start-x + :internal.gradient/start-y + :internal.gradient/end-x + :internal.gradient/end-y + :internal.gradient/width + :internal.gradient/stops])) ;; Page Options (s/def :internal.page.grid.color/value string?) @@ -292,13 +295,26 @@ :internal.page/options :internal.page/objects])) + (s/def :internal.color/name ::string) (s/def :internal.color/value ::string) +(s/def :internal.color/color ::string) +(s/def :internal.color/opacity ::safe-number) +(s/def :internal.color/gradient ::gradient) (s/def ::color (s/keys :req-un [::id - :internal.color/name - :internal.color/value])) + :internal.color/name] + :opt-un [:internal.color/value + :internal.color/color + :internal.color/opacity + :internal.color/gradient])) + +(s/def ::recent-color + (s/keys :opt-un [:internal.color/value + :internal.color/color + :internal.color/opacity + :internal.color/gradient])) (s/def :internal.media-object/name ::string) (s/def :internal.media-object/path ::string) @@ -324,7 +340,7 @@ (s/map-of ::uuid ::color)) (s/def :internal.file/recent-colors - (s/coll-of ::string :kind vector?)) + (s/coll-of ::recent-color :kind vector?)) (s/def :internal.typography/id ::id) (s/def :internal.typography/name ::string) @@ -438,8 +454,10 @@ (defmethod change-spec :del-color [_] (s/keys :req-un [::id])) +(s/def :internal.changes.add-recent-color/color ::recent-color) + (defmethod change-spec :add-recent-color [_] - (s/keys :req-un [:recent-color/color])) + (s/keys :req-un [:internal.changes.add-recent-color/color])) (s/def :internal.changes.media/object ::media-object) diff --git a/frontend/resources/styles/main-default.scss b/frontend/resources/styles/main-default.scss index 134d14d655..cef562e72d 100644 --- a/frontend/resources/styles/main-default.scss +++ b/frontend/resources/styles/main-default.scss @@ -78,3 +78,4 @@ @import 'main/partials/user-settings'; @import 'main/partials/workspace'; @import 'main/partials/workspace-header'; +@import 'main/partials/color-bullet'; diff --git a/frontend/resources/styles/main/partials/color-bullet.scss b/frontend/resources/styles/main/partials/color-bullet.scss new file mode 100644 index 0000000000..a8045591c4 --- /dev/null +++ b/frontend/resources/styles/main/partials/color-bullet.scss @@ -0,0 +1,173 @@ +// 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/. +// +// This Source Code Form is "Incompatible With Secondary Licenses", as +// defined by the Mozilla Public License, v. 2.0. +// +// Copyright (c) 2020 UXBOX Labs SL + +.color-cell { + .color-bullet { + background-color: $color-white; + // Creates strange artifacts + border: 2px solid $color-gray-60; + // box-shadow: 0 0 0 2px $color-gray-60; + border-radius: 50%; + } + + &.cell-big .color-bullet { + width: 50px; + height: 50px; + } + + &.cell-small .color-bullet { + width: 40px; + height: 40px; + } + + .color-bullet.color-big { + width: 50px; + height: 50px; + } + +} + +.color-cell.current { + .color-bullet { + border-color: $color-gray-50; + } +} + +ul.palette-menu .color-bullet { + width: 20px; + height: 20px; + border-radius: 12px; + border: 1px solid $color-gray-10; + margin-right: 5px; + background-size: 8px; +} +.color-cell.add-color .color-bullet { + align-items: center; + background-color: $color-gray-50; + border: 3px dashed $color-gray-10; + cursor: pointer; + display: flex; + justify-content: center; + margin-bottom: 1rem; + padding: .6rem; + + svg { + fill: $color-gray-10; + height: 30px; + width: 30px; + } +} + +.colorpicker-content .color-bullet { + grid-area: color; + width: 20px; + height: 20px; + background-color: rgba(var(--color)); + border-radius: 12px; + border: 1px solid $color-gray-10; + background-size: 8px; +} + +.asset-group .group-list-item .color-bullet { + width: 20px; + height: 20px; + border-radius: 10px; + margin-right: $x-small; +} + +.color-cell.add-color:hover .color-bullet { + border-color: $color-gray-30; + + svg { + fill: $color-gray-30; + } +} + +.color-bullet { + display: flex; + flex-direction: row; + overflow: hidden; + + background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAADFJREFUOE9jZGBgEAFifOANPknGUQMYhkkYEEgG+NMJKAwIAbwJbdQABnBCIgRoG4gAIF8IsXB/Rs4AAAAASUVORK5CYII=") left center; + + & > * { + width: 100%; + height: 100%; + } +} + + +.color-data .color-bullet { + background-color: $color-gray-30; + border: 1px solid $color-gray-30; + border-radius: $br-small; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + color: $color-gray-10; + flex-shrink: 0; + height: 20px; + margin: 5px 4px 0 0; + width: 20px; + + &.color-name { + border-radius: 10px; + } + + &.palette-th { + align-items: center; + border: 1px solid $color-gray-30; + display: flex; + justify-content: center; + + svg { + fill: $color-gray-30; + height: 16px; + width: 16px; + } + + &:hover { + border-color: $color-primary; + svg { + fill: $color-primary; + } + + } + } +} + +.colorpicker-content .libraries .selected-colors .color-bullet { + grid-area: auto; + margin-bottom: 0.25rem; + cursor: pointer; + + &:hover { + border-color: $color-primary; + } + + &.button { + background: $color-white; + display: flex; + align-items: center; + justify-content: center; + } + + &.button svg { + width: 12px; + height: 12px; + fill: $color-gray-30; + } + + &.plus-button svg { + width: 8px; + height: 8px; + fill: $color-black; + } +} diff --git a/frontend/resources/styles/main/partials/color-palette.scss b/frontend/resources/styles/main/partials/color-palette.scss index c4a702eee6..7bd2ab47f9 100644 --- a/frontend/resources/styles/main/partials/color-palette.scss +++ b/frontend/resources/styles/main/partials/color-palette.scss @@ -123,7 +123,7 @@ display: flex; overflow: hidden; width: 100%; - height: 4.8rem; + height: 5rem; padding: 0.25rem; &.size-small { @@ -156,28 +156,6 @@ flex-basis: 52px; } - .color { - background-color: $color-gray-10; - border: 2px solid $color-gray-60; - border-radius: 50%; - flex-shrink: 0; - } - - &.cell-big .color { - width: 50px; - height: 50px; - } - - &.cell-small .color { - width: 40px; - height: 40px; - } - - .color.color-big { - width: 50px; - height: 50px; - } - .color-text { color: $color-gray-20; font-size: $fs12; @@ -186,11 +164,9 @@ text-overflow: ellipsis; width: 66px; text-align: center; + margin-top: 0.25rem; } &.current { - .color { - border-color: $color-gray-50; - } .color-text { color: $color-gray-50; font-weight: bold; @@ -217,31 +193,11 @@ } &.add-color { margin-left: 1.5rem; - .color { - align-items: center; - background-color: $color-gray-50; - border: 3px dashed $color-gray-10; - cursor: pointer; - display: flex; - justify-content: center; - margin-bottom: 1rem; - padding: .6rem; - svg { - fill: $color-gray-10; - height: 30px; - width: 30px; - } - } + .color-text { font-weight: bold; } &:hover { - .color { - border-color: $color-gray-30; - svg { - fill: $color-gray-30; - } - } .color-text { color: $color-gray-40; } @@ -336,12 +292,5 @@ ul.palette-menu { margin-top: 0.5rem; } - .color-bullet { - width: 20px; - height: 20px; - border-radius: 12px; - border: 1px solid $color-gray-10; - margin-right: 5px; - } } diff --git a/frontend/resources/styles/main/partials/colorpicker.scss b/frontend/resources/styles/main/partials/colorpicker.scss index 45a62c6c0d..e65658d937 100644 --- a/frontend/resources/styles/main/partials/colorpicker.scss +++ b/frontend/resources/styles/main/partials/colorpicker.scss @@ -241,15 +241,6 @@ } } - .color-bullet { - grid-area: color; - width: 20px; - height: 20px; - background-color: rgba(var(--color)); - border-radius: 12px; - border: 1px solid $color-gray-10; - } - .shade-selector { display: grid; justify-items: center; @@ -340,34 +331,6 @@ content: ""; flex: auto; } - - .selected-colors .color-bullet { - grid-area: auto; - margin-bottom: 0.25rem; - cursor: pointer; - - &:hover { - border-color: $color-primary; - } - - &.button { - display: flex; - align-items: center; - justify-content: center; - } - - &.button svg { - width: 12px; - height: 12px; - fill: $color-gray-30; - } - - &.plus-button svg { - width: 8px; - height: 8px; - fill: $color-black; - } - } } .actions { diff --git a/frontend/resources/styles/main/partials/sidebar-assets.scss b/frontend/resources/styles/main/partials/sidebar-assets.scss index d4d6ff4ed9..a99b426fbf 100644 --- a/frontend/resources/styles/main/partials/sidebar-assets.scss +++ b/frontend/resources/styles/main/partials/sidebar-assets.scss @@ -241,13 +241,6 @@ color: $color-white; cursor: pointer; - & .color-block { - width: 20px; - height: 20px; - border-radius: 10px; - margin-right: $x-small; - } - & span { margin-left: $x-small; color: $color-gray-30; diff --git a/frontend/resources/styles/main/partials/sidebar-element-options.scss b/frontend/resources/styles/main/partials/sidebar-element-options.scss index 12510697f6..4f2077067a 100644 --- a/frontend/resources/styles/main/partials/sidebar-element-options.scss +++ b/frontend/resources/styles/main/partials/sidebar-element-options.scss @@ -467,47 +467,6 @@ margin-bottom: 0; } -.color-th { - background-color: $color-gray-30; - border: 1px solid $color-gray-30; - border-radius: $br-small; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - color: $color-gray-10; - flex-shrink: 0; - height: 20px; - margin: 5px 4px 0 0; - width: 20px; - - &.color-name { - border-radius: 10px; - } - - &.palette-th { - align-items: center; - border: 1px solid $color-gray-30; - display: flex; - justify-content: center; - - svg { - fill: $color-gray-30; - height: 16px; - width: 16px; - } - - &:hover { - border-color: $color-primary; - svg { - fill: $color-primary; - } - - } - - } -} - .presets { .custom-select-dropdown { width: 200px; diff --git a/frontend/src/app/main/data/colors.cljs b/frontend/src/app/main/data/colors.cljs index 038ec1ed73..16d49b9b0c 100644 --- a/frontend/src/app/main/data/colors.cljs +++ b/frontend/src/app/main/data/colors.cljs @@ -104,7 +104,7 @@ (assoc-in [:workspace-local :picked-color-select] value) (assoc-in [:workspace-local :picked-shift?] shift?))))) -(defn change-fill2 +(defn change-fill ([ids color] (ptk/reify ::change-fill ptk/WatchEvent @@ -133,7 +133,7 @@ (map #(dwt/update-text-attrs {:id % :editor (get editors %) :attrs attrs}) text-ids) (dwc/update-shapes shape-ids update-fn)))))))) -(defn change-fill +#_(defn change-fill ([ids color id file-id] (change-fill ids color 1 id file-id)) ([ids color opacity id file-id] @@ -163,7 +163,7 @@ (map #(dwt/update-text-attrs {:id % :editor (get editors %) :attrs attrs}) text-ids) (dwc/update-shapes shape-ids update-fn)))))))) -(defn change-stroke2 [ids color] +(defn change-stroke [ids color] (ptk/reify ::change-stroke ptk/WatchEvent (watch [_ state s] @@ -184,7 +184,8 @@ :stroke-width 1 :stroke-opacity 1)))] (rx/of (dwc/update-shapes ids update-fn)))))) -(defn change-stroke [ids color id file-id] + +#_(defn change-stroke [ids color id file-id] (ptk/reify ::change-stroke ptk/WatchEvent (watch [_ state s] @@ -212,8 +213,8 @@ (let [ids (get-in @st/state [:workspace-local :selected])] (st/emit! (if shift? - (change-stroke2 ids color) - (change-fill2 ids color)) + (change-stroke ids color) + (change-fill ids color)) (md/hide))))] (ptk/reify ::start-picker ptk/UpdateEvent diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index 00226a92ef..344e483b54 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -35,23 +35,22 @@ (defn add-color [color] - (us/assert ::us/string color) - (ptk/reify ::add-color - ptk/WatchEvent - (watch [_ state s] - (let [id (uuid/next) - rchg {:type :add-color - :color {:id id - :name color - :value color}} - uchg {:type :del-color - :id id}] - (rx/of #(assoc-in % [:workspace-local :color-for-rename] id) - (dwc/commit-changes [rchg] [uchg] {:commit-local? true})))))) + (let [id (uuid/next) + color (assoc color :id id)] + (us/assert ::cp/color color) + (ptk/reify ::add-color + ptk/WatchEvent + (watch [_ state s] + (let [rchg {:type :add-color + :color color} + uchg {:type :del-color + :id id}] + (rx/of #(assoc-in % [:workspace-local :color-for-rename] id) + (dwc/commit-changes [rchg] [uchg] {:commit-local? true}))))))) (defn add-recent-color [color] - (us/assert ::us/string color) + (us/assert ::cp/recent-color color) (ptk/reify ::add-recent-color ptk/WatchEvent (watch [_ state s] diff --git a/frontend/src/app/main/ui/components/color_bullet.cljs b/frontend/src/app/main/ui/components/color_bullet.cljs new file mode 100644 index 0000000000..f3bb31826c --- /dev/null +++ b/frontend/src/app/main/ui/components/color_bullet.cljs @@ -0,0 +1,52 @@ +;; 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/. +;; +;; This Source Code Form is "Incompatible With Secondary Licenses", as +;; defined by the Mozilla Public License, v. 2.0. +;; +;; Copyright (c) 2020 UXBOX Labs SL + +(ns app.main.ui.components.color-bullet + (:require + #_[beicon.core :as rx] + #_[goog.events :as events] + #_[okulary.core :as l] + [rumext.alpha :as mf] + [app.util.color :as uc] + #_[cuerdas.core :as str] + #_[app.common.math :as mth] + #_[app.main.data.colors :as mdc] + #_[app.main.data.workspace :as udw] + #_[app.main.store :as st] + #_[app.main.ui.components.dropdown :refer [dropdown]] + #_[app.main.ui.icons :as i] + #_[app.main.ui.keyboard :as kbd] + #_[app.util.color :refer [hex->rgb]] + #_[app.util.dom :as dom] + #_[app.util.object :as obj] + #_[app.main.refs :as refs] + #_[app.util.i18n :as i18n :refer [t]])) + +(mf/defc color-bullet [{:keys [color on-click]}] + (let [color (if (string? color) {:color color :opacity 1} color)] + [:div.color-bullet {:on-click #(when on-click (on-click %))} + (when (not (:gradient color)) + [:div.color-bullet-left {:style {:background (uc/color->background (assoc color :opacity 1))}}]) + + [:div.color-bullet-right {:style {:background (uc/color->background color)}}]])) + +(defn gradient-type->string [{:keys [type]}] + (case type + :linear "Linear" + :radial "Radial")) + +(mf/defc color-name [{:keys [color size on-click on-double-click]}] + (let [color (if (string? color) {:color color :opacity 1} color) + {:keys [name color opacity gradient]} color + color-str (or name color (gradient-type->string gradient))] + #_(when (= size :big) [:span.color-text {:title (:name color) } (or (:name color) (:value color))]) + (when (= size :big) + [:span.color-text {:on-click #(when on-click (on-click %)) + :on-double-click #(when on-double-click (on-double-click %)) + :title name } color-str]))) diff --git a/frontend/src/app/main/ui/workspace/colorpalette.cljs b/frontend/src/app/main/ui/workspace/colorpalette.cljs index bfda8cd1b3..8a26130c78 100644 --- a/frontend/src/app/main/ui/workspace/colorpalette.cljs +++ b/frontend/src/app/main/ui/workspace/colorpalette.cljs @@ -19,6 +19,7 @@ [app.main.data.workspace :as udw] [app.main.store :as st] [app.main.ui.components.dropdown :refer [dropdown]] + [app.main.ui.components.color-bullet :refer [color-bullet color-name]] [app.main.ui.icons :as i] [app.main.ui.keyboard :as kbd] [app.util.color :refer [hex->rgb]] @@ -55,14 +56,13 @@ (fn [event] (let [ids (get-in @st/state [:workspace-local :selected])] (if (kbd/shift? event) - (st/emit! (mdc/change-stroke ids (:value color) id file-id)) - (st/emit! (mdc/change-fill ids (:value color) id file-id)))))] + (st/emit! (mdc/change-stroke ids color)) + (st/emit! (mdc/change-fill ids color)))))] [:div.color-cell {:class (str "cell-"(name size)) - :key (or (str (:id color)) (:value color)) :on-click select-color} - [:span.color {:style {:background (:value color)}}] - (when (= size :big) [:span.color-text {:title (:name color) } (or (:name color) (:value color))])])) + [:& color-bullet {:color color}] + [:& color-name {:color color :size size}]])) (mf/defc palette [{:keys [left-sidebar? current-colors recent-colors file-colors shared-libs selected size]}] @@ -138,9 +138,9 @@ (when (= selected (:id cur-library)) i/tick) [:div.library-name (str (:name cur-library) " " (str/format "(%s)" (count colors)))] [:div.color-sample - (for [[idx {:keys [id value]}] (map-indexed vector (take 7 colors))] - [:div.color-bullet {:key (str "color-" idx) - :style {:background-color value}}])]])) + (for [[idx {:keys [id color]}] (map-indexed vector (take 7 colors))] + [:& color-bullet {:key (str "color-" idx) + :color color}])]])) [:li.palette-library @@ -149,9 +149,9 @@ [:div.library-name (str (t locale "workspace.libraries.colors.file-library") (str/format " (%s)" (count file-colors)))] [:div.color-sample - (for [[idx {:keys [value]}] (map-indexed vector (take 7 (vals file-colors))) ] - [:div.color-bullet {:key (str "color-" idx) - :style {:background-color value}}])]] + (for [[idx color] (map-indexed vector (take 7 (vals file-colors))) ] + [:& color-bullet {:key (str "color-" idx) + :color color}])]] [:li.palette-library {:on-click #(st/emit! (mdc/change-palette-selected :recent))} @@ -159,9 +159,9 @@ [:div.library-name (str (t locale "workspace.libraries.colors.recent-colors") (str/format " (%s)" (count recent-colors)))] [:div.color-sample - (for [[idx value] (map-indexed vector (take 7 (reverse recent-colors))) ] - [:div.color-bullet {:key (str "color-" idx) - :style {:background-color value}}])]] + (for [[idx color] (map-indexed vector (take 7 (reverse recent-colors))) ] + [:& color-bullet {:key (str "color-" idx) + :color color}])]] [:hr.dropdown-separator] @@ -191,14 +191,8 @@ [:span.right-arrow {:on-click on-right-arrow-click} i/arrow-slide]])) -(defn recent->colors [recent-colors] - (map #(hash-map :value %) (reverse (or recent-colors [])))) - -(defn file->colors [file-colors] - (map #(select-keys % [:id :value :name]) (vals file-colors))) - (defn library->colors [shared-libs selected] - (map #(merge {:file-id selected} (select-keys % [:id :value :name])) + (map #(merge {:file-id selected} %) (vals (get-in shared-libs [selected :data :colors])))) (mf/defc colorpalette @@ -217,21 +211,21 @@ (reset! current-library-colors (into [] (cond - (= selected :recent) (recent->colors recent-colors) - (= selected :file) (file->colors file-colors) + (= selected :recent) (reverse recent-colors) + (= selected :file) (vals file-colors) :else (library->colors shared-libs selected)))))) (mf/use-effect (mf/deps recent-colors) (fn [] (when (= selected :recent) - (reset! current-library-colors (into [] (recent->colors recent-colors)))))) + (reset! current-library-colors (reverse recent-colors))))) (mf/use-effect (mf/deps file-colors) (fn [] (when (= selected :file) - (reset! current-library-colors (into [] (file->colors file-colors)))))) + (reset! current-library-colors (into [] (vals file-colors)))))) [:& palette {:left-sidebar? left-sidebar? :current-colors @current-library-colors diff --git a/frontend/src/app/main/ui/workspace/colorpicker.cljs b/frontend/src/app/main/ui/workspace/colorpicker.cljs index d8f7936566..dd7df33e5c 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker.cljs @@ -207,7 +207,7 @@ (dom/set-css-property node "--saturation-grad-to" (format-hsl hsl-to))))) ;; When closing the modal we update the recent-color list - #_(mf/use-effect + (mf/use-effect (fn [] (fn [] (st/emit! (dc/stop-picker)) (st/emit! (dwl/add-recent-color (state->data @state)))))) @@ -276,6 +276,14 @@ :editing-stop (:editing-stop @state) :on-select-stop handle-change-stop}] + [:div.colorpicker-tabs + [:div.colorpicker-tab {:class (when (= @active-tab :ramp) "active") + :on-click (change-tab :ramp)} i/picker-ramp] + [:div.colorpicker-tab {:class (when (= @active-tab :harmony) "active") + :on-click (change-tab :harmony)} i/picker-harmony] + [:div.colorpicker-tab {:class (when (= @active-tab :hsva) "active") + :on-click (change-tab :hsva)} i/picker-hsv]] + (if picking-color? [:div.picker-detail-wrapper [:div.center-circle] @@ -299,13 +307,7 @@ [:& libraries {:current-color current-color :on-select-color on-select-library-color}]] - [:div.colorpicker-tabs - [:div.colorpicker-tab {:class (when (= @active-tab :ramp) "active") - :on-click (change-tab :ramp)} i/picker-ramp] - [:div.colorpicker-tab {:class (when (= @active-tab :harmony) "active") - :on-click (change-tab :harmony)} i/picker-harmony] - [:div.colorpicker-tab {:class (when (= @active-tab :hsva) "active") - :on-click (change-tab :hsva)} i/picker-hsv]] + (when on-accept [:div.actions [:button.btn-primary.btn-large diff --git a/frontend/src/app/main/ui/workspace/colorpicker/libraries.cljs b/frontend/src/app/main/ui/workspace/colorpicker/libraries.cljs index 56d1fe0a8f..3c4a04c817 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/libraries.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker/libraries.cljs @@ -25,6 +25,7 @@ [app.main.data.modal :as modal] [app.main.ui.icons :as i] [app.util.i18n :as i18n :refer [t]] + [app.main.ui.components.color-bullet :refer [color-bullet]] [app.main.ui.workspace.colorpicker.gradients :refer [gradients]] [app.main.ui.workspace.colorpicker.harmony :refer [harmony-selector]] [app.main.ui.workspace.colorpicker.hsva :refer [hsva-selector]] @@ -53,13 +54,14 @@ (let [mapped-colors (cond (= @selected-library "recent") - (map #(hash-map :value %) (reverse (or recent-colors []))) + ;; The `map?` check is to keep backwards compatibility. We transform from string to map + (map #(if (map? %) % (hash-map :color %)) (reverse (or recent-colors []))) (= @selected-library "file") - (map #(select-keys % [:id :value]) (vals file-colors)) + (vals file-colors) :else ;; Library UUID - (map #(merge {:file-id (uuid @selected-library)} (select-keys % [:id :value])) + (map #(merge {:file-id (uuid @selected-library)}) (vals (get-in shared-libs [(uuid @selected-library) :data :colors]))))] (reset! current-library-colors (into [] mapped-colors))))) @@ -94,7 +96,6 @@ i/palette] (for [[idx color] (map-indexed vector @current-library-colors)] - [:div.color-bullet - {:key (str "color-" idx) - :on-click #(on-select-color color) - :style {:background (uc/color->background color)}}])]])) + [:& color-bullet {:key (str "color-" idx) + :color color + :on-click #(on-select-color color)}])]])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs index c2c638a7f9..5cc267d926 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs @@ -28,6 +28,7 @@ [app.main.ui.components.file-uploader :refer [file-uploader]] [app.main.ui.components.tab-container :refer [tab-container tab-element]] [app.main.ui.workspace.sidebar.options.typography :refer [typography-entry]] + [app.main.ui.components.color-bullet :refer [color-bullet color-name gradient-type->string]] [app.main.ui.icons :as i] [app.main.ui.keyboard :as kbd] [app.main.data.modal :as modal] @@ -198,12 +199,18 @@ :top nil :left nil :editing rename?}) + + default-name (cond + (:gradient color) (gradient-type->string (:gradient color)) + (:color color) (:color color) + :else (:value color)) + click-color (fn [event] (let [ids (get-in @st/state [:workspace-local :selected])] (if (kbd/shift? event) - (st/emit! (dc/change-stroke ids (:value color) id (if local? nil file-id))) - (st/emit! (dc/change-fill ids (:value color) id (if local? nil file-id)))))) + (st/emit! (dc/change-stroke ids color)) + (st/emit! (dc/change-fill ids color))))) rename-color (fn [name] @@ -211,7 +218,7 @@ edit-color (fn [value] - (st/emit! (dwl/update-color (assoc color :value value)))) + (st/emit! (dwl/update-color (assoc color :color value)))) delete-color (fn [] @@ -245,7 +252,7 @@ {:x (.-clientX event) :y (.-clientY event) :on-accept edit-color - :value (:value color) + :data color :disable-opacity true :position :right})) @@ -269,7 +276,8 @@ nil)) [:div.group-list-item {:on-context-menu on-context-menu} - [:div.color-block {:style {:background-color (:value color)}}] + [:& color-bullet {:color color}] + (if (:editing @state) [:input.element-name {:type "text" @@ -278,12 +286,13 @@ :on-key-down input-key-down :auto-focus true :default-value (:name color "")}] + [:div.name-block {:on-double-click rename-color-clicked :on-click click-color} (:name color) - (when-not (= (:name color) (:value color)) - [:span (:value color)])]) + (when-not (= (:name color) default-name) + [:span default-name])]) (when local? [:& context-menu {:selectable false diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/fill.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/fill.cljs index 005c6333ab..b2b879a7e7 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/fill.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/fill.cljs @@ -63,20 +63,20 @@ (mf/use-callback (mf/deps ids) (fn [event] - (st/emit! (dc/change-fill2 ids {:color cp/default-color + (st/emit! (dc/change-fill ids {:color cp/default-color :opacity 1})))) on-delete (mf/use-callback (mf/deps ids) (fn [event] - (st/emit! (dc/change-fill2 ids nil)))) + (st/emit! (dc/change-fill ids nil)))) on-change (mf/use-callback (mf/deps ids) (fn [color] - (st/emit! (dc/change-fill2 ids color)))) + (st/emit! (dc/change-fill ids color)))) on-open-picker (mf/use-callback diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs index 93439b626d..2812a90568 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs @@ -12,6 +12,7 @@ [rumext.alpha :as mf] [cuerdas.core :as str] [app.common.math :as math] + [app.main.ui.components.color-bullet :refer [color-bullet color-name]] [app.util.dom :as dom] [app.util.data :refer [classnames]] [app.util.i18n :as i18n :refer [tr]] @@ -115,7 +116,9 @@ (modal/update-props! :colorpicker {:data (parse-color color)}))) [:div.row-flex.color-data - [:span.color-th + [:& color-bullet {:color color + :on-click handle-click-color}] + #_[:span.color-th {:class (when (and (:id color) (not= (:id color) :multiple)) "color-name") :style {:background (uc/color->background color)} :on-click handle-click-color} diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/stroke.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/stroke.cljs index a8927137b5..4dd600c140 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/stroke.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/stroke.cljs @@ -14,6 +14,7 @@ [app.common.data :as d] [app.common.math :as math] [app.main.data.workspace.common :as dwc] + [app.main.data.colors :as dc] [app.main.store :as st] [app.main.ui.icons :as i] [app.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row]] @@ -29,7 +30,8 @@ :stroke-color :stroke-color-ref-id :stroke-color-ref-file - :stroke-opacity]) + :stroke-opacity + :stroke-color-gradient]) (defn- stroke-menu-props-equals? [np op] @@ -72,19 +74,17 @@ show-options (not= (:stroke-style values :none) :none) - current-stroke-color {:value (:stroke-color values) + current-stroke-color {:color (:stroke-color values) :opacity (:stroke-opacity values) :id (:stroke-color-ref-id values) - :file-id (:stroke-color-ref-file values)} + :file-id (:stroke-color-ref-file values) + :gradient (:stroke-color-gradient values)} handle-change-stroke-color - (fn [value opacity id file-id] - (let [change #(cond-> % - value (assoc :stroke-color value - :stroke-color-ref-id id - :stroke-color-ref-file file-id) - opacity (assoc :stroke-opacity opacity))] - (st/emit! (dwc/update-shapes ids change)))) + (mf/use-callback + (mf/deps ids) + (fn [color] + (st/emit! (dc/change-stroke ids color)))) on-stroke-style-change (fn [event] From 245c39b1f6c2287cde5aca83d8d931a748d1677b Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Wed, 14 Oct 2020 11:47:11 +0200 Subject: [PATCH 06/14] :sparkles: Integration with library new colors --- common/app/common/pages.cljc | 3 ++- .../resources/images/icons/picker-ramp.svg | 4 +++- .../styles/main/partials/color-bullet.scss | 2 +- .../styles/main/partials/colorpicker.scss | 23 +++++++++++++------ .../app/main/data/workspace/libraries.cljs | 7 +++++- frontend/src/app/main/ui/shapes/attrs.cljs | 4 ++-- .../src/app/main/ui/shapes/gradients.cljs | 7 +++--- .../app/main/ui/workspace/colorpicker.cljs | 17 +++++++------- .../main/ui/workspace/colorpicker/ramp.cljs | 8 ++++--- .../app/main/ui/workspace/shapes/common.cljs | 10 ++++---- .../app/main/ui/workspace/sidebar/assets.cljs | 17 +++++++++----- 11 files changed, 64 insertions(+), 38 deletions(-) diff --git a/common/app/common/pages.cljc b/common/app/common/pages.cljc index f9b0c8e5cd..6a947ec779 100644 --- a/common/app/common/pages.cljc +++ b/common/app/common/pages.cljc @@ -869,7 +869,7 @@ (defmethod process-change :mod-color [data {:keys [color]}] - (d/update-in-when data [:colors (:id color)] merge color)) + (d/assoc-in-when data [:colors (:id color)] color)) (defmethod process-change :del-color [data {:keys [id]}] @@ -965,3 +965,4 @@ (ex/raise :type :not-implemented :code :operation-not-implemented :context {:type (:type op)})) + diff --git a/frontend/resources/images/icons/picker-ramp.svg b/frontend/resources/images/icons/picker-ramp.svg index 0e078a017e..815b3a9449 100644 --- a/frontend/resources/images/icons/picker-ramp.svg +++ b/frontend/resources/images/icons/picker-ramp.svg @@ -1 +1,3 @@ - + + + diff --git a/frontend/resources/styles/main/partials/color-bullet.scss b/frontend/resources/styles/main/partials/color-bullet.scss index a8045591c4..ba0d980c35 100644 --- a/frontend/resources/styles/main/partials/color-bullet.scss +++ b/frontend/resources/styles/main/partials/color-bullet.scss @@ -68,7 +68,6 @@ ul.palette-menu .color-bullet { grid-area: color; width: 20px; height: 20px; - background-color: rgba(var(--color)); border-radius: 12px; border: 1px solid $color-gray-10; background-size: 8px; @@ -95,6 +94,7 @@ ul.palette-menu .color-bullet { overflow: hidden; background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAADFJREFUOE9jZGBgEAFifOANPknGUQMYhkkYEEgG+NMJKAwIAbwJbdQABnBCIgRoG4gAIF8IsXB/Rs4AAAAASUVORK5CYII=") left center; + background-color: $color-white; & > * { width: 100%; diff --git a/frontend/resources/styles/main/partials/colorpicker.scss b/frontend/resources/styles/main/partials/colorpicker.scss index e65658d937..05cf8370d6 100644 --- a/frontend/resources/styles/main/partials/colorpicker.scss +++ b/frontend/resources/styles/main/partials/colorpicker.scss @@ -443,13 +443,10 @@ .colorpicker-tabs { display: flex; - margin-top: 0.25rem; + margin: 0.5rem 0; + border-radius: 5px; + border: 1px solid $color-gray-10; height: 2rem; - background-color: $color-gray-10; - - .active { - background-color: $color-white; - } .colorpicker-tab { cursor: pointer; @@ -461,9 +458,21 @@ svg { width: 16px; height: 16px; - fill: $color-gray-30; + fill: $color-gray-20; } } + + .active { + background-color: $color-gray-10; + svg { + fill: $color-gray-60; + } + } + + :hover svg { + fill: $color-primary; + } + } } diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index 344e483b54..a949430995 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -36,7 +36,12 @@ (defn add-color [color] (let [id (uuid/next) - color (assoc color :id id)] + color (assoc color + :id id + :name (or (:color color) + (case (get-in color [:gradient :type]) + :linear "Linear gradient" + :radial "Radial gradient")))] (us/assert ::cp/color color) (ptk/reify ::add-color ptk/WatchEvent diff --git a/frontend/src/app/main/ui/shapes/attrs.cljs b/frontend/src/app/main/ui/shapes/attrs.cljs index 4c8a4d9e7d..6db6b6f272 100644 --- a/frontend/src/app/main/ui/shapes/attrs.cljs +++ b/frontend/src/app/main/ui/shapes/attrs.cljs @@ -25,7 +25,7 @@ :ry (:ry shape)})) (defn add-fill [attrs shape] - (let [fill-color-gradient-id (str "fill-color-gradient_" (:id shape))] + (let [fill-color-gradient-id (str "fill-color-gradient_" (:render-id shape))] (if (:fill-color-gradient shape) (obj/merge! attrs #js {:fill (str/format "url(#%s)" fill-color-gradient-id)}) (obj/merge! attrs #js {:fill (or (:fill-color shape) "transparent") @@ -33,7 +33,7 @@ (defn add-stroke [attrs shape] (let [stroke-style (:stroke-style shape :none) - stroke-color-gradient-id (str "stroke-color-gradient_" (:id shape))] + stroke-color-gradient-id (str "stroke-color-gradient_" (:render-id shape))] (if (not= stroke-style :none) (if (:stroke-color-gradient shape) (obj/merge! attrs diff --git a/frontend/src/app/main/ui/shapes/gradients.cljs b/frontend/src/app/main/ui/shapes/gradients.cljs index 621f7e278c..a1c216b46c 100644 --- a/frontend/src/app/main/ui/shapes/gradients.cljs +++ b/frontend/src/app/main/ui/shapes/gradients.cljs @@ -49,8 +49,8 @@ scale-factor-x (* scale-factor-y (:width gradient)) scale-vec (gpt/point (* scale-factor-y (/ height 2)) - (* scale-factor-x (/ width 2)) - ) + (* scale-factor-x (/ width 2))) + tr-translate (str/fmt "translate(%s, %s)" (:x translate-vec) (:y translate-vec)) tr-rotate (str/fmt "rotate(%s)" angle) tr-scale (str/fmt "scale(%s, %s)" (:x scale-vec) (:y scale-vec)) @@ -72,8 +72,9 @@ [props] (let [attr (obj/get props "attr") shape (obj/get props "shape") + render-id (obj/get props "render-id") - id (str (name attr) "_" (:id shape)) + id (str (name attr) "_" render-id) gradient (get shape attr) gradient-props #js {:id id :gradient gradient diff --git a/frontend/src/app/main/ui/workspace/colorpicker.cljs b/frontend/src/app/main/ui/workspace/colorpicker.cljs index dd7df33e5c..04be286240 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker.cljs @@ -306,16 +306,15 @@ :on-change handle-change-color}] [:& libraries {:current-color current-color - :on-select-color on-select-library-color}]] + :on-select-color on-select-library-color}] - (when on-accept - [:div.actions - [:button.btn-primary.btn-large - {:on-click (fn [] - ;; TODO: REVIEW FOR GRADIENTS - #_(on-accept @value-ref) - (modal/hide!))} - (t locale "workspace.libraries.colors.save-color")]])]) + (when on-accept + [:div.actions + [:button.btn-primary.btn-large + {:on-click (fn [] + (on-accept (state->data @state)) + (modal/hide!))} + (t locale "workspace.libraries.colors.save-color")]])]]) ) (defn calculate-position diff --git a/frontend/src/app/main/ui/workspace/colorpicker/ramp.cljs b/frontend/src/app/main/ui/workspace/colorpicker/ramp.cljs index ab68a5e578..f88794bab5 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/ramp.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker/ramp.cljs @@ -25,9 +25,9 @@ [app.main.data.modal :as modal] [app.main.ui.icons :as i] [app.util.i18n :as i18n :refer [t]] + [app.main.ui.components.color-bullet :refer [color-bullet]] [app.main.ui.workspace.colorpicker.slider-selector :refer [slider-selector]])) - (mf/defc value-saturation-selector [{:keys [hue saturation value on-change]}] (let [dragging? (mf/use-state false) calculate-pos @@ -50,7 +50,8 @@ (mf/defc ramp-selector [{:keys [color disable-opacity on-change]}] - (let [{hue :h saturation :s value :v alpha :alpha} color + (let [{hex :hex + hue :h saturation :s value :v alpha :alpha} color on-change-value-saturation (fn [new-saturation new-value] @@ -80,7 +81,8 @@ :on-change on-change-value-saturation}] [:div.shade-selector - [:div.color-bullet] + [:& color-bullet {:color {:color hex + :opacity alpha}}] [:& slider-selector {:class "hue" :max-value 360 :value hue diff --git a/frontend/src/app/main/ui/workspace/shapes/common.cljs b/frontend/src/app/main/ui/workspace/shapes/common.cljs index 762469b14c..62c3466199 100644 --- a/frontend/src/app/main/ui/workspace/shapes/common.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/common.cljs @@ -73,22 +73,24 @@ on-context-menu (mf/use-callback (mf/deps shape) #(on-context-menu % shape)) - filter-id (mf/use-memo filters/get-filter-id)] + render-id (mf/use-memo #(str (uuid/next)))] [:g.shape {:on-mouse-down on-mouse-down :on-context-menu on-context-menu - :filter (filters/filter-str filter-id shape)} + :filter (filters/filter-str (str "filter_" render-id) shape)} - [:& filters/filters {:filter-id filter-id :shape shape}] + [:& filters/filters {:filter-id (str "filter_" render-id) :shape shape}] (when (:fill-color-gradient shape) [:& grad/gradient {:attr :fill-color-gradient + :render-id render-id :shape shape}]) (when (:stroke-color-gradient shape) [:& grad/gradient {:attr :stroke-color-gradient + :render-id render-id :shape shape}]) - [:& component {:shape shape}]]))) + [:& component {:shape (assoc shape :render-id render-id)}]]))) diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs index 5cc267d926..9596bad092 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs @@ -217,8 +217,9 @@ (st/emit! (dwl/update-color (assoc color :name name)))) edit-color - (fn [value] - (st/emit! (dwl/update-color (assoc color :color value)))) + (fn [new-color] + (let [updated-color (merge new-color (select-keys color [:id :file-id :name]))] + (st/emit! (dwl/update-color updated-color)))) delete-color (fn [] @@ -253,7 +254,6 @@ :y (.-clientY event) :on-accept edit-color :data color - :disable-opacity true :position :right})) on-context-menu @@ -321,8 +321,8 @@ {:x (.-clientX event) :y (.-clientY event) :on-accept add-color - :value "#406280" - :disable-opacity true + :data {:color "#406280" + :opacity 1} :position :right})))] [:div.asset-group [:div.group-title {:class (when (not open?) "closed")} @@ -334,7 +334,12 @@ [:div.group-list (for [color colors] [:& color-item {:key (:id color) - :color color + :color (if (:value color) + (-> color + (assoc :color (:value color) + :opacity 1) + (dissoc :value)) + color) :file-id file-id :local? local? :locale locale}])])])) From 9f0a443b5c230ab64cbd0c401e1946fe3df4b1cc Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Wed, 14 Oct 2020 18:12:15 +0200 Subject: [PATCH 07/14] :sparkles: Fixes problems with pixel picker --- common/app/common/pages.cljc | 8 +- frontend/resources/images/icons/picker.svg | 1 - .../styles/main/partials/colorpicker.scss | 2 +- frontend/src/app/main/data/colors.cljs | 101 ++++------- frontend/src/app/main/data/modal.cljs | 4 +- frontend/src/app/main/ui/modal.cljs | 21 ++- .../app/main/ui/workspace/colorpicker.cljs | 80 ++++----- .../ui/workspace/colorpicker/libraries.cljs | 6 +- .../workspace/colorpicker/pixel_overlay.cljs | 159 ++++++++++++++++-- .../src/app/main/ui/workspace/viewport.cljs | 111 +----------- 10 files changed, 248 insertions(+), 245 deletions(-) diff --git a/common/app/common/pages.cljc b/common/app/common/pages.cljc index 6a947ec779..93dc8ec048 100644 --- a/common/app/common/pages.cljc +++ b/common/app/common/pages.cljc @@ -297,10 +297,10 @@ (s/def :internal.color/name ::string) -(s/def :internal.color/value ::string) -(s/def :internal.color/color ::string) -(s/def :internal.color/opacity ::safe-number) -(s/def :internal.color/gradient ::gradient) +(s/def :internal.color/value (s/nilable ::string)) +(s/def :internal.color/color (s/nilable ::string)) +(s/def :internal.color/opacity (s/nilable ::safe-number)) +(s/def :internal.color/gradient (s/nilable ::gradient)) (s/def ::color (s/keys :req-un [::id diff --git a/frontend/resources/images/icons/picker.svg b/frontend/resources/images/icons/picker.svg index be86a18081..3fc711f38c 100644 --- a/frontend/resources/images/icons/picker.svg +++ b/frontend/resources/images/icons/picker.svg @@ -1,4 +1,3 @@ - diff --git a/frontend/resources/styles/main/partials/colorpicker.scss b/frontend/resources/styles/main/partials/colorpicker.scss index 05cf8370d6..70a43f3b34 100644 --- a/frontend/resources/styles/main/partials/colorpicker.scss +++ b/frontend/resources/styles/main/partials/colorpicker.scss @@ -29,7 +29,7 @@ border: none; cursor: pointer; - &.active, + &.active svg, &:hover svg { fill: $color-primary; } diff --git a/frontend/src/app/main/data/colors.cljs b/frontend/src/app/main/data/colors.cljs index 16d49b9b0c..7b7edc9877 100644 --- a/frontend/src/app/main/data/colors.cljs +++ b/frontend/src/app/main/data/colors.cljs @@ -133,36 +133,6 @@ (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] - (change-fill ids color 1 id file-id)) - ([ids color opacity id file-id] - (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 - :fill-color-ref-id id - :fill-color-ref-file file-id} - (and opacity (not= opacity :multiple)) (assoc :fill-opacity opacity)) - - 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-stroke [ids color] (ptk/reify ::change-stroke ptk/WatchEvent @@ -185,49 +155,44 @@ :stroke-opacity 1)))] (rx/of (dwc/update-shapes ids update-fn)))))) -#_(defn change-stroke [ids color id file-id] - (ptk/reify ::change-stroke - ptk/WatchEvent - (watch [_ state s] - (let [objects (get-in state [:workspace-data :pages-index (:current-page-id state) :objects]) - children (mapcat #(cph/get-children % objects) ids) - ids (into ids children) - - update-fn (fn [s] - (cond-> s - true - (assoc :stroke-color color - :stroke-color-ref-id id - :stroke-color-ref-file file-id) - - (= (:stroke-style s) :none) - (assoc :stroke-style :solid - :stroke-width 1 - :stroke-opacity 1)))] - (rx/of (dwc/update-shapes ids update-fn)))))) - (defn picker-for-selected-shape [] - ;; TODO: replace st/emit! by a subject push and set that in the WatchEvent - (let [handle-change-color - (fn [color shift?] - (let [ids (get-in @st/state [:workspace-local :selected])] - (st/emit! - (if shift? - (change-stroke ids color) - (change-fill ids color)) - (md/hide))))] - (ptk/reify ::start-picker + (let [sub (rx/subject)] + (ptk/reify ::picker-for-selected-shape + ptk/WatchEvent + (watch [_ state stream] + (let [ids (get-in state [:workspace-local :selected]) + stop? (->> stream + (rx/filter (ptk/type? ::stop-picker))) + + update-events (fn [[color shift?]] + (rx/of (if shift? + (change-stroke ids color) + (change-fill ids color)) + (stop-picker)))] + (rx/merge + ;; Stream that updates the stroke/width and stops if `esc` pressed + (->> sub + (rx/take-until stop?) + (rx/flat-map update-events)) + + ;; Hide the modal if the stop event is emitted + (->> stop? + (rx/first) + (rx/map #(md/hide)))))) + ptk/UpdateEvent (update [_ state] - (-> state - (assoc-in [:workspace-local :picking-color?] true) - (assoc ::md/modal {:id (random-uuid) - :type :colorpicker - :props {:on-change handle-change-color} - :allow-click-outside true})))))) + (let [handle-change-color (fn [color shift?] (rx/push! sub [color shift?]))] + (-> state + (assoc-in [:workspace-local :picking-color?] true) + (assoc ::md/modal {:id (random-uuid) + :data {:color "#000000" :opacity 1} + :type :colorpicker + :props {:on-change handle-change-color} + :allow-click-outside true}))))))) (defn select-gradient-stop [spot] - (ptk/reify ::start-picker + (ptk/reify ::select-gradient-stop ptk/UpdateEvent (update [_ state] (-> state diff --git a/frontend/src/app/main/data/modal.cljs b/frontend/src/app/main/data/modal.cljs index 976ecff65d..cd0a7c2c54 100644 --- a/frontend/src/app/main/data/modal.cljs +++ b/frontend/src/app/main/data/modal.cljs @@ -50,7 +50,9 @@ (ptk/reify ::update-modal ptk/UpdateEvent (update [_ state] - (c/update state ::modal merge options)))) + (cond-> state + (::modal state) + (c/update ::modal merge options))))) (defn show! [type props] diff --git a/frontend/src/app/main/ui/modal.cljs b/frontend/src/app/main/ui/modal.cljs index ac6a3c9aa0..4b2d36256a 100644 --- a/frontend/src/app/main/ui/modal.cljs +++ b/frontend/src/app/main/ui/modal.cljs @@ -20,10 +20,10 @@ (:import goog.events.EventType)) (defn- on-esc-clicked - [event] - (when (k/esc? event) - (st/emit! (dm/hide)) - (dom/stop-propagation event))) + [event allow-click-outside] + (when (and (k/esc? event) (not allow-click-outside)) + (do (dom/stop-propagation event) + (st/emit! (dm/hide))))) (defn- on-pop-state [event] @@ -62,16 +62,23 @@ (let [data (unchecked-get props "data") wrapper-ref (mf/use-ref nil) + allow-click-outside (:allow-click-outside data) + handle-click-outside (fn [event] - (on-click-outside event wrapper-ref (:type data) (:allow-click-outside data)))] + (on-click-outside event wrapper-ref (:type data) allow-click-outside)) + + handle-keydown + (fn [event] + (on-esc-clicked event allow-click-outside))] (mf/use-layout-effect + (mf/deps allow-click-outside) (fn [] - (let [keys [(events/listen js/document EventType.KEYDOWN on-esc-clicked) + (let [keys [(events/listen js/document EventType.KEYDOWN handle-keydown) (events/listen js/window EventType.POPSTATE on-pop-state) (events/listen js/document EventType.CLICK handle-click-outside)]] - #(for [key keys] + #(doseq [key keys] (events/unlistenByKey key))))) [:div.modal-wrapper {:ref wrapper-ref} diff --git a/frontend/src/app/main/ui/workspace/colorpicker.cljs b/frontend/src/app/main/ui/workspace/colorpicker.cljs index 04be286240..b63d2e29d2 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker.cljs @@ -121,6 +121,9 @@ ref-picker (mf/use-ref) + dirty? (mf/use-var false) + last-color (mf/use-var data) + picking-color? (mf/deref picking-color?) picked-color (mf/deref picked-color) picked-color-select (mf/deref picked-color-select) @@ -128,8 +131,6 @@ editing-spot-state (mf/deref editing-spot-state-ref) - ;; data-ref (mf/use-var data) - current-color (:current-color @state) change-tab @@ -140,15 +141,9 @@ (fn [changes] (let [editing-stop (:editing-stop @state)] (swap! state #(cond-> % - true (update :current-color merge changes) - editing-stop (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))))) + true (update :current-color merge changes) + editing-stop (update-in [:stops editing-stop] merge changes))) + (reset! dirty? true))) handle-change-stop (fn [offset] @@ -160,11 +155,15 @@ (st/emit! (dc/select-gradient-stop offset))))) on-select-library-color - (fn [color] (prn "color" color)) + (fn [color] (reset! state (data->state color))) + + on-add-library-color + (fn [color] (st/emit! (dwl/add-color (state->data @state)))) on-activate-gradient (fn [type] (fn [] + (reset! dirty? true) (if (= type (:type @state)) (do (swap! state assoc :type :color) @@ -179,13 +178,6 @@ 1 (-> (:current-color @state) (assoc :alpha 0))}))))))] - ;; Update state when there is a change in the props upstream - ;; TODO: Review for gradients - #_(mf/use-effect - (mf/deps value opacity) - (fn [] - (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) @@ -208,48 +200,47 @@ ;; When closing the modal we update the recent-color list (mf/use-effect - (fn [] (fn [] - (st/emit! (dc/stop-picker)) - (st/emit! (dwl/add-recent-color (state->data @state)))))) + #(fn [] + (st/emit! (dc/stop-picker)) + (when @last-color + (st/emit! (dwl/add-recent-color @last-color))))) + ;; Updates color when used el pixel picker (mf/use-effect - (mf/deps picking-color? picked-color) + (mf/deps picking-color? picked-color-select) (fn [] - (when picking-color? - (let [[r g b] (or picked-color [0 0 0]) + (when (and picking-color? picked-color-select) + (let [[r g b alpha] picked-color hex (uc/rgb->hex [r g b]) [h s v] (uc/hex->hsv hex)] + (handle-change-color {:hex hex + :r r :g g :b b + :h h :s s :v v + :alpha alpha}))))) - (swap! state update :current-color assoc - :r r :g g :b b - :h h :s s :v v - :hex hex) - - ;; 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?)))) - + ;; Changes when another gradient handler is selected (mf/use-effect (mf/deps editing-spot-state) #(when (not= editing-spot-state (:editing-stop @state)) (handle-change-stop (or editing-spot-state 0)))) + ;; Changes on the viewport when moving a gradient handler (mf/use-effect (mf/deps data) #(if-let [gradient-data (-> data data->state :gradient-data)] - (swap! state assoc :gradient-data gradient-data))) + (do + (reset! dirty? true) + (swap! state assoc :gradient-data gradient-data)))) + ;; Send the properties to the store (mf/use-effect (mf/deps @state) (fn [] - (on-change (state->data @state)))) + (if @dirty? + (let [color (state->data @state)] + (reset! dirty? false) + (reset! last-color color) + (on-change color))))) [:div.colorpicker {:ref ref-picker} [:div.colorpicker-content @@ -306,7 +297,8 @@ :on-change handle-change-color}] [:& libraries {:current-color current-color - :on-select-color on-select-library-color}] + :on-select-color on-select-library-color + :on-add-library-color on-add-library-color}] (when on-accept [:div.actions diff --git a/frontend/src/app/main/ui/workspace/colorpicker/libraries.cljs b/frontend/src/app/main/ui/workspace/colorpicker/libraries.cljs index 3c4a04c817..09d5b95187 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/libraries.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker/libraries.cljs @@ -32,7 +32,7 @@ [app.main.ui.workspace.colorpicker.ramp :refer [ramp-selector]] [app.main.ui.workspace.colorpicker.color-inputs :refer [color-inputs]])) -(mf/defc libraries [{:keys [current-color on-select-color]}] +(mf/defc libraries [{:keys [current-color on-select-color on-add-library-color]}] (let [selected-library (mf/use-state "recent") current-library-colors (mf/use-state []) @@ -69,7 +69,7 @@ (mf/use-effect (mf/deps file-colors) (fn [] (when (= @selected-library "file") - (let [colors (map #(select-keys % [:id :value]) (vals file-colors))] + (let [colors (vals file-colors)] (reset! current-library-colors (into [] colors)))))) @@ -88,7 +88,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 current-color))} + :on-click on-add-library-color} i/plus]) [:div.color-bullet.button {:style {:background-color "white"} diff --git a/frontend/src/app/main/ui/workspace/colorpicker/pixel_overlay.cljs b/frontend/src/app/main/ui/workspace/colorpicker/pixel_overlay.cljs index faa61fbe58..9aea134b3b 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/pixel_overlay.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker/pixel_overlay.cljs @@ -10,20 +10,157 @@ (ns app.main.ui.workspace.colorpicker.pixel-overlay (:require [rumext.alpha :as mf] - [okulary.core :as l] [cuerdas.core :as str] - [app.common.geom.point :as gpt] - [app.common.math :as math] - [app.common.uuid :refer [uuid]] + [okulary.core :as l] + [promesa.core :as p] + [goog.events :as events] + [app.common.uuid :as uuid] + [app.util.timers :as timers] [app.util.dom :as dom] - [app.util.color :as uc] [app.util.object :as obj] - [app.main.store :as st] - [app.main.refs :as refs] - [app.main.data.workspace.libraries :as dwl] - [app.main.data.colors :as dc] + [app.main.data.colors :as dwc] + [app.main.data.fetch :as mdf] [app.main.data.modal :as modal] - [app.main.ui.icons :as i] - [app.util.i18n :as i18n :refer [t]])) + [app.main.refs :as refs] + [app.main.store :as st] + [app.main.ui.context :as muc] + [app.main.ui.cursors :as cur] + [app.main.ui.keyboard :as kbd] + [app.main.ui.workspace.shapes :refer [shape-wrapper frame-wrapper]]) + (:import goog.events.EventType)) +(defn format-viewbox [vbox] + (str/join " " [(+ (:x vbox 0) (:left-offset vbox 0)) + (:y vbox 0) + (:width vbox 0) + (:height vbox 0)])) +(mf/defc overlay-frames + {::mf/wrap [mf/memo] + ::mf/wrap-props false} + [] + (let [data (mf/deref refs/workspace-page) + objects (:objects data) + root (get objects uuid/zero) + shapes (->> (:shapes root) (map #(get objects %)))] + [:* + [:g.shapes + (for [item shapes] + (if (= (:type item) :frame) + [:& frame-wrapper {:shape item + :key (:id item) + :objects objects}] + [:& shape-wrapper {:shape item + :key (:id item)}]))]])) + +(mf/defc pixel-overlay + {::mf/wrap-props false} + [props] + (let [vport (unchecked-get props "vport") + vbox (unchecked-get props "vbox") + viewport-ref (unchecked-get props "viewport-ref") + options (unchecked-get props "options") + svg-ref (mf/use-ref nil) + canvas-ref (mf/use-ref nil) + fetch-pending (mf/deref (mdf/pending-ref)) + + handle-keydown + (fn [event] + (when (and (kbd/esc? event)) + (do (dom/stop-propagation event) + (dom/prevent-default event) + (st/emit! (dwc/stop-picker)) + (modal/disallow-click-outside!)))) + + on-mouse-move-picker + (fn [event] + (when-let [zoom-view-node (.getElementById js/document "picker-detail")] + (let [{brx :left bry :top} (dom/get-bounding-rect (mf/ref-val viewport-ref)) + x (- (.-clientX event) brx) + y (- (.-clientY event) bry) + + zoom-context (.getContext zoom-view-node "2d") + canvas-node (mf/ref-val canvas-ref) + canvas-context (.getContext canvas-node "2d") + pixel-data (.getImageData canvas-context x y 1 1) + rgba (.-data pixel-data) + r (obj/get rgba 0) + g (obj/get rgba 1) + b (obj/get rgba 2) + a (obj/get rgba 3) + + area-data (.getImageData canvas-context (- x 25) (- y 20) 50 40)] + + (-> (js/createImageBitmap area-data) + (p/then (fn [image] + ;; Draw area + (obj/set! zoom-context "imageSmoothingEnabled" false) + (.drawImage zoom-context image 0 0 200 160)))) + (st/emit! (dwc/pick-color [r g b a]))))) + + on-mouse-down-picker + (fn [event] + (dom/prevent-default event) + (dom/stop-propagation event) + (st/emit! (dwc/pick-color-select true (kbd/shift? event)))) + + on-mouse-up-picker + (fn [event] + (dom/prevent-default event) + (dom/stop-propagation event) + (st/emit! (dwc/stop-picker)) + (modal/disallow-click-outside!))] + + (mf/use-effect + (fn [] + (let [listener (events/listen js/document EventType.KEYDOWN handle-keydown)] + #(events/unlistenByKey listener)))) + + (mf/use-effect + ;; Everytime we finish retrieving a new URL we redraw the canvas + ;; so even if we're not finished the user can start to pick basic + ;; shapes + (mf/deps fetch-pending) + (fn [] + (try + (let [canvas-node (mf/ref-val canvas-ref) + canvas-context (.getContext canvas-node "2d") + svg-node (mf/ref-val svg-ref)] + (timers/schedule 100 + #(let [xml (.serializeToString (js/XMLSerializer.) svg-node) + img-src (str "data:image/svg+xml;base64," + (-> xml js/encodeURIComponent js/unescape js/btoa)) + img (js/Image.) + on-error (fn [err] (.error js/console "ERROR" err)) + on-load (fn [] (.drawImage canvas-context img 0 0))] + (.addEventListener img "error" on-error) + (.addEventListener img "load" on-load) + (obj/set! img "src" img-src)))) + (catch :default e (.error js/console e))))) + + [:* + [:div.overlay + {:tab-index 0 + :style {:position "absolute" + :top 0 + :left 0 + :width "100%" + :height "100%" + :cursor cur/picker} + :on-mouse-down on-mouse-down-picker + :on-mouse-up on-mouse-up-picker + :on-mouse-move on-mouse-move-picker}] + [:canvas {:ref canvas-ref + :width (:width vport 0) + :height (:height vport 0) + :style {:display "none"}}] + [:& (mf/provider muc/embed-ctx) {:value true} + [:svg.viewport + {:ref svg-ref + :preserveAspectRatio "xMidYMid meet" + :width (:width vport 0) + :height (:height vport 0) + :view-box (format-viewbox vbox) + :style {:display "none" + :background-color (get options :background "#E8E9EA")}} + [:& overlay-frames]]]])) diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index e69c427d36..256b577b57 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -41,6 +41,7 @@ [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.main.ui.workspace.colorpicker.pixel-overlay :refer [pixel-overlay]] [app.common.math :as mth] [app.util.dom :as dom] [app.util.dom.dnd :as dnd] @@ -185,104 +186,6 @@ (:width vbox 0) (:height vbox 0)])) -(mf/defc pixel-picker-overlay - {::mf/wrap-props false} - [props] - (let [vport (unchecked-get props "vport") - vbox (unchecked-get props "vbox") - viewport-ref (unchecked-get props "viewport-ref") - options (unchecked-get props "options") - svg-ref (mf/use-ref nil) - canvas-ref (mf/use-ref nil) - fetch-pending (mf/deref (mdf/pending-ref)) - - on-mouse-move-picker - (fn [event] - (when-let [zoom-view-node (.getElementById js/document "picker-detail")] - (let [{brx :left bry :top} (dom/get-bounding-rect (mf/ref-val viewport-ref)) - x (- (.-clientX event) brx) - y (- (.-clientY event) bry) - - zoom-context (.getContext zoom-view-node "2d") - canvas-node (mf/ref-val canvas-ref) - canvas-context (.getContext canvas-node "2d") - pixel-data (.getImageData canvas-context x y 1 1) - rgba (.-data pixel-data) - r (obj/get rgba 0) - g (obj/get rgba 1) - b (obj/get rgba 2) - a (obj/get rgba 3) - - area-data (.getImageData canvas-context (- x 25) (- y 20) 50 40)] - - (-> (js/createImageBitmap area-data) - (p/then (fn [image] - ;; Draw area - (obj/set! zoom-context "imageSmoothingEnabled" false) - (.drawImage zoom-context image 0 0 200 160)))) - (st/emit! (dwc/pick-color [r g b a]))))) - - on-mouse-down-picker - (fn [event] - (dom/prevent-default event) - (dom/stop-propagation event) - (st/emit! (dwc/pick-color-select true (kbd/shift? event)))) - - on-mouse-up-picker - (fn [event] - (dom/prevent-default event) - (dom/stop-propagation event) - (st/emit! (dwc/stop-picker)) - (modal/disallow-click-outside!))] - - (mf/use-effect - ;; Everytime we finish retrieving a new URL we redraw the canvas - ;; so even if we're not finished the user can start to pick basic - ;; shapes - (mf/deps fetch-pending) - (fn [] - (try - (let [canvas-node (mf/ref-val canvas-ref) - canvas-context (.getContext canvas-node "2d") - svg-node (mf/ref-val svg-ref)] - (timers/schedule 100 - #(let [xml (.serializeToString (js/XMLSerializer.) svg-node) - img-src (str "data:image/svg+xml;base64," - (-> xml js/encodeURIComponent js/unescape js/btoa)) - img (js/Image.) - on-error (fn [err] (.error js/console "ERROR" err)) - on-load (fn [] (.drawImage canvas-context img 0 0))] - (.addEventListener img "error" on-error) - (.addEventListener img "load" on-load) - (obj/set! img "src" img-src)))) - (catch :default e (.error js/console e))))) - - [:* - [:div.overlay - {:style {:position "absolute" - :top 0 - :left 0 - :width "100%" - :height "100%" - :cursor cur/picker} - :on-mouse-down on-mouse-down-picker - :on-mouse-up on-mouse-up-picker - :on-mouse-move on-mouse-move-picker}] - [:canvas {:ref canvas-ref - :width (:width vport 0) - :height (:height vport 0) - :style {:display "none"}}] - [:& (mf/provider muc/embed-ctx) {:value true} - [:svg.viewport - {:ref svg-ref - :preserveAspectRatio "xMidYMid meet" - :width (:width vport 0) - :height (:height vport 0) - :view-box (format-viewbox vbox) - :style {:display "none" - :background-color (get options :background "#E8E9EA")}} - [:& frames]]]])) - (mf/defc viewport [{:keys [page-id page local layout] :as props}] (let [{:keys [options-mode @@ -310,8 +213,6 @@ drawing-tool (:tool drawing) drawing-obj (:object drawing) - pick-color (mf/use-state [255 255 255 255]) - zoom (or zoom 1) on-mouse-down @@ -587,11 +488,11 @@ [:* (when picking-color? - [:& pixel-picker-overlay {:vport vport - :vbox vbox - :viewport-ref viewport-ref - :options options - :layout layout}]) + [:& pixel-overlay {:vport vport + :vbox vbox + :viewport-ref viewport-ref + :options options + :layout layout}]) [:svg.viewport {:preserveAspectRatio "xMidYMid meet" From 4bb832b597eba8ec7d42cecf121121f5b6825fcf Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 15 Oct 2020 09:35:29 +0200 Subject: [PATCH 08/14] :sparkles: Fixes issues with some shapes --- .../styles/main/partials/color-bullet.scss | 7 ++++ .../src/app/main/data/workspace/grid.cljs | 4 +- .../app/main/data/workspace/selection.cljs | 12 ++++-- .../app/main/ui/components/color_bullet.cljs | 32 +++++---------- frontend/src/app/main/ui/shapes/filters.cljs | 13 +++--- .../app/main/ui/workspace/colorpicker.cljs | 8 +++- .../ui/workspace/colorpicker/libraries.cljs | 13 ++++-- .../src/app/main/ui/workspace/frame_grid.cljs | 8 +++- .../workspace/sidebar/options/frame_grid.cljs | 14 ++++--- .../sidebar/options/rows/color_row.cljs | 40 ++++++++++++------- .../ui/workspace/sidebar/options/shadow.cljs | 6 ++- frontend/src/app/util/color.cljs | 6 +++ 12 files changed, 101 insertions(+), 62 deletions(-) diff --git a/frontend/resources/styles/main/partials/color-bullet.scss b/frontend/resources/styles/main/partials/color-bullet.scss index ba0d980c35..abde93a402 100644 --- a/frontend/resources/styles/main/partials/color-bullet.scss +++ b/frontend/resources/styles/main/partials/color-bullet.scss @@ -102,6 +102,13 @@ ul.palette-menu .color-bullet { } } +.color-data .color-bullet.multiple { + background: transparent; + + &::before { + content: "?" + } +} .color-data .color-bullet { background-color: $color-gray-30; diff --git a/frontend/src/app/main/data/workspace/grid.cljs b/frontend/src/app/main/data/workspace/grid.cljs index 9d70190873..feacd4fbff 100644 --- a/frontend/src/app/main/data/workspace/grid.cljs +++ b/frontend/src/app/main/data/workspace/grid.cljs @@ -21,7 +21,7 @@ (defonce ^:private default-square-params {:size 16 - :color {:value "#59B9E2" + :color {:color "#59B9E2" :opacity 0.2}}) (defonce ^:private default-layout-params @@ -30,7 +30,7 @@ :item-length nil :gutter 8 :margin 0 - :color {:value "#DE4762" + :color {:color "#DE4762" :opacity 0.1}}) (defonce default-grid-params diff --git a/frontend/src/app/main/data/workspace/selection.cljs b/frontend/src/app/main/data/workspace/selection.cljs index 928e87da4d..d4b4ee3be5 100644 --- a/frontend/src/app/main/data/workspace/selection.cljs +++ b/frontend/src/app/main/data/workspace/selection.cljs @@ -22,6 +22,7 @@ [app.common.spec :as us] [app.common.uuid :as uuid] [app.main.data.workspace.common :as dwc] + [app.main.data.modal :as md] [app.main.streams :as ms] [app.main.worker :as uw])) @@ -123,9 +124,14 @@ (ptk/reify ::deselect-all ptk/UpdateEvent (update [_ state] - (update state :workspace-local #(-> % - (assoc :selected (d/ordered-set)) - (dissoc :selected-frame)))))) + + ;; Only deselect if there is no modal openned + (cond-> state + (not (::md/modal state)) + (update :workspace-local + #(-> % + (assoc :selected (d/ordered-set)) + (dissoc :selected-frame))))))) ;; --- Select Shapes (By selrect) diff --git a/frontend/src/app/main/ui/components/color_bullet.cljs b/frontend/src/app/main/ui/components/color_bullet.cljs index f3bb31826c..b1d19b6661 100644 --- a/frontend/src/app/main/ui/components/color_bullet.cljs +++ b/frontend/src/app/main/ui/components/color_bullet.cljs @@ -9,32 +9,20 @@ (ns app.main.ui.components.color-bullet (:require - #_[beicon.core :as rx] - #_[goog.events :as events] - #_[okulary.core :as l] [rumext.alpha :as mf] - [app.util.color :as uc] - #_[cuerdas.core :as str] - #_[app.common.math :as mth] - #_[app.main.data.colors :as mdc] - #_[app.main.data.workspace :as udw] - #_[app.main.store :as st] - #_[app.main.ui.components.dropdown :refer [dropdown]] - #_[app.main.ui.icons :as i] - #_[app.main.ui.keyboard :as kbd] - #_[app.util.color :refer [hex->rgb]] - #_[app.util.dom :as dom] - #_[app.util.object :as obj] - #_[app.main.refs :as refs] - #_[app.util.i18n :as i18n :refer [t]])) + [app.util.color :as uc])) (mf/defc color-bullet [{:keys [color on-click]}] - (let [color (if (string? color) {:color color :opacity 1} color)] - [:div.color-bullet {:on-click #(when on-click (on-click %))} - (when (not (:gradient color)) - [:div.color-bullet-left {:style {:background (uc/color->background (assoc color :opacity 1))}}]) + (if (uc/multiple? color) + [:div.color-bullet.multiple {:on-click #(when on-click (on-click %))}] - [:div.color-bullet-right {:style {:background (uc/color->background color)}}]])) + ;; No multiple selection + (let [color (if (string? color) {:color color :opacity 1} color)] + [:div.color-bullet {:on-click #(when on-click (on-click %))} + (when (not (:gradient color)) + [:div.color-bullet-left {:style {:background (uc/color->background (assoc color :opacity 1))}}]) + + [:div.color-bullet-right {:style {:background (uc/color->background color)}}]]))) (defn gradient-type->string [{:keys [type]}] (case type diff --git a/frontend/src/app/main/ui/shapes/filters.cljs b/frontend/src/app/main/ui/shapes/filters.cljs index 5f583b2dce..e7286e8588 100644 --- a/frontend/src/app/main/ui/shapes/filters.cljs +++ b/frontend/src/app/main/ui/shapes/filters.cljs @@ -25,8 +25,9 @@ (str/fmt "url(#$0)" [filter-id]))) (mf/defc color-matrix - [{:keys [color opacity]}] - (let [[r g b a] (color/hex->rgba color opacity) + [{:keys [color]}] + (let [{:keys [color opacity]} color + [r g b a] (color/hex->rgba color opacity) [r g b] [(/ r 255) (/ g 255) (/ b 255)]] [:feColorMatrix {:type "matrix" @@ -36,7 +37,7 @@ [{:keys [filter-id filter shape]}] (let [{:keys [x y width height]} (:selrect shape) - {:keys [id in-filter color opacity offset-x offset-y blur spread]} filter] + {:keys [id in-filter color offset-x offset-y blur spread]} filter] [:* [:feColorMatrix {:in "SourceAlpha" :type "matrix" :values "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"}] @@ -48,7 +49,7 @@ [:feOffset {:dx offset-x :dy offset-y}] [:feGaussianBlur {:stdDeviation (/ blur 2)}] - [:& color-matrix {:color color :opacity opacity}] + [:& color-matrix {:color color}] [:feBlend {:mode "normal" :in2 in-filter @@ -58,7 +59,7 @@ [{:keys [filter-id filter shape]}] (let [{:keys [x y width height]} (:selrect shape) - {:keys [id in-filter color opacity offset-x offset-y blur spread]} filter] + {:keys [id in-filter color offset-x offset-y blur spread]} filter] [:* [:feColorMatrix {:in "SourceAlpha" :type "matrix" :values "0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" @@ -78,7 +79,7 @@ :k2 "-1" :k3 "1"}] - [:& color-matrix {:color color :opacity opacity}] + [:& color-matrix {:color color}] [:feBlend {:mode "normal" :in2 in-filter diff --git a/frontend/src/app/main/ui/workspace/colorpicker.cljs b/frontend/src/app/main/ui/workspace/colorpicker.cljs index b63d2e29d2..a328b2cbf8 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker.cljs @@ -155,7 +155,9 @@ (st/emit! (dc/select-gradient-stop offset))))) on-select-library-color - (fn [color] (reset! state (data->state color))) + (fn [color] + (reset! dirty? true) + (reset! state (data->state color))) on-add-library-color (fn [color] (st/emit! (dwl/add-color (state->data @state)))) @@ -216,7 +218,7 @@ (handle-change-color {:hex hex :r r :g g :b b :h h :s s :v v - :alpha alpha}))))) + :alpha (/ alpha 255)}))))) ;; Changes when another gradient handler is selected (mf/use-effect @@ -297,6 +299,8 @@ :on-change handle-change-color}] [:& libraries {:current-color current-color + :disable-gradient disable-gradient + :disable-opacity disable-opacity :on-select-color on-select-library-color :on-add-library-color on-add-library-color}] diff --git a/frontend/src/app/main/ui/workspace/colorpicker/libraries.cljs b/frontend/src/app/main/ui/workspace/colorpicker/libraries.cljs index 09d5b95187..a281007892 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/libraries.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker/libraries.cljs @@ -32,7 +32,8 @@ [app.main.ui.workspace.colorpicker.ramp :refer [ramp-selector]] [app.main.ui.workspace.colorpicker.color-inputs :refer [color-inputs]])) -(mf/defc libraries [{:keys [current-color on-select-color on-add-library-color]}] +(mf/defc libraries [{:keys [current-color on-select-color on-add-library-color + disable-gradient disable-opacity]}] (let [selected-library (mf/use-state "recent") current-library-colors (mf/use-state []) @@ -45,7 +46,11 @@ (fn [selected] (if (#{"recent" "file"} selected) (keyword selected) - (uuid selected)) )] + (uuid selected)) ) + + check-valid-color? (fn [color] + (and (or (not disable-gradient) (not (:gradient color))) + (or (not disable-opacity) (= 1 (:opacity color)))))] ;; Load library colors when the select is changed (mf/use-effect @@ -63,14 +68,14 @@ :else ;; Library UUID (map #(merge {:file-id (uuid @selected-library)}) (vals (get-in shared-libs [(uuid @selected-library) :data :colors]))))] - (reset! current-library-colors (into [] mapped-colors))))) + (reset! current-library-colors (into [] (filter check-valid-color?) mapped-colors))))) ;; If the file colors change and the file option is selected updates the state (mf/use-effect (mf/deps file-colors) (fn [] (when (= @selected-library "file") (let [colors (vals file-colors)] - (reset! current-library-colors (into [] colors)))))) + (reset! current-library-colors (into [] (filter check-valid-color?) colors)))))) [:div.libraries diff --git a/frontend/src/app/main/ui/workspace/frame_grid.cljs b/frontend/src/app/main/ui/workspace/frame_grid.cljs index 969a55414d..88b2000963 100644 --- a/frontend/src/app/main/ui/workspace/frame_grid.cljs +++ b/frontend/src/app/main/ui/workspace/frame_grid.cljs @@ -18,7 +18,9 @@ (mf/defc square-grid [{:keys [frame zoom grid] :as props}] (let [{:keys [color size] :as params} (-> grid :params) - {color-value :value color-opacity :opacity} (-> grid :params :color) + {color-value :color color-opacity :opacity} (-> grid :params :color) + ;; Support for old color format + color-value (or color-value (:value (get-in grid [:params :color :value]))) {frame-width :width frame-height :height :keys [x y]} frame] (when (> size 0) [:g.grid @@ -43,7 +45,9 @@ :stroke-width (str (/ 1 zoom))}}])]]))) (mf/defc layout-grid [{:keys [key frame zoom grid]}] - (let [{color-value :value color-opacity :opacity} (-> grid :params :color) + (let [{color-value :color color-opacity :opacity} (-> grid :params :color) + ;; Support for old color format + color-value (or color-value (:value (get-in grid [:params :color :value]))) gutter (-> grid :params :gutter) gutter? (and (not (nil? gutter)) (not= gutter 0)) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/frame_grid.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/frame_grid.cljs index 04e55265d8..d1aba7a4f5 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/frame_grid.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/frame_grid.cljs @@ -101,14 +101,17 @@ (assoc-in [:params :item-length] item-length))))) handle-change-color - (fn [value opacity] - (emit-changes! #(-> % - (assoc-in [:params :color :value] value) - (assoc-in [:params :color :opacity] opacity)))) + (fn [color] + (emit-changes! #(-> % (assoc-in [:params :color] color)))) handle-use-default (fn [] - (emit-changes! #(hash-map :params ((:type grid) default-grid-params)))) + (let [params ((:type grid) default-grid-params) + color (or (get-in params [:color :value]) (get-in params [:color :color])) + params (-> params + (assoc-in [:color :color] color) + (update :color dissoc :value))] + (emit-changes! #(hash-map :params params)))) handle-set-as-default (fn [] @@ -214,6 +217,7 @@ :on-change (handle-change :params :margin)}]]) [:& color-row {:color (:color params) + :disable-gradient true :on-change handle-change-color}] [:div.row-flex [:button.btn-options {:disabled is-default diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs index 2812a90568..ae30c75846 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs @@ -12,14 +12,15 @@ [rumext.alpha :as mf] [cuerdas.core :as str] [app.common.math :as math] - [app.main.ui.components.color-bullet :refer [color-bullet color-name]] + [app.common.pages :as cp] + [app.common.data :as d] [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.util.color :as uc] [app.main.refs :as refs] - [app.util.color :as uc])) + [app.main.data.modal :as modal] + [app.main.ui.components.color-bullet :refer [color-bullet color-name]])) (defn color-picker-callback [color disable-gradient disable-opacity handle-change-color handle-open handle-close] @@ -73,7 +74,9 @@ (update :color #(or % (:value color))))) change-value (fn [new-value] - (when on-change (on-change (assoc color :color new-value)))) + (when on-change (on-change (-> color + (assoc :color new-value) + (dissoc :gradient))))) change-opacity (fn [new-opacity] (when on-change (on-change (assoc color :opacity new-opacity)))) @@ -107,8 +110,16 @@ handle-click-color (mf/use-callback (mf/deps color) - (color-picker-callback color disable-gradient disable-opacity - handle-pick-color handle-open handle-close))] + (let [;; If multiple, we change to default color + color (if (uc/multiple? color) + {:color cp/default-color :opacity 1} + color)] + (color-picker-callback color + disable-gradient + disable-opacity + handle-pick-color + handle-open + handle-close)))] (mf/use-effect (mf/deps color) @@ -118,11 +129,6 @@ [:div.row-flex.color-data [:& color-bullet {:color color :on-click handle-click-color}] - #_[:span.color-th - {:class (when (and (:id color) (not= (:id color) :multiple)) "color-name") - :style {:background (uc/color->background color)} - :on-click handle-click-color} - (when (= (:color color) :multiple) "?")] (cond ;; Rendering a color with ID @@ -131,7 +137,8 @@ [:div.color-name (str (get-color-name color))]] ;; Rendering a gradient - (and (:gradient color) (get-in color [:gradient :type])) + (and (not (uc/multiple? color)) + (:gradient color) (get-in color [:gradient :type])) [:div.color-info [:div.color-name (case (get-in color [:gradient :type]) @@ -142,13 +149,16 @@ :else [:* [:div.color-info - [:input {:value (-> color :color remove-hash) + [:input {:value (if (uc/multiple? color) + "" + (-> color :color remove-hash)) :pattern "^[0-9a-fA-F]{0,6}$" :placeholder (tr "settings.multiple") :on-click select-all :on-change handle-value-change}]] - (when (not disable-opacity) + (when (and (not disable-opacity) + (not (:gradient color))) [:div.input-element {:class (classnames :percentail (not= (:opacity color) :multiple))} [:input.input-text {:type "number" diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shadow.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shadow.cljs index 0f13a906b6..ceedd32f05 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shadow.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shadow.cljs @@ -174,7 +174,11 @@ [:span.after (t locale "workspace.options.shadow-options.spread")]]] [:div.color-row-wrap - [:& color-row {:color {:value (:color value) :opacity (:opacity value)} + [:& color-row {:color (if (string? (:color value)) + ;; Support for old format colors + {:color (:color value) :opacity (:opacity value)} + (:color value)) + :disable-gradient true :on-change (update-color index) :on-open #(st/emit! dwc/start-undo-transaction) :on-close #(st/emit! dwc/commit-undo-transaction)}]]]])) diff --git a/frontend/src/app/util/color.cljs b/frontend/src/app/util/color.cljs index a77a63d0fa..2ed47a672d 100644 --- a/frontend/src/app/util/color.cljs +++ b/frontend/src/app/util/color.cljs @@ -103,3 +103,9 @@ (str/fmt "rgba(%s, %s, %s, %s)" r g b opacity)) :else "transparent"))) + +(defn multiple? [{:keys [value color gradient]}] + (or (= value :multiple) + (= color :multiple) + (= gradient :multiple) + (and gradient color))) From e96149219215422ba8c0e455289b5f916ad26049 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 15 Oct 2020 11:50:45 +0200 Subject: [PATCH 09/14] :sparkles: Improved handlers behaviour --- frontend/src/app/main/data/colors.cljs | 23 ++++++++ frontend/src/app/main/data/workspace.cljs | 6 +-- .../src/app/main/data/workspace/drawing.cljs | 2 +- .../app/main/data/workspace/selection.cljs | 32 +++++++----- .../app/main/ui/workspace/colorpicker.cljs | 52 ++++++++++++++----- .../src/app/main/ui/workspace/gradients.cljs | 19 ++++--- .../app/main/ui/workspace/shapes/common.cljs | 2 +- .../app/main/ui/workspace/shapes/frame.cljs | 2 +- .../ui/workspace/shapes/interactions.cljs | 2 +- .../app/main/ui/workspace/sidebar/layers.cljs | 6 +-- 10 files changed, 99 insertions(+), 47 deletions(-) diff --git a/frontend/src/app/main/data/colors.cljs b/frontend/src/app/main/data/colors.cljs index 7b7edc9877..efc6716bf7 100644 --- a/frontend/src/app/main/data/colors.cljs +++ b/frontend/src/app/main/data/colors.cljs @@ -191,6 +191,29 @@ :props {:on-change handle-change-color} :allow-click-outside true}))))))) +(defn start-gradient [gradient] + (ptk/reify ::start-gradient + ptk/UpdateEvent + (update [_ state] + (let [id (first (get-in state [:workspace-local :selected]))] + (-> state + (assoc-in [:workspace-local :current-gradient] gradient) + (assoc-in [:workspace-local :current-gradient :shape-id] id)))))) + +(defn stop-gradient [] + (ptk/reify ::stop-gradient + ptk/UpdateEvent + (update [_ state] + (-> state + (update :workspace-local dissoc :current-gradient))))) + +(defn update-gradient [changes] + (ptk/reify ::update-gradient + ptk/UpdateEvent + (update [_ state] + (-> state + (update-in [:workspace-local :current-gradient] merge changes))))) + (defn select-gradient-stop [spot] (ptk/reify ::select-gradient-stop ptk/UpdateEvent diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index e62276f982..959f8a0592 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -741,7 +741,7 @@ (watch [_ state stream] (let [selected (get-in state [:workspace-local :selected])] (rx/of (delete-shapes selected) - dws/deselect-all))))) + (dws/deselect-all)))))) ;; --- Shape Vertical Ordering @@ -1318,7 +1318,7 @@ :grow-type (if (> (count text) 100) :auto-height :auto-width) :content (as-content text)})] (rx/of dwc/start-undo-transaction - dws/deselect-all + (dws/deselect-all) (add-shape shape) (dwc/rehash-shape-frame-relationship [id]) dwc/commit-undo-transaction))))) @@ -1531,7 +1531,7 @@ (select-for-drawing :text)) "ctrl+c" #(st/emit! copy-selected) "ctrl+v" #(st/emit! paste) - "escape" #(st/emit! :interrupt deselect-all) + "escape" #(st/emit! :interrupt (deselect-all true)) "del" #(st/emit! delete-selected) "backspace" #(st/emit! delete-selected) "ctrl+up" #(st/emit! (vertical-order-selected :up)) diff --git a/frontend/src/app/main/data/workspace/drawing.cljs b/frontend/src/app/main/data/workspace/drawing.cljs index c61420901d..bfe1144180 100644 --- a/frontend/src/app/main/data/workspace/drawing.cljs +++ b/frontend/src/app/main/data/workspace/drawing.cljs @@ -299,7 +299,7 @@ (rx/of dwc/start-undo-transaction) (rx/empty)) - (rx/of dw/deselect-all + (rx/of (dw/deselect-all) (dw/add-shape shape)))))))))) (def close-drawing-path diff --git a/frontend/src/app/main/data/workspace/selection.cljs b/frontend/src/app/main/data/workspace/selection.cljs index d4b4ee3be5..b2a91a9171 100644 --- a/frontend/src/app/main/data/workspace/selection.cljs +++ b/frontend/src/app/main/data/workspace/selection.cljs @@ -67,7 +67,7 @@ (ms/mouse-up? %)) stream)] (rx/concat - (rx/of deselect-all) + (rx/of (deselect-all)) (->> ms/mouse-position (rx/scan (fn [data pos] (if data @@ -118,20 +118,26 @@ objects (dwc/lookup-page-objects state page-id)] (rx/of (dwc/expand-all-parents ids objects)))))) -(def deselect-all +(defn deselect-all "Clear all possible state of drawing, edition - or any similar action taken by the user." - (ptk/reify ::deselect-all - ptk/UpdateEvent - (update [_ state] + or any similar action taken by the user. + When `check-modal` the method will check if a modal is opened + and not deselect if it's true" + ([] (deselect-all false)) - ;; Only deselect if there is no modal openned - (cond-> state - (not (::md/modal state)) - (update :workspace-local - #(-> % - (assoc :selected (d/ordered-set)) - (dissoc :selected-frame))))))) + ([check-modal] + (ptk/reify ::deselect-all + ptk/UpdateEvent + (update [_ state] + + ;; Only deselect if there is no modal openned + (cond-> state + (or (not check-modal) + (not (::md/modal state))) + (update :workspace-local + #(-> % + (assoc :selected (d/ordered-set)) + (dissoc :selected-frame)))))))) ;; --- Select Shapes (By selrect) diff --git a/frontend/src/app/main/ui/workspace/colorpicker.cljs b/frontend/src/app/main/ui/workspace/colorpicker.cljs index a328b2cbf8..29f4a21ecf 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker.cljs @@ -52,6 +52,9 @@ (def editing-spot-state-ref (l/derived (l/in [:workspace-local :editing-stop]) st/state)) +(def current-gradient-ref + (l/derived (l/in [:workspace-local :current-gradient]) st/state)) + ;; --- Color Picker Modal (defn color->components [value opacity] @@ -130,6 +133,7 @@ picked-shift? (mf/deref picked-shift?) editing-spot-state (mf/deref editing-spot-state-ref) + current-gradient (mf/deref current-gradient-ref) current-color (:current-color @state) @@ -145,6 +149,13 @@ editing-stop (update-in [:stops editing-stop] merge changes))) (reset! dirty? true))) + handle-click-picker (fn [] + (if picking-color? + (do (modal/disallow-click-outside!) + (st/emit! (dc/stop-picker))) + (do (modal/allow-click-outside!) + (st/emit! (dc/start-picker))))) + handle-change-stop (fn [offset] (when-let [offset-color (get-in @state [:stops offset])] @@ -169,10 +180,10 @@ (if (= type (:type @state)) (do (swap! state assoc :type :color) - (swap! state dissoc :editing-stop :stops :gradient-data)) - (do - (swap! state assoc :type type - :gradient-data (create-gradient-data type)) + (swap! state dissoc :editing-stop :stops :gradient-data) + (st/emit! (dc/stop-gradient))) + (let [gradient-data (create-gradient-data type)] + (swap! state assoc :type type :gradient-data gradient-data) (when (not (:stops @state)) (swap! state assoc :editing-stop 0 @@ -228,11 +239,25 @@ ;; Changes on the viewport when moving a gradient handler (mf/use-effect - (mf/deps data) - #(if-let [gradient-data (-> data data->state :gradient-data)] - (do - (reset! dirty? true) - (swap! state assoc :gradient-data gradient-data)))) + (mf/deps current-gradient) + (fn [] + (when current-gradient + (let [gradient-data (select-keys current-gradient [:start-x :start-y + :end-x :end-y + :width])] + (when (not= (:gradient-data @state) gradient-data) + (do + (reset! dirty? true) + (swap! state assoc :gradient-data gradient-data))))))) + + ;; Check if we've opened a color with gradient + (mf/use-effect + (fn [] + (when (:gradient data) + (st/emit! (dc/start-gradient (:gradient data)))) + + ;; on-unmount we stop the handlers + #(st/emit! (dc/stop-gradient)))) ;; Send the properties to the store (mf/use-effect @@ -242,6 +267,8 @@ (let [color (state->data @state)] (reset! dirty? false) (reset! last-color color) + (when (:gradient color) + (st/emit! (dc/start-gradient (:gradient color)))) (on-change color))))) [:div.colorpicker {:ref ref-picker} @@ -249,9 +276,7 @@ [:div.top-actions [:button.picker-btn {:class (when picking-color? "active") - :on-click (fn [] - (modal/allow-click-outside!) - (st/emit! (dc/start-picker)))} + :on-click handle-click-picker} i/picker] (when (not disable-gradient) @@ -310,8 +335,7 @@ {:on-click (fn [] (on-accept (state->data @state)) (modal/hide!))} - (t locale "workspace.libraries.colors.save-color")]])]]) - ) + (t locale "workspace.libraries.colors.save-color")]])]])) (defn calculate-position "Calculates the style properties for the given coordinates and position" diff --git a/frontend/src/app/main/ui/workspace/gradients.cljs b/frontend/src/app/main/ui/workspace/gradients.cljs index 38dc28471a..085872e576 100644 --- a/frontend/src/app/main/ui/workspace/gradients.cljs +++ b/frontend/src/app/main/ui/workspace/gradients.cljs @@ -38,6 +38,9 @@ (def editing-spot-ref (l/derived (l/in [:workspace-local :editing-stop]) st/state)) +(def current-gradient-ref + (l/derived (l/in [:workspace-local :current-gradient]) st/state)) + (mf/defc shadow [{:keys [id x y width height offset]}] [:filter {:id id :x x @@ -228,17 +231,15 @@ :on-mouse-down (partial on-mouse-down :to-p) :on-mouse-up (partial on-mouse-up :to-p)}]])) -(def modal-type-ref - (l/derived (comp :type ::modal/modal) st/state)) (mf/defc gradient-handlers [{: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) - modal (mf/deref modal-type-ref) + gradient (mf/deref current-gradient-ref) editing-spot (mf/deref editing-spot-ref) + {:keys [x y width height] :as sr} (:selrect shape) + [{start-color :color start-opacity :opacity} {end-color :color end-opacity :opacity}] (:stops gradient) @@ -258,10 +259,8 @@ width-p (gpt/add from-p width-v) - change! (fn [change] - (st/emit! (dwc/update-shapes - [(:id shape)] - #(update % :fill-color-gradient merge change)))) + change! (fn [changes] + (st/emit! (dc/update-gradient changes))) on-change-start (fn [point] (let [start-x (/ (- (:x point) x) width) @@ -285,7 +284,7 @@ (change! {:width norm-dist})))] - (when (and gradient (= modal :colorpicker)) + (when (and gradient (= id (:shape-id gradient))) [:& gradient-handler-transformed {:editing editing-spot :from-p from-p diff --git a/frontend/src/app/main/ui/workspace/shapes/common.cljs b/frontend/src/app/main/ui/workspace/shapes/common.cljs index 62c3466199..51c183c23f 100644 --- a/frontend/src/app/main/ui/workspace/shapes/common.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/common.cljs @@ -49,7 +49,7 @@ (st/emit! (dw/select-shape id true))) (do (when-not (or (empty? selected) (kbd/shift? event)) - (st/emit! dw/deselect-all)) + (st/emit! (dw/deselect-all))) (st/emit! (dw/select-shape id)))) (st/emit! (dw/start-move-selected))))))) diff --git a/frontend/src/app/main/ui/workspace/shapes/frame.cljs b/frontend/src/app/main/ui/workspace/shapes/frame.cljs index 0882bf6498..4a27b69466 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame.cljs @@ -100,7 +100,7 @@ (mf/deps (:id shape)) (fn [event] (dom/prevent-default event) - (st/emit! dw/deselect-all + (st/emit! (dw/deselect-all) (dw/select-shape (:id shape))))) on-mouse-over diff --git a/frontend/src/app/main/ui/workspace/shapes/interactions.cljs b/frontend/src/app/main/ui/workspace/shapes/interactions.cljs index fb36edad84..d4a8684fea 100644 --- a/frontend/src/app/main/ui/workspace/shapes/interactions.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/interactions.cljs @@ -32,7 +32,7 @@ (do (dom/stop-propagation event) (when-not (empty? selected) - (st/emit! dw/deselect-all)) + (st/emit! (dw/deselect-all))) (st/emit! (dw/select-shape id)) (st/emit! (dw/start-create-interaction)))) diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs index 889d04dcde..5080656f53 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs @@ -143,10 +143,10 @@ (st/emit! (dw/select-shape id true)) (> (count selected) 1) - (st/emit! dw/deselect-all + (st/emit! (dw/deselect-all) (dw/select-shape id)) :else - (st/emit! dw/deselect-all + (st/emit! (dw/deselect-all) (dw/select-shape id))))) on-context-menu @@ -160,7 +160,7 @@ on-drag (fn [{:keys [id]}] (when (not (contains? selected id)) - (st/emit! dw/deselect-all + (st/emit! (dw/deselect-all) (dw/select-shape id)))) on-drop From 929d620c60109770b21b99070a2af518771ea398 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 15 Oct 2020 15:44:00 +0200 Subject: [PATCH 10/14] :sparkles: Fixes exports and preview --- frontend/src/app/main/exports.cljs | 46 +++++++----- frontend/src/app/main/ui/context.cljs | 2 + frontend/src/app/main/ui/shapes/attrs.cljs | 21 +++--- frontend/src/app/main/ui/shapes/filters.cljs | 65 ++++++++--------- .../src/app/main/ui/shapes/gradients.cljs | 14 ++-- frontend/src/app/main/ui/shapes/text.cljs | 25 +++---- frontend/src/app/main/ui/viewer/shapes.cljs | 45 +++++++----- .../app/main/ui/workspace/colorpicker.cljs | 10 +-- .../workspace/colorpicker/pixel_overlay.cljs | 59 ++++++++++----- .../src/app/main/ui/workspace/gradients.cljs | 4 +- .../app/main/ui/workspace/shapes/common.cljs | 28 +++---- .../app/main/ui/workspace/shapes/frame.cljs | 28 ++++--- .../app/main/ui/workspace/shapes/path.cljs | 32 ++++---- .../app/main/ui/workspace/shapes/text.cljs | 73 +++++++++++++------ .../ui/workspace/sidebar/options/text.cljs | 2 + 15 files changed, 255 insertions(+), 199 deletions(-) diff --git a/frontend/src/app/main/exports.cljs b/frontend/src/app/main/exports.cljs index 7169782d6f..c495b37667 100644 --- a/frontend/src/app/main/exports.cljs +++ b/frontend/src/app/main/exports.cljs @@ -26,7 +26,9 @@ [app.main.ui.shapes.path :as path] [app.main.ui.shapes.rect :as rect] [app.main.ui.shapes.text :as text] - [app.main.ui.shapes.group :as group])) + [app.main.ui.shapes.group :as group] + [app.main.ui.shapes.gradients :as grad] + [app.main.ui.context :as muc])) (def ^:private default-color "#E8E9EA") ;; $color-canvas @@ -55,8 +57,14 @@ (mf/fnc frame-wrapper [{:keys [shape] :as props}] (let [childs (mapv #(get objects %) (:shapes shape)) - shape (geom/transform-shape shape)] - [:& frame-shape {:shape shape :childs childs}])))) + shape (geom/transform-shape shape) + render-id (mf/use-memo #(str (uuid/next)))] + [:& (mf/provider muc/render-ctx) {:value render-id} + [:g.frame + [:defs + [:& grad/gradient {:shape shape :attr :fill-color-gradient}] + [:& grad/gradient {:shape shape :attr :stroke-color-gradient}]] + [:& frame-shape {:shape shape :childs childs}]]])))) (defn group-wrapper-factory [objects] @@ -79,20 +87,24 @@ (when (and shape (not (:hidden shape))) (let [shape (geom/transform-shape frame shape) opts #js {:shape shape} - filter-id (filters/get-filter-id)] - [:g {:filter (filters/filter-str filter-id shape)} - [:& filters/filters {:filter-id filter-id :shape shape}] - (case (:type shape) - :curve [:> path/path-shape opts] - :text [:> text/text-shape opts] - :icon [:> icon/icon-shape opts] - :rect [:> rect/rect-shape opts] - :path [:> path/path-shape opts] - :image [:> image/image-shape opts] - :circle [:> circle/circle-shape opts] - :frame [:> frame-wrapper {:shape shape}] - :group [:> group-wrapper {:shape shape :frame frame}] - nil)]))))) + render-id (mf/use-memo #(str (uuid/next)))] + [:& (mf/provider muc/render-ctx) {:value render-id} + [:g {:filter (filters/filter-str (str "filter_" render-id) shape)} + [:defs + [:& filters/filters {:shape shape}] + [:& grad/gradient {:shape shape :attr :fill-color-gradient}] + [:& grad/gradient {:shape shape :attr :stroke-color-gradient}]] + (case (:type shape) + :curve [:> path/path-shape opts] + :text [:> text/text-shape opts] + :icon [:> icon/icon-shape opts] + :rect [:> rect/rect-shape opts] + :path [:> path/path-shape opts] + :image [:> image/image-shape opts] + :circle [:> circle/circle-shape opts] + :frame [:> frame-wrapper {:shape shape}] + :group [:> group-wrapper {:shape shape :frame frame}] + nil)]]))))) (mf/defc page-svg {::mf/wrap [mf/memo]} diff --git a/frontend/src/app/main/ui/context.cljs b/frontend/src/app/main/ui/context.cljs index 9b56bc217e..27df726cd7 100644 --- a/frontend/src/app/main/ui/context.cljs +++ b/frontend/src/app/main/ui/context.cljs @@ -12,3 +12,5 @@ [rumext.alpha :as mf])) (def embed-ctx (mf/create-context false)) + +(def render-ctx (mf/create-context nil)) diff --git a/frontend/src/app/main/ui/shapes/attrs.cljs b/frontend/src/app/main/ui/shapes/attrs.cljs index 6db6b6f272..27083b0ab3 100644 --- a/frontend/src/app/main/ui/shapes/attrs.cljs +++ b/frontend/src/app/main/ui/shapes/attrs.cljs @@ -9,8 +9,10 @@ (ns app.main.ui.shapes.attrs (:require + [rumext.alpha :as mf] [cuerdas.core :as str] - [app.util.object :as obj])) + [app.util.object :as obj] + [app.main.ui.context :as muc])) (defn- stroke-type->dasharray [style] @@ -24,16 +26,16 @@ (obj/merge! attrs #js {:rx (:rx shape) :ry (:ry shape)})) -(defn add-fill [attrs shape] - (let [fill-color-gradient-id (str "fill-color-gradient_" (:render-id shape))] +(defn add-fill [attrs shape render-id] + (let [fill-color-gradient-id (str "fill-color-gradient_" render-id)] (if (:fill-color-gradient shape) (obj/merge! attrs #js {:fill (str/format "url(#%s)" fill-color-gradient-id)}) (obj/merge! attrs #js {:fill (or (:fill-color shape) "transparent") :fillOpacity (:fill-opacity shape nil)})))) -(defn add-stroke [attrs shape] +(defn add-stroke [attrs shape render-id] (let [stroke-style (:stroke-style shape :none) - stroke-color-gradient-id (str "stroke-color-gradient_" (:render-id shape))] + stroke-color-gradient-id (str "stroke-color-gradient_" render-id)] (if (not= stroke-style :none) (if (:stroke-color-gradient shape) (obj/merge! attrs @@ -49,7 +51,8 @@ (defn extract-style-attrs ([shape] - (-> (obj/new) - (add-border-radius shape) - (add-fill shape) - (add-stroke shape)))) + (let [render-id (mf/use-ctx muc/render-ctx)] + (-> (obj/new) + (add-border-radius shape) + (add-fill shape render-id) + (add-stroke shape render-id))))) diff --git a/frontend/src/app/main/ui/shapes/filters.cljs b/frontend/src/app/main/ui/shapes/filters.cljs index e7286e8588..3fafec419a 100644 --- a/frontend/src/app/main/ui/shapes/filters.cljs +++ b/frontend/src/app/main/ui/shapes/filters.cljs @@ -123,45 +123,42 @@ [filter-x filter-y filter-width filter-height] (get-filters-bounds shape filters)] (when (seq filters) - [:defs - [:filter {:id filter-id - :x filter-x :y filter-y - :width filter-width :height filter-height - :filterUnits "userSpaceOnUse" - :color-interpolation-filters "sRGB"} + [:filter {:id filter-id + :x filter-x :y filter-y + :width filter-width :height filter-height + :filterUnits "userSpaceOnUse" + :color-interpolation-filters "sRGB"} - (let [;; Add as a paramter the input filter - drop-shadow-filters (->> filters (filter #(= :drop-shadow (:style %)))) - drop-shadow-filters (->> drop-shadow-filters - (map #(str "filter" (:id %))) - (cons "BackgroundImageFix") - (map add-in-filter drop-shadow-filters)) + (let [;; Add as a paramter the input filter + drop-shadow-filters (->> filters (filter #(= :drop-shadow (:style %)))) + drop-shadow-filters (->> drop-shadow-filters + (map #(str "filter" (:id %))) + (cons "BackgroundImageFix") + (map add-in-filter drop-shadow-filters)) - inner-shadow-filters (->> filters (filter #(= :inner-shadow (:style %)))) - inner-shadow-filters (->> inner-shadow-filters + inner-shadow-filters (->> filters (filter #(= :inner-shadow (:style %)))) + inner-shadow-filters (->> inner-shadow-filters (map #(str "filter" (:id %))) (cons "shape") (map add-in-filter inner-shadow-filters))] - [:* - [:feFlood {:flood-opacity 0 :result "BackgroundImageFix"}] - (for [{:keys [id type] :as filter} drop-shadow-filters] - [:& drop-shadow-filter {:key id + [:* + [:feFlood {:flood-opacity 0 :result "BackgroundImageFix"}] + (for [{:keys [id type] :as filter} drop-shadow-filters] + [:& drop-shadow-filter {:key id + :filter-id filter-id + :filter filter + :shape shape}]) + + [:feBlend {:mode "normal" + :in "SourceGraphic" + :in2 (if (seq drop-shadow-filters) + (str "filter" (:id (last drop-shadow-filters))) + "BackgroundImageFix") + :result "shape"}] + + (for [{:keys [id type] :as filter} inner-shadow-filters] + [:& inner-shadow-filter {:key id :filter-id filter-id :filter filter - :shape shape}]) - - [:feBlend {:mode "normal" - :in "SourceGraphic" - :in2 (if (seq drop-shadow-filters) - (str "filter" (:id (last drop-shadow-filters))) - "BackgroundImageFix") - :result "shape"}] - - (for [{:keys [id type] :as filter} inner-shadow-filters] - [:& inner-shadow-filter {:key id - :filter-id filter-id - :filter filter - :shape shape}]) - ]) - ]]))) + :shape shape}])])]))) diff --git a/frontend/src/app/main/ui/shapes/gradients.cljs b/frontend/src/app/main/ui/shapes/gradients.cljs index a1c216b46c..120e623dd7 100644 --- a/frontend/src/app/main/ui/shapes/gradients.cljs +++ b/frontend/src/app/main/ui/shapes/gradients.cljs @@ -13,6 +13,7 @@ [cuerdas.core :as str] [app.util.object :as obj] [app.common.uuid :as uuid] + [app.main.ui.context :as muc] [app.common.geom.point :as gpt])) (mf/defc linear-gradient [{:keys [id gradient shape]}] @@ -32,7 +33,8 @@ (mf/defc radial-gradient [{:keys [id gradient shape]}] (let [{:keys [x y width height]} shape] [:defs - (let [translate-vec (gpt/point (+ x (* width (:start-x gradient))) + (let [[x y] (if (= (:type shape) :frame) [0 0] [x y]) + translate-vec (gpt/point (+ x (* width (:start-x gradient))) (+ y (* height (:start-y gradient)))) gradient-vec (gpt/to-vec (gpt/point (* width (:start-x gradient)) @@ -72,13 +74,13 @@ [props] (let [attr (obj/get props "attr") shape (obj/get props "shape") - render-id (obj/get props "render-id") - + render-id (mf/use-ctx muc/render-ctx) id (str (name attr) "_" render-id) gradient (get shape attr) gradient-props #js {:id id :gradient gradient :shape shape}] - (case (:type gradient) - :linear [:> linear-gradient gradient-props] - :radial [:> radial-gradient gradient-props]))) + (when gradient + (case (:type gradient) + :linear [:> linear-gradient gradient-props] + :radial [:> radial-gradient gradient-props])))) diff --git a/frontend/src/app/main/ui/shapes/text.cljs b/frontend/src/app/main/ui/shapes/text.cljs index 205a6817b1..ba773fc449 100644 --- a/frontend/src/app/main/ui/shapes/text.cljs +++ b/frontend/src/app/main/ui/shapes/text.cljs @@ -68,9 +68,10 @@ fill-color (obj/get data "fill-color" fill) fill-opacity (obj/get data "fill-opacity" opacity) - fill-color-gradient (obj/get data "fill-color-gradient" opacity) - fill-color-gradient (-> (js->clj fill-color-gradient :keywordize-keys true) - (update :type keyword)) + fill-color-gradient (obj/get data "fill-color-gradient" nil) + fill-color-gradient (when fill-color-gradient + (-> (js->clj fill-color-gradient :keywordize-keys true) + (update :type keyword))) fill-color-ref-id (obj/get data "fill-color-ref-id") fill-color-ref-file (obj/get data "fill-color-ref-file") @@ -83,14 +84,9 @@ fontsdb (deref fonts/fontsdb) base #js {:textDecoration text-decoration - ;:color (str/format "rgba(%s, %s, %s, %s)" r g b a) :textTransform text-transform :lineHeight (or line-height "inherit") - - :background background - :WebkitTextFillColor "transparent" - :WebkitBackgroundClip "text" - }] + "--text-color" background}] (when (and (string? letter-spacing) (pos? (alength letter-spacing))) @@ -179,8 +175,7 @@ (if (string? text) (let [style (generate-text-styles (clj->js node))] - [:span {:style style - :key (str index "-" (:fill-color node))} (if (= text "") "\u00A0" text)]) + [:span.text-node {:style style} (if (= text "") "\u00A0" text)]) (let [children (map-indexed (fn [index node] (mf/element text-node {:index index :node node :key index})) children)] @@ -192,13 +187,15 @@ {:key index :style style :xmlns "http://www.w3.org/1999/xhtml"} - (when (not (nil? @embeded-fonts)) - [:style @embeded-fonts]) + [:* + [:style ".text-node { background: var(--text-color); -webkit-text-fill-color: transparent; -webkit-background-clip: text;"] + (when (not (nil? @embeded-fonts)) + [:style @embeded-fonts])] children]) "paragraph-set" (let [style #js {:display "inline-block"}] - [:div.paragraphs {:key index :style style} children]) + [:div.paragraphs {:key index :style style} children]) "paragraph" (let [style (generate-paragraph-styles (clj->js node))] diff --git a/frontend/src/app/main/ui/viewer/shapes.cljs b/frontend/src/app/main/ui/viewer/shapes.cljs index 2ed021856c..ceaa0f7b19 100644 --- a/frontend/src/app/main/ui/viewer/shapes.cljs +++ b/frontend/src/app/main/ui/viewer/shapes.cljs @@ -29,7 +29,10 @@ [app.util.object :as obj] [app.common.geom.matrix :as gmt] [app.common.geom.point :as gpt] - [app.common.geom.shapes :as geom])) + [app.common.geom.shapes :as geom] + [app.common.uuid :as uuid] + [app.main.ui.shapes.gradients :as grad] + [app.main.ui.context :as muc])) (defn on-mouse-down [event {:keys [interactions] :as shape}] @@ -56,25 +59,29 @@ (mf/deps shape) #(on-mouse-down % shape)) - filter-id (filters/get-filter-id)] + render-id (mf/use-memo #(str (uuid/next)))] - [:g.shape {:on-mouse-down on-mouse-down - :cursor (when (:interactions shape) "pointer") - :filter (filters/filter-str filter-id shape)} - [:& filters/filters {:filter-id filter-id :shape shape}] - [:& component {:shape shape - :frame frame - :childs childs - :is-child-selected? true}] - (when (and (:interactions shape) show-interactions?) - [:rect {:x (- x 1) - :y (- y 1) - :width (+ width 2) - :height (+ height 2) - :fill "#31EFB8" - :stroke "#31EFB8" - :stroke-width 1 - :fill-opacity 0.2}])]))) + [:& (mf/provider muc/render-ctx) {:value render-id} + [:g.shape {:on-mouse-down on-mouse-down + :cursor (when (:interactions shape) "pointer") + :filter (filters/filter-str (str "filter_" render-id) shape)} + [:defs + [:& filters/filters {:shape shape}] + [:& grad/gradient {:shape shape :attr :fill-color-gradient}] + [:& grad/gradient {:shape shape :attr :stroke-color-gradient}]] + [:& component {:shape shape + :frame frame + :childs childs + :is-child-selected? true}] + (when (and (:interactions shape) show-interactions?) + [:rect {:x (- x 1) + :y (- y 1) + :width (+ width 2) + :height (+ height 2) + :fill "#31EFB8" + :stroke "#31EFB8" + :stroke-width 1 + :fill-opacity 0.2}])]]))) (defn frame-wrapper [shape-container show-interactions?] diff --git a/frontend/src/app/main/ui/workspace/colorpicker.cljs b/frontend/src/app/main/ui/workspace/colorpicker.cljs index 29f4a21ecf..dc81151e47 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker.cljs @@ -369,15 +369,7 @@ (reset! dirty? (not= data new-data)) (reset! last-change new-data) (when on-change - (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?))) - ] + (on-change new-data)))] (mf/use-effect (fn [] diff --git a/frontend/src/app/main/ui/workspace/colorpicker/pixel_overlay.cljs b/frontend/src/app/main/ui/workspace/colorpicker/pixel_overlay.cljs index 9aea134b3b..4f6bb25e82 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/pixel_overlay.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker/pixel_overlay.cljs @@ -13,6 +13,7 @@ [cuerdas.core :as str] [okulary.core :as l] [promesa.core :as p] + [beicon.core :as rx] [goog.events :as events] [app.common.uuid :as uuid] [app.util.timers :as timers] @@ -53,6 +54,19 @@ [:& shape-wrapper {:shape item :key (:id item)}]))]])) +(defn draw-picker-canvas [svg-node canvas-node] + (let [canvas-context (.getContext canvas-node "2d") + xml (.serializeToString (js/XMLSerializer.) svg-node) + img-src (str "data:image/svg+xml;base64," + (-> xml js/encodeURIComponent js/unescape js/btoa)) + img (js/Image.) + + on-error (fn [err] (.error js/console "ERROR" err)) + on-load (fn [] (.drawImage canvas-context img 0 0))] + (.addEventListener img "error" on-error) + (.addEventListener img "load" on-load) + (obj/set! img "src" img-src))) + (mf/defc pixel-overlay {::mf/wrap-props false} [props] @@ -64,6 +78,8 @@ canvas-ref (mf/use-ref nil) fetch-pending (mf/deref (mdf/pending-ref)) + update-canvas-stream (rx/subject) + handle-keydown (fn [event] (when (and (kbd/esc? event)) @@ -117,26 +133,30 @@ #(events/unlistenByKey listener)))) (mf/use-effect - ;; Everytime we finish retrieving a new URL we redraw the canvas - ;; so even if we're not finished the user can start to pick basic - ;; shapes - (mf/deps fetch-pending) (fn [] - (try - (let [canvas-node (mf/ref-val canvas-ref) - canvas-context (.getContext canvas-node "2d") - svg-node (mf/ref-val svg-ref)] - (timers/schedule 100 - #(let [xml (.serializeToString (js/XMLSerializer.) svg-node) - img-src (str "data:image/svg+xml;base64," - (-> xml js/encodeURIComponent js/unescape js/btoa)) - img (js/Image.) - on-error (fn [err] (.error js/console "ERROR" err)) - on-load (fn [] (.drawImage canvas-context img 0 0))] - (.addEventListener img "error" on-error) - (.addEventListener img "load" on-load) - (obj/set! img "src" img-src)))) - (catch :default e (.error js/console e))))) + (let [sub (->> update-canvas-stream + (rx/debounce 10) + (rx/subs #(draw-picker-canvas (mf/ref-val svg-ref) + (mf/ref-val canvas-ref))))] + + #(rx/dispose! sub)))) + + (mf/use-effect + (mf/deps svg-ref canvas-ref) + (fn [] + (when (and svg-ref canvas-ref) + + (let [config (clj->js {:attributes true + :childList true + :subtree true + :characterData true}) + on-svg-change (fn [mutation-list] (rx/push! update-canvas-stream :update)) + observer (js/MutationObserver. on-svg-change)] + + (.observe observer (mf/ref-val svg-ref) config) + + ;; Disconnect on unmount + #(.disconnect observer))))) [:* [:div.overlay @@ -154,6 +174,7 @@ :width (:width vport 0) :height (:height vport 0) :style {:display "none"}}] + [:& (mf/provider muc/embed-ctx) {:value true} [:svg.viewport {:ref svg-ref diff --git a/frontend/src/app/main/ui/workspace/gradients.cljs b/frontend/src/app/main/ui/workspace/gradients.cljs index 085872e576..007b8631d8 100644 --- a/frontend/src/app/main/ui/workspace/gradients.cljs +++ b/frontend/src/app/main/ui/workspace/gradients.cljs @@ -284,7 +284,9 @@ (change! {:width norm-dist})))] - (when (and gradient (= id (:shape-id gradient))) + (when (and gradient + (= id (:shape-id gradient)) + (not= (:type shape) :text)) [:& gradient-handler-transformed {:editing editing-spot :from-p from-p diff --git a/frontend/src/app/main/ui/workspace/shapes/common.cljs b/frontend/src/app/main/ui/workspace/shapes/common.cljs index 51c183c23f..698b4a642b 100644 --- a/frontend/src/app/main/ui/workspace/shapes/common.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/common.cljs @@ -20,7 +20,8 @@ [app.common.uuid :as uuid] [app.common.geom.matrix :as gmt] [app.common.geom.point :as gpt] - [app.common.geom.shapes :as geom])) + [app.common.geom.shapes :as geom] + [app.main.ui.context :as muc])) (defn- on-mouse-down [event {:keys [id type] :as shape}] @@ -75,22 +76,15 @@ #(on-context-menu % shape)) render-id (mf/use-memo #(str (uuid/next)))] - [:g.shape {:on-mouse-down on-mouse-down - :on-context-menu on-context-menu - :filter (filters/filter-str (str "filter_" render-id) shape)} + [:& (mf/provider muc/render-ctx) {:value render-id} + [:g.shape {:on-mouse-down on-mouse-down + :on-context-menu on-context-menu + :filter (filters/filter-str (str "filter_" render-id) shape)} + [:defs + [:& filters/filters {:shape shape}] + [:& grad/gradient {:shape shape :attr :fill-color-gradient}] + [:& grad/gradient {:shape shape :attr :stroke-color-gradient}]] - [:& filters/filters {:filter-id (str "filter_" render-id) :shape shape}] - - (when (:fill-color-gradient shape) - [:& grad/gradient {:attr :fill-color-gradient - :render-id render-id - :shape shape}]) - - (when (:stroke-color-gradient shape) - [:& grad/gradient {:attr :stroke-color-gradient - :render-id render-id - :shape shape}]) - - [:& component {:shape (assoc shape :render-id render-id)}]]))) + [:& component {:shape shape}]]]))) diff --git a/frontend/src/app/main/ui/workspace/shapes/frame.cljs b/frontend/src/app/main/ui/workspace/shapes/frame.cljs index 4a27b69466..18086d3adb 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame.cljs @@ -26,7 +26,9 @@ [app.common.geom.shapes :as geom] [app.util.dom :as dom] [app.main.streams :as ms] - [app.util.timers :as ts])) + [app.util.timers :as ts] + [app.main.ui.context :as muc] + [app.common.uuid :as uuid])) (defn- frame-wrapper-factory-equals? [np op] @@ -115,7 +117,7 @@ (fn [] (st/emit! (dws/change-hover-state (:id shape) false)))) - filter-id (mf/use-memo filters/get-filter-id)] + render-id (mf/use-memo #(str (uuid/next)))] (when-not (:hidden shape) [:g {:class (when selected? "selected") @@ -128,18 +130,14 @@ :on-double-click on-double-click :on-mouse-down on-mouse-down}] - [:g.frame {:filter (filters/filter-str filter-id shape)} - [:& filters/filters {:filter-id filter-id :shape shape}] - - (when (:fill-color-gradient shape) - [:& grad/gradient {:attr :fill-color-gradient - :shape shape}]) + [:& (mf/provider muc/render-ctx) {:value render-id} + [:g.frame {:filter (filters/filter-str (str "filter_" render-id) shape)} + [:defs + [:& filters/filters {:shape shape}] + [:& grad/gradient {:shape shape :attr :fill-color-gradient}] + [:& grad/gradient {:shape shape :attr :stroke-color-gradient}]] - (when (:stroke-color-gradient shape) - [:& grad/gradient {:attr :stroke-color-gradient - :shape shape}]) - - [:& frame-shape - {:shape shape - :childs children}]]]))))) + [:& frame-shape + {:shape shape + :childs children}]]]]))))) diff --git a/frontend/src/app/main/ui/workspace/shapes/path.cljs b/frontend/src/app/main/ui/workspace/shapes/path.cljs index bf19a128e7..125b9f2372 100644 --- a/frontend/src/app/main/ui/workspace/shapes/path.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/path.cljs @@ -23,7 +23,10 @@ [app.main.data.workspace.drawing :as dr] [app.util.dom :as dom] [app.main.streams :as ms] - [app.util.timers :as ts])) + [app.util.timers :as ts] + [app.common.uuid :as uuid] + [app.main.ui.shapes.gradients :as grad] + [app.main.ui.context :as muc])) (mf/defc path-wrapper {::mf/wrap-props false} @@ -45,21 +48,18 @@ (dom/prevent-default event) (st/emit! (dw/start-edition-mode (:id shape))))))) - filter-id (mf/use-memo filters/get-filter-id)] + render-id (mf/use-memo #(str (uuid/next)))] - [:g.shape {:on-double-click on-double-click - :on-mouse-down on-mouse-down - :on-context-menu on-context-menu - :filter (filters/filter-str filter-id shape)} - [:& filters/filters {:filter-id filter-id :shape shape}] + [:& (mf/provider muc/render-ctx) {:value render-id} + [:g.shape {:on-double-click on-double-click + :on-mouse-down on-mouse-down + :on-context-menu on-context-menu + :filter (filters/filter-str (str "filter_" render-id) shape)} + [:defs + [:& filters/filters {:shape shape}] + [:& grad/gradient {:shape shape :attr :fill-color-gradient}] + [:& grad/gradient {:shape shape :attr :stroke-color-gradient}]] - (when (:fill-color-gradient shape) - [:& grad/gradient {:attr :fill-color-gradient - :shape shape}]) - - (when (:stroke-color-gradient shape) - [:& grad/gradient {:attr :stroke-color-gradient - :shape shape}]) - [:& path/path-shape {:shape shape - :background? true}]])) + [:& path/path-shape {:shape shape + :background? true}]]])) diff --git a/frontend/src/app/main/ui/workspace/shapes/text.cljs b/frontend/src/app/main/ui/workspace/shapes/text.cljs index 3f22cbf7f1..f93dae4b72 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text.cljs @@ -34,7 +34,10 @@ [app.util.color :as uc] [app.util.timers :as timers] ["slate" :as slate] - ["slate-react" :as rslate]) + ["slate-react" :as rslate] + [app.common.uuid :as uuid] + [app.main.ui.shapes.gradients :as grad] + [app.main.ui.context :as muc]) (:import goog.events.EventType goog.events.KeyCodes)) @@ -80,7 +83,7 @@ (when selected? (st/emit! (dw/start-edition-mode (:id shape))))) - filter-id (mf/use-memo filters/get-filter-id)] + render-id (mf/use-memo #(str (uuid/next)))] (mf/use-effect (mf/deps shape edition selected? current-transform) @@ -91,23 +94,27 @@ (nil? current-transform))] (timers/schedule #(reset! render-editor check?))))) - [:g.shape {:on-double-click on-double-click - :on-mouse-down on-mouse-down - :on-context-menu on-context-menu - :filter (filters/filter-str filter-id shape)} - [:& filters/filters {:filter-id filter-id :shape shape}] - [:* - (when @render-editor - [:g {:opacity 0 - :style {:pointer-events "none"}} - ;; We only render the component for its side-effect - [:& text-shape-edit {:shape shape - :read-only? true}]]) + [:& (mf/provider muc/render-ctx) {:value render-id} + [:g.shape {:on-double-click on-double-click + :on-mouse-down on-mouse-down + :on-context-menu on-context-menu + :filter (filters/filter-str (str "filter_" render-id) shape)} + [:defs + [:& filters/filters {:shape shape}] + [:& grad/gradient {:shape shape :attr :fill-color-gradient}] + [:& grad/gradient {:shape shape :attr :stroke-color-gradient}]] + [:* + (when @render-editor + [:g {:opacity 0 + :style {:pointer-events "none"}} + ;; We only render the component for its side-effect + [:& text-shape-edit {:shape shape + :read-only? true}]]) - (if edition? - [:& text-shape-edit {:shape shape}] - [:& text/text-shape {:shape shape - :selected? selected?}])]])) + (if edition? + [:& text-shape-edit {:shape shape}] + [:& text/text-shape {:shape shape + :selected? selected?}])]]])) ;; --- Text Editor Rendering @@ -158,17 +165,25 @@ fill-color (obj/get data "fill-color" fill) fill-opacity (obj/get data "fill-opacity" opacity) + fill-color-gradient (obj/get data "fill-color-gradient" nil) + fill-color-gradient (when fill-color-gradient + (-> (js->clj fill-color-gradient :keywordize-keys true) + (update :type keyword))) + fill-color-ref-id (obj/get data "fill-color-ref-id") fill-color-ref-file (obj/get data "fill-color-ref-file") [r g b a] (uc/hex->rgba fill-color fill-opacity) + background (if fill-color-gradient + (uc/gradient->css (js->clj fill-color-gradient)) + (str/format "rgba(%s, %s, %s, %s)" r g b a)) fontsdb (deref fonts/fontsdb) base #js {:textDecoration text-decoration - :color (str/format "rgba(%s, %s, %s, %s)" r g b a) :textTransform text-transform - :lineHeight (or line-height "inherit")}] + :lineHeight (or line-height "inherit") + "--text-color" background}] (when (and (string? letter-spacing) (pos? (alength letter-spacing))) @@ -243,7 +258,9 @@ childs (obj/get props "children") data (obj/get props "leaf") style (generate-text-styles data) - attrs (obj/set! attrs "style" style)] + attrs (-> attrs + (obj/set! "style" style) + (obj/set! "className" "text-node"))] [:> :span attrs childs])) (defn- render-element @@ -284,6 +301,14 @@ children-count (->> node :children (map content-size) (reduce +))] (+ current children-count))) +(defn fix-gradients + "Fix for the gradient types that need to be keywords" + [content] + (let [fix-node + (fn [node] + (d/update-in-when node [:fill-color-gradient :type] keyword))] + (ut/map-node fix-node content))) + (mf/defc text-shape-edit {::mf/wrap [mf/memo]} [{:keys [shape read-only?] :or {read-only? false} :as props}] @@ -364,7 +389,8 @@ (fn [val] (when (not read-only?) (let [content (js->clj val :keywordize-keys true) - content (first content)] + content (first content) + content (fix-gradients content)] ;; Append timestamp so we can react to cursor change events (st/emit! (dw/update-shape id {:content (assoc content :ts (js->clj (.now js/Date)))})) (reset! state val) @@ -419,7 +445,8 @@ :x x :y y :width (if (= :auto-width grow-type) 10000 width) :height height} - [:style "span { line-height: inherit; }"] + [:style "span { line-height: inherit; } + .text-node { background: var(--text-color); -webkit-text-fill-color: transparent; -webkit-background-clip: text;"] [:> rslate/Slate {:editor editor :value @state :on-change on-change} diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/text.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/text.cljs index 4c1a219966..682a7ba5ce 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/text.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/text.cljs @@ -290,6 +290,8 @@ :shape shape :attrs text-fill-attrs}) + fill-values (d/update-in-when fill-values [:fill-color-gradient :type] keyword) + fill-values (cond-> fill-values ;; Keep for backwards compatibility (:fill fill-values) (assoc :fill-color (:fill fill-values)) From f8abcc1b9cf1d4d6d237c47ed03804fa82bc7d2e Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 15 Oct 2020 16:19:36 +0200 Subject: [PATCH 11/14] :sparkles: Adds locale to new strings --- frontend/resources/locales.json | 206 +++++++++--------- .../app/main/data/workspace/libraries.cljs | 11 +- .../app/main/ui/components/color_bullet.cljs | 11 +- .../app/main/ui/workspace/colorpalette.cljs | 18 +- .../app/main/ui/workspace/sidebar/assets.cljs | 6 +- .../sidebar/options/rows/color_row.cljs | 9 +- 6 files changed, 137 insertions(+), 124 deletions(-) diff --git a/frontend/resources/locales.json b/frontend/resources/locales.json index 129b1f618f..589aef3434 100644 --- a/frontend/resources/locales.json +++ b/frontend/resources/locales.json @@ -18,7 +18,7 @@ } }, "auth.create-demo-profile" : { - "used-in" : [ "src/app/main/ui/auth/register.cljs:133", "src/app/main/ui/auth/register.cljs:136", "src/app/main/ui/auth/login.cljs:144", "src/app/main/ui/auth/login.cljs:147" ], + "used-in" : [ "src/app/main/ui/auth/login.cljs:144", "src/app/main/ui/auth/login.cljs:147", "src/app/main/ui/auth/register.cljs:133", "src/app/main/ui/auth/register.cljs:136" ], "translations" : { "en" : "Just wanna try it?", "fr" : "Vous voulez juste essayer?", @@ -36,7 +36,7 @@ } }, "auth.email" : { - "used-in" : [ "src/app/main/ui/auth/register.cljs:101", "src/app/main/ui/auth/recovery_request.cljs:47", "src/app/main/ui/auth/login.cljs:92" ], + "used-in" : [ "src/app/main/ui/auth/login.cljs:92", "src/app/main/ui/auth/register.cljs:101", "src/app/main/ui/auth/recovery_request.cljs:47" ], "translations" : { "en" : "Email", "fr" : "Adresse email", @@ -177,7 +177,7 @@ } }, "auth.password" : { - "used-in" : [ "src/app/main/ui/auth/register.cljs:106", "src/app/main/ui/auth/login.cljs:99" ], + "used-in" : [ "src/app/main/ui/auth/login.cljs:99", "src/app/main/ui/auth/register.cljs:106" ], "translations" : { "en" : "Password", "fr" : "Mot de passe", @@ -276,7 +276,7 @@ } }, "dashboard.add-shared" : { - "used-in" : [ "src/app/main/ui/workspace/header.cljs:221", "src/app/main/ui/dashboard/grid.cljs:177" ], + "used-in" : [ "src/app/main/ui/workspace/header.cljs:224", "src/app/main/ui/dashboard/grid.cljs:180" ], "translations" : { "en" : "Add as Shared Library", "fr" : "", @@ -322,7 +322,7 @@ } }, "dashboard.empty-files" : { - "used-in" : [ "src/app/main/ui/dashboard/grid.cljs:184" ], + "used-in" : [ "src/app/main/ui/dashboard/grid.cljs:187" ], "translations" : { "en" : "You still have no files here", "fr" : "Vous n'avez encore aucun fichier ici", @@ -533,7 +533,7 @@ } }, "dashboard.remove-shared" : { - "used-in" : [ "src/app/main/ui/workspace/header.cljs:219", "src/app/main/ui/dashboard/grid.cljs:176" ], + "used-in" : [ "src/app/main/ui/workspace/header.cljs:222", "src/app/main/ui/dashboard/grid.cljs:179" ], "translations" : { "en" : "Remove as Shared Library", "fr" : "", @@ -578,7 +578,7 @@ } }, "dashboard.show-all-files" : { - "used-in" : [ "src/app/main/ui/dashboard/grid.cljs:246" ], + "used-in" : [ "src/app/main/ui/dashboard/grid.cljs:249" ], "translations" : { "en" : "Show all files", "es" : "Ver todos los ficheros" @@ -636,7 +636,7 @@ } }, "dashboard.update-settings" : { - "used-in" : [ "src/app/main/ui/settings/profile.cljs:80", "src/app/main/ui/settings/password.cljs:96", "src/app/main/ui/settings/options.cljs:72" ], + "used-in" : [ "src/app/main/ui/settings/options.cljs:72", "src/app/main/ui/settings/profile.cljs:80", "src/app/main/ui/settings/password.cljs:96" ], "translations" : { "en" : "Update settings", "fr" : "Mettre à jour les paramètres", @@ -645,7 +645,7 @@ } }, "dashboard.your-account-title" : { - "used-in" : [ "src/app/main/ui/settings.cljs:28" ], + "used-in" : [ "src/app/main/ui/settings.cljs:29" ], "translations" : { "en" : "Your account", "es" : "Su cuenta" @@ -712,7 +712,7 @@ "unused" : true }, "ds.confirm-cancel" : { - "used-in" : [ "src/app/main/ui/confirm.cljs:28" ], + "used-in" : [ "src/app/main/ui/confirm.cljs:36" ], "translations" : { "en" : "Cancel", "fr" : "Annuler", @@ -721,7 +721,7 @@ } }, "ds.confirm-ok" : { - "used-in" : [ "src/app/main/ui/confirm.cljs:29" ], + "used-in" : [ "src/app/main/ui/confirm.cljs:37" ], "translations" : { "en" : "Ok", "fr" : "Ok", @@ -730,7 +730,7 @@ } }, "ds.confirm-title" : { - "used-in" : [ "src/app/main/ui/confirm.cljs:27", "src/app/main/ui/confirm.cljs:30" ], + "used-in" : [ "src/app/main/ui/confirm.cljs:35", "src/app/main/ui/confirm.cljs:39" ], "translations" : { "en" : "Are you sure?", "fr" : "Êtes-vous sûr?", @@ -757,7 +757,7 @@ } }, "errors.email-already-exists" : { - "used-in" : [ "src/app/main/ui/auth/verify_token.cljs:80", "src/app/main/ui/settings/change_email.cljs:47" ], + "used-in" : [ "src/app/main/ui/settings/change_email.cljs:47", "src/app/main/ui/auth/verify_token.cljs:80" ], "translations" : { "en" : "Email already used", "fr" : "Adresse e-mail déjà utilisée", @@ -784,7 +784,7 @@ } }, "errors.generic" : { - "used-in" : [ "src/app/main/ui/auth/verify_token.cljs:89", "src/app/main/ui/settings/profile.cljs:40", "src/app/main/ui/settings/options.cljs:32" ], + "used-in" : [ "src/app/main/ui/settings/options.cljs:32", "src/app/main/ui/settings/profile.cljs:40", "src/app/main/ui/auth/verify_token.cljs:89" ], "translations" : { "en" : "Something wrong has happened.", "fr" : "Quelque chose c'est mal passé.", @@ -811,7 +811,7 @@ } }, "errors.media-type-mismatch" : { - "used-in" : [ "src/app/main/data/workspace/persistence.cljs:413", "src/app/main/data/media.cljs:61" ], + "used-in" : [ "src/app/main/data/media.cljs:61", "src/app/main/data/workspace/persistence.cljs:413" ], "translations" : { "en" : "Seems that the contents of the image does not match the file extension.", "fr" : "", @@ -820,7 +820,7 @@ } }, "errors.media-type-not-allowed" : { - "used-in" : [ "src/app/main/data/workspace/persistence.cljs:410", "src/app/main/data/media.cljs:58" ], + "used-in" : [ "src/app/main/data/media.cljs:58", "src/app/main/data/workspace/persistence.cljs:410" ], "translations" : { "en" : "Seems that this is not a valid image.", "fr" : "", @@ -865,7 +865,7 @@ } }, "errors.unexpected-error" : { - "used-in" : [ "src/app/main/data/media.cljs:64", "src/app/main/ui/workspace/sidebar/options/exports.cljs:66", "src/app/main/ui/auth/register.cljs:45" ], + "used-in" : [ "src/app/main/data/media.cljs:64", "src/app/main/ui/auth/register.cljs:45", "src/app/main/ui/workspace/sidebar/options/exports.cljs:66" ], "translations" : { "en" : "An unexpected error occurred.", "fr" : "Une erreur inattendue c'est produite", @@ -931,7 +931,7 @@ } }, "labels.delete" : { - "used-in" : [ "src/app/main/ui/dashboard/grid.cljs:174", "src/app/main/ui/dashboard/files.cljs:85" ], + "used-in" : [ "src/app/main/ui/dashboard/files.cljs:85", "src/app/main/ui/dashboard/grid.cljs:177" ], "translations" : { "en" : "Delete", "fr" : "Supprimer", @@ -973,7 +973,7 @@ } }, "labels.logout" : { - "used-in" : [ "src/app/main/ui/settings.cljs:30", "src/app/main/ui/dashboard/sidebar.cljs:459" ], + "used-in" : [ "src/app/main/ui/settings.cljs:31", "src/app/main/ui/dashboard/sidebar.cljs:459" ], "translations" : { "en" : "Logout", "fr" : "Quitter", @@ -982,7 +982,7 @@ } }, "labels.members" : { - "used-in" : [ "src/app/main/ui/dashboard/team.cljs:59", "src/app/main/ui/dashboard/team.cljs:63", "src/app/main/ui/dashboard/sidebar.cljs:295" ], + "used-in" : [ "src/app/main/ui/dashboard/sidebar.cljs:295", "src/app/main/ui/dashboard/team.cljs:59", "src/app/main/ui/dashboard/team.cljs:63" ], "translations" : { "en" : "Members" } @@ -1064,7 +1064,7 @@ } }, "labels.rename" : { - "used-in" : [ "src/app/main/ui/dashboard/grid.cljs:173", "src/app/main/ui/dashboard/sidebar.cljs:298", "src/app/main/ui/dashboard/files.cljs:84" ], + "used-in" : [ "src/app/main/ui/dashboard/sidebar.cljs:298", "src/app/main/ui/dashboard/files.cljs:84", "src/app/main/ui/dashboard/grid.cljs:176" ], "translations" : { "en" : "Rename", "es" : "Renombrar" @@ -1077,7 +1077,7 @@ } }, "labels.settings" : { - "used-in" : [ "src/app/main/ui/settings/sidebar.cljs:80", "src/app/main/ui/dashboard/team.cljs:65", "src/app/main/ui/dashboard/sidebar.cljs:296" ], + "used-in" : [ "src/app/main/ui/settings/sidebar.cljs:80", "src/app/main/ui/dashboard/sidebar.cljs:296", "src/app/main/ui/dashboard/team.cljs:65" ], "translations" : { "en" : "Settings", "fr" : "Settings", @@ -1111,7 +1111,7 @@ } }, "media.loading" : { - "used-in" : [ "src/app/main/data/workspace/persistence.cljs:394", "src/app/main/data/media.cljs:43" ], + "used-in" : [ "src/app/main/data/media.cljs:43", "src/app/main/data/workspace/persistence.cljs:394" ], "translations" : { "en" : "Loading image...", "fr" : "Chargement de l'image...", @@ -1129,7 +1129,7 @@ "unused" : true }, "modals.add-shared-confirm.accept" : { - "used-in" : [ "src/app/main/ui/workspace/header.cljs:113", "src/app/main/ui/dashboard/grid.cljs:115" ], + "used-in" : [ "src/app/main/ui/workspace/header.cljs:114", "src/app/main/ui/dashboard/grid.cljs:116" ], "translations" : { "en" : "Add as Shared Library", "fr" : "", @@ -1147,7 +1147,7 @@ } }, "modals.add-shared-confirm.message" : { - "used-in" : [ "src/app/main/ui/workspace/header.cljs:110", "src/app/main/ui/dashboard/grid.cljs:112" ], + "used-in" : [ "src/app/main/ui/workspace/header.cljs:111", "src/app/main/ui/dashboard/grid.cljs:113" ], "translations" : { "en" : "Add “%s” as Shared Library", "fr" : "", @@ -1381,7 +1381,7 @@ } }, "modals.remove-shared-confirm.accept" : { - "used-in" : [ "src/app/main/ui/workspace/header.cljs:124", "src/app/main/ui/dashboard/grid.cljs:129" ], + "used-in" : [ "src/app/main/ui/workspace/header.cljs:127", "src/app/main/ui/dashboard/grid.cljs:132" ], "translations" : { "en" : "Remove as Shared Library", "fr" : "", @@ -1390,7 +1390,7 @@ } }, "modals.remove-shared-confirm.hint" : { - "used-in" : [ "src/app/main/ui/workspace/header.cljs:123", "src/app/main/ui/dashboard/grid.cljs:128" ], + "used-in" : [ "src/app/main/ui/workspace/header.cljs:125", "src/app/main/ui/dashboard/grid.cljs:130" ], "translations" : { "en" : "Once removed as Shared Library, the File Library of this file will stop being available to be used among the rest of your files.", "fr" : "", @@ -1399,7 +1399,7 @@ } }, "modals.remove-shared-confirm.message" : { - "used-in" : [ "src/app/main/ui/workspace/header.cljs:122", "src/app/main/ui/dashboard/grid.cljs:127" ], + "used-in" : [ "src/app/main/ui/workspace/header.cljs:124", "src/app/main/ui/dashboard/grid.cljs:129" ], "translations" : { "en" : "Remove “%s” as Shared Library", "fr" : "", @@ -1417,7 +1417,7 @@ } }, "notifications.profile-saved" : { - "used-in" : [ "src/app/main/ui/settings/profile.cljs:36", "src/app/main/ui/settings/options.cljs:36" ], + "used-in" : [ "src/app/main/ui/settings/options.cljs:36", "src/app/main/ui/settings/profile.cljs:36" ], "translations" : { "en" : "Profile saved successfully!", "fr" : "Profil enregistré avec succès!", @@ -1426,7 +1426,7 @@ } }, "notifications.validation-email-sent" : { - "used-in" : [ "src/app/main/ui/auth/register.cljs:54", "src/app/main/ui/settings/change_email.cljs:56" ], + "used-in" : [ "src/app/main/ui/settings/change_email.cljs:56", "src/app/main/ui/auth/register.cljs:54" ], "translations" : { "en" : "Verification email sent to %s; check your email!" } @@ -1441,7 +1441,7 @@ } }, "settings.multiple" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/options/stroke.cljs:156", "src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:136", "src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:145", "src/app/main/ui/workspace/sidebar/options/typography.cljs:99", "src/app/main/ui/workspace/sidebar/options/typography.cljs:149", "src/app/main/ui/workspace/sidebar/options/typography.cljs:162" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:153", "src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs:163", "src/app/main/ui/workspace/sidebar/options/typography.cljs:99", "src/app/main/ui/workspace/sidebar/options/typography.cljs:149", "src/app/main/ui/workspace/sidebar/options/typography.cljs:162", "src/app/main/ui/workspace/sidebar/options/stroke.cljs:156" ], "translations" : { "en" : "Mixed", "fr" : null, @@ -1666,7 +1666,7 @@ } }, "workspace.assets.assets" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:615" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:629" ], "translations" : { "en" : "Assets", "fr" : "", @@ -1675,7 +1675,7 @@ } }, "workspace.assets.box-filter-all" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:635" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:649" ], "translations" : { "en" : "All assets", "fr" : "", @@ -1702,7 +1702,7 @@ "unused" : true }, "workspace.assets.colors" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:320", "src/app/main/ui/workspace/sidebar/assets.cljs:638" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:329", "src/app/main/ui/workspace/sidebar/assets.cljs:652" ], "translations" : { "en" : "Colors", "fr" : "", @@ -1711,7 +1711,7 @@ } }, "workspace.assets.components" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:82", "src/app/main/ui/workspace/sidebar/assets.cljs:636" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:83", "src/app/main/ui/workspace/sidebar/assets.cljs:650" ], "translations" : { "en" : "Components", "fr" : "", @@ -1720,7 +1720,7 @@ } }, "workspace.assets.delete" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:102", "src/app/main/ui/workspace/sidebar/assets.cljs:190", "src/app/main/ui/workspace/sidebar/assets.cljs:296", "src/app/main/ui/workspace/sidebar/assets.cljs:419" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:103", "src/app/main/ui/workspace/sidebar/assets.cljs:191", "src/app/main/ui/workspace/sidebar/assets.cljs:305", "src/app/main/ui/workspace/sidebar/assets.cljs:433" ], "translations" : { "en" : "Delete", "fr" : "", @@ -1729,7 +1729,7 @@ } }, "workspace.assets.edit" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:295", "src/app/main/ui/workspace/sidebar/assets.cljs:418" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:304", "src/app/main/ui/workspace/sidebar/assets.cljs:432" ], "translations" : { "en" : "Edit", "fr" : "", @@ -1738,7 +1738,7 @@ } }, "workspace.assets.file-library" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:518" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:532" ], "translations" : { "en" : "File library", "fr" : "", @@ -1747,7 +1747,7 @@ } }, "workspace.assets.graphics" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:163", "src/app/main/ui/workspace/sidebar/assets.cljs:637" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:164", "src/app/main/ui/workspace/sidebar/assets.cljs:651" ], "translations" : { "en" : "Graphics", "fr" : "", @@ -1756,7 +1756,7 @@ } }, "workspace.assets.libraries" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:618" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:632" ], "translations" : { "en" : "Libraries", "fr" : "", @@ -1765,7 +1765,7 @@ } }, "workspace.assets.not-found" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:579" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:593" ], "translations" : { "en" : "No assets found", "fr" : "", @@ -1774,7 +1774,7 @@ } }, "workspace.assets.rename" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:294", "src/app/main/ui/workspace/sidebar/assets.cljs:417" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:303", "src/app/main/ui/workspace/sidebar/assets.cljs:431" ], "translations" : { "en" : "Rename", "fr" : "", @@ -1783,7 +1783,7 @@ } }, "workspace.assets.search" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:622" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:636" ], "translations" : { "en" : "Search assets", "fr" : "", @@ -1792,7 +1792,7 @@ } }, "workspace.assets.shared" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:520" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:534" ], "translations" : { "en" : "SHARED", "fr" : "", @@ -1801,7 +1801,7 @@ } }, "workspace.assets.typography" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:406", "src/app/main/ui/workspace/sidebar/assets.cljs:639" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/assets.cljs:420", "src/app/main/ui/workspace/sidebar/assets.cljs:653" ], "translations" : { "en" : "Typographies" } @@ -1854,8 +1854,20 @@ "en" : "Text Transform" } }, + "workspace.gradients.linear" : { + "used-in" : [ "src/app/main/data/workspace/libraries.cljs:39", "src/app/main/ui/components/color_bullet.cljs:30" ], + "translations" : { + "en" : "Linear gradient" + } + }, + "workspace.gradients.radial" : { + "used-in" : [ "src/app/main/data/workspace/libraries.cljs:40", "src/app/main/ui/components/color_bullet.cljs:31" ], + "translations" : { + "en" : "Radial gradient" + } + }, "workspace.header.menu.disable-dynamic-alignment" : { - "used-in" : [ "src/app/main/ui/workspace/header.cljs:213" ], + "used-in" : [ "src/app/main/ui/workspace/header.cljs:216" ], "translations" : { "en" : "Disable dynamic alignment", "fr" : "Désactiver l'alignement dynamique", @@ -1864,7 +1876,7 @@ } }, "workspace.header.menu.disable-snap-grid" : { - "used-in" : [ "src/app/main/ui/workspace/header.cljs:185" ], + "used-in" : [ "src/app/main/ui/workspace/header.cljs:188" ], "translations" : { "en" : "Disable snap to grid", "fr" : "Désactiver l'alignement sur la grille", @@ -1873,7 +1885,7 @@ } }, "workspace.header.menu.enable-dynamic-alignment" : { - "used-in" : [ "src/app/main/ui/workspace/header.cljs:214" ], + "used-in" : [ "src/app/main/ui/workspace/header.cljs:217" ], "translations" : { "en" : "Enable dynamic aligment", "fr" : "Activer l'alignement dynamique", @@ -1882,7 +1894,7 @@ } }, "workspace.header.menu.enable-snap-grid" : { - "used-in" : [ "src/app/main/ui/workspace/header.cljs:186" ], + "used-in" : [ "src/app/main/ui/workspace/header.cljs:189" ], "translations" : { "en" : "Snap to grid", "fr" : "Aligner sur la grille", @@ -1891,7 +1903,7 @@ } }, "workspace.header.menu.hide-assets" : { - "used-in" : [ "src/app/main/ui/workspace/header.cljs:206" ], + "used-in" : [ "src/app/main/ui/workspace/header.cljs:209" ], "translations" : { "en" : "Hide assets", "fr" : "", @@ -1900,7 +1912,7 @@ } }, "workspace.header.menu.hide-grid" : { - "used-in" : [ "src/app/main/ui/workspace/header.cljs:178" ], + "used-in" : [ "src/app/main/ui/workspace/header.cljs:181" ], "translations" : { "en" : "Hide grids", "fr" : "Masquer la grille", @@ -1909,7 +1921,7 @@ } }, "workspace.header.menu.hide-layers" : { - "used-in" : [ "src/app/main/ui/workspace/header.cljs:192" ], + "used-in" : [ "src/app/main/ui/workspace/header.cljs:195" ], "translations" : { "en" : "Hide layers", "fr" : "Masquer les couches", @@ -1918,7 +1930,7 @@ } }, "workspace.header.menu.hide-palette" : { - "used-in" : [ "src/app/main/ui/workspace/header.cljs:199" ], + "used-in" : [ "src/app/main/ui/workspace/header.cljs:202" ], "translations" : { "en" : "Hide color palette", "fr" : "Masquer la palette de couleurs", @@ -1927,7 +1939,7 @@ } }, "workspace.header.menu.hide-rules" : { - "used-in" : [ "src/app/main/ui/workspace/header.cljs:171" ], + "used-in" : [ "src/app/main/ui/workspace/header.cljs:174" ], "translations" : { "en" : "Hide rules", "fr" : "Masquer les règles", @@ -1936,7 +1948,7 @@ } }, "workspace.header.menu.show-assets" : { - "used-in" : [ "src/app/main/ui/workspace/header.cljs:207" ], + "used-in" : [ "src/app/main/ui/workspace/header.cljs:210" ], "translations" : { "en" : "Show assets", "fr" : "", @@ -1945,7 +1957,7 @@ } }, "workspace.header.menu.show-grid" : { - "used-in" : [ "src/app/main/ui/workspace/header.cljs:179" ], + "used-in" : [ "src/app/main/ui/workspace/header.cljs:182" ], "translations" : { "en" : "Show grid", "fr" : "Montrer la grille", @@ -1954,7 +1966,7 @@ } }, "workspace.header.menu.show-layers" : { - "used-in" : [ "src/app/main/ui/workspace/header.cljs:193" ], + "used-in" : [ "src/app/main/ui/workspace/header.cljs:196" ], "translations" : { "en" : "Show layers", "fr" : "Montrer les couches", @@ -1963,7 +1975,7 @@ } }, "workspace.header.menu.show-palette" : { - "used-in" : [ "src/app/main/ui/workspace/header.cljs:200" ], + "used-in" : [ "src/app/main/ui/workspace/header.cljs:203" ], "translations" : { "en" : "Show color palette", "fr" : "Montrer la palette de couleurs", @@ -1972,7 +1984,7 @@ } }, "workspace.header.menu.show-rules" : { - "used-in" : [ "src/app/main/ui/workspace/header.cljs:172" ], + "used-in" : [ "src/app/main/ui/workspace/header.cljs:175" ], "translations" : { "en" : "Show rules", "fr" : "Montrer les règles", @@ -2005,7 +2017,7 @@ } }, "workspace.header.viewer" : { - "used-in" : [ "src/app/main/ui/workspace/header.cljs:260" ], + "used-in" : [ "src/app/main/ui/workspace/header.cljs:263" ], "translations" : { "en" : "View mode (Ctrl + P)", "fr" : "Mode visualisation (Ctrl + P)", @@ -2038,19 +2050,19 @@ } }, "workspace.libraries.colors.file-library" : { - "used-in" : [ "src/app/main/ui/workspace/colorpicker.cljs:340", "src/app/main/ui/workspace/colorpalette.cljs:149" ], + "used-in" : [ "src/app/main/ui/workspace/colorpicker/libraries.cljs:87", "src/app/main/ui/workspace/colorpalette.cljs:149" ], "translations" : { "en" : "File library" } }, "workspace.libraries.colors.recent-colors" : { - "used-in" : [ "src/app/main/ui/workspace/colorpicker.cljs:339", "src/app/main/ui/workspace/colorpalette.cljs:159" ], + "used-in" : [ "src/app/main/ui/workspace/colorpicker/libraries.cljs:86", "src/app/main/ui/workspace/colorpalette.cljs:159" ], "translations" : { "en" : "Recent colors" } }, "workspace.libraries.colors.save-color" : { - "used-in" : [ "src/app/main/ui/workspace/colorpicker.cljs:375" ], + "used-in" : [ "src/app/main/ui/workspace/colorpicker.cljs:338" ], "translations" : { "en" : "Save color" } @@ -2290,7 +2302,7 @@ } }, "workspace.options.fill" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/options/fill.cljs:51" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/options/fill.cljs:54" ], "translations" : { "en" : "Fill", "fr" : "Remplissage", @@ -2308,7 +2320,7 @@ } }, "workspace.options.grid.column" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:129" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:132" ], "translations" : { "en" : "Columns", "fr" : "Colonnes", @@ -2317,7 +2329,7 @@ } }, "workspace.options.grid.params.columns" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:170" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:173" ], "translations" : { "en" : "Columns", "fr" : "Colonnes", @@ -2326,7 +2338,7 @@ } }, "workspace.options.grid.params.gutter" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:203" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:206" ], "translations" : { "en" : "Gutter", "fr" : "Gouttière", @@ -2335,7 +2347,7 @@ } }, "workspace.options.grid.params.height" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:194" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:197" ], "translations" : { "en" : "Height", "fr" : "Hauteur", @@ -2344,7 +2356,7 @@ } }, "workspace.options.grid.params.margin" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:209" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:212" ], "translations" : { "en" : "Margin", "fr" : "Marge", @@ -2353,7 +2365,7 @@ } }, "workspace.options.grid.params.rows" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:161" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:164" ], "translations" : { "en" : "Rows", "fr" : "Lignes", @@ -2362,7 +2374,7 @@ } }, "workspace.options.grid.params.set-default" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:222" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:226" ], "translations" : { "en" : "Set as default", "fr" : "Définir par défaut", @@ -2371,7 +2383,7 @@ } }, "workspace.options.grid.params.size" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:154" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:157" ], "translations" : { "en" : "Size", "fr" : "Taille", @@ -2380,7 +2392,7 @@ } }, "workspace.options.grid.params.type" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:179" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:182" ], "translations" : { "en" : "Type", "fr" : "Type", @@ -2389,7 +2401,7 @@ } }, "workspace.options.grid.params.type.bottom" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:187" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:190" ], "translations" : { "en" : "Bottom", "fr" : "Bas", @@ -2398,7 +2410,7 @@ } }, "workspace.options.grid.params.type.center" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:185" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:188" ], "translations" : { "en" : "Center", "fr" : "Centre", @@ -2407,7 +2419,7 @@ } }, "workspace.options.grid.params.type.left" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:184" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:187" ], "translations" : { "en" : "Left", "fr" : "Gauche", @@ -2416,7 +2428,7 @@ } }, "workspace.options.grid.params.type.right" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:188" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:191" ], "translations" : { "en" : "Right", "fr" : "Droite", @@ -2425,7 +2437,7 @@ } }, "workspace.options.grid.params.type.stretch" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:181" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:184" ], "translations" : { "en" : "Stretch", "fr" : "Étirer", @@ -2434,7 +2446,7 @@ } }, "workspace.options.grid.params.type.top" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:183" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:186" ], "translations" : { "en" : "Top", "fr" : "Haut", @@ -2443,7 +2455,7 @@ } }, "workspace.options.grid.params.use-default" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:220" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:224" ], "translations" : { "en" : "Use default", "fr" : "Utiliser la valeur par défaut", @@ -2452,7 +2464,7 @@ } }, "workspace.options.grid.params.width" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:195" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:198" ], "translations" : { "en" : "Width", "fr" : "Largeur", @@ -2461,7 +2473,7 @@ } }, "workspace.options.grid.row" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:130" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:133" ], "translations" : { "en" : "Rows", "fr" : "Lignes", @@ -2470,7 +2482,7 @@ } }, "workspace.options.grid.square" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:128" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:131" ], "translations" : { "en" : "Square", "fr" : "Carré", @@ -2479,7 +2491,7 @@ } }, "workspace.options.grid.title" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:234" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame_grid.cljs:238" ], "translations" : { "en" : "Grid & Layouts", "fr" : "Grille & couches", @@ -2488,7 +2500,7 @@ } }, "workspace.options.group-fill" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/options/fill.cljs:50" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/options/fill.cljs:53" ], "translations" : { "en" : "Group fill", "fr" : null, @@ -2497,7 +2509,7 @@ } }, "workspace.options.group-stroke" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/options/stroke.cljs:70" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/options/stroke.cljs:72" ], "translations" : { "en" : "Group stroke", "fr" : null, @@ -2524,7 +2536,7 @@ } }, "workspace.options.position" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/options/measures.cljs:146", "src/app/main/ui/workspace/sidebar/options/frame.cljs:126" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame.cljs:126", "src/app/main/ui/workspace/sidebar/options/measures.cljs:146" ], "translations" : { "en" : "Position", "fr" : "Position", @@ -2578,7 +2590,7 @@ } }, "workspace.options.selection-fill" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/options/fill.cljs:49" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/options/fill.cljs:52" ], "translations" : { "en" : "Selection fill", "fr" : null, @@ -2587,7 +2599,7 @@ } }, "workspace.options.selection-stroke" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/options/stroke.cljs:69" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/options/stroke.cljs:71" ], "translations" : { "en" : "Selection stroke", "fr" : null, @@ -2632,13 +2644,13 @@ } }, "workspace.options.shadow-options.title" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/options/shadow.cljs:190" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/options/shadow.cljs:194" ], "translations" : { "en" : "Shadow" } }, "workspace.options.size" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/options/measures.cljs:116", "src/app/main/ui/workspace/sidebar/options/frame.cljs:99" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/options/frame.cljs:99", "src/app/main/ui/workspace/sidebar/options/measures.cljs:116" ], "translations" : { "en" : "Size", "fr" : "Taille", @@ -2656,7 +2668,7 @@ } }, "workspace.options.stroke" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/options/stroke.cljs:71" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/options/stroke.cljs:73" ], "translations" : { "en" : "Stroke", "fr" : "Bordure", @@ -2845,7 +2857,7 @@ } }, "workspace.options.text-options.none" : { - "used-in" : [ "src/app/main/ui/workspace/sidebar/options/text.cljs:153", "src/app/main/ui/workspace/sidebar/options/typography.cljs:178" ], + "used-in" : [ "src/app/main/ui/workspace/sidebar/options/typography.cljs:178", "src/app/main/ui/workspace/sidebar/options/text.cljs:153" ], "translations" : { "en" : "None", "fr" : "Aucune", @@ -2960,7 +2972,7 @@ } }, "workspace.sitemap" : { - "used-in" : [ "src/app/main/ui/workspace/header.cljs:146" ], + "used-in" : [ "src/app/main/ui/workspace/header.cljs:149" ], "translations" : { "en" : "Sitemap", "fr" : null, @@ -3059,7 +3071,7 @@ } }, "workspace.updates.dismiss" : { - "used-in" : [ "src/app/main/data/workspace/libraries.cljs:521" ], + "used-in" : [ "src/app/main/data/workspace/libraries.cljs:538" ], "translations" : { "en" : "Dismiss", "fr" : "", @@ -3068,7 +3080,7 @@ } }, "workspace.updates.there-are-updates" : { - "used-in" : [ "src/app/main/data/workspace/libraries.cljs:517" ], + "used-in" : [ "src/app/main/data/workspace/libraries.cljs:534" ], "translations" : { "en" : "There are updates in shared libraries", "fr" : "", @@ -3077,7 +3089,7 @@ } }, "workspace.updates.update" : { - "used-in" : [ "src/app/main/data/workspace/libraries.cljs:519" ], + "used-in" : [ "src/app/main/data/workspace/libraries.cljs:536" ], "translations" : { "en" : "Update", "fr" : "", diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index a949430995..3b0840a879 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -33,15 +33,18 @@ (declare sync-file) +(defn default-color-name [color] + (or (:color color) + (case (get-in color [:gradient :type]) + :linear (tr "workspace.gradients.linear") + :radial (tr "workspace.gradients.radial")))) + (defn add-color [color] (let [id (uuid/next) color (assoc color :id id - :name (or (:color color) - (case (get-in color [:gradient :type]) - :linear "Linear gradient" - :radial "Radial gradient")))] + :name (default-color-name color))] (us/assert ::cp/color color) (ptk/reify ::add-color ptk/WatchEvent diff --git a/frontend/src/app/main/ui/components/color_bullet.cljs b/frontend/src/app/main/ui/components/color_bullet.cljs index b1d19b6661..c4acf00ce2 100644 --- a/frontend/src/app/main/ui/components/color_bullet.cljs +++ b/frontend/src/app/main/ui/components/color_bullet.cljs @@ -10,6 +10,7 @@ (ns app.main.ui.components.color-bullet (:require [rumext.alpha :as mf] + [app.util.i18n :as i18n :refer [tr]] [app.util.color :as uc])) (mf/defc color-bullet [{:keys [color on-click]}] @@ -24,16 +25,16 @@ [:div.color-bullet-right {:style {:background (uc/color->background color)}}]]))) -(defn gradient-type->string [{:keys [type]}] +(defn gradient-type->string [type] (case type - :linear "Linear" - :radial "Radial")) + :linear (tr "workspace.gradients.linear") + :radial (tr "workspace.gradients.radial") + (str "???" type))) (mf/defc color-name [{:keys [color size on-click on-double-click]}] (let [color (if (string? color) {:color color :opacity 1} color) {:keys [name color opacity gradient]} color - color-str (or name color (gradient-type->string gradient))] - #_(when (= size :big) [:span.color-text {:title (:name color) } (or (:name color) (:value color))]) + color-str (or name color (gradient-type->string (:type gradient)))] (when (= size :big) [:span.color-text {:on-click #(when on-click (on-click %)) :on-double-click #(when on-double-click (on-double-click %)) diff --git a/frontend/src/app/main/ui/workspace/colorpalette.cljs b/frontend/src/app/main/ui/workspace/colorpalette.cljs index 8a26130c78..9aa0b01668 100644 --- a/frontend/src/app/main/ui/workspace/colorpalette.cljs +++ b/frontend/src/app/main/ui/workspace/colorpalette.cljs @@ -19,7 +19,7 @@ [app.main.data.workspace :as udw] [app.main.store :as st] [app.main.ui.components.dropdown :refer [dropdown]] - [app.main.ui.components.color-bullet :refer [color-bullet color-name]] + [app.main.ui.components.color-bullet :as cb] [app.main.ui.icons :as i] [app.main.ui.keyboard :as kbd] [app.util.color :refer [hex->rgb]] @@ -61,8 +61,8 @@ [:div.color-cell {:class (str "cell-"(name size)) :on-click select-color} - [:& color-bullet {:color color}] - [:& color-name {:color color :size size}]])) + [:& cb/color-bullet {:color color}] + [:& cb/color-name {:color color :size size}]])) (mf/defc palette [{:keys [left-sidebar? current-colors recent-colors file-colors shared-libs selected size]}] @@ -139,8 +139,8 @@ [:div.library-name (str (:name cur-library) " " (str/format "(%s)" (count colors)))] [:div.color-sample (for [[idx {:keys [id color]}] (map-indexed vector (take 7 colors))] - [:& color-bullet {:key (str "color-" idx) - :color color}])]])) + [:& cb/color-bullet {:key (str "color-" idx) + :color color}])]])) [:li.palette-library @@ -150,8 +150,8 @@ (str/format " (%s)" (count file-colors)))] [:div.color-sample (for [[idx color] (map-indexed vector (take 7 (vals file-colors))) ] - [:& color-bullet {:key (str "color-" idx) - :color color}])]] + [:& cb/color-bullet {:key (str "color-" idx) + :color color}])]] [:li.palette-library {:on-click #(st/emit! (mdc/change-palette-selected :recent))} @@ -160,8 +160,8 @@ (str/format " (%s)" (count recent-colors)))] [:div.color-sample (for [[idx color] (map-indexed vector (take 7 (reverse recent-colors))) ] - [:& color-bullet {:key (str "color-" idx) - :color color}])]] + [:& cb/color-bullet {:key (str "color-" idx) + :color color}])]] [:hr.dropdown-separator] diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs index 9596bad092..a3df093b4a 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs @@ -28,7 +28,7 @@ [app.main.ui.components.file-uploader :refer [file-uploader]] [app.main.ui.components.tab-container :refer [tab-container tab-element]] [app.main.ui.workspace.sidebar.options.typography :refer [typography-entry]] - [app.main.ui.components.color-bullet :refer [color-bullet color-name gradient-type->string]] + [app.main.ui.components.color-bullet :as bc] [app.main.ui.icons :as i] [app.main.ui.keyboard :as kbd] [app.main.data.modal :as modal] @@ -201,7 +201,7 @@ :editing rename?}) default-name (cond - (:gradient color) (gradient-type->string (:gradient color)) + (:gradient color) (bc/gradient-type->string (get-in color [:gradient :type])) (:color color) (:color color) :else (:value color)) @@ -276,7 +276,7 @@ nil)) [:div.group-list-item {:on-context-menu on-context-menu} - [:& color-bullet {:color color}] + [:& bc/color-bullet {:color color}] (if (:editing @state) [:input.element-name diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs index ae30c75846..2347e0d09e 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs @@ -20,7 +20,7 @@ [app.util.color :as uc] [app.main.refs :as refs] [app.main.data.modal :as modal] - [app.main.ui.components.color-bullet :refer [color-bullet color-name]])) + [app.main.ui.components.color-bullet :as cb])) (defn color-picker-callback [color disable-gradient disable-opacity handle-change-color handle-open handle-close] @@ -127,7 +127,7 @@ (modal/update-props! :colorpicker {:data (parse-color color)}))) [:div.row-flex.color-data - [:& color-bullet {:color color + [:& cb/color-bullet {:color color :on-click handle-click-color}] (cond @@ -140,10 +140,7 @@ (and (not (uc/multiple? color)) (:gradient color) (get-in color [:gradient :type])) [:div.color-info - [:div.color-name - (case (get-in color [:gradient :type]) - :linear "Linear gradient" - :radial "Radial gradient")]] + [:div.color-name (cb/gradient-type->string (get-in color [:gradient :type]))]] ;; Rendering a plain color/opacity :else From f992c740d597c8579a8be1b6cf834ebf0d42e5e3 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Fri, 16 Oct 2020 07:32:22 +0200 Subject: [PATCH 12/14] :bug: Fixed problem with recursive color changes for frames --- frontend/src/app/main/data/colors.cljs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/main/data/colors.cljs b/frontend/src/app/main/data/colors.cljs index efc6716bf7..049452a001 100644 --- a/frontend/src/app/main/data/colors.cljs +++ b/frontend/src/app/main/data/colors.cljs @@ -111,7 +111,8 @@ (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) + not-frame (fn [shape-id] (not= (get-in objects [shape-id :type]) :frame)) + children (->> ids (filter not-frame) (mapcat #(cph/get-children % objects))) ids (into ids children) is-text? #(= :text (:type (get objects %))) @@ -138,7 +139,8 @@ ptk/WatchEvent (watch [_ state s] (let [objects (get-in state [:workspace-data :pages-index (:current-page-id state) :objects]) - children (mapcat #(cph/get-children % objects) ids) + not-frame (fn [shape-id] (not= (get-in objects [shape-id :type]) :frame)) + children (->> ids (filter not-frame) (mapcat #(cph/get-children % objects))) ids (into ids children) update-fn (fn [s] From 5e299551b7e01e25cbe310920bf690e9213d466a Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Fri, 16 Oct 2020 08:25:11 +0200 Subject: [PATCH 13/14] :bug: Fixes problem with library and colors --- .../app/main/ui/workspace/sidebar/assets.cljs | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs index a3df093b4a..1693f21a4c 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs @@ -191,7 +191,7 @@ :options [[(tr "workspace.assets.delete") on-delete]]}])])])) (mf/defc color-item - [{:keys [color local? locale file-id] :as props}] + [{:keys [color local? locale] :as props}] (let [rename? (= (:color-for-rename @refs/workspace-local) (:id color)) id (:id color) input-ref (mf/use-ref) @@ -333,16 +333,14 @@ (when open? [:div.group-list (for [color colors] - [:& color-item {:key (:id color) - :color (if (:value color) - (-> color - (assoc :color (:value color) - :opacity 1) - (dissoc :value)) - color) - :file-id file-id - :local? local? - :locale locale}])])])) + (let [color (cond-> color + (:value color) (assoc :color (:value color) :opacity 1) + (:value color) (dissoc :value) + true (assoc :file-id file-id))] + [:& color-item {:key (:id color) + :color color + :local? local? + :locale locale}]))])])) (mf/defc typography-box [{:keys [file file-id local? typographies locale open? on-open on-close] :as props}] From 57c93f80e28b6cdf6b6b8e025cfec2fee2cdf4ff Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Fri, 16 Oct 2020 11:40:49 +0200 Subject: [PATCH 14/14] :sparkles: Reusable shape container refactor --- frontend/src/app/main/exports.cljs | 46 +++++------- frontend/src/app/main/ui/shapes/shape.cljs | 30 +++++++- frontend/src/app/main/ui/viewer/shapes.cljs | 45 +++++------ .../app/main/ui/workspace/shapes/common.cljs | 22 ++---- .../app/main/ui/workspace/shapes/frame.cljs | 23 ++---- .../app/main/ui/workspace/shapes/path.cljs | 38 ++++------ .../app/main/ui/workspace/shapes/text.cljs | 74 +++++++++---------- 7 files changed, 123 insertions(+), 155 deletions(-) diff --git a/frontend/src/app/main/exports.cljs b/frontend/src/app/main/exports.cljs index c495b37667..7146814df8 100644 --- a/frontend/src/app/main/exports.cljs +++ b/frontend/src/app/main/exports.cljs @@ -27,8 +27,7 @@ [app.main.ui.shapes.rect :as rect] [app.main.ui.shapes.text :as text] [app.main.ui.shapes.group :as group] - [app.main.ui.shapes.gradients :as grad] - [app.main.ui.context :as muc])) + [app.main.ui.shapes.shape :refer [shape-container]])) (def ^:private default-color "#E8E9EA") ;; $color-canvas @@ -57,14 +56,9 @@ (mf/fnc frame-wrapper [{:keys [shape] :as props}] (let [childs (mapv #(get objects %) (:shapes shape)) - shape (geom/transform-shape shape) - render-id (mf/use-memo #(str (uuid/next)))] - [:& (mf/provider muc/render-ctx) {:value render-id} - [:g.frame - [:defs - [:& grad/gradient {:shape shape :attr :fill-color-gradient}] - [:& grad/gradient {:shape shape :attr :stroke-color-gradient}]] - [:& frame-shape {:shape shape :childs childs}]]])))) + shape (geom/transform-shape shape)] + [:> shape-container {:shape shape} + [:& frame-shape {:shape shape :childs childs}]])))) (defn group-wrapper-factory [objects] @@ -86,25 +80,19 @@ frame-wrapper (mf/use-memo (mf/deps objects) #(frame-wrapper-factory objects))] (when (and shape (not (:hidden shape))) (let [shape (geom/transform-shape frame shape) - opts #js {:shape shape} - render-id (mf/use-memo #(str (uuid/next)))] - [:& (mf/provider muc/render-ctx) {:value render-id} - [:g {:filter (filters/filter-str (str "filter_" render-id) shape)} - [:defs - [:& filters/filters {:shape shape}] - [:& grad/gradient {:shape shape :attr :fill-color-gradient}] - [:& grad/gradient {:shape shape :attr :stroke-color-gradient}]] - (case (:type shape) - :curve [:> path/path-shape opts] - :text [:> text/text-shape opts] - :icon [:> icon/icon-shape opts] - :rect [:> rect/rect-shape opts] - :path [:> path/path-shape opts] - :image [:> image/image-shape opts] - :circle [:> circle/circle-shape opts] - :frame [:> frame-wrapper {:shape shape}] - :group [:> group-wrapper {:shape shape :frame frame}] - nil)]]))))) + opts #js {:shape shape}] + [:> shape-container {:shape shape} + (case (:type shape) + :curve [:> path/path-shape opts] + :text [:> text/text-shape opts] + :icon [:> icon/icon-shape opts] + :rect [:> rect/rect-shape opts] + :path [:> path/path-shape opts] + :image [:> image/image-shape opts] + :circle [:> circle/circle-shape opts] + :frame [:> frame-wrapper {:shape shape}] + :group [:> group-wrapper {:shape shape :frame frame}] + nil)]))))) (mf/defc page-svg {::mf/wrap [mf/memo]} diff --git a/frontend/src/app/main/ui/shapes/shape.cljs b/frontend/src/app/main/ui/shapes/shape.cljs index a4202b8449..0567c10748 100644 --- a/frontend/src/app/main/ui/shapes/shape.cljs +++ b/frontend/src/app/main/ui/shapes/shape.cljs @@ -7,5 +7,33 @@ ;; ;; Copyright (c) 2020 UXBOX Labs SL -(ns app.main.ui.shapes.shape) +(ns app.main.ui.shapes.shape + (:require + [rumext.alpha :as mf] + [app.util.object :as obj] + [app.common.uuid :as uuid] + [app.main.ui.shapes.filters :as filters] + [app.main.ui.shapes.gradients :as grad] + [app.main.ui.context :as muc])) +(mf/defc shape-container + {::mf/wrap-props false} + [props] + + (let [shape (unchecked-get props "shape") + children (unchecked-get props "children") + render-id (mf/use-memo #(str (uuid/next))) + filter-id (str "filter_" render-id) + group-props (-> props + (obj/clone) + (obj/without ["shape" "children"]) + (obj/set! "className" "shape") + (obj/set! "filter" (filters/filter-str filter-id shape)))] + [:& (mf/provider muc/render-ctx) {:value render-id} + [:> :g group-props + [:defs + [:& filters/filters {:shape shape :filter-id filter-id}] + [:& grad/gradient {:shape shape :attr :fill-color-gradient}] + [:& grad/gradient {:shape shape :attr :stroke-color-gradient}]] + + children]])) diff --git a/frontend/src/app/main/ui/viewer/shapes.cljs b/frontend/src/app/main/ui/viewer/shapes.cljs index ceaa0f7b19..7ceb6166c8 100644 --- a/frontend/src/app/main/ui/viewer/shapes.cljs +++ b/frontend/src/app/main/ui/viewer/shapes.cljs @@ -30,9 +30,7 @@ [app.common.geom.matrix :as gmt] [app.common.geom.point :as gpt] [app.common.geom.shapes :as geom] - [app.common.uuid :as uuid] - [app.main.ui.shapes.gradients :as grad] - [app.main.ui.context :as muc])) + [app.main.ui.shapes.shape :refer [shape-container]])) (defn on-mouse-down [event {:keys [interactions] :as shape}] @@ -57,31 +55,24 @@ on-mouse-down (mf/use-callback (mf/deps shape) - #(on-mouse-down % shape)) + #(on-mouse-down % shape))] - render-id (mf/use-memo #(str (uuid/next)))] - - [:& (mf/provider muc/render-ctx) {:value render-id} - [:g.shape {:on-mouse-down on-mouse-down - :cursor (when (:interactions shape) "pointer") - :filter (filters/filter-str (str "filter_" render-id) shape)} - [:defs - [:& filters/filters {:shape shape}] - [:& grad/gradient {:shape shape :attr :fill-color-gradient}] - [:& grad/gradient {:shape shape :attr :stroke-color-gradient}]] - [:& component {:shape shape - :frame frame - :childs childs - :is-child-selected? true}] - (when (and (:interactions shape) show-interactions?) - [:rect {:x (- x 1) - :y (- y 1) - :width (+ width 2) - :height (+ height 2) - :fill "#31EFB8" - :stroke "#31EFB8" - :stroke-width 1 - :fill-opacity 0.2}])]]))) + [:> shape-container {:shape shape + :on-mouse-down on-mouse-down + :cursor (when (:interactions shape) "pointer")} + [:& component {:shape shape + :frame frame + :childs childs + :is-child-selected? true}] + (when (and (:interactions shape) show-interactions?) + [:rect {:x (- x 1) + :y (- y 1) + :width (+ width 2) + :height (+ height 2) + :fill "#31EFB8" + :stroke "#31EFB8" + :stroke-width 1 + :fill-opacity 0.2}])]))) (defn frame-wrapper [shape-container show-interactions?] diff --git a/frontend/src/app/main/ui/workspace/shapes/common.cljs b/frontend/src/app/main/ui/workspace/shapes/common.cljs index 698b4a642b..302c0cf482 100644 --- a/frontend/src/app/main/ui/workspace/shapes/common.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/common.cljs @@ -14,14 +14,11 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.keyboard :as kbd] - [app.main.ui.shapes.filters :as filters] - [app.main.ui.shapes.gradients :as grad] [app.util.dom :as dom] - [app.common.uuid :as uuid] [app.common.geom.matrix :as gmt] [app.common.geom.point :as gpt] [app.common.geom.shapes :as geom] - [app.main.ui.context :as muc])) + [app.main.ui.shapes.shape :refer [shape-container]])) (defn- on-mouse-down [event {:keys [id type] :as shape}] @@ -73,18 +70,11 @@ #(on-mouse-down % shape)) on-context-menu (mf/use-callback (mf/deps shape) - #(on-context-menu % shape)) - render-id (mf/use-memo #(str (uuid/next)))] + #(on-context-menu % shape))] - [:& (mf/provider muc/render-ctx) {:value render-id} - [:g.shape {:on-mouse-down on-mouse-down - :on-context-menu on-context-menu - :filter (filters/filter-str (str "filter_" render-id) shape)} - [:defs - [:& filters/filters {:shape shape}] - [:& grad/gradient {:shape shape :attr :fill-color-gradient}] - [:& grad/gradient {:shape shape :attr :stroke-color-gradient}]] - - [:& component {:shape shape}]]]))) + [:> shape-container {:shape shape + :on-mouse-down on-mouse-down + :on-context-menu on-context-menu} + [:& component {:shape shape}]]))) diff --git a/frontend/src/app/main/ui/workspace/shapes/frame.cljs b/frontend/src/app/main/ui/workspace/shapes/frame.cljs index 18086d3adb..d72bc71ca3 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame.cljs @@ -19,16 +19,13 @@ [app.main.ui.workspace.shapes.common :as common] [app.main.data.workspace.selection :as dws] [app.main.ui.shapes.frame :as frame] - [app.main.ui.shapes.gradients :as grad] - [app.main.ui.shapes.filters :as filters] [app.common.geom.matrix :as gmt] [app.common.geom.point :as gpt] [app.common.geom.shapes :as geom] [app.util.dom :as dom] [app.main.streams :as ms] [app.util.timers :as ts] - [app.main.ui.context :as muc] - [app.common.uuid :as uuid])) + [app.main.ui.shapes.shape :refer [shape-container]])) (defn- frame-wrapper-factory-equals? [np op] @@ -115,9 +112,7 @@ (mf/use-callback (mf/deps (:id shape)) (fn [] - (st/emit! (dws/change-hover-state (:id shape) false)))) - - render-id (mf/use-memo #(str (uuid/next)))] + (st/emit! (dws/change-hover-state (:id shape) false))))] (when-not (:hidden shape) [:g {:class (when selected? "selected") @@ -130,14 +125,8 @@ :on-double-click on-double-click :on-mouse-down on-mouse-down}] - [:& (mf/provider muc/render-ctx) {:value render-id} - [:g.frame {:filter (filters/filter-str (str "filter_" render-id) shape)} - [:defs - [:& filters/filters {:shape shape}] - [:& grad/gradient {:shape shape :attr :fill-color-gradient}] - [:& grad/gradient {:shape shape :attr :stroke-color-gradient}]] - - [:& frame-shape - {:shape shape - :childs children}]]]]))))) + [:> shape-container {:shape shape} + [:& frame-shape + {:shape shape + :childs children}]]]))))) diff --git a/frontend/src/app/main/ui/workspace/shapes/path.cljs b/frontend/src/app/main/ui/workspace/shapes/path.cljs index 125b9f2372..81fd438163 100644 --- a/frontend/src/app/main/ui/workspace/shapes/path.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/path.cljs @@ -11,22 +11,19 @@ (:require [rumext.alpha :as mf] [app.common.data :as d] + [app.util.dom :as dom] + [app.util.timers :as ts] + [app.main.streams :as ms] [app.main.constants :as c] - [app.main.data.workspace :as dw] [app.main.refs :as refs] [app.main.store :as st] + [app.main.data.workspace :as dw] + [app.main.data.workspace.drawing :as dr] [app.main.ui.keyboard :as kbd] [app.main.ui.shapes.path :as path] [app.main.ui.shapes.filters :as filters] - [app.main.ui.shapes.gradients :as grad] - [app.main.ui.workspace.shapes.common :as common] - [app.main.data.workspace.drawing :as dr] - [app.util.dom :as dom] - [app.main.streams :as ms] - [app.util.timers :as ts] - [app.common.uuid :as uuid] - [app.main.ui.shapes.gradients :as grad] - [app.main.ui.context :as muc])) + [app.main.ui.shapes.shape :refer [shape-container]] + [app.main.ui.workspace.shapes.common :as common])) (mf/defc path-wrapper {::mf/wrap-props false} @@ -46,20 +43,13 @@ (do (dom/stop-propagation event) (dom/prevent-default event) - (st/emit! (dw/start-edition-mode (:id shape))))))) + (st/emit! (dw/start-edition-mode (:id shape)))))))] - render-id (mf/use-memo #(str (uuid/next)))] + [:> shape-container {:shape shape + :on-double-click on-double-click + :on-mouse-down on-mouse-down + :on-context-menu on-context-menu} - [:& (mf/provider muc/render-ctx) {:value render-id} - [:g.shape {:on-double-click on-double-click - :on-mouse-down on-mouse-down - :on-context-menu on-context-menu - :filter (filters/filter-str (str "filter_" render-id) shape)} - [:defs - [:& filters/filters {:shape shape}] - [:& grad/gradient {:shape shape :attr :fill-color-gradient}] - [:& grad/gradient {:shape shape :attr :stroke-color-gradient}]] - - [:& path/path-shape {:shape shape - :background? true}]]])) + [:& path/path-shape {:shape shape + :background? true}]])) diff --git a/frontend/src/app/main/ui/workspace/shapes/text.cljs b/frontend/src/app/main/ui/workspace/shapes/text.cljs index f93dae4b72..1fccaefdd2 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text.cljs @@ -9,35 +9,34 @@ (ns app.main.ui.workspace.shapes.text (:require - [cuerdas.core :as str] + ["slate" :as slate] + ["slate-react" :as rslate] [goog.events :as events] [goog.object :as gobj] + [cuerdas.core :as str] [rumext.alpha :as mf] + [beicon.core :as rx] + [app.util.color :as color] + [app.util.dom :as dom] + [app.util.text :as ut] + [app.util.object :as obj] + [app.util.color :as uc] + [app.util.timers :as timers] [app.common.data :as d] + [app.common.geom.shapes :as geom] + [app.main.refs :as refs] + [app.main.store :as st] + [app.main.fonts :as fonts] [app.main.data.workspace :as dw] [app.main.data.workspace.common :as dwc] [app.main.data.workspace.texts :as dwt] - [app.main.refs :as refs] - [app.main.store :as st] [app.main.ui.cursors :as cur] [app.main.ui.workspace.shapes.common :as common] [app.main.ui.shapes.text :as text] [app.main.ui.keyboard :as kbd] [app.main.ui.context :as muc] [app.main.ui.shapes.filters :as filters] - [app.main.fonts :as fonts] - [app.util.color :as color] - [app.util.dom :as dom] - [app.util.text :as ut] - [app.common.geom.shapes :as geom] - [app.util.object :as obj] - [app.util.color :as uc] - [app.util.timers :as timers] - ["slate" :as slate] - ["slate-react" :as rslate] - [app.common.uuid :as uuid] - [app.main.ui.shapes.gradients :as grad] - [app.main.ui.context :as muc]) + [app.main.ui.shapes.shape :refer [shape-container]]) (:import goog.events.EventType goog.events.KeyCodes)) @@ -81,9 +80,7 @@ (dom/stop-propagation event) (dom/prevent-default event) (when selected? - (st/emit! (dw/start-edition-mode (:id shape))))) - - render-id (mf/use-memo #(str (uuid/next)))] + (st/emit! (dw/start-edition-mode (:id shape)))))] (mf/use-effect (mf/deps shape edition selected? current-transform) @@ -91,30 +88,25 @@ selected? (not edition?) (not embed-resources?) - (nil? current-transform))] - (timers/schedule #(reset! render-editor check?))))) + (nil? current-transform)) + result (timers/schedule #(reset! render-editor check?))] + #(rx/dispose! result)))) - [:& (mf/provider muc/render-ctx) {:value render-id} - [:g.shape {:on-double-click on-double-click - :on-mouse-down on-mouse-down - :on-context-menu on-context-menu - :filter (filters/filter-str (str "filter_" render-id) shape)} - [:defs - [:& filters/filters {:shape shape}] - [:& grad/gradient {:shape shape :attr :fill-color-gradient}] - [:& grad/gradient {:shape shape :attr :stroke-color-gradient}]] - [:* - (when @render-editor - [:g {:opacity 0 - :style {:pointer-events "none"}} - ;; We only render the component for its side-effect - [:& text-shape-edit {:shape shape - :read-only? true}]]) + [:> shape-container {:shape shape + :on-double-click on-double-click + :on-mouse-down on-mouse-down + :on-context-menu on-context-menu} + (when @render-editor + [:g {:opacity 0 + :style {:pointer-events "none"}} + ;; We only render the component for its side-effect + [:& text-shape-edit {:shape shape + :read-only? true}]]) - (if edition? - [:& text-shape-edit {:shape shape}] - [:& text/text-shape {:shape shape - :selected? selected?}])]]])) + (if edition? + [:& text-shape-edit {:shape shape}] + [:& text/text-shape {:shape shape + :selected? selected?}])])) ;; --- Text Editor Rendering