mirror of
https://github.com/penpot/penpot.git
synced 2025-02-13 18:48:37 -05:00
🎉 Add resize constraints to shapes
This commit is contained in:
parent
55b0f6e950
commit
092a973f9a
9 changed files with 602 additions and 43 deletions
|
@ -188,6 +188,7 @@
|
||||||
(d/export gpr/center->rect)
|
(d/export gpr/center->rect)
|
||||||
|
|
||||||
(d/export gtr/transform-shape)
|
(d/export gtr/transform-shape)
|
||||||
|
(d/export gtr/calc-child-modifiers)
|
||||||
(d/export gtr/transform-matrix)
|
(d/export gtr/transform-matrix)
|
||||||
(d/export gtr/inverse-transform-matrix)
|
(d/export gtr/inverse-transform-matrix)
|
||||||
(d/export gtr/transform-point-center)
|
(d/export gtr/transform-point-center)
|
||||||
|
|
|
@ -9,6 +9,11 @@
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.geom.shapes.common :as gco]))
|
[app.common.geom.shapes.common :as gco]))
|
||||||
|
|
||||||
|
(defn left-of [rect] (:x rect))
|
||||||
|
(defn right-of [rect] (+ (:x rect) (:width rect)))
|
||||||
|
(defn top-of [rect] (:y rect))
|
||||||
|
(defn bottom-of [rect] (+ (:y rect) (:height rect)))
|
||||||
|
|
||||||
(defn rect->points [{:keys [x y width height]}]
|
(defn rect->points [{:keys [x y width height]}]
|
||||||
;; (assert (number? x))
|
;; (assert (number? x))
|
||||||
;; (assert (number? y))
|
;; (assert (number? y))
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
[app.common.geom.shapes.path :as gpa]
|
[app.common.geom.shapes.path :as gpa]
|
||||||
[app.common.geom.shapes.rect :as gpr]
|
[app.common.geom.shapes.rect :as gpr]
|
||||||
[app.common.math :as mth]
|
[app.common.math :as mth]
|
||||||
|
[app.common.pages.spec :as spec]
|
||||||
[app.common.text :as txt]))
|
[app.common.text :as txt]))
|
||||||
|
|
||||||
;; --- Relative Movement
|
;; --- Relative Movement
|
||||||
|
@ -362,6 +363,138 @@
|
||||||
(dissoc :modifiers)))
|
(dissoc :modifiers)))
|
||||||
shape))))
|
shape))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn calc-child-modifiers
|
||||||
|
[parent transformed-parent child parent-modifiers]
|
||||||
|
(let [parent-rect (:selrect parent)
|
||||||
|
transformed-parent-rect (:selrect transformed-parent)
|
||||||
|
child-rect (:selrect child)
|
||||||
|
|
||||||
|
origin (:resize-origin parent-modifiers)
|
||||||
|
|
||||||
|
orig-h (when origin
|
||||||
|
(cond
|
||||||
|
(mth/close? (:x origin) (gpr/left-of parent-rect)) :left
|
||||||
|
(mth/close? (:x origin) (gpr/right-of parent-rect)) :right
|
||||||
|
:else :middle))
|
||||||
|
|
||||||
|
orig-v (when origin
|
||||||
|
(cond
|
||||||
|
(mth/close? (:y origin) (gpr/top-of parent-rect)) :top
|
||||||
|
(mth/close? (:y origin) (gpr/bottom-of parent-rect)) :bottom
|
||||||
|
:else :middle))
|
||||||
|
|
||||||
|
delta-h (when orig-h
|
||||||
|
(cond (= orig-h :left)
|
||||||
|
(- (gpr/right-of transformed-parent-rect) (gpr/right-of parent-rect))
|
||||||
|
|
||||||
|
(= orig-h :right)
|
||||||
|
(- (gpr/left-of transformed-parent-rect) (gpr/left-of parent-rect))
|
||||||
|
|
||||||
|
:else 0))
|
||||||
|
|
||||||
|
delta-v (when orig-v
|
||||||
|
(cond (= orig-v :top)
|
||||||
|
(- (gpr/bottom-of transformed-parent-rect) (gpr/bottom-of parent-rect))
|
||||||
|
|
||||||
|
(= orig-v :bottom)
|
||||||
|
(- (gpr/top-of transformed-parent-rect) (gpr/top-of parent-rect))
|
||||||
|
|
||||||
|
:else 0))
|
||||||
|
|
||||||
|
constraints-h (get child :constraints-h (spec/default-constraints-h child))
|
||||||
|
constraints-v (get child :constraints-v (spec/default-constraints-v child))
|
||||||
|
|
||||||
|
modifiers-h (case constraints-h
|
||||||
|
:left
|
||||||
|
(if (= orig-h :right)
|
||||||
|
{:displacement (gpt/point delta-h 0)} ;; we convert to matrix below
|
||||||
|
{})
|
||||||
|
|
||||||
|
:right
|
||||||
|
(if (= orig-h :left)
|
||||||
|
{:displacement (gpt/point delta-h 0)}
|
||||||
|
{})
|
||||||
|
|
||||||
|
:leftright
|
||||||
|
(cond (= orig-h :left)
|
||||||
|
{:resize-origin (gpt/point (gpr/left-of child-rect) (gpr/top-of child-rect))
|
||||||
|
:resize-vector (gpt/point (/ (+ (:width child-rect) delta-h)
|
||||||
|
(:width child-rect))
|
||||||
|
1)}
|
||||||
|
|
||||||
|
(= orig-h :right)
|
||||||
|
{:resize-origin (gpt/point (gpr/right-of child-rect) (gpr/top-of child-rect))
|
||||||
|
:resize-vector (gpt/point (/ (- (:width child-rect) delta-h)
|
||||||
|
(:width child-rect))
|
||||||
|
1)}
|
||||||
|
|
||||||
|
:else {})
|
||||||
|
|
||||||
|
:center
|
||||||
|
{:displacement (gpt/point (/ delta-h 2) 0)}
|
||||||
|
|
||||||
|
:scale
|
||||||
|
(if (:resize-origin parent-modifiers)
|
||||||
|
{:resize-origin (:resize-origin parent-modifiers)
|
||||||
|
:resize-vector (gpt/point (:x (:resize-vector parent-modifiers)) 1)}
|
||||||
|
{})
|
||||||
|
|
||||||
|
{})
|
||||||
|
|
||||||
|
modifiers-v (case constraints-v
|
||||||
|
:top
|
||||||
|
(if (= orig-v :bottom)
|
||||||
|
{:displacement (gpt/point 0 delta-v)}
|
||||||
|
{})
|
||||||
|
|
||||||
|
:bottom
|
||||||
|
(if (= orig-v :top)
|
||||||
|
{:displacement (gpt/point 0 delta-v)}
|
||||||
|
{})
|
||||||
|
|
||||||
|
:topbottom
|
||||||
|
(cond (= orig-v :top)
|
||||||
|
{:resize-origin (gpt/point (gpr/left-of child-rect) (gpr/top-of child-rect))
|
||||||
|
:resize-vector (gpt/point 1
|
||||||
|
(/ (+ (:height child-rect) delta-v)
|
||||||
|
(:height child-rect)))}
|
||||||
|
|
||||||
|
(= orig-v :bottom)
|
||||||
|
{:resize-origin (gpt/point (gpr/left-of child-rect) (gpr/bottom-of child-rect))
|
||||||
|
:resize-vector (gpt/point 1
|
||||||
|
(/ (- (:height child-rect) delta-v)
|
||||||
|
(:height child-rect)))}
|
||||||
|
|
||||||
|
:else {})
|
||||||
|
|
||||||
|
:center
|
||||||
|
{:displacement (gpt/point 0 (/ delta-v 2))}
|
||||||
|
|
||||||
|
:scale
|
||||||
|
(if (:resize-origin parent-modifiers)
|
||||||
|
{:resize-origin (:resize-origin parent-modifiers)
|
||||||
|
:resize-vector (gpt/point 1 (:y (:resize-vector parent-modifiers)))}
|
||||||
|
{})
|
||||||
|
|
||||||
|
{})]
|
||||||
|
|
||||||
|
(cond-> {}
|
||||||
|
(or (:displacement modifiers-h) (:displacement modifiers-v))
|
||||||
|
(assoc :displacement (gmt/translate-matrix
|
||||||
|
(gpt/point (get (:displacement modifiers-h) :x 0)
|
||||||
|
(get (:displacement modifiers-v) :y 0))))
|
||||||
|
|
||||||
|
(or (:resize-vector modifiers-h) (:resize-vector modifiers-v))
|
||||||
|
(assoc :resize-origin (or (:resize-origin modifiers-h) ;; we assume that the origin is the same
|
||||||
|
(:resize-origin modifiers-v)) ;; in any direction
|
||||||
|
:resize-vector (gpt/point (get (:resize-vector modifiers-h) :x 1)
|
||||||
|
(get (:resize-vector modifiers-v) :y 1)))
|
||||||
|
(:displacement parent-modifiers)
|
||||||
|
(update :displacement #(if (nil? %)
|
||||||
|
(:displacement parent-modifiers)
|
||||||
|
(gmt/multiply % (:displacement parent-modifiers)))))))
|
||||||
|
|
||||||
(defn update-group-viewbox
|
(defn update-group-viewbox
|
||||||
"Updates the viewbox for groups imported from SVG's"
|
"Updates the viewbox for groups imported from SVG's"
|
||||||
[{:keys [selrect svg-viewbox] :as group} new-selrect]
|
[{:keys [selrect svg-viewbox] :as group} new-selrect]
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
[app.common.geom.matrix :as gmt]
|
[app.common.geom.matrix :as gmt]
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
[clojure.spec.alpha :as s]))
|
[clojure.spec.alpha :as s]))
|
||||||
|
|
||||||
;; --- Specs
|
;; --- Specs
|
||||||
|
@ -195,6 +196,30 @@
|
||||||
(s/def :internal.shape/interactions
|
(s/def :internal.shape/interactions
|
||||||
(s/coll-of :internal.shape/interaction :kind vector?))
|
(s/coll-of :internal.shape/interaction :kind vector?))
|
||||||
|
|
||||||
|
;; Size constraints
|
||||||
|
|
||||||
|
(s/def :internal.shape/constraints-h #{:left :right :leftright :center :scale})
|
||||||
|
(s/def :internal.shape/constraints-v #{:top :bottom :topbottom :center :scale})
|
||||||
|
(s/def :internal.shape/fixed-scroll boolean?)
|
||||||
|
|
||||||
|
; Shapes in the top frame have no constraints. Shapes directly below some
|
||||||
|
; frame are left-top constrained. Else (shapes in a group) are scaled.
|
||||||
|
(defn default-constraints-h
|
||||||
|
[shape]
|
||||||
|
(if (= (:parent-id shape) uuid/zero)
|
||||||
|
nil
|
||||||
|
(if (= (:parent-id shape) (:frame-id shape))
|
||||||
|
:left
|
||||||
|
:scale)))
|
||||||
|
|
||||||
|
(defn default-constraints-v
|
||||||
|
[shape]
|
||||||
|
(if (= (:parent-id shape) uuid/zero)
|
||||||
|
nil
|
||||||
|
(if (= (:parent-id shape) (:frame-id shape))
|
||||||
|
:top
|
||||||
|
:scale)))
|
||||||
|
|
||||||
;; Page Data related
|
;; Page Data related
|
||||||
(s/def :internal.shape/blocked boolean?)
|
(s/def :internal.shape/blocked boolean?)
|
||||||
(s/def :internal.shape/collapsed boolean?)
|
(s/def :internal.shape/collapsed boolean?)
|
||||||
|
@ -297,6 +322,9 @@
|
||||||
:internal.shape/locked
|
:internal.shape/locked
|
||||||
:internal.shape/proportion
|
:internal.shape/proportion
|
||||||
:internal.shape/proportion-lock
|
:internal.shape/proportion-lock
|
||||||
|
:internal.shape/constraints-h
|
||||||
|
:internal.shape/constraints-v
|
||||||
|
:internal.shape/fixed-scroll
|
||||||
:internal.shape/rx
|
:internal.shape/rx
|
||||||
:internal.shape/ry
|
:internal.shape/ry
|
||||||
:internal.shape/r1
|
:internal.shape/r1
|
||||||
|
|
|
@ -1257,4 +1257,154 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.row-flex.align-top {
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.constraints-widget {
|
||||||
|
min-width: 72px;
|
||||||
|
min-height: 72px;
|
||||||
|
position: relative;
|
||||||
|
background-color: $color-gray-60;
|
||||||
|
flex-grow: 0;
|
||||||
|
|
||||||
|
.constraints-box {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
position: absolute;
|
||||||
|
top: 22px;
|
||||||
|
left: 22px;
|
||||||
|
border: 2px solid $color-gray-50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.constraint-button {
|
||||||
|
position: absolute;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: ' ';
|
||||||
|
background-color: $color-gray-20;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active,
|
||||||
|
&:hover {
|
||||||
|
&::after {
|
||||||
|
background-color: $color-primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.top,
|
||||||
|
&.bottom {
|
||||||
|
width: 28px;
|
||||||
|
height: 22px;
|
||||||
|
left: calc(50% - 14px);
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
width: 3px;
|
||||||
|
height: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.top {
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.bottom {
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.left,
|
||||||
|
&.right {
|
||||||
|
width: 22px;
|
||||||
|
height: 28px;
|
||||||
|
top: calc(50% - 14px);
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
width: 15px;
|
||||||
|
height: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.left {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.right {
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.centerv {
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
left: calc(50% - 14px);
|
||||||
|
top: calc(50% - 14px);
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
width: 3px;
|
||||||
|
height: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.centerh {
|
||||||
|
width: 28px;
|
||||||
|
height: 15px;
|
||||||
|
left: calc(50% - 14px);
|
||||||
|
top: calc(50% - 7px);
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
width: 15px;
|
||||||
|
height: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.constraints-form {
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
.input-select {
|
||||||
|
font-size: $fs11;
|
||||||
|
margin: 0 $x-small;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 15px;
|
||||||
|
height: 15px;
|
||||||
|
margin-left: $medium;
|
||||||
|
fill: $color-gray-20;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-right svg {
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.top-down svg {
|
||||||
|
transform: rotate(-45deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fix-when {
|
||||||
|
font-size: $fs11;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
span {
|
||||||
|
margin-left: $small;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&.active {
|
||||||
|
color: $color-primary;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: $color-primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -151,8 +151,7 @@
|
||||||
:resize-origin origin
|
:resize-origin origin
|
||||||
:resize-transform shape-transform
|
:resize-transform shape-transform
|
||||||
:resize-scale-text scale-text
|
:resize-scale-text scale-text
|
||||||
:resize-transform-inverse shape-transform-inverse}
|
:resize-transform-inverse shape-transform-inverse}))))
|
||||||
false))))
|
|
||||||
|
|
||||||
;; Unifies the instantaneous proportion lock modifier
|
;; Unifies the instantaneous proportion lock modifier
|
||||||
;; activated by Shift key and the shapes own proportion
|
;; activated by Shift key and the shapes own proportion
|
||||||
|
@ -426,10 +425,34 @@
|
||||||
|
|
||||||
;; -- Apply modifiers
|
;; -- Apply modifiers
|
||||||
|
|
||||||
|
(defn- set-modifiers-recursive
|
||||||
|
[modif-tree objects shape modifiers]
|
||||||
|
(let [children (->> (get shape :shapes [])
|
||||||
|
(map #(get objects %)))
|
||||||
|
|
||||||
|
transformed-shape (when (seq children) ; <- don't calculate it if not needed
|
||||||
|
(gsh/transform-shape
|
||||||
|
(assoc shape :modifiers (select-keys modifiers
|
||||||
|
[:resize-origin
|
||||||
|
:resize-vector]))))
|
||||||
|
|
||||||
|
set-child (fn [modif-tree child]
|
||||||
|
(let [child-modifiers (gsh/calc-child-modifiers shape
|
||||||
|
transformed-shape
|
||||||
|
child
|
||||||
|
modifiers)]
|
||||||
|
(set-modifiers-recursive modif-tree
|
||||||
|
objects
|
||||||
|
child
|
||||||
|
child-modifiers)))]
|
||||||
|
|
||||||
|
(reduce set-child
|
||||||
|
(update-in modif-tree [(:id shape) :modifiers] #(merge % modifiers))
|
||||||
|
children)))
|
||||||
|
|
||||||
(defn set-modifiers
|
(defn set-modifiers
|
||||||
([ids] (set-modifiers ids nil true))
|
([ids] (set-modifiers ids nil))
|
||||||
([ids modifiers] (set-modifiers ids modifiers true))
|
([ids modifiers]
|
||||||
([ids modifiers recurse-frames?]
|
|
||||||
(us/verify (s/coll-of uuid?) ids)
|
(us/verify (s/coll-of uuid?) ids)
|
||||||
(ptk/reify ::set-modifiers
|
(ptk/reify ::set-modifiers
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
|
@ -438,25 +461,16 @@
|
||||||
page-id (:current-page-id state)
|
page-id (:current-page-id state)
|
||||||
objects (wsh/lookup-page-objects state page-id)
|
objects (wsh/lookup-page-objects state page-id)
|
||||||
|
|
||||||
ids (->> ids (into #{} (remove #(get-in objects [% :blocked] false))))
|
ids (->> ids (into #{} (remove #(get-in objects [% :blocked] false))))]
|
||||||
|
|
||||||
not-frame-id?
|
|
||||||
(fn [shape-id]
|
|
||||||
(let [shape (get objects shape-id)]
|
|
||||||
(or recurse-frames? (not (= :frame (:type shape))))))
|
|
||||||
|
|
||||||
;; For each shape updates the modifiers given as arguments
|
|
||||||
update-shape
|
|
||||||
(fn [objects shape-id]
|
|
||||||
(update-in objects [shape-id :modifiers] #(merge % modifiers)))
|
|
||||||
|
|
||||||
;; ID's + Children but remove frame children if the flag is set to false
|
|
||||||
ids-with-children (concat ids (mapcat #(cp/get-children % objects)
|
|
||||||
(filter not-frame-id? ids)))]
|
|
||||||
|
|
||||||
(update state :workspace-modifiers
|
|
||||||
#(reduce update-shape % ids-with-children)))))))
|
|
||||||
|
|
||||||
|
(reduce (fn [state id]
|
||||||
|
(update state :workspace-modifiers
|
||||||
|
#(set-modifiers-recursive %
|
||||||
|
objects
|
||||||
|
(get objects id)
|
||||||
|
modifiers)))
|
||||||
|
state
|
||||||
|
ids))))))
|
||||||
|
|
||||||
;; Set-rotation is custom because applies different modifiers to each
|
;; Set-rotation is custom because applies different modifiers to each
|
||||||
;; shape adjusting their position.
|
;; shape adjusting their position.
|
||||||
|
@ -567,10 +581,7 @@
|
||||||
(fn [objects shape-id]
|
(fn [objects shape-id]
|
||||||
(let [shape (get objects shape-id)
|
(let [shape (get objects shape-id)
|
||||||
modifier (gsh/resize-modifiers shape attr value)]
|
modifier (gsh/resize-modifiers shape attr value)]
|
||||||
(-> objects
|
(set-modifiers-recursive objects objects shape modifier)))]
|
||||||
(assoc-in [shape-id :modifiers] modifier)
|
|
||||||
(cond-> (not (= :frame (:type shape)))
|
|
||||||
(update-children (cp/get-children shape-id objects) modifier)))))]
|
|
||||||
|
|
||||||
(d/update-in-when
|
(d/update-in-when
|
||||||
state
|
state
|
||||||
|
@ -597,8 +608,7 @@
|
||||||
(rx/of (set-modifiers selected
|
(rx/of (set-modifiers selected
|
||||||
{:resize-vector (gpt/point -1.0 1.0)
|
{:resize-vector (gpt/point -1.0 1.0)
|
||||||
:resize-origin origin
|
:resize-origin origin
|
||||||
:displacement (gmt/translate-matrix (gpt/point (- (:width selrect)) 0))}
|
:displacement (gmt/translate-matrix (gpt/point (- (:width selrect)) 0))})
|
||||||
false)
|
|
||||||
(apply-modifiers selected))))))
|
(apply-modifiers selected))))))
|
||||||
|
|
||||||
(defn flip-vertical-selected []
|
(defn flip-vertical-selected []
|
||||||
|
@ -614,8 +624,7 @@
|
||||||
(rx/of (set-modifiers selected
|
(rx/of (set-modifiers selected
|
||||||
{:resize-vector (gpt/point 1.0 -1.0)
|
{:resize-vector (gpt/point 1.0 -1.0)
|
||||||
:resize-origin origin
|
:resize-origin origin
|
||||||
:displacement (gmt/translate-matrix (gpt/point 0 (- (:height selrect))))}
|
:displacement (gmt/translate-matrix (gpt/point 0 (- (:height selrect))))})
|
||||||
false)
|
|
||||||
(apply-modifiers selected))))))
|
(apply-modifiers selected))))))
|
||||||
|
|
||||||
(defn start-local-displacement [point]
|
(defn start-local-displacement [point]
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
(ns app.main.ui.workspace.sidebar.options.menus.measures
|
(ns app.main.ui.workspace.sidebar.options.menus.measures
|
||||||
(:require
|
(:require
|
||||||
|
[cuerdas.core :as str]
|
||||||
[rumext.alpha :as mf]
|
[rumext.alpha :as mf]
|
||||||
[app.main.ui.icons :as i]
|
[app.main.ui.icons :as i]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
|
@ -15,12 +16,14 @@
|
||||||
[app.util.data :refer [classnames]]
|
[app.util.data :refer [classnames]]
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
|
[app.common.pages.spec :as spec]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
[app.main.data.workspace :as udw]
|
[app.main.data.workspace :as udw]
|
||||||
[app.main.data.workspace.common :as dwc]
|
[app.main.data.workspace.common :as dwc]
|
||||||
[app.main.data.workspace.changes :as dch]
|
[app.main.data.workspace.changes :as dch]
|
||||||
[app.main.ui.components.numeric-input :refer [numeric-input]]
|
[app.main.ui.components.numeric-input :refer [numeric-input]]
|
||||||
[app.common.math :as math]
|
[app.common.math :as math]
|
||||||
[app.util.i18n :refer [t] :as i18n]))
|
[app.util.i18n :refer [tr] :as i18n]))
|
||||||
|
|
||||||
(def measure-attrs [:proportion-lock
|
(def measure-attrs [:proportion-lock
|
||||||
:width :height
|
:width :height
|
||||||
|
@ -28,7 +31,12 @@
|
||||||
:rotation
|
:rotation
|
||||||
:rx :ry
|
:rx :ry
|
||||||
:r1 :r2 :r3 :r4
|
:r1 :r2 :r3 :r4
|
||||||
:selrect])
|
:selrect
|
||||||
|
:constraints-h
|
||||||
|
:constraints-v
|
||||||
|
:fixed-scroll
|
||||||
|
:parent-id
|
||||||
|
:frame-id])
|
||||||
|
|
||||||
(defn- attr->string [attr values]
|
(defn- attr->string [attr values]
|
||||||
(let [value (attr values)]
|
(let [value (attr values)]
|
||||||
|
@ -42,7 +50,6 @@
|
||||||
(mf/defc measures-menu
|
(mf/defc measures-menu
|
||||||
[{:keys [options ids ids-with-children values] :as props}]
|
[{:keys [options ids ids-with-children values] :as props}]
|
||||||
(let [options (or options #{:size :position :rotation :radius})
|
(let [options (or options #{:size :position :rotation :radius})
|
||||||
locale (i18n/use-locale)
|
|
||||||
|
|
||||||
ids-with-children (or ids-with-children ids)
|
ids-with-children (or ids-with-children ids)
|
||||||
|
|
||||||
|
@ -65,6 +72,13 @@
|
||||||
|
|
||||||
proportion-lock (:proportion-lock values)
|
proportion-lock (:proportion-lock values)
|
||||||
|
|
||||||
|
in-frame? (not= (:parent-id values) uuid/zero)
|
||||||
|
first-level? (and in-frame?
|
||||||
|
(= (:parent-id values) (:frame-id values)))
|
||||||
|
|
||||||
|
constraints-h (get values :constraints-h (spec/default-constraints-h values))
|
||||||
|
constraints-v (get values :constraints-v (spec/default-constraints-v values))
|
||||||
|
|
||||||
on-size-change
|
on-size-change
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
(mf/deps ids)
|
(mf/deps ids)
|
||||||
|
@ -161,15 +175,75 @@
|
||||||
on-radius-r2-change #(on-radius-4-change % :r2)
|
on-radius-r2-change #(on-radius-4-change % :r2)
|
||||||
on-radius-r3-change #(on-radius-4-change % :r3)
|
on-radius-r3-change #(on-radius-4-change % :r3)
|
||||||
on-radius-r4-change #(on-radius-4-change % :r4)
|
on-radius-r4-change #(on-radius-4-change % :r4)
|
||||||
select-all #(-> % (dom/get-target) (.select))]
|
|
||||||
|
|
||||||
|
select-all #(-> % (dom/get-target) (.select))
|
||||||
|
|
||||||
|
on-constraint-button-clicked
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps [ids values])
|
||||||
|
(fn [button]
|
||||||
|
(fn [event]
|
||||||
|
(let [constraints-h (get values :constraints-h :scale)
|
||||||
|
constraints-v (get values :constraints-v :scale)
|
||||||
|
|
||||||
|
[constraint new-value]
|
||||||
|
(case button
|
||||||
|
:top (case constraints-v
|
||||||
|
:top [:constraints-v :scale]
|
||||||
|
:topbottom [:constraints-v :bottom]
|
||||||
|
:bottom [:constraints-v :topbottom]
|
||||||
|
[:constraints-v :top])
|
||||||
|
:bottom (case constraints-v
|
||||||
|
:bottom [:constraints-v :scale]
|
||||||
|
:topbottom [:constraints-v :top]
|
||||||
|
:top [:constraints-v :topbottom]
|
||||||
|
[:constraints-v :bottom])
|
||||||
|
:left (case constraints-h
|
||||||
|
:left [:constraints-h :scale]
|
||||||
|
:leftright [:constraints-h :right]
|
||||||
|
:right [:constraints-h :leftright]
|
||||||
|
[:constraints-h :left])
|
||||||
|
:right (case constraints-h
|
||||||
|
:right [:constraints-h :scale]
|
||||||
|
:leftright [:constraints-h :left]
|
||||||
|
:left [:constraints-h :leftright]
|
||||||
|
[:constraints-h :right])
|
||||||
|
:centerv (case constraints-v
|
||||||
|
:center [:constraints-v :scale]
|
||||||
|
[:constraints-v :center])
|
||||||
|
:centerh (case constraints-h
|
||||||
|
:center [:constraints-h :scale]
|
||||||
|
[:constraints-h :center]))]
|
||||||
|
(st/emit! (dch/update-shapes
|
||||||
|
ids
|
||||||
|
#(assoc % constraint new-value)))))))
|
||||||
|
|
||||||
|
on-constraint-select-changed
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps [ids values])
|
||||||
|
(fn [constraint]
|
||||||
|
(fn [event]
|
||||||
|
(let [value (-> (dom/get-target-val event) (keyword))]
|
||||||
|
(when-not (str/empty? value)
|
||||||
|
(st/emit! (dch/update-shapes
|
||||||
|
ids
|
||||||
|
#(assoc % constraint value))))))))
|
||||||
|
|
||||||
|
on-fixed-scroll-clicked
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps [ids values])
|
||||||
|
(fn [event]
|
||||||
|
(st/emit! (dch/update-shapes
|
||||||
|
ids
|
||||||
|
#(update % :fixed-scroll not)))))]
|
||||||
|
[:*
|
||||||
[:div.element-set
|
[:div.element-set
|
||||||
[:div.element-set-content
|
[:div.element-set-content
|
||||||
|
|
||||||
;; WIDTH & HEIGHT
|
;; WIDTH & HEIGHT
|
||||||
(when (options :size)
|
(when (options :size)
|
||||||
[:div.row-flex
|
[:div.row-flex
|
||||||
[:span.element-set-subtitle (t locale "workspace.options.size")]
|
[:span.element-set-subtitle (tr "workspace.options.size")]
|
||||||
[:div.input-element.width
|
[:div.input-element.width
|
||||||
[:> numeric-input {:min 1
|
[:> numeric-input {:min 1
|
||||||
:no-validate true
|
:no-validate true
|
||||||
|
@ -197,7 +271,7 @@
|
||||||
;; POSITION
|
;; POSITION
|
||||||
(when (options :position)
|
(when (options :position)
|
||||||
[:div.row-flex
|
[:div.row-flex
|
||||||
[:span.element-set-subtitle (t locale "workspace.options.position")]
|
[:span.element-set-subtitle (tr "workspace.options.position")]
|
||||||
[:div.input-element.Xaxis
|
[:div.input-element.Xaxis
|
||||||
[:> numeric-input {:no-validate true
|
[:> numeric-input {:no-validate true
|
||||||
:placeholder "--"
|
:placeholder "--"
|
||||||
|
@ -214,7 +288,7 @@
|
||||||
;; ROTATION
|
;; ROTATION
|
||||||
(when (options :rotation)
|
(when (options :rotation)
|
||||||
[:div.row-flex
|
[:div.row-flex
|
||||||
[:span.element-set-subtitle (t locale "workspace.options.rotation")]
|
[:span.element-set-subtitle (tr "workspace.options.rotation")]
|
||||||
[:div.input-element.degrees
|
[:div.input-element.degrees
|
||||||
[:> numeric-input
|
[:> numeric-input
|
||||||
{:no-validate true
|
{:no-validate true
|
||||||
|
@ -244,14 +318,14 @@
|
||||||
{:class (classnames
|
{:class (classnames
|
||||||
:selected
|
:selected
|
||||||
(and radius-1? (not radius-4?)))
|
(and radius-1? (not radius-4?)))
|
||||||
:alt (t locale "workspace.options.radius.all-corners")
|
:alt (tr "workspace.options.radius.all-corners")
|
||||||
:on-click on-switch-to-radius-1}
|
:on-click on-switch-to-radius-1}
|
||||||
i/radius-1]
|
i/radius-1]
|
||||||
[:div.radius-icon.tooltip.tooltip-bottom
|
[:div.radius-icon.tooltip.tooltip-bottom
|
||||||
{:class (classnames
|
{:class (classnames
|
||||||
:selected
|
:selected
|
||||||
(and radius-4? (not radius-1?)))
|
(and radius-4? (not radius-1?)))
|
||||||
:alt (t locale "workspace.options.radius.single-corners")
|
:alt (tr "workspace.options.radius.single-corners")
|
||||||
:on-click on-switch-to-radius-4}
|
:on-click on-switch-to-radius-4}
|
||||||
i/radius-4]]
|
i/radius-4]]
|
||||||
(if radius-1?
|
(if radius-1?
|
||||||
|
@ -291,5 +365,68 @@
|
||||||
:min 0
|
:min 0
|
||||||
:on-click select-all
|
:on-click select-all
|
||||||
:on-change on-radius-r4-change
|
:on-change on-radius-r4-change
|
||||||
:value (attr->string :r4 values)}]]])
|
:value (attr->string :r4 values)}]]])]))]]
|
||||||
]))]]))
|
|
||||||
|
;; CONSTRAINTS
|
||||||
|
(when in-frame?
|
||||||
|
[:div.element-set
|
||||||
|
[:div.element-set-title
|
||||||
|
[:span (tr "workspace.options.constraints")]]
|
||||||
|
|
||||||
|
[:div.element-set-content
|
||||||
|
[:div.row-flex.align-top
|
||||||
|
|
||||||
|
[:div.constraints-widget
|
||||||
|
[:div.constraints-box]
|
||||||
|
[:div.constraint-button.top
|
||||||
|
{:class (classnames :active (or (= constraints-v :top)
|
||||||
|
(= constraints-v :topbottom)))
|
||||||
|
:on-click (on-constraint-button-clicked :top)}]
|
||||||
|
[:div.constraint-button.bottom
|
||||||
|
{:class (classnames :active (or (= constraints-v :bottom)
|
||||||
|
(= constraints-v :topbottom)))
|
||||||
|
:on-click (on-constraint-button-clicked :bottom)}]
|
||||||
|
[:div.constraint-button.left
|
||||||
|
{:class (classnames :active (or (= constraints-h :left)
|
||||||
|
(= constraints-h :leftright)))
|
||||||
|
:on-click (on-constraint-button-clicked :left)}]
|
||||||
|
[:div.constraint-button.right
|
||||||
|
{:class (classnames :active (or (= constraints-h :right)
|
||||||
|
(= constraints-h :leftright)))
|
||||||
|
:on-click (on-constraint-button-clicked :right)}]
|
||||||
|
[:div.constraint-button.centerv
|
||||||
|
{:class (classnames :active (= constraints-v :center))
|
||||||
|
:on-click (on-constraint-button-clicked :centerv)}]
|
||||||
|
[:div.constraint-button.centerh
|
||||||
|
{:class (classnames :active (= constraints-h :center))
|
||||||
|
:on-click (on-constraint-button-clicked :centerh)}]]
|
||||||
|
|
||||||
|
[:div.constraints-form
|
||||||
|
[:div.row-flex
|
||||||
|
[:span.left-right i/full-screen]
|
||||||
|
[:select.input-select {:on-change (on-constraint-select-changed :constraints-h)
|
||||||
|
:value (d/name constraints-h "scale")}
|
||||||
|
(when (= constraints-h :multiple)
|
||||||
|
[:option {:value ""} (tr "settings.multiple")])
|
||||||
|
[:option {:value "left"} (tr "workspace.options.constraints.left")]
|
||||||
|
[:option {:value "right"} (tr "workspace.options.constraints.right")]
|
||||||
|
[:option {:value "leftright"} (tr "workspace.options.constraints.leftright")]
|
||||||
|
[:option {:value "center"} (tr "workspace.options.constraints.center")]
|
||||||
|
[:option {:value "scale"} (tr "workspace.options.constraints.scale")]]]
|
||||||
|
[:div.row-flex
|
||||||
|
[:span.top-bottom i/full-screen]
|
||||||
|
[:select.input-select {:on-change (on-constraint-select-changed :constraints-v)
|
||||||
|
:value (d/name constraints-v "scale")}
|
||||||
|
(when (= constraints-v :multiple)
|
||||||
|
[:option {:value ""} (tr "settings.multiple")])
|
||||||
|
[:option {:value "top"} (tr "workspace.options.constraints.top")]
|
||||||
|
[:option {:value "bottom"} (tr "workspace.options.constraints.bottom")]
|
||||||
|
[:option {:value "topbottom"} (tr "workspace.options.constraints.topbottom")]
|
||||||
|
[:option {:value "center"} (tr "workspace.options.constraints.center")]
|
||||||
|
[:option {:value "scale"} (tr "workspace.options.constraints.scale")]]]
|
||||||
|
(when first-level?
|
||||||
|
[:div.row-flex
|
||||||
|
[:div.fix-when {:class (classnames :active (:fixed-scroll values))
|
||||||
|
:on-click on-fixed-scroll-clicked}
|
||||||
|
i/pin
|
||||||
|
[:span (tr "workspace.options.constraints.fix-when-scrolling")]]])]]]])]))
|
||||||
|
|
|
@ -1884,6 +1884,54 @@ msgstr "Canvas background"
|
||||||
msgid "workspace.options.component"
|
msgid "workspace.options.component"
|
||||||
msgstr "Component"
|
msgstr "Component"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs
|
||||||
|
msgid "workspace.options.constraints"
|
||||||
|
msgstr "Constraints"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs
|
||||||
|
msgid "workspace.options.constraints.left"
|
||||||
|
msgstr "Left"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs
|
||||||
|
msgid "workspace.options.constraints.right"
|
||||||
|
msgstr "Right"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs
|
||||||
|
msgid "workspace.options.constraints.leftright"
|
||||||
|
msgstr "Left & Right"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs
|
||||||
|
msgid "workspace.options.constraints.center"
|
||||||
|
msgstr "Center"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs
|
||||||
|
msgid "workspace.options.constraints.scale"
|
||||||
|
msgstr "Scale"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs
|
||||||
|
msgid "workspace.options.constraints.top"
|
||||||
|
msgstr "Top"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs
|
||||||
|
msgid "workspace.options.constraints.bottom"
|
||||||
|
msgstr "Bottom"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs
|
||||||
|
msgid "workspace.options.constraints.topbottom"
|
||||||
|
msgstr "Top & Bottom"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs
|
||||||
|
msgid "workspace.options.constraints.center"
|
||||||
|
msgstr "Center"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs
|
||||||
|
msgid "workspace.options.constraints.scale"
|
||||||
|
msgstr "Scale"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs
|
||||||
|
msgid "workspace.options.constraints.fix-when-scrolling"
|
||||||
|
msgstr "Fix when scrolling"
|
||||||
|
|
||||||
#: src/app/main/ui/workspace/sidebar/options.cljs
|
#: src/app/main/ui/workspace/sidebar/options.cljs
|
||||||
msgid "workspace.options.design"
|
msgid "workspace.options.design"
|
||||||
msgstr "Design"
|
msgstr "Design"
|
||||||
|
|
|
@ -1876,6 +1876,54 @@ msgstr "Color de fondo"
|
||||||
msgid "workspace.options.component"
|
msgid "workspace.options.component"
|
||||||
msgstr "Componente"
|
msgstr "Componente"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs
|
||||||
|
msgid "workspace.options.constraints"
|
||||||
|
msgstr "Restricciones"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs
|
||||||
|
msgid "workspace.options.constraints.left"
|
||||||
|
msgstr "Izquierda"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs
|
||||||
|
msgid "workspace.options.constraints.right"
|
||||||
|
msgstr "Derecha"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs
|
||||||
|
msgid "workspace.options.constraints.leftright"
|
||||||
|
msgstr "Izq. y Der."
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs
|
||||||
|
msgid "workspace.options.constraints.center"
|
||||||
|
msgstr "Centro"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs
|
||||||
|
msgid "workspace.options.constraints.scale"
|
||||||
|
msgstr "Escalar"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs
|
||||||
|
msgid "workspace.options.constraints.top"
|
||||||
|
msgstr "Arriba"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs
|
||||||
|
msgid "workspace.options.constraints.bottom"
|
||||||
|
msgstr "Abajo"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs
|
||||||
|
msgid "workspace.options.constraints.topbottom"
|
||||||
|
msgstr "Arriba y Abajo"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs
|
||||||
|
msgid "workspace.options.constraints.center"
|
||||||
|
msgstr "Centro"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs
|
||||||
|
msgid "workspace.options.constraints.scale"
|
||||||
|
msgstr "Escalar"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/sidebar/options/menus/measures.cljs
|
||||||
|
msgid "workspace.options.constraints.fix-when-scrolling"
|
||||||
|
msgstr "Fijo al desplazar"
|
||||||
|
|
||||||
#: src/app/main/ui/workspace/sidebar/options.cljs
|
#: src/app/main/ui/workspace/sidebar/options.cljs
|
||||||
msgid "workspace.options.design"
|
msgid "workspace.options.design"
|
||||||
msgstr "Diseño"
|
msgstr "Diseño"
|
||||||
|
@ -2632,4 +2680,4 @@ msgid "workspace.updates.update"
|
||||||
msgstr "Actualizar"
|
msgstr "Actualizar"
|
||||||
|
|
||||||
msgid "workspace.viewport.click-to-close-path"
|
msgid "workspace.viewport.click-to-close-path"
|
||||||
msgstr "Pulsar para cerrar la ruta"
|
msgstr "Pulsar para cerrar la ruta"
|
||||||
|
|
Loading…
Add table
Reference in a new issue