mirror of
https://github.com/penpot/penpot.git
synced 2025-06-11 21:11:38 +02:00
Merge pull request #2659 from penpot/alotor-flex-layout-features
Flex layout features
This commit is contained in:
commit
507800ae4e
10 changed files with 195 additions and 58 deletions
|
@ -8,7 +8,6 @@
|
||||||
(:require
|
(:require
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.geom.shapes.points :as gpo]
|
[app.common.geom.shapes.points :as gpo]
|
||||||
[app.common.math :as mth]
|
|
||||||
[app.common.types.shape.layout :as ctl]))
|
[app.common.types.shape.layout :as ctl]))
|
||||||
|
|
||||||
(defn child-layout-bound-points
|
(defn child-layout-bound-points
|
||||||
|
@ -55,18 +54,13 @@
|
||||||
(gpt/add (hv (/ width 2)))
|
(gpt/add (hv (/ width 2)))
|
||||||
|
|
||||||
(and col? h-end?)
|
(and col? h-end?)
|
||||||
(gpt/add (hv width)))]
|
(gpt/add (hv width)))
|
||||||
|
|
||||||
(cond-> [base-p]
|
;; We need some height/width to calculate the bounds. We stablish the minimum
|
||||||
(and (mth/almost-zero? min-width) (mth/almost-zero? min-height))
|
min-width (max min-width 0.01)
|
||||||
(conj (cond-> base-p
|
min-height (max min-height 0.01)]
|
||||||
row?
|
|
||||||
(gpt/add (hv width))
|
|
||||||
|
|
||||||
col?
|
(-> [base-p]
|
||||||
(gpt/add (vv height))))
|
|
||||||
|
|
||||||
(not (mth/almost-zero? min-width))
|
|
||||||
(conj (cond-> base-p
|
(conj (cond-> base-p
|
||||||
(or row? h-start?)
|
(or row? h-start?)
|
||||||
(gpt/add (hv min-width))
|
(gpt/add (hv min-width))
|
||||||
|
@ -77,7 +71,6 @@
|
||||||
(and col? h-center?)
|
(and col? h-center?)
|
||||||
(gpt/subtract (hv min-width))))
|
(gpt/subtract (hv min-width))))
|
||||||
|
|
||||||
(not (mth/almost-zero? min-height))
|
|
||||||
(conj (cond-> base-p
|
(conj (cond-> base-p
|
||||||
(or col? v-start?)
|
(or col? v-start?)
|
||||||
(gpt/add (vv min-height))
|
(gpt/add (vv min-height))
|
||||||
|
@ -107,8 +100,11 @@
|
||||||
child-bounds
|
child-bounds
|
||||||
(if (or (ctl/fill-height? child) (ctl/fill-height? child))
|
(if (or (ctl/fill-height? child) (ctl/fill-height? child))
|
||||||
(child-layout-bound-points parent child parent-bounds child-bounds)
|
(child-layout-bound-points parent child parent-bounds child-bounds)
|
||||||
child-bounds)]
|
child-bounds)
|
||||||
(gpo/parent-coords-bounds child-bounds parent-bounds)))]
|
|
||||||
|
[margin-top margin-right margin-bottom margin-left] (ctl/child-margins child)]
|
||||||
|
(-> (gpo/parent-coords-bounds child-bounds parent-bounds)
|
||||||
|
(gpo/pad-points (- margin-top) (- margin-right) (- margin-bottom) (- margin-left)))))]
|
||||||
|
|
||||||
(as-> children $
|
(as-> children $
|
||||||
(map child-bounds $)
|
(map child-bounds $)
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
ratio-width (/ target-width curr-width)
|
ratio-width (/ target-width curr-width)
|
||||||
ratio-height (/ target-height curr-height)
|
ratio-height (/ target-height curr-height)
|
||||||
scalev (gpt/point ratio-width ratio-height)]
|
scalev (gpt/point ratio-width ratio-height)]
|
||||||
|
|
||||||
(-> modifiers
|
(-> modifiers
|
||||||
(ctm/resize scalev origin transform transform-inverse))))
|
(ctm/resize scalev origin transform transform-inverse))))
|
||||||
|
|
||||||
|
@ -41,7 +42,6 @@
|
||||||
corner (gpt/point bounds)
|
corner (gpt/point bounds)
|
||||||
target-corner (gpt/round corner)
|
target-corner (gpt/round corner)
|
||||||
deltav (gpt/to-vec corner target-corner)]
|
deltav (gpt/to-vec corner target-corner)]
|
||||||
|
|
||||||
(ctm/move modifiers deltav)))
|
(ctm/move modifiers deltav)))
|
||||||
|
|
||||||
(defn set-pixel-precision
|
(defn set-pixel-precision
|
||||||
|
@ -56,8 +56,10 @@
|
||||||
has-resize? (size-pixel-precision shape points))
|
has-resize? (size-pixel-precision shape points))
|
||||||
|
|
||||||
points
|
points
|
||||||
(cond-> (:points shape)
|
(if has-resize?
|
||||||
has-resize? (gco/transform-points (ctm/modifiers->transform modifiers)))]
|
(-> (:points shape)
|
||||||
|
(gco/transform-points (ctm/modifiers->transform modifiers)) )
|
||||||
|
points)]
|
||||||
[modifiers points])]
|
[modifiers points])]
|
||||||
(position-pixel-precision modifiers shape points)))
|
(position-pixel-precision modifiers shape points)))
|
||||||
|
|
||||||
|
|
|
@ -76,7 +76,6 @@
|
||||||
:layout :layout-container
|
:layout :layout-container
|
||||||
:layout-dir :layout-container
|
:layout-dir :layout-container
|
||||||
:layout-gap :layout-container
|
:layout-gap :layout-container
|
||||||
:layout-type :layout-container
|
|
||||||
:layout-wrap-type :layout-container
|
:layout-wrap-type :layout-container
|
||||||
:layout-padding-type :layout-container
|
:layout-padding-type :layout-container
|
||||||
:layout-padding :layout-container
|
:layout-padding :layout-container
|
||||||
|
|
|
@ -50,7 +50,6 @@
|
||||||
|
|
||||||
(s/def ::row-gap ::us/safe-number)
|
(s/def ::row-gap ::us/safe-number)
|
||||||
(s/def ::column-gap ::us/safe-number)
|
(s/def ::column-gap ::us/safe-number)
|
||||||
(s/def ::layout-type #{:flex :grid})
|
|
||||||
|
|
||||||
(s/def ::layout-gap
|
(s/def ::layout-gap
|
||||||
(s/keys :opt-un [::row-gap ::column-gap]))
|
(s/keys :opt-un [::row-gap ::column-gap]))
|
||||||
|
@ -60,7 +59,6 @@
|
||||||
::layout-flex-dir
|
::layout-flex-dir
|
||||||
::layout-gap
|
::layout-gap
|
||||||
::layout-gap-type
|
::layout-gap-type
|
||||||
::layout-type
|
|
||||||
::layout-wrap-type
|
::layout-wrap-type
|
||||||
::layout-padding-type
|
::layout-padding-type
|
||||||
::layout-padding
|
::layout-padding
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
[app.main.data.workspace.undo :as dwu]
|
[app.main.data.workspace.undo :as dwu]
|
||||||
[app.main.snap :as snap]
|
[app.main.snap :as snap]
|
||||||
[app.main.streams :as ms]
|
[app.main.streams :as ms]
|
||||||
|
[app.util.dom :as dom]
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
[cljs.spec.alpha :as s]
|
[cljs.spec.alpha :as s]
|
||||||
[potok.core :as ptk]))
|
[potok.core :as ptk]))
|
||||||
|
@ -415,6 +416,14 @@
|
||||||
(rx/take 1)
|
(rx/take 1)
|
||||||
(rx/map #(start-move from-position))))))
|
(rx/map #(start-move from-position))))))
|
||||||
|
|
||||||
|
(defn set-ghost-displacement
|
||||||
|
[move-vector]
|
||||||
|
(ptk/reify ::set-ghost-displacement
|
||||||
|
ptk/EffectEvent
|
||||||
|
(effect [_ _ _]
|
||||||
|
(when-let [node (dom/get-element-by-class "ghost-outline")]
|
||||||
|
(dom/set-property! node "transform" (gmt/translate-matrix move-vector))))))
|
||||||
|
|
||||||
(defn- start-move
|
(defn- start-move
|
||||||
([from-position] (start-move from-position nil))
|
([from-position] (start-move from-position nil))
|
||||||
([from-position ids]
|
([from-position ids]
|
||||||
|
@ -501,6 +510,9 @@
|
||||||
(dwm/build-change-frame-modifiers objects selected target-frame drop-index)
|
(dwm/build-change-frame-modifiers objects selected target-frame drop-index)
|
||||||
(dwm/set-modifiers)))))
|
(dwm/set-modifiers)))))
|
||||||
|
|
||||||
|
(->> move-stream
|
||||||
|
(rx/map (comp set-ghost-displacement first)))
|
||||||
|
|
||||||
;; Last event will write the modifiers creating the changes
|
;; Last event will write the modifiers creating the changes
|
||||||
(->> move-stream
|
(->> move-stream
|
||||||
(rx/last)
|
(rx/last)
|
||||||
|
|
|
@ -332,6 +332,72 @@
|
||||||
:penpot:preserve-scroll ((d/nilf str) (:preserve-scroll interaction))}])])))
|
:penpot:preserve-scroll ((d/nilf str) (:preserve-scroll interaction))}])])))
|
||||||
|
|
||||||
|
|
||||||
|
(defn- export-layout-container-data
|
||||||
|
[{:keys [layout
|
||||||
|
layout-flex-dir
|
||||||
|
layout-gap
|
||||||
|
layout-gap-type
|
||||||
|
layout-wrap-type
|
||||||
|
layout-padding-type
|
||||||
|
layout-padding
|
||||||
|
layout-justify-content
|
||||||
|
layout-align-items
|
||||||
|
layout-align-content]}]
|
||||||
|
|
||||||
|
(when layout
|
||||||
|
(mf/html
|
||||||
|
[:> "penpot:layout"
|
||||||
|
#js {:penpot:layout (d/name layout)
|
||||||
|
:penpot:layout-flex-dir (d/name layout-flex-dir)
|
||||||
|
:penpot:layout-gap-type (d/name layout-gap-type)
|
||||||
|
:penpot:layout-gap-row (:row-gap layout-gap)
|
||||||
|
:penpot:layout-gap-column (:column-gap layout-gap)
|
||||||
|
:penpot:layout-wrap-type (d/name layout-wrap-type)
|
||||||
|
:penpot:layout-padding-type (d/name layout-padding-type)
|
||||||
|
:penpot:layout-padding-p1 (:p1 layout-padding)
|
||||||
|
:penpot:layout-padding-p2 (:p2 layout-padding)
|
||||||
|
:penpot:layout-padding-p3 (:p3 layout-padding)
|
||||||
|
:penpot:layout-padding-p4 (:p4 layout-padding)
|
||||||
|
:penpot:layout-justify-content (d/name layout-justify-content)
|
||||||
|
:penpot:layout-align-items (d/name layout-align-items)
|
||||||
|
:penpot:layout-align-content (d/name layout-align-content)}])))
|
||||||
|
|
||||||
|
(defn- export-layout-item-data
|
||||||
|
[{:keys [layout-item-margin
|
||||||
|
layout-item-margin-type
|
||||||
|
layout-item-h-sizing
|
||||||
|
layout-item-v-sizing
|
||||||
|
layout-item-max-h
|
||||||
|
layout-item-min-h
|
||||||
|
layout-item-max-w
|
||||||
|
layout-item-min-w
|
||||||
|
layout-item-align-self]}]
|
||||||
|
|
||||||
|
(when (or layout-item-margin
|
||||||
|
layout-item-margin-type
|
||||||
|
layout-item-h-sizing
|
||||||
|
layout-item-v-sizing
|
||||||
|
layout-item-max-h
|
||||||
|
layout-item-min-h
|
||||||
|
layout-item-max-w
|
||||||
|
layout-item-min-w
|
||||||
|
layout-item-align-self)
|
||||||
|
(mf/html
|
||||||
|
[:> "penpot:layout-item"
|
||||||
|
#js {:penpot:layout-item-margin-m1 (:m1 layout-item-margin)
|
||||||
|
:penpot:layout-item-margin-m2 (:m2 layout-item-margin)
|
||||||
|
:penpot:layout-item-margin-m3 (:m3 layout-item-margin)
|
||||||
|
:penpot:layout-item-margin-m4 (:m4 layout-item-margin)
|
||||||
|
:penpot:layout-item-margin-type (d/name layout-item-margin-type)
|
||||||
|
:penpot:layout-item-h-sizing (d/name layout-item-h-sizing)
|
||||||
|
:penpot:layout-item-v-sizing (d/name layout-item-v-sizing)
|
||||||
|
:penpot:layout-item-max-h layout-item-max-h
|
||||||
|
:penpot:layout-item-min-h layout-item-min-h
|
||||||
|
:penpot:layout-item-max-w layout-item-max-w
|
||||||
|
:penpot:layout-item-min-w layout-item-min-w
|
||||||
|
:penpot:layout-item-align-self (d/name layout-item-align-self)}])))
|
||||||
|
|
||||||
|
|
||||||
(mf/defc export-data
|
(mf/defc export-data
|
||||||
[{:keys [shape]}]
|
[{:keys [shape]}]
|
||||||
(let [props (-> (obj/create) (add-data shape) (add-library-refs shape))]
|
(let [props (-> (obj/create) (add-data shape) (add-library-refs shape))]
|
||||||
|
@ -343,5 +409,7 @@
|
||||||
(export-interactions-data shape)
|
(export-interactions-data shape)
|
||||||
(export-fills-data shape)
|
(export-fills-data shape)
|
||||||
(export-strokes-data shape)
|
(export-strokes-data shape)
|
||||||
(export-grid-data shape)]))
|
(export-grid-data shape)
|
||||||
|
(export-layout-container-data shape)
|
||||||
|
(export-layout-item-data shape)]))
|
||||||
|
|
||||||
|
|
|
@ -382,7 +382,6 @@
|
||||||
|
|
||||||
on-padding-change
|
on-padding-change
|
||||||
(fn [type prop val]
|
(fn [type prop val]
|
||||||
(prn "??" type prop val)
|
|
||||||
(cond
|
(cond
|
||||||
(and (= type :simple) (= prop :p1))
|
(and (= type :simple) (= prop :p1))
|
||||||
(st/emit! (dwsl/update-layout ids {:layout-padding {:p1 val :p3 val}}))
|
(st/emit! (dwsl/update-layout ids {:layout-padding {:p1 val :p3 val}}))
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
[app.common.pages.helpers :as cph]
|
[app.common.pages.helpers :as cph]
|
||||||
|
[app.common.types.shape.layout :as ctl]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.ui.context :as ctx]
|
[app.main.ui.context :as ctx]
|
||||||
[app.main.ui.hooks :as ui-hooks]
|
[app.main.ui.hooks :as ui-hooks]
|
||||||
|
@ -293,14 +294,25 @@
|
||||||
:modifiers modifiers}])
|
:modifiers modifiers}])
|
||||||
|
|
||||||
(when show-frame-outline?
|
(when show-frame-outline?
|
||||||
[:& outline/shape-outlines
|
(let [outlined-frame-id
|
||||||
{:objects objects-modified
|
(->> @hover-ids
|
||||||
:hover #{(->> @hover-ids
|
|
||||||
(filter #(cph/frame-shape? (get base-objects %)))
|
(filter #(cph/frame-shape? (get base-objects %)))
|
||||||
(remove selected)
|
(remove selected)
|
||||||
(first))}
|
(first))
|
||||||
|
outlined-frame (get objects outlined-frame-id)]
|
||||||
|
[:*
|
||||||
|
[:& outline/shape-outlines
|
||||||
|
{:objects objects-modified
|
||||||
|
:hover #{outlined-frame-id}
|
||||||
:zoom zoom
|
:zoom zoom
|
||||||
:modifiers modifiers}])
|
:modifiers modifiers}]
|
||||||
|
|
||||||
|
(when (ctl/layout? outlined-frame)
|
||||||
|
[:g.ghost-outline
|
||||||
|
[:& outline/shape-outlines
|
||||||
|
{:objects base-objects
|
||||||
|
:selected selected
|
||||||
|
:zoom zoom}]])]))
|
||||||
|
|
||||||
(when show-outlines?
|
(when show-outlines?
|
||||||
[:& outline/shape-outlines
|
[:& outline/shape-outlines
|
||||||
|
|
|
@ -75,8 +75,8 @@
|
||||||
points [start-p
|
points [start-p
|
||||||
(-> start-p (gpt/add (xv line-width)))
|
(-> start-p (gpt/add (xv line-width)))
|
||||||
(-> start-p (gpt/add (xv line-width)) (gpt/add (yv line-height)))
|
(-> start-p (gpt/add (xv line-width)) (gpt/add (yv line-height)))
|
||||||
(-> start-p (gpt/add (yv line-height)))
|
(-> start-p (gpt/add (yv line-height)))]]
|
||||||
]]
|
|
||||||
[:g.layout-line {:key (dm/str "line-" idx)}
|
[:g.layout-line {:key (dm/str "line-" idx)}
|
||||||
[:polygon {:points (->> points (map #(dm/fmt "%, %" (:x %) (:y %))) (str/join " "))
|
[:polygon {:points (->> points (map #(dm/fmt "%, %" (:x %) (:y %))) (str/join " "))
|
||||||
:style {:stroke "red" :stroke-width (/ 2 zoom) :stroke-dasharray (dm/str (/ 10 zoom) " " (/ 5 zoom))}}]]))]))))
|
:style {:stroke "red" :stroke-width (/ 2 zoom) :stroke-dasharray (dm/str (/ 10 zoom) " " (/ 5 zoom))}}]]))]))))
|
||||||
|
|
|
@ -869,6 +869,54 @@
|
||||||
:style
|
:style
|
||||||
parse-style))))
|
parse-style))))
|
||||||
|
|
||||||
|
(defn add-layout-container-data [props node]
|
||||||
|
(if-let [data (get-data node :penpot:layout)]
|
||||||
|
(merge props
|
||||||
|
(d/without-nils
|
||||||
|
{:layout (get-meta data :layout keyword)
|
||||||
|
:layout-flex-dir (get-meta data :layout-flex-dir keyword)
|
||||||
|
:layout-wrap-type (get-meta data :layout-wrap-type keyword)
|
||||||
|
|
||||||
|
:layout-gap-type (get-meta data :layout-gap-type keyword)
|
||||||
|
:layout-gap
|
||||||
|
(d/without-nils
|
||||||
|
{:row-gap (get-meta data :layout-gap-row d/parse-double)
|
||||||
|
:column-gap (get-meta data :layout-gap-column d/parse-double)})
|
||||||
|
|
||||||
|
:layout-padding-type (get-meta data :layout-padding-type keyword)
|
||||||
|
:layout-padding
|
||||||
|
(d/without-nils
|
||||||
|
{:p1 (get-meta data :layout-padding-p1 d/parse-double)
|
||||||
|
:p2 (get-meta data :layout-padding-p2 d/parse-double)
|
||||||
|
:p3 (get-meta data :layout-padding-p3 d/parse-double)
|
||||||
|
:p4 (get-meta data :layout-padding-p4 d/parse-double)})
|
||||||
|
|
||||||
|
:layout-justify-content (get-meta data :layout-justify-content keyword)
|
||||||
|
:layout-align-items (get-meta data :layout-align-items keyword)
|
||||||
|
:layout-align-content (get-meta data :layout-align-content keyword)}))
|
||||||
|
props))
|
||||||
|
|
||||||
|
(defn add-layout-item-data [props node]
|
||||||
|
(if-let [data (get-data node :penpot:layout-item)]
|
||||||
|
(merge props
|
||||||
|
(d/without-nils
|
||||||
|
{:layout-item-margin
|
||||||
|
(d/without-nils
|
||||||
|
{:m1 (get-meta data :layout-item-margin-m1 d/parse-double)
|
||||||
|
:m2 (get-meta data :layout-item-margin-m2 d/parse-double)
|
||||||
|
:m3 (get-meta data :layout-item-margin-m3 d/parse-double)
|
||||||
|
:m4 (get-meta data :layout-item-margin-m4 d/parse-double)})
|
||||||
|
|
||||||
|
:layout-item-margin-type (get-meta data :layout-item-margin-type keyword)
|
||||||
|
:layout-item-h-sizing (get-meta data :layout-item-h-sizing keyword)
|
||||||
|
:layout-item-v-sizing (get-meta data :layout-item-v-sizing keyword)
|
||||||
|
:layout-item-max-h (get-meta data :layout-item-max-h d/parse-double)
|
||||||
|
:layout-item-min-h (get-meta data :layout-item-min-h d/parse-double)
|
||||||
|
:layout-item-max-w (get-meta data :layout-item-max-w d/parse-double)
|
||||||
|
:layout-item-min-w (get-meta data :layout-item-min-w d/parse-double)
|
||||||
|
:layout-item-align-self (get-meta data :layout-item-align-self keyword)}))
|
||||||
|
props))
|
||||||
|
|
||||||
(defn parse-data
|
(defn parse-data
|
||||||
[type node]
|
[type node]
|
||||||
|
|
||||||
|
@ -894,7 +942,10 @@
|
||||||
(add-svg-content node))
|
(add-svg-content node))
|
||||||
|
|
||||||
(cond-> (= :frame type)
|
(cond-> (= :frame type)
|
||||||
(add-frame-data node))
|
(-> (add-frame-data node)
|
||||||
|
(add-layout-container-data node)))
|
||||||
|
|
||||||
|
(add-layout-item-data node)
|
||||||
|
|
||||||
(cond-> (= :group type)
|
(cond-> (= :group type)
|
||||||
(add-group-data node))
|
(add-group-data node))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue