0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-09 16:48:16 -05:00

Add border radius to artboards

This commit is contained in:
Eva 2022-02-28 12:50:37 +01:00 committed by Alonso Torres
parent 78d7fe3e10
commit d4af28c52b
7 changed files with 242 additions and 154 deletions

View file

@ -5,6 +5,7 @@
### :boom: Breaking changes
### :sparkles: New features
- Add border radius to our artboars [Taiga #2056](https://tree.taiga.io/project/penpot/us/2056)
- Persist color palette and color picker across refresh [Taiga #1660](https://tree.taiga.io/project/penpot/issue/1660)
- Ability to add multiple strokes to a shape [Taiga #2778](https://tree.taiga.io/project/penpot/us/2778)
- Scroll to selected size in font size selector [Taiga #2825](https://tree.taiga.io/project/penpot/us/2825)

View file

@ -72,6 +72,7 @@
{:frame #{:proportion-lock
:width :height
:x :y
:rx :ry :r1 :r2 :r3 :r4
:selrect
:opacity

View file

@ -73,7 +73,14 @@
:name "Artboard-1"
:fills [{:fill-color clr/white
:fill-opacity 1}]
:strokes []}
:strokes []
:stroke-style :none
:stroke-alignment :center
:stroke-width 0
:stroke-color clr/black
:stroke-opacity 0
:rx 0
:ry 0}
{:type :text
:name "Text-1"

View file

@ -27,9 +27,18 @@
[{:keys [shape render-id]}]
(when (= :frame (:type shape))
(let [{:keys [x y width height]} shape
padding (filters/calculate-padding shape)]
padding (filters/calculate-padding shape)
props (-> (attrs/extract-style-attrs shape)
(obj/merge!
#js {:x (- x padding)
:y (- y padding)
:width (+ width (* 2 padding))
:height (+ height (* 2 padding))}))
path? (some? (.-d props))]
[:clipPath {:id (frame-clip-id shape render-id) :class "frame-clip"}
[:rect {:x (- x padding) :y (- y padding) :width (+ width (* 2 padding)) :height (+ height (* 2 padding))}]])))
(if path?
[:> :path props]
[:> :rect props])])))
(mf/defc frame-thumbnail
{::mf/wrap-props false}
@ -49,25 +58,28 @@
(defn frame-shape
[shape-wrapper]
(mf/fnc frame-shape
{::mf/wrap-props false}
[props]
(let [childs (unchecked-get props "childs")
shape (unchecked-get props "shape")
{:keys [x y width height]} shape
{::mf/wrap-props false}
[props]
(let [childs (unchecked-get props "childs")
shape (unchecked-get props "shape")
{:keys [x y width height]} shape
props (-> (attrs/extract-style-attrs shape)
(obj/merge!
#js {:x x
:y y
:width width
:height height
:className "frame-background"}))]
props (-> (attrs/extract-style-attrs shape)
(obj/merge!
#js {:x x
:y y
:width width
:height height
:className "frame-background"}))
path? (some? (.-d props))]
[:*
[:& shape-custom-strokes {:shape shape}
(if path?
[:> :path props]
[:> :rect props])
[:*
[:& shape-custom-strokes {:shape shape}
[:> :rect props]]
(for [item childs]
[:& shape-wrapper {:shape item
:key (dm/str (:id item))}])])))
(for [item childs]
[:& shape-wrapper {:shape item
:key (dm/str (:id item))}])]])))

View file

@ -9,13 +9,12 @@
[app.common.data :as d]
[app.common.geom.shapes :as gsh]
[app.common.math :as math]
[app.common.spec.radius :as ctr]
[app.main.data.workspace :as udw]
[app.main.data.workspace.changes :as dch]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.components.numeric-input :refer [numeric-input]]
[app.main.ui.icons :as i]
[app.main.ui.workspace.sidebar.options.menus.radius :refer [border-radius]]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]]
[rumext.alpha :as mf]))
@ -42,8 +41,6 @@
[{:keys [options ids ids-with-children values] :as props}]
(let [options (or options #{:size :position :rotation :radius})
ids-with-children (or ids-with-children ids)
old-shapes (deref (refs/objects-by-id ids))
frames (map #(deref (refs/object-by-id (:frame-id %))) old-shapes)
@ -63,11 +60,6 @@
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)
@ -100,64 +92,13 @@
(fn [value]
(st/emit! (udw/increase-rotation ids value))))
on-switch-to-radius-1
(mf/use-callback
(mf/deps ids)
(fn [_value]
(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]
(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]
(st/emit! (dch/update-shapes ids-with-children #(ctr/set-radius-1 % value)))))
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]
(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)
on-pos-x-change #(on-position-change % :x)
on-pos-y-change #(on-position-change % :y)
on-radius-r1-change #(on-radius-4-change % :r1)
on-radius-r2-change #(on-radius-4-change % :r2)
on-radius-r3-change #(on-radius-4-change % :r3)
on-radius-r4-change #(on-radius-4-change % :r4)
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,72 +174,4 @@
:value (attr->string :rotation values)}]])
;; RADIUS
(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]]
(cond
(= radius-mode :radius-1)
[:div.input-element.mini {:title (tr "workspace.options.radius")}
[:> 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 {:title (tr "workspace.options.radius")}
[: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 {:title (tr "workspace.options.radius")}
[:> numeric-input
{:placeholder "--"
:min 0
:on-click select-all
:on-change on-radius-r1-change
:value (attr->string :r1 values)}]]
[:div.input-element.mini {:title (tr "workspace.options.radius")}
[:> numeric-input
{:placeholder "--"
:min 0
:on-click select-all
:on-change on-radius-r2-change
:value (attr->string :r2 values)}]]
[:div.input-element.mini {:title (tr "workspace.options.radius")}
[:> numeric-input
{:placeholder "--"
:min 0
:on-click select-all
:on-change on-radius-r3-change
:value (attr->string :r3 values)}]]
[:div.input-element.mini {:title (tr "workspace.options.radius")}
[:> numeric-input
{:placeholder "--"
:min 0
:on-click select-all
:on-change on-radius-r4-change
:value (attr->string :r4 values)}]]])])]]]))
[:& border-radius {:options options :ids-with-children ids-with-children :values values :ids ids}]]]]))

View file

@ -0,0 +1,190 @@
;; 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
;; 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.main.ui.workspace.sidebar.options.menus.radius
(:require
[app.common.data :as d]
[app.common.geom.shapes :as gsh]
[app.common.math :as math]
[app.common.spec.radius :as ctr]
[app.main.data.workspace.changes :as dch]
[app.main.refs :as refs]
[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]))
(defn- attr->string [attr values]
(let [value (attr values)]
(if (= value :multiple)
""
(str (-> value
(d/coalesce 0)
(math/precision 2))))))
(mf/defc border-radius
[{:keys [options ids ids-with-children values] :as props}]
(let [options (or options #{:size :position :rotation :radius})
ids-with-children (or ids-with-children ids)
old-shapes (deref (refs/objects-by-id ids))
frames (map #(deref (refs/object-by-id (:frame-id %))) old-shapes)
shapes (as-> old-shapes $
(map gsh/transform-shape $)
(map gsh/translate-to-frame $ frames))
values (let [{:keys [x y]} (-> shapes first :points gsh/points->selrect)]
(cond-> values
(not= (:x values) :multiple) (assoc :x x)
(not= (:y values) :multiple) (assoc :y y)))
values (let [{:keys [width height]} (-> shapes first :selrect)]
(cond-> values
(not= (:width values) :multiple) (assoc :width width)
(not= (:height values) :multiple) (assoc :height height)))
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-switch-to-radius-1
(mf/use-callback
(mf/deps ids)
(fn [_value]
(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]
(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]
(st/emit! (dch/update-shapes ids-with-children #(ctr/set-radius-1 % value)))))
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]
(st/emit! (dch/update-shapes ids-with-children #(ctr/set-radius-4 % attr value)))))
on-radius-r1-change #(on-radius-4-change % :r1)
on-radius-r2-change #(on-radius-4-change % :r2)
on-radius-r3-change #(on-radius-4-change % :r3)
on-radius-r4-change #(on-radius-4-change % :r4)
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)))))
[:*
;; RADIUS
(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]]
(cond
(= radius-mode :radius-1)
[:div.input-element.mini {:title (tr "workspace.options.radius")}
[:> 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 {:title (tr "workspace.options.radius")}
[: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 {:title (tr "workspace.options.radius")}
[:> numeric-input
{:placeholder "--"
:min 0
:on-click select-all
:on-change on-radius-r1-change
:value (attr->string :r1 values)}]]
[:div.input-element.mini {:title (tr "workspace.options.radius")}
[:> numeric-input
{:placeholder "--"
:min 0
:on-click select-all
:on-change on-radius-r2-change
:value (attr->string :r2 values)}]]
[:div.input-element.mini {:title (tr "workspace.options.radius")}
[:> numeric-input
{:placeholder "--"
:min 0
:on-click select-all
:on-change on-radius-r3-change
:value (attr->string :r3 values)}]]
[:div.input-element.mini {:title (tr "workspace.options.radius")}
[:> numeric-input
{:placeholder "--"
:min 0
:on-click select-all
:on-change on-radius-r4-change
:value (attr->string :r4 values)}]]])])]))

View file

@ -17,6 +17,7 @@
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs-shape fill-menu]]
[app.main.ui.workspace.sidebar.options.menus.frame-grid :refer [frame-grid]]
[app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]]
[app.main.ui.workspace.sidebar.options.menus.radius :refer [border-radius]]
[app.main.ui.workspace.sidebar.options.menus.shadow :refer [shadow-menu]]
[app.main.ui.workspace.sidebar.options.menus.stroke :refer [stroke-attrs stroke-menu]]
[app.util.dom :as dom]
@ -25,10 +26,12 @@
(declare +size-presets+)
(mf/defc measures-menu
[{:keys [shape] :as props}]
(let [show-presets-dropdown? (mf/use-state false)
id (:id shape)
on-preset-selected
(fn [width height]
@ -63,9 +66,7 @@
select-all #(-> % (dom/get-target) (.select))]
[:div.element-set
[:div.element-set-content
[:div.row-flex
[:div.presets.custom-select.flex-grow {:on-click #(reset! show-presets-dropdown? true)}
[:span (tr "workspace.options.size-presets")]
@ -125,7 +126,10 @@
:on-change on-pos-y-change
:value (-> (:y shape)
(math/precision 2)
(d/coalesce-str "0"))}]]]]]))
(d/coalesce-str "0"))}]]]
;; RADIUS
[:& border-radius {:ids [id] :values shape}]]]))
(def +size-presets+
[{:name "APPLE"}