mirror of
https://github.com/penpot/penpot.git
synced 2025-06-15 11:21:39 +02:00
✨ Adds support for drop shadow
This commit is contained in:
parent
23b53faac7
commit
64c0884eb9
15 changed files with 396 additions and 35 deletions
|
@ -48,6 +48,11 @@ $br-big: 8px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.row-grid-2 {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
.flex-grow {
|
.flex-grow {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -364,7 +364,8 @@ ul.slider-dots {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 75px;
|
width: 75px;
|
||||||
|
|
||||||
&::after {
|
&::after,
|
||||||
|
.after {
|
||||||
color: $color-gray-20;
|
color: $color-gray-20;
|
||||||
font-size: $fs12;
|
font-size: $fs12;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
|
@ -374,6 +375,11 @@ ul.slider-dots {
|
||||||
width: 20px;
|
width: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.after {
|
||||||
|
width: auto;
|
||||||
|
right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
// Input amounts
|
// Input amounts
|
||||||
|
|
||||||
&.pixels {
|
&.pixels {
|
||||||
|
|
|
@ -468,8 +468,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-th {
|
.color-th {
|
||||||
background-color: $color-gray-10;
|
background-color: $color-gray-30;
|
||||||
border: 1px solid $color-gray-10;
|
border: 1px solid $color-gray-30;
|
||||||
border-radius: $br-small;
|
border-radius: $br-small;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -765,6 +765,7 @@
|
||||||
top: 0;
|
top: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
|
z-index: 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
.element-set-content .advanced-options {
|
.element-set-content .advanced-options {
|
||||||
|
@ -775,6 +776,7 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 2px;
|
top: 2px;
|
||||||
width: calc(100% + 16px);
|
width: calc(100% + 16px);
|
||||||
|
z-index: 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-options {
|
.btn-options {
|
||||||
|
@ -815,7 +817,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.exports-options {
|
.exports-options,
|
||||||
|
.shadow-options{
|
||||||
.element-set-options-group {
|
.element-set-options-group {
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
.delete-icon {
|
.delete-icon {
|
||||||
|
@ -841,3 +844,35 @@
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.shadow-options .color-row-wrap {
|
||||||
|
margin-left: 6px;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.element-set-actions-button {
|
||||||
|
display: flex;
|
||||||
|
min-width: 30px;
|
||||||
|
min-height: 30px;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
svg {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
fill: $color-gray-20;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover svg {
|
||||||
|
fill: $color-primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.element-set-actions {
|
||||||
|
display: flex;
|
||||||
|
visibility: hidden;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.element-set-options-group:hover .element-set-actions {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
"The main logic for SVG export functionality."
|
"The main logic for SVG export functionality."
|
||||||
(:require
|
(:require
|
||||||
[rumext.alpha :as mf]
|
[rumext.alpha :as mf]
|
||||||
|
[cuerdas.core :as str]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.common.pages :as cp]
|
[app.common.pages :as cp]
|
||||||
[app.common.pages-helpers :as cph]
|
[app.common.pages-helpers :as cph]
|
||||||
|
@ -18,6 +19,7 @@
|
||||||
[app.common.geom.shapes :as geom]
|
[app.common.geom.shapes :as geom]
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.geom.matrix :as gmt]
|
[app.common.geom.matrix :as gmt]
|
||||||
|
[app.main.ui.shapes.filters :as filters]
|
||||||
[app.main.ui.shapes.frame :as frame]
|
[app.main.ui.shapes.frame :as frame]
|
||||||
[app.main.ui.shapes.circle :as circle]
|
[app.main.ui.shapes.circle :as circle]
|
||||||
[app.main.ui.shapes.icon :as icon]
|
[app.main.ui.shapes.icon :as icon]
|
||||||
|
@ -77,18 +79,21 @@
|
||||||
frame-wrapper (mf/use-memo (mf/deps objects) #(frame-wrapper-factory objects))]
|
frame-wrapper (mf/use-memo (mf/deps objects) #(frame-wrapper-factory objects))]
|
||||||
(when (and shape (not (:hidden shape)))
|
(when (and shape (not (:hidden shape)))
|
||||||
(let [shape (geom/transform-shape frame shape)
|
(let [shape (geom/transform-shape frame shape)
|
||||||
opts #js {:shape shape}]
|
opts #js {:shape shape}
|
||||||
(case (:type shape)
|
filter-id (filters/get-filter-id)]
|
||||||
:curve [:> path/path-shape opts]
|
[:g {:filter (filters/filter-str filter-id shape)}
|
||||||
:text [:> text/text-shape opts]
|
[:& filters/filters {:filter-id filter-id :shape shape}]
|
||||||
:icon [:> icon/icon-shape opts]
|
(case (:type shape)
|
||||||
:rect [:> rect/rect-shape opts]
|
:curve [:> path/path-shape opts]
|
||||||
:path [:> path/path-shape opts]
|
:text [:> text/text-shape opts]
|
||||||
:image [:> image/image-shape opts]
|
:icon [:> icon/icon-shape opts]
|
||||||
:circle [:> circle/circle-shape opts]
|
:rect [:> rect/rect-shape opts]
|
||||||
:frame [:> frame-wrapper {:shape shape}]
|
:path [:> path/path-shape opts]
|
||||||
:group [:> group-wrapper {:shape shape :frame frame}]
|
:image [:> image/image-shape opts]
|
||||||
nil))))))
|
:circle [:> circle/circle-shape opts]
|
||||||
|
:frame [:> frame-wrapper {:shape shape}]
|
||||||
|
:group [:> group-wrapper {:shape shape :frame frame}]
|
||||||
|
nil)])))))
|
||||||
|
|
||||||
(mf/defc page-svg
|
(mf/defc page-svg
|
||||||
{::mf/wrap [mf/memo]}
|
{::mf/wrap [mf/memo]}
|
||||||
|
|
|
@ -21,7 +21,8 @@
|
||||||
(defn extract-style-attrs
|
(defn extract-style-attrs
|
||||||
[shape]
|
[shape]
|
||||||
(let [stroke-style (:stroke-style shape :none)
|
(let [stroke-style (:stroke-style shape :none)
|
||||||
attrs #js {:fill (or (:fill-color shape) "transparent")
|
attrs #js {;:filter (when (not= :frame (:type shape)) (str "url(#filter_" (:id shape) ")"))
|
||||||
|
:fill (or (:fill-color shape) "transparent")
|
||||||
:fillOpacity (:fill-opacity shape nil)
|
:fillOpacity (:fill-opacity shape nil)
|
||||||
:rx (:rx shape nil)
|
:rx (:rx shape nil)
|
||||||
:ry (:ry shape nil)}]
|
:ry (:ry shape nil)}]
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
(ns app.main.ui.shapes.custom-stroke
|
(ns app.main.ui.shapes.custom-stroke
|
||||||
(:require
|
(:require
|
||||||
[rumext.alpha :as mf]
|
[rumext.alpha :as mf]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
[app.common.geom.shapes :as geom]
|
[app.common.geom.shapes :as geom]
|
||||||
[app.util.object :as obj]))
|
[app.util.object :as obj]))
|
||||||
|
|
||||||
|
@ -21,7 +22,8 @@
|
||||||
(let [shape (unchecked-get props "shape")
|
(let [shape (unchecked-get props "shape")
|
||||||
base-props (unchecked-get props "base-props")
|
base-props (unchecked-get props "base-props")
|
||||||
elem-name (unchecked-get props "elem-name")
|
elem-name (unchecked-get props "elem-name")
|
||||||
{:keys [id x y width height]} (geom/shape->rect-shape shape)
|
{:keys [x y width height]} (geom/shape->rect-shape shape)
|
||||||
|
id (uuid/next)
|
||||||
stroke-style (:stroke-style shape :none)
|
stroke-style (:stroke-style shape :none)
|
||||||
stroke-position (:stroke-alignment shape :center)]
|
stroke-position (:stroke-alignment shape :center)]
|
||||||
(cond
|
(cond
|
||||||
|
|
77
frontend/src/app/main/ui/shapes/filters.cljs
Normal file
77
frontend/src/app/main/ui/shapes/filters.cljs
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
;; 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.shapes.filters
|
||||||
|
(:require
|
||||||
|
[rumext.alpha :as mf]
|
||||||
|
[cuerdas.core :as str]
|
||||||
|
[app.util.color :as color]
|
||||||
|
[app.common.math :as mth]
|
||||||
|
[app.common.uuid :as uuid]))
|
||||||
|
|
||||||
|
(defn get-filter-id []
|
||||||
|
(str "filter_" (uuid/next)))
|
||||||
|
|
||||||
|
(defn filter-str
|
||||||
|
[filter-id shape]
|
||||||
|
|
||||||
|
(when (seq (:shadow shape))
|
||||||
|
(str/fmt "url(#$0)" [filter-id])))
|
||||||
|
|
||||||
|
(mf/defc drop-shadow-filter
|
||||||
|
[{:keys [filter-id filter shape]}]
|
||||||
|
|
||||||
|
(let [{:keys [x y width height]} (:selrect shape)
|
||||||
|
{:keys [fid color opacity offset-x offset-y blur spread]} filter
|
||||||
|
|
||||||
|
filter-x (min x (+ x offset-x (- spread) (- blur) -5))
|
||||||
|
filter-y (min y (+ y offset-y (- spread) (- blur) -5))
|
||||||
|
filter-width (+ width (mth/abs offset-x) (* spread 2) (* blur 2) 10)
|
||||||
|
filter-height (+ height (mth/abs offset-x) (* spread 2) (* blur 2) 10)
|
||||||
|
|
||||||
|
[r g b a] (color/hex->rgba color opacity)
|
||||||
|
[r g b] [(/ r 255) (/ g 255) (/ b 255)]
|
||||||
|
color-matrix (str/fmt "0 0 0 0 $0 0 0 0 0 $1 0 0 0 0 $2 0 0 0 $3 0" [r g b a])]
|
||||||
|
[:filter {:id filter-id
|
||||||
|
:x filter-x :y filter-y
|
||||||
|
:width filter-width :height filter-height
|
||||||
|
:filterUnits "userSpaceOnUse"
|
||||||
|
:color-interpolation-filters "sRGB"}
|
||||||
|
[:feFlood {:flood-opacity 0 :result "BackgroundImageFix"}]
|
||||||
|
[: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"}]
|
||||||
|
(when (> spread 0)
|
||||||
|
[:feMorphology {:radius spread
|
||||||
|
:operator "dilate"
|
||||||
|
:in "SourceAlpha"
|
||||||
|
:result "effect1_dropShadow"}])
|
||||||
|
|
||||||
|
[:feOffset {:dx offset-x :dy offset-y}]
|
||||||
|
[:feGaussianBlur {:stdDeviation (/ blur 2)}]
|
||||||
|
|
||||||
|
[:feColorMatrix {:type "matrix" :values color-matrix}]
|
||||||
|
|
||||||
|
[:feBlend {:mode "normal"
|
||||||
|
:in2 "BackgroundImageFix"
|
||||||
|
:result "effect1_dropShadow"}]
|
||||||
|
|
||||||
|
[:feBlend {:mode "normal"
|
||||||
|
:in "SourceGraphic"
|
||||||
|
:in2 "effect1_dropShadow"
|
||||||
|
:result "shape"}]]))
|
||||||
|
|
||||||
|
(mf/defc filters
|
||||||
|
[{:keys [filter-id shape]}]
|
||||||
|
[:defs
|
||||||
|
(for [{:keys [id type hidden] :as filter} (:shadow shape)]
|
||||||
|
(when (not hidden)
|
||||||
|
[:& drop-shadow-filter {:key id
|
||||||
|
:filter-id filter-id
|
||||||
|
:filter filter
|
||||||
|
:shape shape}]))])
|
|
@ -11,12 +11,15 @@
|
||||||
"The main container for a frame in viewer mode"
|
"The main container for a frame in viewer mode"
|
||||||
(:require
|
(:require
|
||||||
[rumext.alpha :as mf]
|
[rumext.alpha :as mf]
|
||||||
|
[cuerdas.core :as str]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.pages :as cp]
|
[app.common.pages :as cp]
|
||||||
[app.common.pages-helpers :as cph]
|
[app.common.pages-helpers :as cph]
|
||||||
[app.main.data.viewer :as dv]
|
[app.main.data.viewer :as dv]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
|
[app.main.ui.shapes.filters :as filters]
|
||||||
[app.main.ui.shapes.circle :as circle]
|
[app.main.ui.shapes.circle :as circle]
|
||||||
[app.main.ui.shapes.frame :as frame]
|
[app.main.ui.shapes.frame :as frame]
|
||||||
[app.main.ui.shapes.group :as group]
|
[app.main.ui.shapes.group :as group]
|
||||||
|
@ -53,10 +56,14 @@
|
||||||
|
|
||||||
on-mouse-down (mf/use-callback
|
on-mouse-down (mf/use-callback
|
||||||
(mf/deps shape)
|
(mf/deps shape)
|
||||||
#(on-mouse-down % shape))]
|
#(on-mouse-down % shape))
|
||||||
|
|
||||||
|
filter-id (filters/get-filter-id)]
|
||||||
|
|
||||||
[:g.shape {:on-mouse-down on-mouse-down
|
[:g.shape {:on-mouse-down on-mouse-down
|
||||||
:cursor (when (:interactions shape) "pointer")}
|
:cursor (when (:interactions shape) "pointer")
|
||||||
|
:filter (filters/filter-str filter-id shape)}
|
||||||
|
[:& filters/filters {:filter-id filter-id :shape shape}]
|
||||||
[:& component {:shape shape
|
[:& component {:shape shape
|
||||||
:frame frame
|
:frame frame
|
||||||
:childs childs
|
:childs childs
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.keyboard :as kbd]
|
[app.main.ui.keyboard :as kbd]
|
||||||
|
[app.main.ui.shapes.filters :as filters]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.common.geom.matrix :as gmt]
|
[app.common.geom.matrix :as gmt]
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
|
@ -69,9 +70,12 @@
|
||||||
#(on-mouse-down % shape))
|
#(on-mouse-down % shape))
|
||||||
on-context-menu (mf/use-callback
|
on-context-menu (mf/use-callback
|
||||||
(mf/deps shape)
|
(mf/deps shape)
|
||||||
#(on-context-menu % shape))]
|
#(on-context-menu % shape))
|
||||||
|
filter-id (filters/get-filter-id)]
|
||||||
[:g.shape {:on-mouse-down on-mouse-down
|
[:g.shape {:on-mouse-down on-mouse-down
|
||||||
:on-context-menu on-context-menu}
|
:on-context-menu on-context-menu
|
||||||
|
:filter (filters/filter-str filter-id shape)}
|
||||||
|
[:& filters/filters {:filter-id filter-id :shape shape}]
|
||||||
[:& component {:shape shape}]])))
|
[:& component {:shape shape}]])))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
;; 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.sidebar.options.common
|
||||||
|
(:require
|
||||||
|
[rumext.alpha :as mf]
|
||||||
|
[app.util.dom :as dom]))
|
||||||
|
|
||||||
|
(mf/defc advanced-options [{:keys [visible? on-close children]}]
|
||||||
|
(let [handle-click (fn [event] (when on-close
|
||||||
|
(do (dom/stop-propagation event)
|
||||||
|
(on-close))))]
|
||||||
|
(when visible?
|
||||||
|
[:*
|
||||||
|
[:div.focus-overlay {:on-click handle-click}]
|
||||||
|
[:div.advanced-options {}
|
||||||
|
children]])))
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
[app.main.data.workspace.grid :as dw]
|
[app.main.data.workspace.grid :as dw]
|
||||||
[app.util.geom.grid :as gg]
|
[app.util.geom.grid :as gg]
|
||||||
[app.main.ui.icons :as i]
|
[app.main.ui.icons :as i]
|
||||||
|
[app.main.ui.workspace.sidebar.options.common :refer [advanced-options]]
|
||||||
[app.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row]]
|
[app.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row]]
|
||||||
[app.main.ui.workspace.sidebar.options.rows.input-row :refer [input-row]]
|
[app.main.ui.workspace.sidebar.options.rows.input-row :refer [input-row]]
|
||||||
[app.main.ui.components.select :refer [select]]
|
[app.main.ui.components.select :refer [select]]
|
||||||
|
@ -30,16 +31,6 @@
|
||||||
(def workspace-saved-grids
|
(def workspace-saved-grids
|
||||||
(l/derived :saved-grids refs/workspace-page-options))
|
(l/derived :saved-grids refs/workspace-page-options))
|
||||||
|
|
||||||
(mf/defc advanced-options [{:keys [visible? on-close children]}]
|
|
||||||
(when visible?
|
|
||||||
[:*
|
|
||||||
[:div.focus-overlay {:on-click #(when on-close
|
|
||||||
(do
|
|
||||||
(dom/stop-propagation %)
|
|
||||||
(on-close)))}]
|
|
||||||
[:div.advanced-options {}
|
|
||||||
children]]))
|
|
||||||
|
|
||||||
(defn- get-size-options [locale]
|
(defn- get-size-options [locale]
|
||||||
[{:value :auto :label (t locale "workspace.options.grid.auto")}
|
[{:value :auto :label (t locale "workspace.options.grid.auto")}
|
||||||
:separator
|
:separator
|
||||||
|
@ -254,3 +245,4 @@
|
||||||
:on-remove (handle-remove-grid index)
|
:on-remove (handle-remove-grid index)
|
||||||
:on-save-grid handle-save-grid}])])]))
|
:on-save-grid handle-save-grid}])])]))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,8 @@
|
||||||
[rumext.alpha :as mf]
|
[rumext.alpha :as mf]
|
||||||
[app.main.ui.workspace.sidebar.options.measures :refer [measure-attrs measures-menu]]
|
[app.main.ui.workspace.sidebar.options.measures :refer [measure-attrs measures-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.fill :refer [fill-attrs fill-menu]]
|
[app.main.ui.workspace.sidebar.options.fill :refer [fill-attrs fill-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.stroke :refer [stroke-attrs stroke-menu]]))
|
[app.main.ui.workspace.sidebar.options.stroke :refer [stroke-attrs stroke-menu]]
|
||||||
|
[app.main.ui.workspace.sidebar.options.shadow :refer [shadow-menu]]))
|
||||||
|
|
||||||
(mf/defc options
|
(mf/defc options
|
||||||
{::mf/wrap [mf/memo]}
|
{::mf/wrap [mf/memo]}
|
||||||
|
@ -31,5 +32,8 @@
|
||||||
:values fill-values}]
|
:values fill-values}]
|
||||||
[:& stroke-menu {:ids ids
|
[:& stroke-menu {:ids ids
|
||||||
:type type
|
:type type
|
||||||
:values stroke-values}]]))
|
:values stroke-values}]
|
||||||
|
[:& shadow-menu {:ids ids
|
||||||
|
:type type
|
||||||
|
:values (select-keys shape [:shadow])}]]))
|
||||||
|
|
||||||
|
|
195
frontend/src/app/main/ui/workspace/sidebar/options/shadow.cljs
Normal file
195
frontend/src/app/main/ui/workspace/sidebar/options/shadow.cljs
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
;; 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.sidebar.options.shadow
|
||||||
|
(:require
|
||||||
|
[rumext.alpha :as mf]
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
|
[app.main.data.workspace.common :as dwc]
|
||||||
|
[app.main.store :as st]
|
||||||
|
[app.main.ui.icons :as i]
|
||||||
|
[app.main.ui.workspace.sidebar.options.common :refer [advanced-options]]
|
||||||
|
[app.main.ui.workspace.sidebar.options.rows.input-row :refer [input-row]]
|
||||||
|
[app.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row]]
|
||||||
|
[app.util.dom :as dom]))
|
||||||
|
|
||||||
|
(defn create-shadow []
|
||||||
|
(let [id (uuid/next)]
|
||||||
|
{:id id
|
||||||
|
:style :drop-shadow
|
||||||
|
:color "#000000"
|
||||||
|
:opacity 0.2
|
||||||
|
:offset-x 4
|
||||||
|
:offset-y 4
|
||||||
|
:blur 4
|
||||||
|
:spread 0
|
||||||
|
:hidden false}))
|
||||||
|
|
||||||
|
(defn valid-number? [value]
|
||||||
|
(or (number? value) (not (js/isNaN (js/parseInt value)))))
|
||||||
|
|
||||||
|
(mf/defc shadow-entry
|
||||||
|
[{:keys [ids index value]}]
|
||||||
|
(let [open-shadow (mf/use-state false)
|
||||||
|
|
||||||
|
basic-offset-x-ref (mf/use-ref nil)
|
||||||
|
basic-offset-y-ref (mf/use-ref nil)
|
||||||
|
basic-blur-ref (mf/use-ref nil)
|
||||||
|
|
||||||
|
adv-offset-x-ref (mf/use-ref nil)
|
||||||
|
adv-offset-y-ref (mf/use-ref nil)
|
||||||
|
adv-blur-ref (mf/use-ref nil)
|
||||||
|
adv-spread-ref (mf/use-ref nil)
|
||||||
|
|
||||||
|
remove-shadow-by-id
|
||||||
|
(fn [values id] (->> values (filterv (fn [s] (not= (:id s) id)))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
on-remove-shadow
|
||||||
|
(fn [id]
|
||||||
|
(fn []
|
||||||
|
(st/emit! (dwc/update-shapes ids #(update % :shadow remove-shadow-by-id id) ))))
|
||||||
|
|
||||||
|
select-text
|
||||||
|
(fn [ref] (fn [event] (dom/select-text! (mf/ref-val ref))))
|
||||||
|
|
||||||
|
update-attr
|
||||||
|
(fn update-attr
|
||||||
|
([index attr valid?]
|
||||||
|
(update-attr index attr valid? nil))
|
||||||
|
|
||||||
|
([index attr valid? update-ref]
|
||||||
|
(fn [event]
|
||||||
|
(let [value (dom/get-value (dom/get-target event))]
|
||||||
|
(when (or (not valid?) (valid? value))
|
||||||
|
(do
|
||||||
|
(when update-ref
|
||||||
|
(dom/set-value! (mf/ref-val update-ref) value))
|
||||||
|
(st/emit! (dwc/update-shapes ids #(assoc-in % [:shadow index attr] (js/parseInt value 10))))))))))
|
||||||
|
|
||||||
|
update-color
|
||||||
|
(fn [index]
|
||||||
|
(fn [color opacity]
|
||||||
|
(st/emit! (dwc/update-shapes
|
||||||
|
ids
|
||||||
|
#(-> %
|
||||||
|
(assoc-in [:shadow index :color] color)
|
||||||
|
(assoc-in [:shadow index :opacity] opacity))))))
|
||||||
|
|
||||||
|
toggle-visibility
|
||||||
|
(fn [index]
|
||||||
|
(fn []
|
||||||
|
(st/emit! (dwc/update-shapes ids #(update-in % [:shadow index :hidden] not)))))]
|
||||||
|
[:*
|
||||||
|
[:div.element-set-options-group
|
||||||
|
|
||||||
|
[:div.element-set-actions-button
|
||||||
|
{:on-click #(reset! open-shadow true)}
|
||||||
|
i/actions]
|
||||||
|
|
||||||
|
[:input.input-text {:type "number"
|
||||||
|
:ref basic-offset-x-ref
|
||||||
|
:on-change (update-attr index :offset-x valid-number?)
|
||||||
|
:on-click (select-text basic-offset-x-ref)
|
||||||
|
:default-value (:offset-x value)}]
|
||||||
|
[:input.input-text {:type "number"
|
||||||
|
:ref basic-offset-y-ref
|
||||||
|
:on-change (update-attr index :offset-y valid-number?)
|
||||||
|
:on-click (select-text basic-offset-y-ref)
|
||||||
|
:default-value (:offset-y value)}]
|
||||||
|
[:input.input-text {:type "number"
|
||||||
|
:ref basic-blur-ref
|
||||||
|
:on-click (select-text basic-blur-ref)
|
||||||
|
:on-change (update-attr index :blur valid-number?)
|
||||||
|
:min 0
|
||||||
|
:default-value (:blur value)}]
|
||||||
|
|
||||||
|
[:div.element-set-actions
|
||||||
|
[:div.element-set-actions-button {:on-click (toggle-visibility index)}
|
||||||
|
(if (:hidden value) i/eye-closed i/eye)]
|
||||||
|
[:div.element-set-actions-button {:on-click (on-remove-shadow (:id value))}
|
||||||
|
i/minus]]]
|
||||||
|
|
||||||
|
[:& advanced-options {:visible? @open-shadow
|
||||||
|
:on-close #(reset! open-shadow false)}
|
||||||
|
[:div.row-grid-2
|
||||||
|
[:select.input-select
|
||||||
|
[:option {:value ":drop-shadow"} "Drop shadow"]
|
||||||
|
#_[:option {:value ":inner-shadow"} "Inner shadow"]]]
|
||||||
|
|
||||||
|
[:div.row-grid-2
|
||||||
|
[:div.input-element
|
||||||
|
[:input.input-text {:type "number"
|
||||||
|
:ref adv-offset-x-ref
|
||||||
|
:no-validate true
|
||||||
|
:placeholder "--"
|
||||||
|
:on-click (select-text adv-offset-x-ref)
|
||||||
|
:on-change (update-attr index :offset-x valid-number? basic-offset-x-ref)
|
||||||
|
:default-value (:offset-x value)}]
|
||||||
|
[:span.after "X"]]
|
||||||
|
|
||||||
|
[:div.input-element
|
||||||
|
[:input.input-text {:type "number"
|
||||||
|
:ref adv-offset-y-ref
|
||||||
|
:no-validate true
|
||||||
|
:placeholder "--"
|
||||||
|
:on-click (select-text adv-offset-y-ref)
|
||||||
|
:on-change (update-attr index :offset-y valid-number? basic-offset-y-ref)
|
||||||
|
:default-value (:offset-y value)}]
|
||||||
|
[:span.after "Y"]]]
|
||||||
|
|
||||||
|
[:div.row-grid-2
|
||||||
|
[:div.input-element
|
||||||
|
[:input.input-text {:type "number"
|
||||||
|
:ref adv-blur-ref
|
||||||
|
:no-validate true
|
||||||
|
:placeholder "--"
|
||||||
|
:on-click (select-text adv-blur-ref)
|
||||||
|
:on-change (update-attr index :blur valid-number? basic-blur-ref)
|
||||||
|
:min 0
|
||||||
|
:default-value (:blur value)}]
|
||||||
|
[:span.after "Blur"]]
|
||||||
|
|
||||||
|
[:div.input-element
|
||||||
|
[:input.input-text {:type "number"
|
||||||
|
:ref adv-spread-ref
|
||||||
|
:no-validate true
|
||||||
|
:placeholder "--"
|
||||||
|
:on-click (select-text adv-spread-ref)
|
||||||
|
:on-change (update-attr index :spread valid-number?)
|
||||||
|
:min 0
|
||||||
|
:default-value (:spread value)}]
|
||||||
|
[:span.after "Spread"]]]
|
||||||
|
|
||||||
|
[:div.color-row-wrap
|
||||||
|
[:& color-row {:color {:value (:color value) :opacity (:opacity value)}
|
||||||
|
:on-change (update-color index)
|
||||||
|
:on-open #(st/emit! dwc/start-undo-transaction)
|
||||||
|
:on-close #(st/emit! dwc/commit-undo-transaction)}]]]]))
|
||||||
|
(mf/defc shadow-menu
|
||||||
|
[{:keys [ids type values] :as props}]
|
||||||
|
|
||||||
|
(.log js/console "values" (clj->js values))
|
||||||
|
(let [on-add-shadow
|
||||||
|
(fn []
|
||||||
|
(st/emit! (dwc/update-shapes ids #(update % :shadow (fnil conj []) (create-shadow)) )))]
|
||||||
|
[:div.element-set.shadow-options
|
||||||
|
[:div.element-set-title
|
||||||
|
[:span "Shadow"]
|
||||||
|
[:div.add-page {:on-click on-add-shadow} i/close]]
|
||||||
|
|
||||||
|
(when (seq (:shadow values))
|
||||||
|
[:div.element-set-content
|
||||||
|
(for [[index {:keys [id] :as value}] (d/enumerate (:shadow values []))]
|
||||||
|
[:& shadow-entry {:key (str "shadow-" id)
|
||||||
|
:ids ids
|
||||||
|
:value value
|
||||||
|
:index index}])])]))
|
|
@ -70,7 +70,7 @@
|
||||||
:group (t locale "workspace.options.group-stroke")
|
:group (t locale "workspace.options.group-stroke")
|
||||||
(t locale "workspace.options.stroke"))
|
(t locale "workspace.options.stroke"))
|
||||||
|
|
||||||
show-options (not= (or (:stroke-style values) :none) :none)
|
show-options (not= (:stroke-style values :none) :none)
|
||||||
|
|
||||||
current-stroke-color {:value (:stroke-color values)
|
current-stroke-color {:value (:stroke-color values)
|
||||||
:opacity (:stroke-opacity values)
|
:opacity (:stroke-opacity values)
|
||||||
|
|
|
@ -103,6 +103,10 @@
|
||||||
[node]
|
[node]
|
||||||
(set! (.-value node) ""))
|
(set! (.-value node) ""))
|
||||||
|
|
||||||
|
(defn set-value!
|
||||||
|
[node value]
|
||||||
|
(set! (.-value node) value))
|
||||||
|
|
||||||
(defn select-text!
|
(defn select-text!
|
||||||
[node]
|
[node]
|
||||||
(.select node))
|
(.select node))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue