From ab7fee50eac84303ad116db997cf30b88445923f Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Fri, 4 Sep 2020 12:03:14 +0200 Subject: [PATCH] :tada: Color picker integration with library --- common/app/common/math.cljc | 5 + common/app/common/pages.cljc | 16 + .../styles/main/partials/colorpicker.scss | 17 +- .../app/main/data/workspace/libraries.cljs | 9 + frontend/src/app/main/refs.cljs | 6 + .../app/main/ui/workspace/colorpicker.cljs | 351 +++++++++++------- .../app/main/ui/workspace/sidebar/assets.cljs | 28 +- frontend/src/app/util/color.cljs | 21 +- frontend/src/app/util/dom.cljs | 6 + 9 files changed, 313 insertions(+), 146 deletions(-) diff --git a/common/app/common/math.cljc b/common/app/common/math.cljc index 4e3cb9eb59..d49bcf42cf 100644 --- a/common/app/common/math.cljc +++ b/common/app/common/math.cljc @@ -126,3 +126,8 @@ [x] #?(:cljs (js/Math.log10 x) :clj (Math/log10 x))) + +(defn clamp [num from to] + (if (< num from) + from + (if (> num to) to num))) diff --git a/common/app/common/pages.cljc b/common/app/common/pages.cljc index 34b8fd0636..8fb0313b3b 100644 --- a/common/app/common/pages.cljc +++ b/common/app/common/pages.cljc @@ -210,6 +210,9 @@ (s/def :internal.file/colors (s/map-of ::uuid ::color)) +(s/def :internal.file/recent-colors + (s/coll-of ::string :kind vector?)) + (s/def :internal.file/pages (s/coll-of ::uuid :kind vector?)) @@ -223,6 +226,7 @@ (s/keys :req-un [:internal.file/pages-index :internal.file/pages] :opt-un [:internal.file/colors + :internal.file/recent-colors :internal.file/media])) (defmulti operation-spec :type) @@ -291,6 +295,9 @@ (defmethod change-spec :del-color [_] (s/keys :req-un [::id])) +(defmethod change-spec :add-recent-color [_] + (s/keys :req-un [:recent-color/color])) + (s/def :internal.changes.media/object ::media-object) (defmethod change-spec :add-media [_] @@ -670,6 +677,15 @@ [data {:keys [id]}] (update data :colors dissoc id)) +(defmethod process-change :add-recent-color + [data {:keys [color]}] + ;; Moves the color to the top of the list and then truncates up to 15 + (update data :recent-colors (fn [rc] + (let [rc (conj (filterv (comp not #{color}) (or rc [])) color)] + (if (> (count rc) 15) + (subvec rc 1) + rc))))) + (defmethod process-change :add-media [data {:keys [object]}] (update data :media assoc (:id object) object)) diff --git a/frontend/resources/styles/main/partials/colorpicker.scss b/frontend/resources/styles/main/partials/colorpicker.scss index 757d51dbd1..77d5bb0d34 100644 --- a/frontend/resources/styles/main/partials/colorpicker.scss +++ b/frontend/resources/styles/main/partials/colorpicker.scss @@ -26,6 +26,7 @@ position: relative; height: 6.75rem; width: 100%; + cursor: pointer; .handler { box-shadow: rgb(255, 255, 255) 0px 0px 0px 1px inset; @@ -59,6 +60,7 @@ grid-template-columns: 2.5rem 1fr; height: 3.5rem; grid-row-gap: 0.5rem; + cursor: pointer; } .color-bullet { @@ -80,6 +82,7 @@ #f00 0%, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, #f00 100%); position: relative; + cursor: pointer; } .hue-selector .handler, @@ -88,7 +91,6 @@ box-shadow: rgba(0, 0, 0, 0.37) 0px 1px 4px 0px; transform: translate(-6px, -2px); left: 50%; - cursor: pointer; } .opacity-selector { @@ -198,6 +200,19 @@ } } } + + .actions { + margin-top: 0.5rem; + display: flex; + flex-direction: row; + justify-content: center; + + .btn-primary { + height: 1.5rem; + padding: 0 2.5rem; + font-size: $fs12; + } + } } .color-picker { diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index 061c02c29a..94636042dd 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -38,6 +38,15 @@ (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) + (ptk/reify ::add-recent-color + ptk/WatchEvent + (watch [_ state s] + (let [rchg {:type :add-recent-color + :color color}] + (rx/of (dwc/commit-changes [rchg] [] {:commit-local? true})))))) (def clear-color-for-rename (ptk/reify ::clear-color-for-rename diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index bd035122e3..90359f85c0 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -99,6 +99,12 @@ (get-in file [:data :colors]))) st/state)) +(def workspace-recent-colors + (l/derived (fn [state] + (when-let [file (:workspace-file state)] + (get-in file [:data :recent-colors]))) + st/state)) + (def workspace-project (l/derived :workspace-project st/state)) diff --git a/frontend/src/app/main/ui/workspace/colorpicker.cljs b/frontend/src/app/main/ui/workspace/colorpicker.cljs index ff8ba0a4bc..361e151f52 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker.cljs @@ -2,8 +2,10 @@ ;; License, v. 2.0. If a copy of the MPL was not distributed with this ;; file, You can obtain one at http://mozilla.org/MPL/2.0/. ;; -;; Copyright (c) 2016-2017 Juan de la Cruz -;; Copyright (c) 2016-2019 Andrey Antukh +;; 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 (:require @@ -15,187 +17,248 @@ [app.util.dom :as dom] [app.util.color :as uc] [app.main.ui.icons :as i] - [app.common.math :as math])) + [app.common.math :as math] + [app.common.uuid :refer [uuid]] + [app.main.data.workspace.libraries :as dwl] + [app.main.ui.modal :as modal] + [okulary.core :as l] + [app.main.refs :as refs])) + +(def recent-colors + (letfn [(selector [{:keys [objects]}] + (as-> {} $ + (reduce (fn [acc shape] + (-> acc + (update (:fill-color shape) (fnil inc 0)) + (update (:stroke-color shape) (fnil inc 0)))) + $ (vals objects)) + (reverse (sort-by second $)) + (map first $) + (remove nil? $)))] + (l/derived selector st/state))) + ;; --- Color Picker Modal -(mf/defc value-selector [{:keys [saturation luminance on-change]}] - (let [dragging? (mf/use-state false)] +(mf/defc value-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-selector {:on-mouse-down #(reset! dragging? true) :on-mouse-up #(reset! dragging? false) - :on-mouse-move - (fn [ev] - (when @dragging? - (let [{:keys [left right top bottom]} (-> ev dom/get-target dom/get-bounding-rect) - {:keys [x y]} (-> ev dom/get-client-position) - px (/ (- x left) (- right left)) - py (/ (- y top) (- bottom top)) - - luminance (* (- 1.0 py) (- 1 (* 0.5 px)))] - - (on-change px luminance))))} + :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 (* saturation 100) "%") - :top (str (* (- 1 (/ luminance (- 1 (* 0.5 saturation))) ) 100) "%")}}]])) + :left (str (* 100 saturation) "%") + :top (str (* 100 (- 1 (/ value 255))) "%")}}]])) (mf/defc hue-selector [{:keys [hue on-change]}] - (let [dragging? (mf/use-state false)] + (let [dragging? (mf/use-state false) + calculate-pos + (fn [ev] + (let [{:keys [left right]} (-> ev dom/get-target dom/get-bounding-rect) + {:keys [x]} (-> ev dom/get-client-position) + px (math/clamp (/ (- x left) (- right left)) 0 1)] + (on-change (* px 360))))] [:div.hue-selector {:on-mouse-down #(reset! dragging? true) :on-mouse-up #(reset! dragging? false) - :on-mouse-move - (fn [ev] - (when @dragging? - (let [{:keys [left right]} (-> ev dom/get-target dom/get-bounding-rect) - {:keys [x]} (-> ev dom/get-client-position) - px (/ (- x left) (- right left))] - (on-change (* px 360)))))} + :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 (* (/ hue 360) 100) "%")}}]])) (mf/defc opacity-selector [{:keys [opacity on-change]}] - (let [dragging? (mf/use-state false)] + (let [dragging? (mf/use-state false) + calculate-pos + (fn [ev] + (let [{:keys [left right]} (-> ev dom/get-target dom/get-bounding-rect) + {:keys [x]} (-> ev dom/get-client-position) + px (math/clamp (/ (- x left) (- right left)) 0 1)] + (on-change px)))] [:div.opacity-selector {:on-mouse-down #(reset! dragging? true) :on-mouse-up #(reset! dragging? false) - :on-mouse-move - (fn [ev] - (when @dragging? - (let [{:keys [left right]} (-> ev dom/get-target dom/get-bounding-rect) - {:keys [x]} (-> ev dom/get-client-position) - px (/ (- x left) (- right left))] - (on-change px))))} + :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 (* opacity 100) "%")}}]])) -(defn as-color-state [value opacity] +(defn as-color-components [value opacity] (let [[r g b] (uc/hex->rgb (or value "000000")) - [h s l] (uc/hex->hsl (or value "000000"))] + [h s v] (uc/hex->hsv (or value "000000"))] {:hex (or value "000000") :alpha (or opacity 1) - :r r - :g g - :b b - :h h - :s s - :l l})) + :r r :g g :b b + :h h :s s :v v})) (mf/defc colorpicker - [{:keys [value opacity]}] - (let [state (mf/use-state (as-color-state value opacity)) - ref-picker (mf/use-ref)] + [{:keys [value opacity on-change on-accept]}] + (let [current-color (mf/use-state (as-color-components value opacity)) + selected-library (mf/use-state "recent") + current-library-colors (mf/use-state []) + ref-picker (mf/use-ref) - (mf/use-effect (mf/deps value opacity) - (fn [] - (reset! state (as-color-state value opacity)))) + file-colors (mf/deref refs/workspace-file-colors) + shared-libs (mf/deref refs/workspace-libraries) + recent-colors (mf/deref refs/workspace-recent-colors) + + value-ref (mf/use-var value) + + on-change (or on-change identity)] + + ;; Update state when there is a change in the props upstream + (mf/use-effect + (mf/deps value opacity) + (fn [] + (reset! 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) + (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])] + (dom/set-css-property node "--color" (str/join ", " rgb)) + (dom/set-css-property node "--hue" (str/join ", " hue-rgb))))) + + ;; Load library colors when the select is changed + (mf/use-effect + (mf/deps @selected-library) + (fn [] (cond + (= @selected-library "recent") (reset! current-library-colors (reverse (or recent-colors []))) + (= @selected-library "file") (reset! current-library-colors (into [] (map :value (vals file-colors)))) + :else ;; Library UUID + (reset! current-library-colors (into [] (map :value (vals (get-in shared-libs [(uuid @selected-library) :data :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") + (reset! current-library-colors (into [] (map :value (vals file-colors))))))) + + ;; When closing the modal we update the recent-color list + (mf/use-effect + (fn [] #(st/emit! (dwl/add-recent-color @value-ref)))) - (mf/use-effect (mf/deps state) - (fn [] (let [node (mf/ref-val ref-picker) - rgb [(:r @state) (:g @state) (:b @state)] - hue-rgb (uc/hsl->rgb (:h @state) 1.0 0.5)] - (dom/set-css-property node "--color" (str/join ", " rgb)) - (dom/set-css-property node "--hue" (str/join ", " hue-rgb))))) [:div.colorpicker-v2 {:ref ref-picker} - [:& value-selector {:luminance (:l @state) - :saturation (:s @state) - :on-change (fn [s l] - (let [hex (uc/hsl->hex (:h @state) s l) + [:& value-selector {:hue (:h @current-color) + :saturation (:s @current-color) + :value (:v @current-color) + :on-change (fn [s v] + (let [hex (uc/hsv->hex [(:h @current-color) s v]) [r g b] (uc/hex->rgb hex)] - (swap! state assoc + (swap! current-color assoc :hex hex :r r :g g :b b - :s s :l l)))}] + :s s :v v) + (reset! value-ref hex) + (on-change hex (:alpha @current-color))))}] [:div.shade-selector [:div.color-bullet] - [:& hue-selector {:hue (:h @state) + [:& hue-selector {:hue (:h @current-color) :on-change (fn [h] - (let [hex (uc/hsl->hex h (:s @state) (:l @state)) + (let [hex (uc/hsv->hex [h (:s @current-color) (:v @current-color)]) [r g b] (uc/hex->rgb hex)] - (swap! state assoc + (swap! current-color assoc :hex hex :r r :g g :b b - :h h )))}] - [:& opacity-selector {:opacity (:alpha @state) + :h h ) + (reset! value-ref hex) + (on-change hex (:alpha @current-color))))}] + [:& opacity-selector {:opacity (:alpha @current-color) :on-change (fn [alpha] - (swap! state assoc - :alpha alpha))}]] + (swap! current-color assoc :alpha alpha) + (on-change (:hex @current-color) alpha))}]] [:div.color-values [:input.hex-value {:id "hex-value" - :value (:hex @state) + :value (:hex @current-color) :on-change (fn [e] (let [val (-> e dom/get-target dom/get-value) val (if (= (first val) \#) val (str \# val))] - (swap! state assoc :hex val) + (swap! current-color assoc :hex val) (when (uc/hex? val) + (reset! value-ref val) (let [[r g b] (uc/hex->rgb val) - [h s l] (uc/hex->hsl val)] - (swap! state assoc + [h s v] (uc/hex->hsv val)] + (swap! current-color assoc :r r :g g :b b - :h h :s s :l l)))))}] + :h h :s s :v v) + (on-change val (:alpha @current-color))))))}] [:input.red-value {:id "red-value" :type "number" :min 0 :max 255 - :value (:r @state) + :value (:r @current-color) :on-change (fn [e] - (let [val (-> e dom/get-target dom/get-value) - val (if (> val 255) 255 val) - val (if (< val 0) 0 val)] - (swap! state assoc :r val) + (let [val (-> e dom/get-target dom/get-value (math/clamp 0 255))] + (swap! current-color assoc :r val) (when (not (nil? val)) - (let [{:keys [g b]} @state + (let [{:keys [g b]} @current-color hex (uc/rgb->hex [val g b]) - [h s l] (uc/hex->hsl hex)] - (swap! state assoc + [h s v] (uc/hex->hsv hex)] + (reset! value-ref hex) + (swap! current-color assoc :hex hex - :h h :s s :l l)))))}] + :h h :s s :v v) + (on-change hex (:alpha @current-color))))))}] [:input.green-value {:id "green-value" :type "number" :min 0 :max 255 - :value (:g @state) + :value (:g @current-color) :on-change (fn [e] - (let [val (-> e dom/get-target dom/get-value) - val (if (> val 255) 255 val) - val (if (< val 0) 0 val)] - (swap! state assoc :g val) + (let [val (-> e dom/get-target dom/get-value (math/clamp 0 255))] + (swap! current-color assoc :g val) (when (not (nil? val)) - (let [{:keys [r b]} @state + (let [{:keys [r b]} @current-color hex (uc/rgb->hex [r val b]) - [h s l] (uc/hex->hsl hex)] - (swap! state assoc + [h s v] (uc/hex->hsv hex)] + (reset! value-ref hex) + (swap! current-color assoc :hex hex - :h h :s s :l l)))))}] + :h h :s s :v v) + (on-change hex (:alpha @current-color))))))}] [:input.blue-value {:id "blue-value" :type "number" :min 0 :max 255 - :value (:b @state) + :value (:b @current-color) :on-change (fn [e] - (let [val (-> e dom/get-target dom/get-value) - val (if (> val 255) 255 val) - val (if (< val 0) 0 val)] - (swap! state assoc :b val) + (let [val (-> e dom/get-target dom/get-value (math/clamp 0 255))] + (swap! current-color assoc :b val) (when (not (nil? val)) - (let [{:keys [r g]} @state + (let [{:keys [r g]} @current-color hex (uc/rgb->hex [r g val]) - [h s l] (uc/hex->hsl hex)] - (swap! state assoc + [h s v] (uc/hex->hsv hex)] + (reset! value-ref hex) + (swap! current-color assoc :hex hex - :h h :s s :l l)))))}] + :h h :s s :v v) + (on-change hex (:alpha @current-color))))))}] [:input.alpha-value {:id "alpha-value" :type "number" :min 0 :step 0.1 :max 1 - :value (math/precision (:alpha @state) 2) + :value (math/precision (:alpha @current-color) 2) :on-change (fn [e] - (let [val (-> e dom/get-target dom/get-value) - val (if (> val 1) 1 val) - val (if (< val 0) 0 val)] - (swap! state assoc :alpha val)))}] + (let [val (-> e dom/get-target dom/get-value (math/clamp 0 1))] + (swap! current-color assoc :alpha val) + (on-change (:hex @current-color) val)))}] [:label.hex-label {:for "hex-value"} "HEX"] [:label.red-label {:for "red-value"} "R"] [:label.green-label {:for "green-value"} "G"] @@ -203,37 +266,67 @@ [:label.alpha-label {:for "alpha-value"} "A"]] [:div.libraries - [:select - [:option {:value :recent} "Recent colors"] - [:option {:value :file} "File library"] - [:option {:value #uuid "f5d51910-ab23-11ea-ac38-e1abed64181a" } "TAIGA library"]] + [:select {:on-change (fn [e] + (let [val (-> e dom/get-target dom/get-value)] + (reset! selected-library val))) + :value @selected-library} + [:option {:value "recent"} "Recent colors"] + [:option {:value "file"} "File library"] + (for [[_ {:keys [name id]}] shared-libs] + [:option {:key id + :value id} name])] [:div.selected-colors - [:div.color-bullet.button.plus-button {:style {:background-color "white"}} - i/plus] + (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"}} i/palette] - #_(for [j (range 0 40)] - [:div.color-bullet {:style {:background-color "#E8E9EA"}}])]]]) + (for [[idx color] (map-indexed vector @current-library-colors)] + [:div.color-bullet {:key (str "color-" idx) + :on-click (fn [] + (swap! current-color assoc :hex color) + (reset! value-ref color) + (let [[r g b] (uc/hex->rgb color) + [h s v] (uc/hex->hsv color)] + (swap! current-color assoc + :r r :g g :b b + :h h :s s :v v) + (on-change color (:alpha @current-color)))) + :style {:background-color color}}])] + + ] + (when on-accept + [:div.actions + [:button.btn-primary.btn-large + {:on-click (fn [] + (println "SAVE" @value-ref) + (on-accept @value-ref) + (modal/hide!))} + "Save color"]])]) ) (mf/defc colorpicker-modal - [{:keys [x y default value opacity page on-change disable-opacity] :as props}] - [:div.modal-overlay.transparent - [:div.colorpicker-tooltip - {:style {:left (str (- x 270) "px") - :top (str (- y 50) "px")}} - #_[:& cp/colorpicker {:value (or value default) - :opacity (or opacity 1) - :colors (into-array @cp/most-used-colors) - :on-change on-change - :disable-opacity disable-opacity}] - [:& colorpicker {:value (or value default) - :opacity (or opacity 1) - :colors (into-array @cp/most-used-colors) - :on-change on-change - :disable-opacity disable-opacity}] - ] - ]) + [{:keys [x y default value opacity page on-change disable-opacity position on-accept] :as props}] + (let [position (or position :left) + style (case position + :left {:left (str (- x 270) "px") + :top (str (- y 50) "px")} + :right {:left (str (+ x 24) "px") + :top (str (- y 50) "px")})] + [:div.modal-overlay.transparent + [:div.colorpicker-tooltip + {:style (clj->js style)} + #_[:& cp/colorpicker {:value (or value default) + :opacity (or opacity 1) + :colors (into-array @cp/most-used-colors) + :on-change on-change + :disable-opacity disable-opacity}] + [:& colorpicker {:value (or value default) + :opacity (or opacity 1) + :on-change on-change + :on-accept on-accept + :disable-opacity disable-opacity}]]])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs index 0943c03bb0..0e3a6dbcf3 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs @@ -29,6 +29,7 @@ [app.main.ui.modal :as modal] [app.main.ui.shapes.icon :as icon] [app.main.ui.workspace.libraries :refer [libraries-dialog]] + [app.main.ui.workspace.colorpicker :refer [colorpicker-modal]] [app.util.data :refer [matches-search]] [app.util.dom :as dom] [app.util.dom.dnd :as dnd] @@ -39,7 +40,7 @@ [okulary.core :as l] [rumext.alpha :as mf])) -(mf/defc modal-edit-color +#_(mf/defc modal-edit-color [{:keys [color-value on-accept on-cancel] :as ctx}] (let [state (mf/use-state {:current-color color-value})] (letfn [(accept [event] @@ -167,8 +168,8 @@ (st/emit! (dwl/update-color (assoc color :name name)))) edit-color - (fn [value opacity] - (st/emit! (dwl/update-color (assoc color :value name)))) + (fn [value] + (st/emit! (dwl/update-color (assoc color :value value)))) delete-color (fn [] @@ -197,9 +198,13 @@ edit-color-clicked (fn [event] - (modal/show! modal-edit-color - {:color-value (:value color) - :on-accept edit-color})) + (modal/show! colorpicker-modal + {:x (.-clientX event) + :y (.-clientY event) + :on-accept edit-color + :value (:value color) + :disable-opacity true + :position :right})) on-context-menu (fn [event] @@ -258,10 +263,13 @@ (mf/use-callback (mf/deps file-id) (fn [event] - (modal/show! modal-edit-color - {:color-value "#406280" - - :on-accept add-color})))] + (modal/show! colorpicker-modal + {:x (.-clientX event) + :y (.-clientY event) + :on-accept add-color + :value "#406280" + :disable-opacity true + :position :right})))] [:div.asset-group [:div.group-title (t locale "workspace.assets.colors") diff --git a/frontend/src/app/util/color.cljs b/frontend/src/app/util/color.cljs index 3ce0401a62..88c2297019 100644 --- a/frontend/src/app/util/color.cljs +++ b/frontend/src/app/util/color.cljs @@ -43,10 +43,6 @@ [v] (into [] (gcolor/hexToHsv v))) -(defn hsv->hex - [[h s v]] - (gcolor/hsvToHex h s v)) - (defn hex->rgba [^string data ^number opacity] (-> (hex->rgb data) @@ -60,13 +56,26 @@ [0 0 0])))) (defn hsl->rgb - [h s l] + [[h s l]] (gcolor/hslToRgb h s l)) -(defn hsl->hex [h s l] +(defn hsl->hex + [[h s l]] (gcolor/hslToHex h s l)) (defn hex? [v] (and (string? v) (re-seq #"^#[0-9A-Fa-f]{6}$" v))) + +(defn hsl->hsv + [[h s l]] + (gcolor/hslToHsv h s l)) + +(defn hsv->hex + [[h s v]] + (gcolor/hsvToHex h s v)) + +(defn hsv->hsl + [hsv] + (hex->hsl (hsv->hex hsv))) diff --git a/frontend/src/app/util/dom.cljs b/frontend/src/app/util/dom.cljs index 1cb1673cd6..b952b51d7c 100644 --- a/frontend/src/app/util/dom.cljs +++ b/frontend/src/app/util/dom.cljs @@ -206,3 +206,9 @@ (defn set-css-property [node property value] (.setProperty (.-style node) property value)) + +(defn capture-pointer [event] + (-> event get-target (.setPointerCapture (.-pointerId event)))) + +(defn release-pointer [event] + (-> event get-target (.releasePointerCapture (.-pointerId event))))