mirror of
https://github.com/penpot/penpot.git
synced 2025-05-24 21:16:11 +02:00
✨ Enhance border radius options form
This commit is contained in:
parent
99d173789e
commit
30cd499014
7 changed files with 214 additions and 117 deletions
|
@ -5,6 +5,9 @@
|
|||
|
||||
### :boom: Breaking changes
|
||||
### :sparkles: New features
|
||||
|
||||
- Enhance corner radius behavior [Taiga #2190](https://tree.taiga.io/project/penpot/issue/2190).
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
- Fix problem with exporting before the document is saved [Taiga #2189](https://tree.taiga.io/project/penpot/issue/2189)
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
[app.common.spec :as us]
|
||||
[app.common.types.interactions :as cti]
|
||||
[app.common.types.page-options :as cto]
|
||||
[app.common.types.radius :as ctr]
|
||||
[app.common.uuid :as uuid]
|
||||
[clojure.set :as set]
|
||||
[clojure.spec.alpha :as s]))
|
||||
|
@ -191,12 +192,6 @@
|
|||
(s/def :internal.shape/page-id uuid?)
|
||||
(s/def :internal.shape/proportion ::us/safe-number)
|
||||
(s/def :internal.shape/proportion-lock boolean?)
|
||||
(s/def :internal.shape/rx ::us/safe-number)
|
||||
(s/def :internal.shape/ry ::us/safe-number)
|
||||
(s/def :internal.shape/r1 ::us/safe-number)
|
||||
(s/def :internal.shape/r2 ::us/safe-number)
|
||||
(s/def :internal.shape/r3 ::us/safe-number)
|
||||
(s/def :internal.shape/r4 ::us/safe-number)
|
||||
(s/def :internal.shape/stroke-color string?)
|
||||
(s/def :internal.shape/stroke-color-gradient (s/nilable ::gradient))
|
||||
(s/def :internal.shape/stroke-color-ref-file (s/nilable uuid?))
|
||||
|
@ -285,12 +280,12 @@
|
|||
:internal.shape/constraints-h
|
||||
:internal.shape/constraints-v
|
||||
:internal.shape/fixed-scroll
|
||||
:internal.shape/rx
|
||||
:internal.shape/ry
|
||||
:internal.shape/r1
|
||||
:internal.shape/r2
|
||||
:internal.shape/r3
|
||||
:internal.shape/r4
|
||||
::ctr/rx
|
||||
::ctr/ry
|
||||
::ctr/r1
|
||||
::ctr/r2
|
||||
::ctr/r3
|
||||
::ctr/r4
|
||||
:internal.shape/x
|
||||
:internal.shape/y
|
||||
:internal.shape/exports
|
||||
|
|
90
common/src/app/common/types/radius.cljc
Normal file
90
common/src/app/common/types/radius.cljc
Normal file
|
@ -0,0 +1,90 @@
|
|||
;; 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/.
|
||||
;;
|
||||
;; Copyright (c) UXBOX Labs SL
|
||||
|
||||
(ns app.common.types.radius
|
||||
(:require
|
||||
[app.common.spec :as us]
|
||||
[clojure.spec.alpha :as s]))
|
||||
|
||||
(s/def ::rx ::us/safe-number)
|
||||
(s/def ::ry ::us/safe-number)
|
||||
(s/def ::r1 ::us/safe-number)
|
||||
(s/def ::r2 ::us/safe-number)
|
||||
(s/def ::r3 ::us/safe-number)
|
||||
(s/def ::r4 ::us/safe-number)
|
||||
|
||||
;; Rectangle shapes may define the radius of the corners in two modes:
|
||||
;; - radius-1 all corners have the same radius (although we store two
|
||||
;; values :rx and :ry because svg uses it this way).
|
||||
;; - radius-4 each corner (top-left, top-right, bottom-right, bottom-left)
|
||||
;; has an independent value. SVG does not allow this directly, so we
|
||||
;; emulate it with paths.
|
||||
|
||||
;; A shape never will have both :rx and :r1 simultaneously
|
||||
|
||||
;; All operations take into account that the shape may not be a rectangle, and so
|
||||
;; it hasn't :rx nor :r1. In this case operations must leave shape untouched.
|
||||
|
||||
(defn radius-mode
|
||||
[shape]
|
||||
(cond (:rx shape) :radius-1
|
||||
(:r1 shape) :radius-4
|
||||
:else nil))
|
||||
|
||||
(defn radius-1?
|
||||
[shape]
|
||||
(and (:rx shape) (not= (:rx shape) 0)))
|
||||
|
||||
(defn radius-4?
|
||||
[shape]
|
||||
(and (:r1 shape)
|
||||
(or (not= (:r1 shape) 0)
|
||||
(not= (:r2 shape) 0)
|
||||
(not= (:r3 shape) 0)
|
||||
(not= (:r4 shape) 0))))
|
||||
|
||||
(defn all-equal?
|
||||
[shape]
|
||||
(= (:r1 shape) (:r2 shape) (:r3 shape) (:r4 shape)))
|
||||
|
||||
(defn switch-to-radius-1
|
||||
[shape]
|
||||
(let [r (if (all-equal? shape) (:r1 shape) 0)]
|
||||
(cond-> shape
|
||||
(:r1 shape)
|
||||
(-> (assoc :rx r :ry r)
|
||||
(dissoc :r1 :r2 :r3 :r4)))))
|
||||
|
||||
(defn switch-to-radius-4
|
||||
[shape]
|
||||
(cond-> shape
|
||||
(:rx shape)
|
||||
(-> (assoc :r1 (:rx shape)
|
||||
:r2 (:rx shape)
|
||||
:r3 (:rx shape)
|
||||
:r4 (:rx shape))
|
||||
(dissoc :rx :ry))))
|
||||
|
||||
(defn set-radius-1
|
||||
[shape value]
|
||||
(cond-> shape
|
||||
(:r1 shape)
|
||||
(-> (dissoc :r1 :r2 :r3 :r4)
|
||||
(assoc :rx 0 :ry 0))
|
||||
|
||||
(:rx shape)
|
||||
(assoc :rx value :ry value)))
|
||||
|
||||
(defn set-radius-4
|
||||
[shape attr value]
|
||||
(cond-> shape
|
||||
(:rx shape)
|
||||
(-> (dissoc :rx :rx)
|
||||
(assoc :r1 0 :r2 0 :r3 0 :r4 0))
|
||||
|
||||
(attr shape)
|
||||
(assoc attr value)))
|
||||
|
|
@ -7,6 +7,7 @@
|
|||
(ns app.main.ui.shapes.attrs
|
||||
(:require
|
||||
[app.common.pages.spec :as spec]
|
||||
[app.common.types.radius :as ctr]
|
||||
[app.main.ui.context :as muc]
|
||||
[app.util.object :as obj]
|
||||
[app.util.svg :as usvg]
|
||||
|
@ -53,7 +54,13 @@
|
|||
(min r-bottom-left r-left-bottom)]))
|
||||
|
||||
(defn add-border-radius [attrs shape]
|
||||
(if (or (:r1 shape) (:r2 shape) (:r3 shape) (:r4 shape))
|
||||
(case (ctr/radius-mode shape)
|
||||
|
||||
:radius-1
|
||||
(obj/merge! attrs #js {:rx (:rx shape)
|
||||
:ry (:ry shape)})
|
||||
|
||||
:radius-4
|
||||
(let [[r1 r2 r3 r4] (truncate-radius shape)
|
||||
top (- (:width shape) r1 r2)
|
||||
right (- (:height shape) r2 r3)
|
||||
|
@ -69,10 +76,7 @@
|
|||
"v" (- left) " "
|
||||
"a" r1 "," r1 " 0 0 1 " r1 "," (- r1) " "
|
||||
"z")}))
|
||||
(if (or (:rx shape) (:ry shape))
|
||||
(obj/merge! attrs #js {:rx (:rx shape)
|
||||
:ry (:ry shape)})
|
||||
attrs)))
|
||||
attrs))
|
||||
|
||||
(defn add-fill [attrs shape render-id]
|
||||
(let [fill-attrs (cond
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
(ns app.main.ui.viewer.handoff.attributes.layout
|
||||
(:require
|
||||
[app.common.math :as mth]
|
||||
[app.common.types.radius :as ctr]
|
||||
[app.main.ui.components.copy-button :refer [copy-button]]
|
||||
[app.util.code-gen :as cg]
|
||||
[app.util.i18n :refer [t]]
|
||||
|
@ -58,20 +59,17 @@
|
|||
[:div.attributes-value (mth/precision y 2) "px"]
|
||||
[:& copy-button {:data (copy-data selrect :y)}]])
|
||||
|
||||
(when (and (:rx shape) (not= (:rx shape) 0))
|
||||
(when (ctr/radius-1? shape)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.layout.radius")]
|
||||
[:div.attributes-value (mth/precision (:rx shape) 2) "px"]
|
||||
[:& copy-button {:data (copy-data shape :rx)}]])
|
||||
|
||||
(when (and (:r1 shape)
|
||||
(or (not= (:r1 shape) 0)
|
||||
(not= (:r2 shape) 0)
|
||||
(not= (:r3 shape) 0)
|
||||
(not= (:r4 shape) 0)))
|
||||
(when (ctr/radius-4? shape)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.layout.radius")]
|
||||
[:div.attributes-value (mth/precision (:r1 shape) 2) ", "
|
||||
[:div.attributes-value
|
||||
(mth/precision (:r1 shape) 2) ", "
|
||||
(mth/precision (:r2 shape) 2) ", "
|
||||
(mth/precision (:r3 shape) 2) ", "
|
||||
(mth/precision (:r4 shape) 2) "px"]
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.math :as math]
|
||||
[app.common.types.radius :as ctr]
|
||||
[app.main.data.workspace :as udw]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.refs :as refs]
|
||||
|
@ -61,6 +62,11 @@
|
|||
|
||||
proportion-lock (:proportion-lock values)
|
||||
|
||||
radius-mode (ctr/radius-mode values)
|
||||
all-equal? (ctr/all-equal? values)
|
||||
radius-multi? (mf/use-state nil)
|
||||
radius-input-ref (mf/use-ref nil)
|
||||
|
||||
on-size-change
|
||||
(mf/use-callback
|
||||
(mf/deps ids)
|
||||
|
@ -97,57 +103,38 @@
|
|||
(mf/use-callback
|
||||
(mf/deps ids)
|
||||
(fn [_value]
|
||||
(let [radius-update
|
||||
(fn [shape]
|
||||
(cond-> shape
|
||||
(:r1 shape)
|
||||
(-> (assoc :rx 0 :ry 0)
|
||||
(dissoc :r1 :r2 :r3 :r4))))]
|
||||
(st/emit! (dch/update-shapes ids-with-children radius-update)))))
|
||||
(if all-equal?
|
||||
(st/emit! (dch/update-shapes ids-with-children ctr/switch-to-radius-1))
|
||||
(reset! radius-multi? true))))
|
||||
|
||||
on-switch-to-radius-4
|
||||
(mf/use-callback
|
||||
(mf/deps ids)
|
||||
(fn [_value]
|
||||
(let [radius-update
|
||||
(fn [shape]
|
||||
(cond-> shape
|
||||
(:rx shape)
|
||||
(-> (assoc :r1 0 :r2 0 :r3 0 :r4 0)
|
||||
(dissoc :rx :ry))))]
|
||||
(st/emit! (dch/update-shapes ids-with-children radius-update)))))
|
||||
(st/emit! (dch/update-shapes ids-with-children ctr/switch-to-radius-4))
|
||||
(reset! radius-multi? false)))
|
||||
|
||||
on-radius-1-change
|
||||
(mf/use-callback
|
||||
(mf/deps ids)
|
||||
(fn [value]
|
||||
(let [radius-update
|
||||
(fn [shape]
|
||||
(cond-> shape
|
||||
(:r1 shape)
|
||||
(-> (dissoc :r1 :r2 :r3 :r4)
|
||||
(assoc :rx 0 :ry 0))
|
||||
(st/emit! (dch/update-shapes ids-with-children #(ctr/set-radius-1 % value)))))
|
||||
|
||||
(or (:rx shape) (:r1 shape))
|
||||
(assoc :rx value :ry value)))]
|
||||
|
||||
(st/emit! (dch/update-shapes ids-with-children radius-update)))))
|
||||
on-radius-multi-change
|
||||
(mf/use-callback
|
||||
(mf/deps ids)
|
||||
(fn [event]
|
||||
(let [value (-> event dom/get-target dom/get-value d/parse-integer)]
|
||||
(when (some? value)
|
||||
(st/emit! (dch/update-shapes ids-with-children ctr/switch-to-radius-1)
|
||||
(dch/update-shapes ids-with-children #(ctr/set-radius-1 % value)))
|
||||
(reset! radius-multi? false)))))
|
||||
|
||||
on-radius-4-change
|
||||
(mf/use-callback
|
||||
(mf/deps ids)
|
||||
(fn [value attr]
|
||||
(let [radius-update
|
||||
(fn [shape]
|
||||
(cond-> shape
|
||||
(:rx shape)
|
||||
(-> (dissoc :rx :rx)
|
||||
(assoc :r1 0 :r2 0 :r3 0 :r4 0))
|
||||
|
||||
(attr shape)
|
||||
(assoc attr value)))]
|
||||
|
||||
(st/emit! (dch/update-shapes ids-with-children radius-update)))))
|
||||
(st/emit! (dch/update-shapes ids-with-children #(ctr/set-radius-4 % attr value)))))
|
||||
|
||||
on-width-change #(on-size-change % :width)
|
||||
on-height-change #(on-size-change % :height)
|
||||
|
@ -160,6 +147,16 @@
|
|||
|
||||
select-all #(-> % (dom/get-target) (.select))]
|
||||
|
||||
(mf/use-layout-effect
|
||||
(mf/deps radius-mode @radius-multi?)
|
||||
(fn []
|
||||
(when (and (= radius-mode :radius-1)
|
||||
(= @radius-multi? false))
|
||||
;; when going back from radius-multi to normal radius-1,
|
||||
;; restore focus to the newly created numeric-input
|
||||
(let [radius-input (mf/ref-val radius-input-ref)]
|
||||
(dom/focus! radius-input)))))
|
||||
|
||||
[:*
|
||||
[:div.element-set
|
||||
[:div.element-set-content
|
||||
|
@ -233,60 +230,70 @@
|
|||
:value (attr->string :rotation values)}]])
|
||||
|
||||
;; RADIUS
|
||||
(let [radius-1? (some? (:rx values))
|
||||
radius-4? (some? (:r1 values))]
|
||||
(when (and (options :radius) (or radius-1? radius-4?))
|
||||
[:div.row-flex
|
||||
[:div.radius-options
|
||||
[:div.radius-icon.tooltip.tooltip-bottom
|
||||
{:class (dom/classnames
|
||||
:selected
|
||||
(and radius-1? (not radius-4?)))
|
||||
:alt (tr "workspace.options.radius.all-corners")
|
||||
:on-click on-switch-to-radius-1}
|
||||
i/radius-1]
|
||||
[:div.radius-icon.tooltip.tooltip-bottom
|
||||
{:class (dom/classnames
|
||||
:selected
|
||||
(and radius-4? (not radius-1?)))
|
||||
:alt (tr "workspace.options.radius.single-corners")
|
||||
:on-click on-switch-to-radius-4}
|
||||
i/radius-4]]
|
||||
(if radius-1?
|
||||
[:div.input-element.mini
|
||||
[:> numeric-input
|
||||
{:placeholder "--"
|
||||
:min 0
|
||||
:on-click select-all
|
||||
:on-change on-radius-1-change
|
||||
:value (attr->string :rx values)}]]
|
||||
(when (and (options :radius) (some? radius-mode))
|
||||
[:div.row-flex
|
||||
[:div.radius-options
|
||||
[:div.radius-icon.tooltip.tooltip-bottom
|
||||
{:class (dom/classnames
|
||||
:selected (or (= radius-mode :radius-1) @radius-multi?))
|
||||
:alt (tr "workspace.options.radius.all-corners")
|
||||
:on-click on-switch-to-radius-1}
|
||||
i/radius-1]
|
||||
[:div.radius-icon.tooltip.tooltip-bottom
|
||||
{:class (dom/classnames
|
||||
:selected (and (= radius-mode :radius-4) (not @radius-multi?)))
|
||||
:alt (tr "workspace.options.radius.single-corners")
|
||||
:on-click on-switch-to-radius-4}
|
||||
i/radius-4]]
|
||||
|
||||
[:*
|
||||
[:div.input-element.mini
|
||||
[:> numeric-input
|
||||
{:placeholder "--"
|
||||
:min 0
|
||||
:on-click select-all
|
||||
:on-change on-radius-r1-change
|
||||
:value (attr->string :r1 values)}]]
|
||||
[:div.input-element.mini
|
||||
[:> numeric-input
|
||||
{:placeholder "--"
|
||||
:min 0
|
||||
:on-click select-all
|
||||
:on-change on-radius-r2-change
|
||||
:value (attr->string :r2 values)}]]
|
||||
[:div.input-element.mini
|
||||
[:> numeric-input
|
||||
{:placeholder "--"
|
||||
:min 0
|
||||
:on-click select-all
|
||||
:on-change on-radius-r3-change
|
||||
:value (attr->string :r3 values)}]]
|
||||
[:div.input-element.mini
|
||||
[:> numeric-input
|
||||
{:placeholder "--"
|
||||
:min 0
|
||||
:on-click select-all
|
||||
:on-change on-radius-r4-change
|
||||
:value (attr->string :r4 values)}]]])]))]]]))
|
||||
(cond
|
||||
|
||||
(= radius-mode :radius-1)
|
||||
[:div.input-element.mini
|
||||
[:> numeric-input
|
||||
{:placeholder "--"
|
||||
:ref radius-input-ref
|
||||
:min 0
|
||||
:on-click select-all
|
||||
:on-change on-radius-1-change
|
||||
:value (attr->string :rx values)}]]
|
||||
|
||||
@radius-multi?
|
||||
[:div.input-element.mini
|
||||
[:input.input-text
|
||||
{:type "number"
|
||||
:placeholder "--"
|
||||
:on-click select-all
|
||||
:on-change on-radius-multi-change
|
||||
:value ""}]]
|
||||
|
||||
(= radius-mode :radius-4)
|
||||
[:*
|
||||
[:div.input-element.mini
|
||||
[:> numeric-input
|
||||
{:placeholder "--"
|
||||
:min 0
|
||||
:on-click select-all
|
||||
:on-change on-radius-r1-change
|
||||
:value (attr->string :r1 values)}]]
|
||||
[:div.input-element.mini
|
||||
[:> numeric-input
|
||||
{:placeholder "--"
|
||||
:min 0
|
||||
:on-click select-all
|
||||
:on-change on-radius-r2-change
|
||||
:value (attr->string :r2 values)}]]
|
||||
[:div.input-element.mini
|
||||
[:> numeric-input
|
||||
{:placeholder "--"
|
||||
:min 0
|
||||
:on-click select-all
|
||||
:on-change on-radius-r3-change
|
||||
:value (attr->string :r3 values)}]]
|
||||
[:div.input-element.mini
|
||||
[:> numeric-input
|
||||
{:placeholder "--"
|
||||
:min 0
|
||||
:on-click select-all
|
||||
:on-change on-radius-r4-change
|
||||
:value (attr->string :r4 values)}]]])])]]]))
|
||||
|
|
|
@ -771,7 +771,7 @@ msgstr "Izquierda"
|
|||
|
||||
#: src/app/main/ui/handoff/attributes/layout.cljs, src/app/main/ui/handoff/attributes/layout.cljs
|
||||
msgid "handoff.attributes.layout.radius"
|
||||
msgstr "Derecha"
|
||||
msgstr "Radio"
|
||||
|
||||
#: src/app/main/ui/handoff/attributes/layout.cljs
|
||||
msgid "handoff.attributes.layout.rotation"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue