diff --git a/common/app/common/data.cljc b/common/app/common/data.cljc index c661194e0..bc3d14dba 100644 --- a/common/app/common/data.cljc +++ b/common/app/common/data.cljc @@ -394,13 +394,15 @@ (defn name "Improved version of name that won't fail if the input is not a keyword" - [maybe-keyword] - (cond - (keyword? maybe-keyword) - (core/name maybe-keyword) + ([maybe-keyword] (name maybe-keyword nil)) + ([maybe-keyword default-value] + (cond + (keyword? maybe-keyword) + (core/name maybe-keyword) - (nil? maybe-keyword) nil + (nil? maybe-keyword) default-value - :else - (str maybe-keyword))) + :else + (or default-value + (str maybe-keyword))))) diff --git a/frontend/resources/styles/main/partials/sidebar-element-options.scss b/frontend/resources/styles/main/partials/sidebar-element-options.scss index 52dc12400..90e2b8f66 100644 --- a/frontend/resources/styles/main/partials/sidebar-element-options.scss +++ b/frontend/resources/styles/main/partials/sidebar-element-options.scss @@ -910,14 +910,16 @@ .element-set-actions { display: flex; visibility: hidden; - } .row-flex-removable:hover .element-set-actions, .element-set-options-group:hover .element-set-actions { - visibility: visible; + visibility: visible; } +.layer-actions { + visibility: visible; +} .typography-entry { margin: 0.5rem 0.3rem; diff --git a/frontend/src/app/main/ui/shapes/attrs.cljs b/frontend/src/app/main/ui/shapes/attrs.cljs index 9d61e85a0..7340f8cd0 100644 --- a/frontend/src/app/main/ui/shapes/attrs.cljs +++ b/frontend/src/app/main/ui/shapes/attrs.cljs @@ -120,6 +120,17 @@ (obj/merge! attrs (clj->js stroke-attrs))) attrs))) +(defn add-layer-props [attrs shape] + (let [layer-attrs + (cond-> {} + (:opacity shape) + (assoc :opacity (:opacity shape)) + + (and (:blend-mode shape) (not= (:blend-mode shape) :normal)) + (assoc :mixBlendMode (:blend-mode shape)))] + + (obj/merge! attrs (clj->js layer-attrs)))) + (defn extract-svg-attrs [render-id svg-defs svg-attrs] (let [replace-id (fn [id] @@ -147,7 +158,8 @@ styles (-> (obj/new) (obj/merge! svg-styles) (add-fill shape render-id) - (add-stroke shape render-id))] + (add-stroke shape render-id) + (add-layer-props shape))] (-> (obj/new) (obj/merge! svg-attrs) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layer.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layer.cljs new file mode 100644 index 000000000..90d9f62e8 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layer.cljs @@ -0,0 +1,141 @@ +;; 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-2021 UXBOX Labs SL + +(ns app.main.ui.workspace.sidebar.options.menus.layer + (:require + [app.common.data :as d] + [app.common.math :as mth] + [app.main.data.workspace.common :as dwc] + [app.main.store :as st] + [app.main.ui.components.numeric-input :refer [numeric-input]] + [app.main.ui.icons :as i] + [app.util.dom :as dom] + [app.util.i18n :as i18n :refer [tr]] + [rumext.alpha :as mf])) + +(def layer-attrs [:opacity :blend-mode :blocked :hidden]) + +(defn opacity->string [opacity] + (if (= opacity :multiple) + "" + (str (-> opacity + (d/coalesce 1) + (* 100) + (mth/round))))) + +(defn select-all [event] + (dom/select-text! (dom/get-target event))) + +(mf/defc layer-menu [{:keys [ids type values]}] + (let [change! + (mf/use-callback + (mf/deps ids) + (fn [prop value] + (st/emit! (dwc/update-shapes ids #(assoc % prop value))))) + + handle-change-blend-mode + (mf/use-callback + (mf/deps change!) + (fn [event] + (let [value (-> (dom/get-target-val event) (keyword))] + (change! :blend-mode value)))) + + handle-opacity-change + (mf/use-callback + (mf/deps change!) + (fn [value] + (let [value (-> value (/ 100))] + (change! :opacity value)))) + + handle-set-hidden + (mf/use-callback + (mf/deps change!) + (fn [event] + (change! :hidden true))) + + handle-set-visible + (mf/use-callback + (mf/deps change!) + (fn [event] + (change! :hidden false))) + + handle-set-blocked + (mf/use-callback + (mf/deps change!) + (fn [event] + (change! :blocked true))) + + handle-set-unblocked + (mf/use-callback + (mf/deps change!) + (fn [event] + (change! :blocked false)))] + + [:div.element-set + [:div.element-set-title + [:span + (case type + :multiple (tr "workspace.options.layer-options.title.multiple") + :group (tr "workspace.options.layer-options.title.group") + (tr "workspace.options.layer-options.title"))]] + + [:div.element-set-content + [:div.row-flex + [:select.input-select {:on-change handle-change-blend-mode + :value (d/name (:blend-mode values) "normal")} + + (when (= :multiple (:blend-mode values)) + [:option {:value "multiple"} "--"]) + + [:option {:value "normal"} (tr "workspace.options.layer-options.blend-mode.normal")] + + [:option {:value "darken"} (tr "workspace.options.layer-options.blend-mode.darken")] + [:option {:value "multiply"} (tr "workspace.options.layer-options.blend-mode.multiply")] + [:option {:value "color-burn"} (tr "workspace.options.layer-options.blend-mode.color-burn")] + + [:option {:value "lighten"} (tr "workspace.options.layer-options.blend-mode.lighten")] + [:option {:value "screen"} (tr "workspace.options.layer-options.blend-mode.screen")] + [:option {:value "color-dodge"} (tr "workspace.options.layer-options.blend-mode.color-dodge")] + + [:option {:value "overlay"} (tr "workspace.options.layer-options.blend-mode.overlay")] + [:option {:value "soft-light"} (tr "workspace.options.layer-options.blend-mode.soft-light")] + [:option {:value "hard-light"} (tr "workspace.options.layer-options.blend-mode.hard-light")] + + [:option {:value "difference"} (tr "workspace.options.layer-options.blend-mode.difference")] + [:option {:value "exclusion"} (tr "workspace.options.layer-options.blend-mode.exclusion")] + + [:option {:value "hue"} (tr "workspace.options.layer-options.blend-mode.hue")] + [:option {:value "saturation"} (tr "workspace.options.layer-options.blend-mode.saturation")] + [:option {:value "color"} (tr "workspace.options.layer-options.blend-mode.color")] + [:option {:value "luminosity"} (tr "workspace.options.layer-options.blend-mode.luminosity")]] + + [:div.input-element + {:class "percentail"} + [:> numeric-input {:value (-> values :opacity opacity->string) + :placeholder (tr "settings.multiple") + :on-click select-all + :on-change handle-opacity-change + :min 0 + :max 100}]] + + + [:div.element-set-actions.layer-actions + (cond + (or (= :multiple (:hidden values)) (not (:hidden values))) + [:div.element-set-actions-button {:on-click handle-set-hidden} i/eye] + + :else + [:div.element-set-actions-button {:on-click handle-set-visible} i/eye-closed]) + + (cond + (or (= :multiple (:blocked values)) (not (:blocked values))) + [:div.element-set-actions-button {:on-click handle-set-blocked} i/lock-open] + + :else + [:div.element-set-actions-button {:on-click handle-set-unblocked} i/lock])]]]]))