0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-13 10:38:13 -05:00

Merge pull request #2088 from penpot/alotor-autolayout

Autolayout basics
This commit is contained in:
Andrey Antukh 2022-07-14 09:54:49 +02:00 committed by GitHub
commit dd180d93f4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 1309 additions and 557 deletions

View file

@ -14,6 +14,8 @@
[app.common.geom.shapes.constraints :as gct]
[app.common.geom.shapes.corners :as gsc]
[app.common.geom.shapes.intersect :as gin]
[app.common.geom.shapes.layout :as gcl]
[app.common.geom.shapes.modifiers :as gsm]
[app.common.geom.shapes.path :as gsp]
[app.common.geom.shapes.rect :as gpr]
[app.common.geom.shapes.transforms :as gtr]
@ -170,10 +172,15 @@
(dm/export gtr/modifiers->transform)
(dm/export gtr/empty-modifiers?)
(dm/export gtr/move-position-data)
(dm/export gtr/apply-transform)
;; Constratins
(dm/export gct/calc-child-modifiers)
;; Layout
(dm/export gcl/calc-layout-data)
(dm/export gcl/calc-layout-modifiers)
;; PATHS
(dm/export gsp/content->selrect)
(dm/export gsp/transform-content)
@ -196,3 +203,6 @@
;; Corners
(dm/export gsc/shape-corners-1)
(dm/export gsc/shape-corners-4)
;; Modifiers
(dm/export gsm/set-objects-modifiers)

View file

@ -197,6 +197,9 @@
;; Build final child modifiers. Apply transform again to the result, to get the
;; real modifiers that need to be applied to the child, including rotation as needed.
(cond-> {}
(some? (:displacement-after modifiers))
(assoc :displacement-after (:displacement-after modifiers))
(or (contains? modifiers-h :displacement)
(contains? modifiers-v :displacement))
(assoc :displacement (cond-> (gpt/point (get-in modifiers-h [:displacement :x] 0)

View file

@ -0,0 +1,327 @@
;; 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.geom.shapes.layout
(:require
[app.common.geom.matrix :as gmt]
[app.common.geom.point :as gpt]
[app.common.geom.shapes.rect :as gre]))
;; :layout ;; true if active, false if not
;; :layout-dir ;; :right, :left, :top, :bottom
;; :layout-gap ;; number could be negative
;; :layout-type ;; :packed, :space-between, :space-around
;; :layout-wrap-type ;; :wrap, :no-wrap
;; :layout-padding-type ;; :simple, :multiple
;; :layout-padding ;; {:p1 num :p2 num :p3 num :p4 num} number could be negative
;; :layout-h-orientation ;; :top, :center, :bottom
;; :layout-v-orientation ;; :left, :center, :right
(defn col?
[{:keys [layout-dir]}]
(or (= :right layout-dir) (= :left layout-dir)))
(defn row?
[{:keys [layout-dir]}]
(or (= :top layout-dir) (= :bottom layout-dir)))
(defn h-start?
[{:keys [layout-h-orientation]}]
(= layout-h-orientation :left))
(defn h-center?
[{:keys [layout-h-orientation]}]
(= layout-h-orientation :center))
(defn h-end?
[{:keys [layout-h-orientation]}]
(= layout-h-orientation :right))
(defn v-start?
[{:keys [layout-v-orientation]}]
(= layout-v-orientation :top))
(defn v-center?
[{:keys [layout-v-orientation]}]
(= layout-v-orientation :center))
(defn v-end?
[{:keys [layout-v-orientation]}]
(= layout-v-orientation :bottom))
(defn add-padding [transformed-rect {:keys [layout-padding-type layout-padding]}]
(let [{:keys [p1 p2 p3 p4]} layout-padding
[p1 p2 p3 p4]
(if (= layout-padding-type :multiple)
[p1 p2 p3 p4]
[p1 p1 p1 p1])]
(-> transformed-rect
(update :y + p1)
(update :width - p2 p3)
(update :x + p3)
(update :height - p1 p4))))
(defn calc-layout-lines
[{:keys [layout-gap layout-wrap-type] :as shape} children {:keys [width height] :as layout-bounds}]
(let [wrap? (= layout-wrap-type :wrap)
reduce-fn
(fn [[{:keys [line-width line-height num-children] :as line-data} result] child]
(let [child-bounds (-> child :points gre/points->rect)
next-width (-> child-bounds :width)
next-height (-> child-bounds :height)]
(if (and (some? line-data)
(or (not wrap?)
(and (col? shape) (<= (+ line-width next-width (* layout-gap num-children)) width))
(and (row? shape) (<= (+ line-height next-height (* layout-gap num-children)) height))))
[{:line-width (if (col? shape) (+ line-width next-width) (max line-width next-width))
:line-height (if (row? shape) (+ line-height next-height) (max line-height next-height))
:num-children (inc num-children)}
result]
[{:line-width next-width
:line-height next-height
:num-children 1}
(cond-> result (some? line-data) (conj line-data))])))
[line-data layout-lines] (reduce reduce-fn [nil []] children)]
(cond-> layout-lines (some? line-data) (conj line-data))))
(defn calc-layout-lines-position
[{:keys [layout-gap layout-type] :as shape} {:keys [x y width height]} layout-lines]
(letfn [(get-base-line
[total-width total-height]
(let [base-x
(cond
(and (row? shape) (h-center? shape))
(+ x (/ (- width total-width) 2))
(and (row? shape) (h-end? shape))
(+ x width (- total-width))
:else x)
base-y
(cond
(and (col? shape) (v-center? shape))
(+ y (/ (- height total-height) 2))
(and (col? shape) (v-end? shape))
(+ y height (- total-height))
:else y)]
[base-x base-y]))
(get-start-line
[{:keys [line-width line-height num-children]} base-x base-y]
(let [children-gap (* layout-gap (dec num-children))
start-x
(cond
(or (and (col? shape) (= :space-between layout-type))
(and (col? shape) (= :space-around layout-type)))
x
(and (col? shape) (h-center? shape))
(- (+ x (/ width 2)) (/ (+ line-width children-gap) 2))
(and (col? shape) (h-end? shape))
(- (+ x width) (+ line-width children-gap))
(and (row? shape) (h-center? shape))
(+ base-x (/ line-width 2))
(and (row? shape) (h-end? shape))
(+ base-x line-width)
(row? shape)
base-x
:else
x)
start-y
(cond
(or (and (row? shape) (= :space-between layout-type))
(and (row? shape) (= :space-around layout-type)))
y
(and (row? shape) (v-center? shape))
(- (+ y (/ height 2)) (/ (+ line-height children-gap) 2))
(and (row? shape) (v-end? shape))
(- (+ y height) (+ line-height children-gap))
(and (col? shape) (v-center? shape))
(+ base-y (/ line-height 2))
(and (col? shape) (v-end? shape))
(+ base-y line-height)
(col? shape)
base-y
:else
y)]
[start-x start-y]))
(get-next-line
[{:keys [line-width line-height]} base-x base-y]
(let [next-x (if (col? shape) base-x (+ base-x line-width layout-gap))
next-y (if (row? shape) base-y (+ base-y line-height layout-gap))]
[next-x next-y]))
(add-lines [[total-width total-height] {:keys [line-width line-height]}]
[(+ total-width line-width)
(+ total-height line-height)])
(add-starts [[result base-x base-y] layout-line]
(let [[start-x start-y] (get-start-line layout-line base-x base-y)
[next-x next-y] (get-next-line layout-line base-x base-y)]
[(conj result
(assoc layout-line
:start-x start-x
:start-y start-y))
next-x
next-y]))]
(let [[total-width total-height]
(->> layout-lines (reduce add-lines [0 0]))
total-width (+ total-width (* layout-gap (dec (count layout-lines))))
total-height (+ total-height (* layout-gap (dec (count layout-lines))))
[base-x base-y]
(get-base-line total-width total-height)
[layout-lines _ _ _ _]
(reduce add-starts [[] base-x base-y] layout-lines)]
layout-lines)))
(defn calc-layout-line-data
[{:keys [layout-type layout-gap] :as shape}
{:keys [width height] :as layout-bounds}
{:keys [num-children line-width line-height] :as line-data}]
(let [layout-gap
(cond
(= :packed layout-type)
layout-gap
(= :space-around layout-type)
0
(and (col? shape) (= :space-between layout-type))
(/ (- width line-width) (dec num-children))
(and (row? shape) (= :space-between layout-type))
(/ (- height line-height) (dec num-children)))
margin-x
(if (and (col? shape) (= :space-around layout-type))
(/ (- width line-width) (inc num-children) )
0)
margin-y
(if (and (row? shape) (= :space-around layout-type))
(/ (- height line-height) (inc num-children))
0)]
(assoc line-data
:layout-gap layout-gap
:margin-x margin-x
:margin-y margin-y)))
(defn calc-layout-data
"Digest the layout data to pass it to the constrains"
[{:keys [layout-dir] :as shape} children layout-bounds]
(let [reverse? (or (= :left layout-dir) (= :bottom layout-dir))
layout-bounds (-> layout-bounds (add-padding shape))
children (cond->> children reverse? reverse)
layout-lines
(->> (calc-layout-lines shape children layout-bounds)
(calc-layout-lines-position shape layout-bounds)
(map (partial calc-layout-line-data shape layout-bounds)))]
{:layout-lines layout-lines
:reverse? reverse?}))
(defn next-p
"Calculates the position for the current shape given the layout-data context"
[shape
{:keys [width height]}
{:keys [start-x start-y layout-gap margin-x margin-y] :as layout-data}]
(let [pos-x
(cond
(and (row? shape) (h-center? shape))
(- start-x (/ width 2))
(and (row? shape) (h-end? shape))
(- start-x width)
:else
start-x)
pos-y
(cond
(and (col? shape) (v-center? shape))
(- start-y (/ height 2))
(and (col? shape) (v-end? shape))
(- start-y height)
:else
start-y)
pos-x (cond-> pos-x (some? margin-x) (+ margin-x))
pos-y (cond-> pos-y (some? margin-y) (+ margin-y))
corner-p (gpt/point pos-x pos-y)
next-x
(if (col? shape)
(+ start-x width layout-gap)
start-x)
next-y
(if (row? shape)
(+ start-y height layout-gap)
start-y)
next-x (cond-> next-x (some? margin-x) (+ margin-x))
next-y (cond-> next-y (some? margin-y) (+ margin-y))
layout-data
(assoc layout-data :start-x next-x :start-y next-y)]
[corner-p layout-data]))
(defn calc-layout-modifiers
"Calculates the modifiers for the layout"
[parent transform child layout-data]
(let [bounds (-> child :points gre/points->selrect)
[corner-p layout-data] (next-p parent bounds layout-data)
delta-p (-> corner-p
(gpt/subtract (gpt/point bounds))
(cond-> (some? transform) (gpt/transform transform)))
modifiers {:displacement-after (gmt/translate-matrix delta-p)}]
[modifiers layout-data]))

View file

@ -0,0 +1,286 @@
;; 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.geom.shapes.modifiers
(:require
[app.common.data :as d]
[app.common.geom.matrix :as gmt]
[app.common.geom.point :as gpt]
[app.common.geom.shapes.common :as gco]
[app.common.geom.shapes.constraints :as gct]
[app.common.geom.shapes.layout :as gcl]
[app.common.geom.shapes.rect :as gpr]
[app.common.geom.shapes.transforms :as gtr]
[app.common.math :as mth]
[app.common.uuid :as uuid]))
(defn set-pixel-precision
"Adjust modifiers so they adjust to the pixel grid"
[modifiers shape]
(if (some? (:resize-transform modifiers))
;; If we're working with a rotation we don't handle pixel precision because
;; the transformation won't have the precision anyway
modifiers
(let [center (gco/center-shape shape)
base-bounds (-> (:points shape) (gpr/points->rect))
raw-bounds
(-> (gtr/transform-bounds (:points shape) center modifiers)
(gpr/points->rect))
flip-x? (neg? (get-in modifiers [:resize-vector :x]))
flip-y? (or (neg? (get-in modifiers [:resize-vector :y]))
(neg? (get-in modifiers [:resize-vector-2 :y])))
path? (= :path (:type shape))
vertical-line? (and path? (<= (:width raw-bounds) 0.01))
horizontal-line? (and path? (<= (:height raw-bounds) 0.01))
target-width (if vertical-line?
(:width raw-bounds)
(max 1 (mth/round (:width raw-bounds))))
target-height (if horizontal-line?
(:height raw-bounds)
(max 1 (mth/round (:height raw-bounds))))
target-p (cond-> (gpt/round (gpt/point raw-bounds))
flip-x?
(update :x + target-width)
flip-y?
(update :y + target-height))
ratio-width (/ target-width (:width raw-bounds))
ratio-height (/ target-height (:height raw-bounds))
modifiers
(-> modifiers
(d/without-nils)
(d/update-in-when
[:resize-vector :x] #(* % ratio-width))
;; If the resize-vector-2 modifier arrives means the resize-vector
;; will only resize on the x axis
(cond-> (nil? (:resize-vector-2 modifiers))
(d/update-in-when
[:resize-vector :y] #(* % ratio-height)))
(d/update-in-when
[:resize-vector-2 :y] #(* % ratio-height)))
origin (get modifiers :resize-origin)
origin-2 (get modifiers :resize-origin-2)
resize-v (get modifiers :resize-vector)
resize-v-2 (get modifiers :resize-vector-2)
displacement (get modifiers :displacement)
target-p-inv
(-> target-p
(gpt/transform
(cond-> (gmt/matrix)
(some? displacement)
(gmt/multiply (gmt/inverse displacement))
(and (some? resize-v) (some? origin))
(gmt/scale (gpt/inverse resize-v) origin)
(and (some? resize-v-2) (some? origin-2))
(gmt/scale (gpt/inverse resize-v-2) origin-2))))
delta-v (gpt/subtract target-p-inv (gpt/point base-bounds))
modifiers
(-> modifiers
(d/update-when :displacement #(gmt/multiply (gmt/translate-matrix delta-v) %))
(cond-> (nil? (:displacement modifiers))
(assoc :displacement (gmt/translate-matrix delta-v))))]
modifiers)))
(defn set-children-modifiers
[modif-tree shape objects ignore-constraints snap-pixel?]
(letfn [(set-child [transformed-rect snap-pixel? modif-tree child]
(let [modifiers (get-in modif-tree [(:id shape) :modifiers])
child-modifiers (gct/calc-child-modifiers shape child modifiers ignore-constraints transformed-rect)
child-modifiers (cond-> child-modifiers snap-pixel? (set-pixel-precision child))]
(cond-> modif-tree
(not (gtr/empty-modifiers? child-modifiers))
(update-in [(:id child) :modifiers] #(merge child-modifiers %)))))]
(let [children (map (d/getf objects) (:shapes shape))
modifiers (get-in modif-tree [(:id shape) :modifiers])
transformed-rect (gtr/transform-selrect (:selrect shape) modifiers)
resize-modif? (or (:resize-vector modifiers) (:resize-vector-2 modifiers))]
(reduce (partial set-child transformed-rect (and snap-pixel? resize-modif?)) modif-tree children))))
(defn group? [shape]
(or (= :group (:type shape))
(= :bool (:type shape))))
(defn merge-modifiers
[modif-tree ids modifiers]
(reduce
(fn [modif-tree id]
(update-in modif-tree [id :modifiers] #(merge % modifiers)))
modif-tree
ids))
(defn set-layout-modifiers
[modif-tree objects id]
(letfn [(transform-child [parent child]
(let [modifiers (get modif-tree (:id child))
child
(cond-> child
(not (group? child))
(-> (merge modifiers) gtr/transform-shape)
(group? child)
(gtr/apply-group-modifiers objects modif-tree))
child
(-> child
(gtr/apply-transform (gmt/transform-in (gco/center-shape parent) (:transform-inverse parent))))]
child))
(set-layout-modifiers [parent transform [layout-data modif-tree] child]
(let [[modifiers layout-data]
(gcl/calc-layout-modifiers parent transform child layout-data)
modif-tree
(cond-> modif-tree
(not (gtr/empty-modifiers? modifiers))
(merge-modifiers [(:id child)] modifiers)
(and (not (gtr/empty-modifiers? modifiers)) (group? child))
(merge-modifiers (:shapes child) modifiers))]
[layout-data modif-tree]))]
(let [modifiers (get modif-tree id)
shape (-> (get objects id) (merge modifiers) gtr/transform-shape)
children (->> (:shapes shape)
(map (d/getf objects))
(map (partial transform-child shape)))
center (gco/center-shape shape)
{:keys [transform transform-inverse]} shape
shape
(-> shape
(gtr/apply-transform (gmt/transform-in center transform-inverse)))
transformed-rect (:selrect shape)
layout-data (gcl/calc-layout-data shape children transformed-rect)
children (into [] (cond-> children (:reverse? layout-data) reverse))
max-idx (dec (count children))
layout-lines (:layout-lines layout-data)]
(loop [modif-tree modif-tree
layout-line (first layout-lines)
pending (rest layout-lines)
from-idx 0]
(if (and (some? layout-line) (<= from-idx max-idx))
(let [to-idx (+ from-idx (:num-children layout-line))
children (subvec children from-idx to-idx)
[_ modif-tree]
(reduce (partial set-layout-modifiers shape transform) [layout-line modif-tree] children)]
(recur modif-tree (first pending) (rest pending) to-idx))
modif-tree)))))
(defn get-first-layout
[id objects]
(loop [current id
result id]
(let [shape (get objects current)
parent (get objects (:parent-id shape))]
(cond
(or (not shape) (= uuid/zero current))
result
;; Frame found, but not layout we return the last layout found (or the id)
(and (= :frame (:type parent))
(not (:layout parent)))
result
;; Layout found. We continue upward but we mark this layout
(and (= :frame (:type parent))
(:layout parent))
(:id parent)
;; If group or boolean or other type of group we continue with the last result
:else
(recur (:id parent) result)))))
(defn resolve-layout-ids
"Given a list of ids, resolve the parent layouts that will need to update. This will go upwards
in the tree while a layout is found"
[ids objects]
(into (d/ordered-set)
(map #(get-first-layout % objects))
ids))
(defn set-objects-modifiers
[ids objects get-modifier ignore-constraints snap-pixel?]
(let [set-modifiers
(fn [modif-tree id]
(assoc modif-tree id {:modifiers (get-modifier (get objects id))}))
modif-tree (reduce set-modifiers {} ids)
ids (resolve-layout-ids ids objects)
;; First: Calculate children modifiers (constraints, etc)
[modif-tree touched-layouts]
(loop [current (first ids)
pending (rest ids)
modif-tree modif-tree
touched-layouts (d/ordered-set)]
(if (some? current)
(let [shape (get objects current)
pending (concat pending (:shapes shape))
touched-layouts
(cond-> touched-layouts
(:layout shape)
(conj (:id shape)))
modif-tree
(-> modif-tree
(set-children-modifiers shape objects ignore-constraints snap-pixel?))]
(recur (first pending) (rest pending) modif-tree touched-layouts))
[modif-tree touched-layouts]))
;; Second: Calculate layout positioning
modif-tree
(loop [current (first touched-layouts)
pending (rest touched-layouts)
modif-tree modif-tree]
(if (some? current)
(let [modif-tree (set-layout-modifiers modif-tree objects current)]
(recur (first pending) (rest pending) modif-tree))
modif-tree))]
modif-tree))

View file

@ -278,7 +278,7 @@
(if transform (gmt/multiply transform matrix) matrix)
(if transform-inverse (gmt/multiply matrix-inverse transform-inverse) matrix-inverse)]))
(defn- apply-transform
(defn apply-transform
"Given a new set of points transformed, set up the rectangle so it keeps
its properties. We adjust de x,y,width,height and create a custom transform"
[shape transform-mtx]
@ -491,6 +491,7 @@
([center modifiers]
(let [displacement (:displacement modifiers)
displacement-after (:displacement-after modifiers)
resize-v1 (:resize-vector modifiers)
resize-v2 (:resize-vector-2 modifiers)
origin-1 (:resize-origin modifiers (gpt/point))
@ -512,6 +513,9 @@
rt-modif (:rotation modifiers)]
(cond-> (gmt/matrix)
(some? displacement-after)
(gmt/multiply displacement-after)
(some? resize-1)
(-> (gmt/translate origin-1)
(cond-> (some? resize-transform)
@ -610,7 +614,7 @@
(dissoc :modifiers))))))
(defn transform-bounds
[points center {:keys [displacement resize-transform-inverse resize-vector resize-origin resize-vector-2 resize-origin-2]}]
[points center {:keys [displacement displacement-after resize-transform-inverse resize-vector resize-origin resize-vector-2 resize-origin-2]}]
;; FIXME: Improve Performance
(let [resize-transform-inverse (or resize-transform-inverse (gmt/matrix))
@ -624,9 +628,10 @@
resize-origin-2
(when (some? resize-origin-2)
(transform-point-center resize-origin-2 center resize-transform-inverse))]
(transform-point-center resize-origin-2 center resize-transform-inverse))
]
(if (and (nil? displacement) (nil? resize-origin) (nil? resize-origin-2))
(if (and (nil? displacement) (nil? resize-origin) (nil? resize-origin-2) (nil? displacement-after))
points
(cond-> points
@ -637,7 +642,10 @@
(gco/transform-points resize-origin (gmt/scale-matrix resize-vector))
(some? resize-origin-2)
(gco/transform-points resize-origin-2 (gmt/scale-matrix resize-vector-2))))))
(gco/transform-points resize-origin-2 (gmt/scale-matrix resize-vector-2))
(some? displacement-after)
(gco/transform-points displacement-after)))))
(defn transform-selrect
[selrect modifiers]
@ -662,3 +670,17 @@
(map (comp gpr/points->selrect :points transform-shape))
(gpr/join-selrects)))
(defn apply-group-modifiers
"Apply the modifiers to the group children to calculate its selection rect"
[group objects modif-tree]
(let [children
(->> (:shapes group)
(map (d/getf objects))
(map (fn [shape]
(let [modifiers (get modif-tree (:id shape))
shape (-> shape (merge modifiers) transform-shape)]
(if (= :group (:type shape))
(apply-group-modifiers shape objects modif-tree)
shape)))))]
(update-group-selrect group children)))

View file

@ -218,7 +218,7 @@
(->> (get-root-shapes objects)
(mapv :id)))
(defn- get-base
(defn get-base
[objects id-a id-b]
(let [parents-a (reverse (get-parents-seq objects id-a))

View file

@ -13,6 +13,7 @@
[app.common.types.shape.blur :as ctsb]
[app.common.types.shape.export :as ctse]
[app.common.types.shape.interactions :as ctsi]
[app.common.types.shape.layout :as ctsl]
[app.common.types.shape.radius :as ctsr]
[app.common.types.shape.shadow :as ctss]
[clojure.set :as set]
@ -156,68 +157,71 @@
:luminosity})
(s/def ::shape-attrs
(s/keys :opt-un [::id
::type
::name
::component-id
::component-file
::component-root?
::shape-ref
::selrect
::points
::blocked
::collapsed
::fills
::fill-color ;; TODO: remove these attributes
::fill-opacity ;; when backward compatibility
::fill-color-gradient ;; is no longer needed
::fill-color-ref-file ;;
::fill-color-ref-id ;;
::hide-fill-on-export
::font-family
::font-size
::font-style
::font-weight
::hidden
::letter-spacing
::line-height
::locked
::proportion
::proportion-lock
::constraints-h
::constraints-v
::fixed-scroll
::ctsr/rx
::ctsr/ry
::ctsr/r1
::ctsr/r2
::ctsr/r3
::ctsr/r4
::x
::y
::exports
::shapes
::strokes
::stroke-color ;; TODO: same thing
::stroke-color-ref-file ;;
::stroke-color-ref-i ;;
::stroke-opacity ;;
::stroke-style
::stroke-width
::stroke-alignment
::stroke-cap-start
::stroke-cap-end
::text-align
::transform
::transform-inverse
::width
::height
::masked-group?
::ctsi/interactions
::ctss/shadow
::ctsb/blur
::opacity
::blend-mode]))
(s/and
::ctsl/layout-container-props
::ctsl/layout-child-props
(s/keys :opt-un [::id
::type
::name
::component-id
::component-file
::component-root?
::shape-ref
::selrect
::points
::blocked
::collapsed
::fills
::fill-color ;; TODO: remove these attributes
::fill-opacity ;; when backward compatibility
::fill-color-gradient ;; is no longer needed
::fill-color-ref-file ;;
::fill-color-ref-id ;;
::hide-fill-on-export
::font-family
::font-size
::font-style
::font-weight
::hidden
::letter-spacing
::line-height
::locked
::proportion
::proportion-lock
::constraints-h
::constraints-v
::fixed-scroll
::ctsr/rx
::ctsr/ry
::ctsr/r1
::ctsr/r2
::ctsr/r3
::ctsr/r4
::x
::y
::exports
::shapes
::strokes
::stroke-color ;; TODO: same thing
::stroke-color-ref-file ;;
::stroke-color-ref-i ;;
::stroke-opacity ;;
::stroke-style
::stroke-width
::stroke-alignment
::stroke-cap-start
::stroke-cap-end
::text-align
::transform
::transform-inverse
::width
::height
::masked-group?
::ctsi/interactions
::ctss/shadow
::ctsb/blur
::opacity
::blend-mode])))
(s/def :internal.shape.text/type #{"root" "paragraph-set" "paragraph"})
(s/def :internal.shape.text/children

View file

@ -0,0 +1,61 @@
;; 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.shape.layout
(:require
[app.common.spec :as us]
[clojure.spec.alpha :as s]))
(s/def ::layout boolean?)
(s/def ::layout-dir #{:right :left :top :bottom})
(s/def ::layout-gap ::us/safe-number)
(s/def ::layout-type #{:packed :space-between :space-around})
(s/def ::layout-wrap-type #{:wrap :no-wrap})
(s/def ::layout-padding-type #{:simple :multiple})
(s/def ::p1 ::us/safe-number)
(s/def ::p2 ::us/safe-number)
(s/def ::p3 ::us/safe-number)
(s/def ::p4 ::us/safe-number)
(s/def ::layout-padding
(s/keys :req-un [::p1]
:opt-un [::p2 ::p3 ::p4]))
(s/def ::layout-h-orientation #{:left :center :right})
(s/def ::layout-v-orientation #{:top :center :bottom})
(s/def ::layout-container-props
(s/keys :opt-un [::layout
::layout-dir
::layout-gap
::layout-type
::layout-wrap-type
::layout-padding-type
::layout-padding
::layout-h-orientation
::layout-v-orientation]))
(s/def ::layout-margin (s/keys :req-un [::m1]
:opt-un [::m2 ::m3 ::m4]))
(s/def ::layout-margin-type #{:simple :multiple})
(s/def ::layout-h-behavior #{:fill :fix :auto})
(s/def ::layout-v-behavior #{:fill :fix :auto})
(s/def ::layout-max-h ::us/safe-number)
(s/def ::layout-min-h ::us/safe-number)
(s/def ::layout-max-w ::us/safe-number)
(s/def ::layout-min-w ::us/safe-number)
(s/def ::layout-child-props
(s/keys :opt-un [::layout-margin
::layout-margin-type
::layout-h-behavior
::layout-v-behavior
::layout-max-h
::layout-min-h
::layout-max-w
::layout-min-w]))

View file

@ -23,8 +23,6 @@
:grid-alignment true
:background "var(--color-white)"})
(def has-layout-item false)
(def size-presets
[{:name "APPLE"}
{:name "iPhone 12/12 Pro"

View file

@ -45,6 +45,7 @@
[app.main.data.workspace.path.shapes-to-path :as dwps]
[app.main.data.workspace.persistence :as dwp]
[app.main.data.workspace.selection :as dws]
[app.main.data.workspace.shape-layout :as dwsl]
[app.main.data.workspace.shapes :as dwsh]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.data.workspace.thumbnails :as dwth]
@ -799,7 +800,8 @@
ids)]
(rx/of (dch/commit-changes changes)
(dwco/expand-collapse parent-id))))))
(dwco/expand-collapse parent-id)
(dwsl/update-layout-positions [parent-id]))))))
(defn relocate-selected-shapes
[parent-id to-index]

View file

@ -0,0 +1,84 @@
;; 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.data.workspace.shape-layout
(:require
[app.common.data :as d]
[app.common.pages.helpers :as cph]
[app.main.data.workspace.changes :as dwc]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.data.workspace.transforms :as dwt]
[beicon.core :as rx]
[potok.core :as ptk]))
(def layout-keys
[:layout
:layout-dir
:layout-gap
:layout-type
:layout-wrap-type
:layout-padding-type
:layout-padding
:layout-h-orientation
:layout-v-orientation])
(def initial-layout
{:layout true
:layout-dir :left
:layout-gap 0
:layout-type :packed
:layout-wrap-type :wrap
:layout-padding-type :simple
:layout-padding {:p1 0 :p2 0 :p3 0 :p4 0}
:layout-h-orientation :left
:layout-v-orientation :top})
(defn update-layout-positions
[ids]
(ptk/reify ::update-layout-positions
ptk/WatchEvent
(watch [_ state _]
(let [objects (wsh/lookup-page-objects state)
ids (->> ids (filter #(get-in objects [% :layout])))]
(if (d/not-empty? ids)
(rx/of (dwt/set-modifiers ids)
(dwt/apply-modifiers))
(rx/empty))))))
;; TODO: Remove constraints from children
(defn create-layout
[ids]
(ptk/reify ::create-layout
ptk/WatchEvent
(watch [_ _ _]
(rx/of (dwc/update-shapes ids #(merge % initial-layout))
(update-layout-positions ids)))))
(defn remove-layout
[ids]
(ptk/reify ::remove-layout
ptk/WatchEvent
(watch [_ _ _]
(rx/of (dwc/update-shapes ids #(apply dissoc % layout-keys))
(update-layout-positions ids)))))
(defn update-layout
[ids changes]
(ptk/reify ::update-layout
ptk/WatchEvent
(watch [_ _ _]
(rx/of (dwc/update-shapes ids #(d/deep-merge % changes))
(update-layout-positions ids)))))
(defn update-layout-child
[ids changes]
(ptk/reify ::update-layout-child
ptk/WatchEvent
(watch [_ state _]
(let [objects (wsh/lookup-page-objects state)
parent-ids (->> ids (map #(cph/get-parent-id objects %)))]
(rx/of (dwc/update-shapes ids #(d/deep-merge (or % {}) changes))
(update-layout-positions parent-ids))))))

View file

@ -19,6 +19,7 @@
[app.main.data.workspace.changes :as dch]
[app.main.data.workspace.edition :as dwe]
[app.main.data.workspace.selection :as dws]
[app.main.data.workspace.shape-layout :as dwsl]
[app.main.data.workspace.state-helpers :as wsh]
[app.main.streams :as ms]
[app.util.names :as un]
@ -98,6 +99,7 @@
(rx/concat
(rx/of (dch/commit-changes changes)
(dwsl/update-layout-positions [(:parent-id shape)])
(when-not no-select?
(dws/select-shapes (d/ordered-set id))))
(when (= :text (:type attrs))
@ -239,7 +241,8 @@
flows
starting-flows)))))]
(rx/of (dch/commit-changes changes))))))
(rx/of (dch/commit-changes changes)
(dwsl/update-layout-positions all-parents))))))
(defn- viewport-center
[state]

View file

@ -110,10 +110,10 @@
;; geometric attributes of the shapes.
(declare clear-local-transform)
(declare set-objects-modifiers)
(declare get-ignore-tree)
(defn- set-modifiers
(defn set-modifiers
([ids]
(set-modifiers ids nil false))
@ -128,20 +128,16 @@
(ptk/reify ::set-modifiers
ptk/UpdateEvent
(update [_ state]
(let [modifiers (or modifiers (get-in state [:workspace-local :modifiers] {}))
page-id (:current-page-id state)
objects (wsh/lookup-page-objects state page-id)
(let [objects (wsh/lookup-page-objects state)
ids (into #{} (remove #(get-in objects [% :blocked] false)) ids)
layout (get state :workspace-layout)
snap-pixel? (and (not ignore-snap-pixel) (contains? layout :snap-pixel-grid))
setup-modifiers
(fn [state id]
(let [shape (get objects id)]
(update state :workspace-modifiers
#(set-objects-modifiers % objects shape modifiers ignore-constraints snap-pixel?))))]
snap-pixel? (and (not ignore-snap-pixel)
(contains? (:workspace-layout state) :snap-pixel-grid))
(reduce setup-modifiers state ids))))))
modif-tree
(gsh/set-objects-modifiers ids objects (constantly modifiers) ignore-constraints snap-pixel?)]
(update state :workspace-modifiers merge modif-tree))))))
;; Rotation use different algorithm to calculate children modifiers (and do not use child constraints).
(defn- set-rotation-modifiers
@ -152,19 +148,23 @@
(ptk/reify ::set-rotation-modifiers
ptk/UpdateEvent
(update [_ state]
(let [objects (wsh/lookup-page-objects state)
shapes (->> shapes
(remove #(get % :blocked false))
(mapcat #(cph/get-children objects (:id %)))
(concat shapes)
(filter #((cpc/editable-attrs (:type %)) :rotation)))
(let [objects (wsh/lookup-page-objects state)
ids
(->> shapes
(remove #(get % :blocked false))
(mapcat #(cph/get-children objects (:id %)))
(concat shapes)
(filter #((cpc/editable-attrs (:type %)) :rotation))
(map :id))
update-shape
(fn [modifiers shape]
(let [rotate-modifiers (gsh/rotation-modifiers shape center angle)]
(assoc-in modifiers [(:id shape) :modifiers] rotate-modifiers)))]
get-modifier
(fn [shape]
(gsh/rotation-modifiers shape center angle))
(update state :workspace-modifiers #(reduce update-shape % shapes)))))))
modif-tree
(gsh/set-objects-modifiers ids objects get-modifier false false)]
(update state :workspace-modifiers merge modif-tree))))))
(defn- update-grow-type
[shape old-shape]
@ -180,18 +180,20 @@
change-to-fixed?
(assoc :grow-type :fixed))))
(defn- apply-modifiers
([ids]
(apply-modifiers ids nil))
(defn apply-modifiers
([]
(apply-modifiers nil))
([ids {:keys [undo-transation?] :or {undo-transation? true}}]
(us/verify (s/coll-of uuid?) ids)
([{:keys [undo-transation?] :or {undo-transation? true}}]
(ptk/reify ::apply-modifiers
ptk/WatchEvent
(watch [_ state _]
(let [objects (wsh/lookup-page-objects state)
ids-with-children (into (vec ids) (mapcat #(cph/get-children-ids objects %)) ids)
object-modifiers (get state :workspace-modifiers)
ids (keys object-modifiers)
ids-with-children (into (vec ids) (mapcat #(cph/get-children-ids objects %)) ids)
shapes (map (d/getf objects) ids)
ignore-tree (->> (map #(get-ignore-tree object-modifiers objects %) shapes)
(reduce merge {}))]
@ -202,7 +204,7 @@
(rx/empty))
(rx/of (dwg/move-frame-guides ids-with-children)
(dch/update-shapes
ids-with-children
ids
(fn [shape]
(let [modif (get object-modifiers (:id shape))
text-shape? (cph/text-shape? shape)]
@ -280,119 +282,9 @@
[root transformed-root ignore-geometry?]))
(defn set-pixel-precision
"Adjust modifiers so they adjust to the pixel grid"
[modifiers shape]
(if (some? (:resize-transform modifiers))
;; If we're working with a rotation we don't handle pixel precision because
;; the transformation won't have the precision anyway
modifiers
(let [center (gsh/center-shape shape)
base-bounds (-> (:points shape) (gsh/points->rect))
raw-bounds
(-> (gsh/transform-bounds (:points shape) center modifiers)
(gsh/points->rect))
flip-x? (neg? (get-in modifiers [:resize-vector :x]))
flip-y? (or (neg? (get-in modifiers [:resize-vector :y]))
(neg? (get-in modifiers [:resize-vector-2 :y])))
path? (= :path (:type shape))
vertical-line? (and path? (<= (:width raw-bounds) 0.01))
horizontal-line? (and path? (<= (:height raw-bounds) 0.01))
target-width (if vertical-line?
(:width raw-bounds)
(max 1 (mth/round (:width raw-bounds))))
target-height (if horizontal-line?
(:height raw-bounds)
(max 1 (mth/round (:height raw-bounds))))
target-p (cond-> (gpt/round (gpt/point raw-bounds))
flip-x?
(update :x + target-width)
flip-y?
(update :y + target-height))
ratio-width (/ target-width (:width raw-bounds))
ratio-height (/ target-height (:height raw-bounds))
modifiers
(-> modifiers
(d/without-nils)
(d/update-in-when
[:resize-vector :x] #(* % ratio-width))
;; If the resize-vector-2 modifier arrives means the resize-vector
;; will only resize on the x axis
(cond-> (nil? (:resize-vector-2 modifiers))
(d/update-in-when
[:resize-vector :y] #(* % ratio-height)))
(d/update-in-when
[:resize-vector-2 :y] #(* % ratio-height)))
origin (get modifiers :resize-origin)
origin-2 (get modifiers :resize-origin-2)
resize-v (get modifiers :resize-vector)
resize-v-2 (get modifiers :resize-vector-2)
displacement (get modifiers :displacement)
target-p-inv
(-> target-p
(gpt/transform
(cond-> (gmt/matrix)
(some? displacement)
(gmt/multiply (gmt/inverse displacement))
(and (some? resize-v) (some? origin))
(gmt/scale (gpt/inverse resize-v) origin)
(and (some? resize-v-2) (some? origin-2))
(gmt/scale (gpt/inverse resize-v-2) origin-2))))
delta-v (gpt/subtract target-p-inv (gpt/point base-bounds))
modifiers
(-> modifiers
(d/update-when :displacement #(gmt/multiply (gmt/translate-matrix delta-v) %))
(cond-> (nil? (:displacement modifiers))
(assoc :displacement (gmt/translate-matrix delta-v))))]
modifiers)))
(defn- set-objects-modifiers
[modif-tree objects shape modifiers ignore-constraints snap-pixel?]
(letfn [(set-modifiers-rec
[modif-tree shape modifiers]
(let [children (map (d/getf objects) (:shapes shape))
transformed-rect (gsh/transform-selrect (:selrect shape) modifiers)
set-child
(fn [snap-pixel? modif-tree child]
(let [child-modifiers (gsh/calc-child-modifiers shape child modifiers ignore-constraints transformed-rect)
child-modifiers (cond-> child-modifiers snap-pixel? (set-pixel-precision child))]
(cond-> modif-tree
(not (gsh/empty-modifiers? child-modifiers))
(set-modifiers-rec child child-modifiers))))
modif-tree
(-> modif-tree
(assoc-in [(:id shape) :modifiers] modifiers))
resize-modif?
(or (:resize-vector modifiers) (:resize-vector-2 modifiers))]
(reduce (partial set-child (and snap-pixel? resize-modif?)) modif-tree children)))]
(let [modifiers (cond-> modifiers snap-pixel? (set-pixel-precision shape))]
(set-modifiers-rec modif-tree shape modifiers))))
(defn- get-ignore-tree
"Retrieves a map with the flag `ignore-geometry?` given a tree of modifiers"
@ -534,7 +426,7 @@
(rx/map #(conj current %)))))
(rx/mapcat (partial resize shape initial-position layout))
(rx/take-until stoper))
(rx/of (apply-modifiers ids)
(rx/of (apply-modifiers)
(finish-transform))))))))
(defn update-dimensions
@ -547,22 +439,20 @@
(ptk/reify ::update-dimensions
ptk/UpdateEvent
(update [_ state]
(let [objects (wsh/lookup-page-objects state)
layout (get state :workspace-layout)
snap-pixel? (contains? layout :snap-pixel-grid)
(let [objects (wsh/lookup-page-objects state)
snap-pixel? (and (contains? (:workspace-layout state) :snap-pixel-grid)
(int? value))
get-modifier
(fn [shape] (gsh/resize-modifiers shape attr value))
update-modifiers
(fn [state id]
(let [shape (get objects id)
modifiers (gsh/resize-modifiers shape attr value)]
(-> state
(update :workspace-modifiers
#(set-objects-modifiers % objects shape modifiers false (and snap-pixel? (int? value)))))))]
(reduce update-modifiers state ids)))
modif-tree
(gsh/set-objects-modifiers ids objects get-modifier false snap-pixel?)]
(assoc state :workspace-modifiers modif-tree)))
ptk/WatchEvent
(watch [_ _ _]
(rx/of (apply-modifiers ids)))))
(rx/of (apply-modifiers)))))
(defn change-orientation
"Change orientation of shapes, from the sidebar options form.
@ -574,21 +464,19 @@
ptk/UpdateEvent
(update [_ state]
(let [objects (wsh/lookup-page-objects state)
layout (get state :workspace-layout)
snap-pixel? (contains? layout :snap-pixel-grid)
snap-pixel? (contains? (get state :workspace-layout) :snap-pixel-grid)
update-modifiers
(fn [state id]
(let [shape (get objects id)
modifiers (gsh/change-orientation-modifiers shape orientation)]
(-> state
(update :workspace-modifiers
#(set-objects-modifiers % objects shape modifiers false snap-pixel?)))))]
(reduce update-modifiers state ids)))
get-modifier
(fn [shape] (gsh/change-orientation-modifiers shape orientation))
modif-tree
(gsh/set-objects-modifiers ids objects get-modifier false snap-pixel?)]
(assoc state :workspace-modifiers modif-tree)))
ptk/WatchEvent
(watch [_ _ _]
(rx/of (apply-modifiers ids)))))
(rx/of (apply-modifiers)))))
;; -- Rotate --------------------------------------------------------
@ -631,7 +519,7 @@
(let [delta-angle (calculate-angle pos mod? shift?)]
(set-rotation-modifiers delta-angle shapes group-center))))
(rx/take-until stoper))
(rx/of (apply-modifiers (map :id shapes))
(rx/of (apply-modifiers)
(finish-transform)))))))
(defn increase-rotation
@ -648,7 +536,7 @@
(set-rotation-modifiers delta [shape])))]
(rx/concat
(rx/from (->> ids (map #(get objects %)) (map rotate-shape)))
(rx/of (apply-modifiers ids)))))))
(rx/of (apply-modifiers)))))))
;; -- Move ----------------------------------------------------------
@ -772,7 +660,7 @@
(rx/of (dwu/start-undo-transaction)
(calculate-frame-for-move ids)
(apply-modifiers ids {:undo-transation? false})
(apply-modifiers {:undo-transation? false})
(finish-transform)
(dwu/commit-undo-transaction)))))))))
@ -820,7 +708,7 @@
(rx/take-until stopper))
(rx/of (move-selected direction shift?)))
(rx/of (apply-modifiers selected)
(rx/of (apply-modifiers)
(finish-transform))))
(rx/empty))))))
@ -850,7 +738,7 @@
displ (gmt/translate-matrix delta)]
(rx/of (set-modifiers [id] {:displacement displ} false true)
(apply-modifiers [id]))))))
(apply-modifiers))))))
(defn check-frame-move?
[target-frame-id objects position shape]
@ -911,7 +799,7 @@
:resize-origin origin
:displacement (gmt/translate-matrix (gpt/point (- (:width selrect)) 0))}
true)
(apply-modifiers selected))))))
(apply-modifiers))))))
(defn flip-vertical-selected []
(ptk/reify ::flip-vertical-selected
@ -928,4 +816,4 @@
:resize-origin origin
:displacement (gmt/translate-matrix (gpt/point 0 (- (:height selrect))))}
true)
(apply-modifiers selected))))))
(apply-modifiers))))))

View file

@ -410,3 +410,11 @@
(defn workspace-text-modifier-by-id [id]
(l/derived #(get % id) workspace-text-modifier =))
(defn is-layout-child?
[ids]
(l/derived
(fn [objects]
(->> ids
(some #(-> (cph/get-parent objects %) :layout))))
workspace-page-objects))

View file

@ -4,10 +4,12 @@
;;
;; Copyright (c) UXBOX Labs SL
(ns app.main.ui.workspace.sidebar.options.menus.layout
(ns app.main.ui.workspace.sidebar.options.menus.layout-container
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.main.data.workspace.shape-layout :as dwsl]
[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]
@ -15,16 +17,16 @@
[cuerdas.core :as str]
[rumext.alpha :as mf]))
(def layout-attrs
[:layout ;; true if active, false if not
:layout-dir ;; :right, :left, :top, :bottom
:gap ;; number could be negative
:layout-type ;; :packed, :space-between, :space-around
:wrap-type ;; :wrap, :no-wrap
:padding-type ;; :simple, :multiple
:padding ;; {:p1 num :p2 num :p3 num :p4 num} number could be negative
:h-orientation ;; :top, :center, :bottom
:v-orientation]) ;; :left, :center, :right
(def layout-container-attrs
[:layout ;; true if active, false if not
:layout-dir ;; :right, :left, :top, :bottom
:layout-gap ;; number could be negative
:layout-type ;; :packed, :space-between, :space-around
:layout-wrap-type ;; :wrap, :no-wrap
:layout-padding-type ;; :simple, :multiple
:layout-padding ;; {:p1 num :p2 num :p3 num :p4 num} number could be negative
:layout-h-orientation ;; :left, :center, :right
:layout-v-orientation]) ;; :top, :center, :bottom
(def grid-pos [[:top :left]
[:top :center]
@ -35,35 +37,65 @@
[:bottom :left]
[:bottom :center]
[:bottom :right]])
(def grid-rows [:top :center :bottom])
(def grid-cols [:left :center :right])
(defn- get-layout-icon
[dir layout-type v h]
(let [row? (or (= dir :right) (= dir :left))
manage-text-icon
(if row?
(case v
:top i/text-align-left
:center i/text-align-center
:bottom i/text-align-right
i/text-align-center)
(case h
:left i/text-align-left
:center i/text-align-center
:right i/text-align-right
i/text-align-center))]
(case layout-type
:packed manage-text-icon
:space-around i/space-around
:space-between i/space-between)))
(mf/defc direction-row
[{:keys [dir saved-dir set-direction] :as props}]
[:button.dir.tooltip.tooltip-bottom
{:class (dom/classnames :active (= saved-dir dir)
:left (= :left dir)
:right (= :right dir)
:top (= :top dir)
:bottom (= :bottom dir))
:key (dm/str "direction-" dir)
:alt (tr (dm/str "workspace.options.layout.direction." (d/name dir)))
:on-click #(set-direction dir)}
i/auto-direction])
(let [handle-on-click
(mf/use-callback
(mf/deps set-direction dir)
(fn []
(when (some? set-direction)
(set-direction dir))))]
[:button.dir.tooltip.tooltip-bottom
{:class (dom/classnames :active (= saved-dir dir)
:left (= :left dir)
:right (= :right dir)
:top (= :top dir)
:bottom (= :bottom dir))
:key (dm/str "direction-" dir)
:alt (tr (dm/str "workspace.options.layout.direction." (d/name dir)))
:on-click handle-on-click}
i/auto-direction]))
(mf/defc orientation-grid
[{:keys [manage-orientation test-values get-icon] :as props}]
(let [dir (:layout-dir @test-values)
type (:layout-type @test-values)
[{:keys [on-change-orientation values] :as props}]
(let [dir (:layout-dir values)
type (:layout-type values)
is-col? (or (= dir :top)
(= dir :bottom))
saved-pos [(:h-orientation @test-values) (:v-orientation @test-values)]]
saved-pos [(:layout-v-orientation values)
(:layout-h-orientation values)]]
(if (= type :packed)
[:div.orientation-grid
[:div.button-wrapper
(for [[pv ph] grid-pos]
[:button.orientation
{:on-click (partial manage-orientation pv ph type)
{:on-click (partial on-change-orientation pv ph type)
:class (dom/classnames
:active (= [pv ph] saved-pos)
:top (= :top pv)
@ -75,35 +107,14 @@
:key (dm/str pv ph)}
[:span.icon
{:class (dom/classnames
:rotated is-col?)}
(get-icon dir type pv ph)]])]]
(if is-col?
[:div.orientation-grid.col
[:div.button-wrapper
(for [col grid-cols]
[:button.orientation
{:on-click (partial manage-orientation :top col type)
:class (dom/classnames
:active (= col (second saved-pos))
:top (= :left col)
:centered (= :center col)
:bottom (= :right col))}
[:span.icon
{:class (dom/classnames :rotated is-col?)}
(get-icon dir type nil col)]
[:span.icon
{:class (dom/classnames :rotated is-col?)}
(get-icon dir type nil col)]
[:span.icon
{:class (dom/classnames :rotated is-col?)}
(get-icon dir type nil col)]])]]
:rotated (not is-col?))}
(get-layout-icon dir type pv ph)]])]]
(if (not is-col?)
[:div.orientation-grid.row
[:div.button-wrapper
(for [row grid-rows]
[:button.orientation
{:on-click (partial manage-orientation row :left type)
{:on-click (partial on-change-orientation row :left type)
:class (dom/classnames
:active (= row (first saved-pos))
:top (= :top row)
@ -111,30 +122,51 @@
:bottom (= :bottom row))}
[:span.icon
{:class (dom/classnames :rotated is-col?)}
(get-icon dir type row nil)]
(get-layout-icon dir type nil row)]
[:span.icon
{:class (dom/classnames :rotated is-col?)}
(get-icon dir type row nil)]
(get-layout-icon dir type nil row)]
[:span.icon
{:class (dom/classnames :rotated is-col?)}
(get-icon dir type row nil)]])]]))))
(get-layout-icon dir type nil row)]])]]
[:div.orientation-grid.col
[:div.button-wrapper
(for [[idx col] (d/enumerate grid-cols)]
[:button.orientation
{:key (dm/str idx col)
:on-click (partial on-change-orientation :top col type)
:class (dom/classnames
:active (= col (second saved-pos))
:top (= :left col)
:centered (= :center col)
:bottom (= :right col))}
[:span.icon
{:class (dom/classnames :rotated is-col?)}
(get-layout-icon dir type col nil)]
[:span.icon
{:class (dom/classnames :rotated is-col?)}
(get-layout-icon dir type col nil)]
[:span.icon
{:class (dom/classnames :rotated is-col?)}
(get-layout-icon dir type col nil)]])]]))))
(mf/defc padding-section
[{:keys [test-values change-padding-style select-all on-padding-change] :as props}]
[{:keys [values on-change-style on-change] :as props}]
(let [padding-type (:padding-type @test-values)]
(let [padding-type (:layout-padding-type values)]
[:div.row-flex
[:div.padding-options
[:div.padding-icon.tooltip.tooltip-bottom
{:class (dom/classnames :selected (= padding-type :simple))
:alt (tr "workspace.options.layout.padding-simple")
:on-click #(change-padding-style :simple)}
:on-click #(on-change-style :simple)}
i/auto-padding]
[:div.padding-icon.tooltip.tooltip-bottom
{:class (dom/classnames :selected (= padding-type :multiple))
:alt (tr "workspace.options.layout.padding")
:on-click #(change-padding-style :multiple)}
:on-click #(on-change-style :multiple)}
i/auto-padding-side]]
(cond
@ -145,9 +177,9 @@
[:> numeric-input
{:placeholder "--"
:on-click select-all
:on-change (partial on-padding-change :simple)
:value (:p1 (:padding @test-values))}]]]
:on-click #(dom/select-target %)
:on-change (partial on-change :simple)
:value (:p1 (:layout-padding values))}]]]
(= padding-type :multiple)
(for [num [:p1 :p2 :p3 :p4]]
@ -161,82 +193,55 @@
[:div.input-element.mini
[:> numeric-input
{:placeholder "--"
:on-click select-all
:on-change (partial on-padding-change num)
:value (num (:padding @test-values))}]]]))]))
:on-click #(dom/select-target %)
:on-change (partial on-change num)
:value (num (:layout-padding values))}]]]))]))
(mf/defc layout-menu
(mf/defc layout-container-menu
{::mf/wrap [#(mf/memo' % (mf/check-props ["ids" "values" "type"]))]}
[{:keys [_ids _type _values] :as props}]
(let [test-values (mf/use-state {:layout false
:layout-dir nil
:gap 0
:layout-type nil
:wrap-type nil
:padding-type nil
:padding {:p1 0 :p2 0 :p3 0 :p4 0}
:h-orientation nil
:v-orientation nil})
open? (mf/use-state false)
[{:keys [ids _type values] :as props}]
(let [open? (mf/use-state false)
gap-selected? (mf/use-state false)
toggle-open (fn [] (swap! open? not))
on-add-layout
(fn [_]
(reset! test-values {:layout true
:layout-dir :left
:gap 0
:layout-type :packed
:wrap-type :wrap
:padding-type :simple
:padding {:p1 0 :p2 0 :p3 0 :p4 0}
:h-orientation :top
:v-orientation :left}))
(st/emit! (dwsl/create-layout ids)))
on-remove-layout
(fn [_]
(reset! test-values {:layout false
:layout-dir nil
:gap 0
:layout-type nil
:wrap-type nil
:padding-type nil
:padding {:p1 0 :p2 0 :p3 0 :p4 0}
:h-orientation nil
:v-orientation nil})
(st/emit! (dwsl/remove-layout ids))
(reset! open? false))
set-direction
(fn [dir]
(swap! test-values assoc :layout-dir dir))
(st/emit! (dwsl/update-layout ids {:layout-dir dir})))
set-gap
(fn [event]
(swap! test-values assoc :gap event))
(fn [gap]
(st/emit! (dwsl/update-layout ids {:layout-gap gap})))
change-padding-style
(fn [type]
(swap! test-values assoc :padding-type type))
(st/emit! (dwsl/update-layout ids {:layout-padding-type type})))
select-all #(dom/select-target %)
select-all-gap #(do (reset! gap-selected? true)
(dom/select-target %))
select-all-gap
(fn [event]
(reset! gap-selected? true)
(dom/select-target event))
on-padding-change
(fn [type val]
(if (= type :simple)
(swap! test-values assoc :padding {:p1 val :p2 val :p3 val :p4 val})
(swap! test-values assoc-in [:padding type] val)))
(st/emit! (dwsl/update-layout ids {:layout-padding {:p1 val :p2 val :p3 val :p4 val}}))
(st/emit! (dwsl/update-layout ids {:layout-padding {type val}}))))
handle-change-type
(fn [event]
(let [target (dom/get-target event)
value (dom/get-value target)
value (keyword value)]
(swap! test-values assoc :layout-type value)))
(st/emit! (dwsl/update-layout ids {:layout-type value}))))
handle-wrap-type
(mf/use-callback
@ -244,47 +249,30 @@
(let [target (dom/get-target event)
value (dom/get-value target)
value (keyword value)]
(swap! test-values assoc :wrap-type value))))
(st/emit! (dwsl/update-layout ids {:layout-wrap-type value})))))
manage-orientation
(fn [h v]
(swap! test-values assoc :h-orientation h :v-orientation v))
get-icon
(fn [dir layout-type v h]
(let [col? (= dir (or :left :right))
manage-text-icon
(if col?
(case h
:left i/text-align-left
:center i/text-align-center
:right i/text-align-right
i/text-align-center)
(case v
:top i/text-align-left
:center i/text-align-center
:bottom i/text-align-right
i/text-align-center))]
(case layout-type
:packed manage-text-icon
:space-around i/space-around
:space-between i/space-between)))
handle-change-orientation
(fn [v-orientation h-orientation]
(st/emit! (dwsl/update-layout ids {:layout-h-orientation h-orientation :layout-v-orientation v-orientation})))
layout-info
(fn []
(let [type (:layout-type @test-values)
dir (:layout-dir @test-values)
is-col? (or (= dir :top)
(= dir :bottom))
h (:v-orientation @test-values)
v (:h-orientation @test-values)
wrap (:wrap-type @test-values)
orientation (if (= type :packed)
(dm/str (tr (dm/str "workspace.options.layout.v." (d/name v))) ", " (tr (dm/str "workspace.options.layout.h." (d/name h))) ", ")
(if is-col?
(dm/str (tr (dm/str "workspace.options.layout.h." (d/name h))) ", ")
(dm/str (tr (dm/str "workspace.options.layout.v." (d/name v))) ", ")))]
(let [type (:layout-type values)
dir (:layout-dir values)
is-col? (or (= dir :top) (= dir :bottom))
h (:layout-h-orientation values)
v (:layout-v-orientation values)
wrap (:layout-wrap-type values)
orientation
(if (= type :packed)
(dm/str (tr (dm/str "workspace.options.layout.v." (d/name v))) ", "
(tr (dm/str "workspace.options.layout.h." (d/name h))) ", ")
(if is-col?
(dm/str (tr (dm/str "workspace.options.layout.h." (d/name h))) ", ")
(dm/str (tr (dm/str "workspace.options.layout.v." (d/name v))) ", ")))]
(dm/str orientation
(str/replace (tr (dm/str "workspace.options.layout." (d/name type))) "-" " ") ", "
@ -294,26 +282,27 @@
[:div.element-set-title
[:*
[:span (tr "workspace.options.layout.title")]
(if (= true (:layout @test-values))
(if (:layout values)
[:div.add-page {:on-click on-remove-layout} i/minus]
[:div.add-page {:on-click on-add-layout} i/close])]]
(when (= true (:layout @test-values))
(when (:layout values)
[:div.element-set-content.layout-menu
;; DIRECTION-GAP
[:div.direction-gap
[:div.direction
[:*
(for [dir [:left :right :bottom :top]]
[:& direction-row {:dir dir
:saved-dir (:layout-dir @test-values)
[:& direction-row {:key (d/name dir)
:dir dir
:saved-dir (:layout-dir values)
:set-direction set-direction}])]]
[:div.gap.tooltip.tooltip-bottom-left
{:alt (tr "workspace.options.layout.gap")}
[:span.icon
{:class (dom/classnames
:rotated (or (= (:layout-dir @test-values) :top)
(= (:layout-dir @test-values) :bottom))
:rotated (or (= (:layout-dir values) :top)
(= (:layout-dir values) :bottom))
:activated (= @gap-selected? true))}
i/auto-gap]
[:> numeric-input {:no-validate true
@ -321,7 +310,7 @@
:on-click select-all-gap
:on-change set-gap
:on-blur #(reset! gap-selected? false)
:value (:gap @test-values)}]]]
:value (:layout-gap values)}]]]
;; LAYOUT FLEX
[:div.layout-container
@ -329,25 +318,24 @@
{:on-click toggle-open
:alt (layout-info)}
[:div.element-set-actions-button i/actions]
[:div.layout-info
(layout-info)]]
(when (= true @open?)
[:div.layout-info (layout-info)]]
(when @open?
[:div.layout-body
[:& orientation-grid {:manage-orientation manage-orientation :test-values test-values :get-icon get-icon}]
[:& orientation-grid {:on-change-orientation handle-change-orientation :values values}]
[:div.selects-wrapper
[:select.input-select {:value (d/name (:layout-type @test-values))
[:select.input-select {:value (d/name (:layout-type values))
:on-change handle-change-type}
[:option {:value "packed" :label (tr "workspace.options.layout.packed")}]
[:option {:value "space-between" :label (tr "workspace.options.layout.space-between")}]
[:option {:value "space-around" :label (tr "workspace.options.layout.space-around")}]]
[:select.input-select {:value (d/name (:wrap-type @test-values))
[:select.input-select {:value (d/name (:layout-wrap-type values))
:on-change handle-wrap-type}
[:option {:value "wrap" :label (tr "workspace.options.layout.wrap")}]
[:option {:value "no-wrap" :label (tr "workspace.options.layout.no-wrap")}]]]])]
[:& padding-section {:test-values test-values
:change-padding-style change-padding-style
:select-all select-all
:on-padding-change on-padding-change}]])]))
[:& padding-section {:values values
:on-change-style change-padding-style
:on-change on-padding-change}]])]))

View file

@ -8,6 +8,8 @@
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.main.data.workspace.shape-layout :as dwsl]
[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]
@ -15,18 +17,19 @@
[rumext.alpha :as mf]))
(def layout-item-attrs
[:margin ;; {:m1 0 :m2 0 :m3 0 :m4 0}
:margin-type ;; :simple :multiple
:h-behavior ;; :fill :fix :auto
:v-behavior ;; :fill :fix :auto
:max-h ;; num
:min-h ;; num
:max-w ;; num
:min-w ]) ;; num
(mf/defc margin-section
[{:keys [test-values change-margin-style select-all on-margin-change] :as props}]
[:layout-margin ;; {:m1 0 :m2 0 :m3 0 :m4 0}
:layout-margin-type ;; :simple :multiple
:layout-h-behavior ;; :fill :fix :auto
:layout-v-behavior ;; :fill :fix :auto
:layout-max-h ;; num
:layout-min-h ;; num
:layout-max-w ;; num
:layout-min-w ]) ;; num
(let [margin-type (:margin-type @test-values)]
(mf/defc margin-section
[{:keys [values change-margin-style on-margin-change] :as props}]
(let [margin-type (or (:layout-margin-type values) :simple)]
[:div.row-flex
[:div.margin-options
@ -49,9 +52,9 @@
[:> numeric-input
{:placeholder "--"
:on-click select-all
:on-click #(dom/select-target %)
:on-change (partial on-margin-change :simple)
:value (:m1 (:margin @test-values))}]]]
:value (or (-> values :layout-margin :m1) 0)}]]]
(= margin-type :multiple)
[:*
@ -67,101 +70,93 @@
[:div.input-element.mini
[:> numeric-input
{:placeholder "--"
:on-click select-all
:on-click #(dom/select-target %)
:on-change (partial on-margin-change num)
:value (num (:margin @test-values))}]]])])]))
:value (or (-> values :layout-margin num) 0)}]]])])]))
(mf/defc element-behavior
[{:keys [is-layout-container? is-layout-item? h-behavior v-behavior on-change-behavior] :as props}]
(let [auto? is-layout-container?
fill? (and (= true is-layout-item?) (not= true is-layout-container?))]
[{:keys [is-layout-container? is-layout-child? layout-h-behavior layout-v-behavior on-change-behavior] :as props}]
(let [fill? is-layout-child?
auto? is-layout-container?]
[:div.layout-behavior
[:div.button-wrapper.horizontal
[:button.behavior-btn.tooltip.tooltip-bottom
{:alt "horizontal fix"
:class (dom/classnames :activated (= h-behavior :fix))
:class (dom/classnames :activated (= layout-h-behavior :fix))
:on-click #(on-change-behavior :h :fix)}
[:span.icon i/auto-fix-layout]]
(when fill?
[:button.behavior-btn.tooltip.tooltip-bottom
{:alt "horizontal fill"
:class (dom/classnames :activated (= h-behavior :fill))
:class (dom/classnames :activated (= layout-h-behavior :fill))
:on-click #(on-change-behavior :h :fill)}
[:span.icon i/auto-fill]])
(when auto?
[:button.behavior-btn.tooltip.tooltip-bottom
{:alt "horizontal auto"
:class (dom/classnames :activated (= h-behavior :auto))
:class (dom/classnames :activated (= layout-v-behavior :auto))
:on-click #(on-change-behavior :h :auto)}
[:span.icon i/auto-hug]])]
[:div.button-wrapper
[:button.behavior-btn.tooltip.tooltip-bottom
{:alt "vertical fix"
:class (dom/classnames :activated (= v-behavior :fix))
:class (dom/classnames :activated (= layout-v-behavior :fix))
:on-click #(on-change-behavior :v :fix)}
[:span.icon i/auto-fix-layout]]
(when fill?
[:button.behavior-btn.tooltip.tooltip-bottom
{:alt "vertical fill"
:class (dom/classnames :activated (= v-behavior :fill))
:class (dom/classnames :activated (= layout-v-behavior :fill))
:on-click #(on-change-behavior :v :fill)}
[:span.icon i/auto-fill]])
(when auto?
[:button.behavior-btn.tooltip.tooltip-bottom
{:alt "vertical auto"
:class (dom/classnames :activated (= v-behavior :auto))
:class (dom/classnames :activated (= layout-v-behavior :auto))
:on-click #(on-change-behavior :v :auto)}
[:span.icon i/auto-hug]])]]))
(mf/defc layout-item-menu
{::mf/wrap [#(mf/memo' % (mf/check-props ["ids" "values" "type"]))]}
[{:keys [_ids _type _values] :as props}]
(let [test-values (mf/use-state {:margin {:m1 0 :m2 0 :m3 0 :m4 0}
:margin-type :simple
:h-behavior :fill
:v-behavior :fill
:max-h 100
:min-h 100
:max-w 100
:min-w 100})
open? (mf/use-state false)
[{:keys [ids _type values is-layout-child? is-layout-container?] :as props}]
(let [open? (mf/use-state false)
toggle-open (fn [] (swap! open? not))
is-layout-container? true
is-layout-item? true
change-margin-style
(fn [type]
(swap! test-values assoc :margin-type type))
select-all #(dom/select-target %)
(st/emit! (dwsl/update-layout-child ids {:layout-margin-type type})))
on-margin-change
(fn [type val]
(if (= type :simple)
(swap! test-values assoc :margin {:m1 val :m2 val :m3 val :m4 val})
(swap! test-values assoc-in [:margin type] val)))
(st/emit! (dwsl/update-layout-child ids {:layout-margin {:m1 val :m2 val :m3 val :m4 val}}))
(st/emit! (dwsl/update-layout-child ids {:layout-margin {type val}}))))
on-change-behavior
(fn [dir value]
(if (= dir :h)
(swap! test-values assoc :h-behavior value)
(swap! test-values assoc :v-behavior value)))
(st/emit! (dwsl/update-layout-child ids {:layout-h-behavior value}))
(st/emit! (dwsl/update-layout-child ids {:layout-v-behavior value}))))
on-size-change
(fn [measure value]
(swap! test-values assoc measure value))]
(st/emit! (dwsl/update-layout-child ids {measure value})))]
[:div.element-set
[:div.element-set-title
[:span (tr "workspace.options.layout-item.title")]]
[:div.element-set-content.layout-item-menu
[:& element-behavior {:is-layout-container? is-layout-container?
:is-layout-item? is-layout-item?
:v-behavior (:v-behavior @test-values)
:h-behavior (:h-behavior @test-values)
[:& element-behavior {:is-layout-child? is-layout-child?
:is-layout-container? is-layout-container?
:layout-v-behavior (or (:layout-v-behavior values) :fix)
:layout-h-behavior (or (:layout-h-behavior values) :fix)
:on-change-behavior on-change-behavior}]
[:div.margin [:& margin-section {:test-values test-values
[:div.margin [:& margin-section {:values values
:change-margin-style change-margin-style
:select-all select-all
:on-margin-change on-margin-change}]]
[:div.advanced-ops-container
[:div.advanced-ops.toltip.tooltip-bottom
@ -169,22 +164,24 @@
:alt (tr "workspace.options.layout-item.advanced-ops")}
[:div.element-set-actions-button i/actions]
[:span (tr "workspace.options.layout-item.advanced-ops")]]]
(when (= true @open?)
(when @open?
[:div.advanced-ops-body
(for [item [:max-h :min-h :max-w :min-w]]
(for [item [:layout-max-h :layout-min-h :layout-max-w :layout-min-w]]
[:div.input-element
{:alt (tr (dm/str "workspace.options.layout-item." (d/name item)))
{:key (d/name item)
:alt (tr (dm/str "workspace.options.layout-item." (d/name item)))
:title (tr (dm/str "workspace.options.layout-item." (d/name item)))
:class (dom/classnames "maxH" (= item :max-h)
"minH" (= item :min-h)
"maxW" (= item :max-w)
"minW" (= item :min-w))
:key item}
:class (dom/classnames "maxH" (= item :layout-max-h)
"minH" (= item :layout-min-h)
"maxW" (= item :layout-max-w)
"minW" (= item :layout-min-w))}
[:> numeric-input
{:no-validate true
:min 0
:data-wrap true
:placeholder "--"
:on-click select-all
:on-click #(dom/select-target %)
:on-change (partial on-size-change item)
:value (item @test-values)}]])])]]))
:value (get values item)}]])])]]))

View file

@ -6,7 +6,7 @@
(ns app.main.ui.workspace.sidebar.options.shapes.bool
(:require
[app.main.constants :refer [has-layout-item]]
[app.main.refs :as refs]
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]]
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]]
@ -25,17 +25,22 @@
stroke-values (select-keys shape stroke-attrs)
layer-values (select-keys shape layer-attrs)
constraint-values (select-keys shape constraint-attrs)
layout-item-values (select-keys shape layout-item-attrs)]
layout-item-values (select-keys shape layout-item-attrs)
is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids))
is-layout-child? (mf/deref is-layout-child-ref)]
[:*
[:& measures-menu {:ids ids
:type type
:values measure-values
:shape shape}]
(when has-layout-item
[:& layout-item-menu {:ids ids
:type type
:values layout-item-values
:shape shape}])
(when is-layout-child?
[:& layout-item-menu
{:ids ids
:type type
:values layout-item-values
:is-layout-child? true
:shape shape}])
[:& constraints-menu {:ids ids
:values constraint-values}]
[:& layer-menu {:ids ids

View file

@ -6,7 +6,7 @@
(ns app.main.ui.workspace.sidebar.options.shapes.circle
(:require
[app.main.constants :refer [has-layout-item]]
[app.main.refs :as refs]
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]]
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]]
@ -22,20 +22,26 @@
[{:keys [shape] :as props}]
(let [ids [(:id shape)]
type (:type shape)
measure-values (select-keys shape measure-attrs)
stroke-values (select-keys shape stroke-attrs)
layer-values (select-keys shape layer-attrs)
constraint-values (select-keys shape constraint-attrs)
layout-item-values (select-keys shape layout-item-attrs)]
layout-item-values (select-keys shape layout-item-attrs)
is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids))
is-layout-child? (mf/deref is-layout-child-ref)]
[:*
[:& measures-menu {:ids ids
:type type
:values measure-values
:shape shape}]
(when has-layout-item
(when is-layout-child?
[:& layout-item-menu {:ids ids
:type type
:values layout-item-values
:is-layout-child? true
:is-layout-container? false
:shape shape}])
[:& constraints-menu {:ids ids
:values constraint-values}]

View file

@ -6,13 +6,14 @@
(ns app.main.ui.workspace.sidebar.options.shapes.frame
(:require
[app.main.constants :refer [has-layout-item]]
[app.main.refs :as refs]
[app.main.ui.features :as features]
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]]
[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.layout :refer [layout-attrs layout-menu]]
[app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-attrs layout-container-menu]]
[app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-attrs layout-item-menu]]
[app.main.ui.workspace.sidebar.options.menus.measures :refer [measure-attrs measures-menu]]
[app.main.ui.workspace.sidebar.options.menus.shadow :refer [shadow-menu]]
@ -23,12 +24,18 @@
[{:keys [shape] :as props}]
(let [ids [(:id shape)]
type (:type shape)
layout-active? (features/use-feature :auto-layout)
stroke-values (select-keys shape stroke-attrs)
layer-values (select-keys shape layer-attrs)
measure-values (select-keys shape measure-attrs)
constraint-values (select-keys shape constraint-attrs)
layout-values (select-keys shape layout-attrs)
layout-item-values (select-keys shape layout-item-attrs)]
layout-container-values (select-keys shape layout-container-attrs)
layout-item-values (select-keys shape layout-item-attrs)
is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids))
is-layout-child? (mf/deref is-layout-child-ref)]
[:*
[:& measures-menu {:ids [(:id shape)]
:values measure-values
@ -36,14 +43,19 @@
:shape shape}]
[:& constraints-menu {:ids ids
:values constraint-values}]
(when has-layout-item
[:& layout-menu {:type type :ids [(:id shape)] :values layout-values}])
(when has-layout-item
[:& layout-item-menu {:ids ids
:type type
:values layout-item-values
:shape shape}])
(when layout-active?
[:*
[:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values}]
(when (or (:layout shape) is-layout-child?)
[:& layout-item-menu
{:ids ids
:type type
:values layout-item-values
:is-layout-child? is-layout-child?
:is-layout-container? (:layout shape)
:shape shape}])])
[:& layer-menu {:ids ids
:type type
:values layer-values}]

View file

@ -7,14 +7,13 @@
(ns app.main.ui.workspace.sidebar.options.shapes.group
(:require
[app.common.data :as d]
[app.main.constants :refer [has-layout-item]]
[app.main.refs :as refs]
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
[app.main.ui.workspace.sidebar.options.menus.color-selection :refer [color-selection-menu]]
[app.main.ui.workspace.sidebar.options.menus.component :refer [component-attrs component-menu]]
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraints-menu]]
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-menu]]
[app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-menu]]
[app.main.ui.workspace.sidebar.options.menus.layout :refer [layout-menu]]
[app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-menu]]
[app.main.ui.workspace.sidebar.options.menus.measures :refer [measures-menu]]
[app.main.ui.workspace.sidebar.options.menus.shadow :refer [shadow-menu]]
@ -34,6 +33,10 @@
objects (->> shape-with-children (group-by :id) (d/mapm (fn [_ v] (first v))))
file-id (unchecked-get props "file-id")
ids [(:id shape)]
is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids))
is-layout-child? (mf/deref is-layout-child-ref)
type :group
[measure-ids measure-values] (get-attrs [shape] objects :measure)
[layer-ids layer-values] (get-attrs [shape] objects :layer)
@ -45,17 +48,20 @@
[text-ids text-values] (get-attrs [shape] objects :text)
[svg-ids svg-values] [[(:id shape)] (select-keys shape [:svg-attrs])]
[comp-ids comp-values] [[(:id shape)] (select-keys shape component-attrs)]
[layout-ids layout-values] (get-attrs [shape] objects :layout)
[layout-item-ids layout-item-values] (get-attrs [shape] objects :layout-item)
]
[layout-item-ids layout-item-values] (get-attrs [shape] objects :layout-item)]
[:div.options
[:& measures-menu {:type type :ids measure-ids :values measure-values :shape shape}]
[:& component-menu {:ids comp-ids :values comp-values :shape-name (:name shape)}]
(when-not (empty? layout-ids)
[:& layout-menu {:type type :ids layout-ids :values layout-values}])
(when has-layout-item
[:& layout-item-menu {:type type :ids layout-item-ids :values layout-item-values}])
(when is-layout-child?
[:& layout-item-menu
{:type type
:ids layout-item-ids
:is-layout-child? true
:is-layout-container? false
:values layout-item-values}])
[:& constraints-menu {:ids constraint-ids :values constraint-values}]
[:& layer-menu {:type type :ids layer-ids :values layer-values}]

View file

@ -6,7 +6,7 @@
(ns app.main.ui.workspace.sidebar.options.shapes.image
(:require
[app.main.constants :refer [has-layout-item]]
[app.main.refs :as refs]
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]]
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]]
@ -21,22 +21,28 @@
[{:keys [shape] :as props}]
(let [ids [(:id shape)]
type (:type shape)
measure-values (select-keys shape measure-attrs)
layer-values (select-keys shape layer-attrs)
constraint-values (select-keys shape constraint-attrs)
fill-values (select-keys shape fill-attrs)
stroke-values (select-keys shape stroke-attrs)
layout-item-values (select-keys shape layout-item-attrs)]
layout-item-values (select-keys shape layout-item-attrs)
is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids))
is-layout-child? (mf/deref is-layout-child-ref)]
[:*
[:& measures-menu {:ids ids
:type type
:values measure-values
:shape shape}]
(when has-layout-item
[:& layout-item-menu {:ids ids
:type type
:values layout-item-values
:shape shape}])
(when is-layout-child?
[:& layout-item-menu
{:ids ids
:type type
:values layout-item-values
:is-layout-child? true
:shape shape}])
[:& constraints-menu {:ids ids
:values constraint-values}]

View file

@ -11,7 +11,7 @@
[app.common.geom.shapes :as gsh]
[app.common.pages.common :as cpc]
[app.common.text :as txt]
[app.main.constants :refer [has-layout-item]]
[app.main.refs :as refs]
[app.main.ui.hooks :as hooks]
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-attrs blur-menu]]
[app.main.ui.workspace.sidebar.options.menus.color-selection :refer [color-selection-menu]]
@ -19,7 +19,7 @@
[app.main.ui.workspace.sidebar.options.menus.exports :refer [exports-attrs exports-menu]]
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]]
[app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]]
[app.main.ui.workspace.sidebar.options.menus.layout :refer [layout-attrs layout-menu]]
[app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-attrs layout-container-menu]]
[app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-attrs layout-item-menu]]
[app.main.ui.workspace.sidebar.options.menus.measures :refer [measure-attrs measures-menu]]
[app.main.ui.workspace.sidebar.options.menus.shadow :refer [shadow-attrs shadow-menu]]
@ -142,7 +142,7 @@
:stroke stroke-attrs
:text ot/attrs
:exports exports-attrs
:layout layout-attrs
:layout layout-container-attrs
:layout-item layout-item-attrs})
(def shadow-keys [:style :color :offset-x :offset-y :blur :spread])
@ -247,6 +247,10 @@
type :multiple
all-types (into #{} (map :type shapes))
ids (->> shapes (map :id))
is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids))
is-layout-child? (mf/deref is-layout-child-ref)
has-text? (contains? all-types :text)
[measure-ids measure-values] (get-attrs shapes objects :measure)
@ -260,7 +264,7 @@
stroke-ids stroke-values
text-ids text-values
exports-ids exports-values
layout-ids layout-values
layout-ids layout-container-values
layout-item-ids layout-item-values]
(mf/use-memo
(mf/deps objects-no-measures)
@ -284,11 +288,16 @@
(when-not (empty? measure-ids)
[:& measures-menu {:type type :all-types all-types :ids measure-ids :values measure-values :shape shapes}])
(when-not (empty? layout-ids)
[:& layout-menu {:type type :ids layout-ids :values layout-values}])
(when (:layout layout-container-values)
[:& layout-container-menu {:type type :ids layout-ids :values layout-container-values}])
(when has-layout-item
[:& layout-item-menu {:type type :ids layout-item-ids :values layout-item-values}])
(when is-layout-child?
[:& layout-item-menu
{:type type
:ids layout-item-ids
:is-layout-child? true
:is-layout-container? true
:values layout-item-values}])
(when-not (empty? constraint-ids)
[:& constraints-menu {:ids constraint-ids :values constraint-values}])

View file

@ -6,7 +6,7 @@
(ns app.main.ui.workspace.sidebar.options.shapes.path
(:require
[app.main.constants :refer [has-layout-item]]
[app.main.refs :as refs]
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]]
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]]
@ -22,20 +22,26 @@
[{:keys [shape] :as props}]
(let [ids [(:id shape)]
type (:type shape)
measure-values (select-keys shape measure-attrs)
stroke-values (select-keys shape stroke-attrs)
layer-values (select-keys shape layer-attrs)
constraint-values (select-keys shape constraint-attrs)
layout-item-values (select-keys shape layout-item-attrs)]
layout-item-values (select-keys shape layout-item-attrs)
is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids))
is-layout-child? (mf/deref is-layout-child-ref)]
[:*
[:& measures-menu {:ids ids
:type type
:values measure-values
:shape shape}]
(when has-layout-item
(when is-layout-child?
[:& layout-item-menu {:ids ids
:type type
:values layout-item-values
:is-layout-child? true
:is-layout-container? false
:shape shape}])
[:& constraints-menu {:ids ids
:values constraint-values}]

View file

@ -6,7 +6,7 @@
(ns app.main.ui.workspace.sidebar.options.shapes.rect
(:require
[app.main.constants :refer [has-layout-item]]
[app.main.refs :as refs]
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]]
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]]
@ -23,22 +23,28 @@
[{:keys [shape] :as props}]
(let [ids [(:id shape)]
type (:type shape)
measure-values (select-keys shape measure-attrs)
layer-values (select-keys shape layer-attrs)
constraint-values (select-keys shape constraint-attrs)
fill-values (select-keys shape fill-attrs)
stroke-values (select-keys shape stroke-attrs)
layout-item-values (select-keys shape layout-item-attrs)]
layout-item-values (select-keys shape layout-item-attrs)
is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids))
is-layout-child? (mf/deref is-layout-child-ref)]
[:*
[:& measures-menu {:ids ids
:type type
:values measure-values
:shape shape}]
(when has-layout-item
[:& layout-item-menu {:ids ids
:type type
:values layout-item-values
:shape shape}])
(when is-layout-child?
[:& layout-item-menu
{:ids ids
:type type
:values layout-item-values
:is-layout-child? true
:shape shape}])
[:& constraints-menu {:ids ids
:values constraint-values}]

View file

@ -8,7 +8,7 @@
(:require
[app.common.colors :as clr]
[app.common.data :as d]
[app.main.constants :refer [has-layout-item]]
[app.main.refs :as refs]
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]]
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]]
@ -95,12 +95,16 @@
(let [ids [(:id shape)]
type (:type shape)
{:keys [tag] :as content} (:content shape)
measure-values (select-keys shape measure-attrs)
constraint-values (select-keys shape constraint-attrs)
fill-values (get-fill-values shape)
stroke-values (get-stroke-values shape)
layout-item-values (select-keys shape layout-item-attrs)]
layout-item-values (select-keys shape layout-item-attrs)
is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids))
is-layout-child? (mf/deref is-layout-child-ref)]
(when (contains? svg-elements tag)
[:*
@ -108,11 +112,13 @@
:type type
:values measure-values
:shape shape}]
(when has-layout-item
[:& layout-item-menu {:ids ids
:type type
:values layout-item-values
:shape shape}])
(when is-layout-child?
[:& layout-item-menu
{:ids ids
:type type
:values layout-item-values
:is-layout-child? true
:shape shape}])
[:& constraints-menu {:ids ids
:values constraint-values}]

View file

@ -7,7 +7,6 @@
(ns app.main.ui.workspace.sidebar.options.shapes.text
(:require
[app.common.data :as d]
[app.main.constants :refer [has-layout-item]]
[app.main.data.workspace.texts :as dwt]
[app.main.refs :as refs]
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
@ -27,6 +26,8 @@
(let [ids [(:id shape)]
type (:type shape)
is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids))
is-layout-child? (mf/deref is-layout-child-ref)
state-map (mf/deref refs/workspace-editor-state)
shared-libs (mf/deref refs/workspace-libraries)
@ -69,11 +70,14 @@
:type type
:values (select-keys shape measure-attrs)
:shape shape}]
(when has-layout-item
[:& layout-item-menu {:ids ids
:type type
:values layout-item-values
:shape shape}])
(when is-layout-child?
[:& layout-item-menu
{:ids ids
:type type
:values layout-item-values
:is-layout-child? true
:shape shape}])
[:& constraints-menu
{:ids ids
:values (select-keys shape constraint-attrs)}]

View file

@ -106,9 +106,7 @@
"Extract the target from event instance and select it"
[^js event]
(when (some? event)
(-> event
(.-target)
(.-select))))
(-> event (.-target) (.select))))
(defn select-node
"Select element by node"

View file

@ -353,3 +353,10 @@
(let [root-node (dom/query ".viewport .render-shapes")
num-nodes (->> (dom/seq-nodes root-node) count)]
#js {:number num-nodes}))
#_(defn modif->js
[modif-tree objects]
(clj->js (into {}
(map (fn [[k v]]
[(get-in objects [k :name]) v]))
modif-tree)))

View file

@ -3503,19 +3503,19 @@ msgid "workspace.options.layout-item.advanced-ops"
msgstr "Advanced options"
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
msgid "workspace.options.layout-item.max-h"
msgid "workspace.options.layout-item.layout-max-h"
msgstr "Max.Height"
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
msgid "workspace.options.layout-item.max-w"
msgid "workspace.options.layout-item.layout-max-w"
msgstr "Max.Width"
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
msgid "workspace.options.layout-item.min-h"
msgid "workspace.options.layout-item.layout-min-h"
msgstr "Min.Height"
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
msgid "workspace.options.layout-item.min-w"
msgid "workspace.options.layout-item.layout-min-w"
msgstr "Min.Width"
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
@ -3523,19 +3523,19 @@ msgid "workspace.options.layout-item.title"
msgstr "Element resizing"
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
msgid "workspace.options.layout-item.title.max-h"
msgid "workspace.options.layout-item.title.layout-max-h"
msgstr "Maximum height"
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
msgid "workspace.options.layout-item.title.max-w"
msgid "workspace.options.layout-item.title.layout-max-w"
msgstr "Maximum width"
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
msgid "workspace.options.layout-item.title.min-h"
msgid "workspace.options.layout-item.title.layout-min-h"
msgstr "Minimum height"
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
msgid "workspace.options.layout-item.title.min-w"
msgid "workspace.options.layout-item.title.layout-min-w"
msgstr "Minimum width"
#: src/app/main/ui/workspace/sidebar/options/menus/layout.cljs
@ -4368,4 +4368,4 @@ msgid "workspace.updates.update"
msgstr "Update"
msgid "workspace.viewport.click-to-close-path"
msgstr "Click to close the path"
msgstr "Click to close the path"

View file

@ -3661,19 +3661,19 @@ msgid "workspace.options.layout-item.advanced-ops"
msgstr "Opciones avanzadas"
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
msgid "workspace.options.layout-item.max-h"
msgid "workspace.options.layout-item.layout-max-h"
msgstr "AlturaMax."
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
msgid "workspace.options.layout-item.max-w"
msgid "workspace.options.layout-item.layout-max-w"
msgstr "AnchoMax."
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
msgid "workspace.options.layout-item.min-h"
msgid "workspace.options.layout-item.layout-min-h"
msgstr "AlturaMin."
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
msgid "workspace.options.layout-item.min-w"
msgid "workspace.options.layout-item.layout-min-w"
msgstr "AnchoMin."
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
@ -3681,19 +3681,19 @@ msgid "workspace.options.layout-item.title"
msgstr "Redimensionado de elemento"
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
msgid "workspace.options.layout-item.title.max-h"
msgid "workspace.options.layout-item.title.layout-max-h"
msgstr "Altura máxima"
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
msgid "workspace.options.layout-item.title.max-w"
msgid "workspace.options.layout-item.title.layout-max-w"
msgstr "Ancho máximo"
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
msgid "workspace.options.layout-item.title.min-h"
msgid "workspace.options.layout-item.title.layout-min-h"
msgstr "Altura mínima"
#: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs
msgid "workspace.options.layout-item.title.min-w"
msgid "workspace.options.layout-item.title.layout-min-w"
msgstr "Ancho mínimo"
#: src/app/main/ui/workspace/sidebar/options/menus/layout.cljs
@ -4547,4 +4547,4 @@ msgid "workspace.updates.update"
msgstr "Actualizar"
msgid "workspace.viewport.click-to-close-path"
msgstr "Pulsar para cerrar la ruta"
msgstr "Pulsar para cerrar la ruta"