From ece11c5958e26c0e21d63eecccab8aafbf713773 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Wed, 17 Jan 2024 12:25:17 +0100 Subject: [PATCH 1/9] :sparkles: Adds debug for shapes drawing --- frontend/src/app/main.cljs | 22 ++--- .../app/main/ui/workspace/shapes/bool.cljs | 5 +- .../app/main/ui/workspace/shapes/common.cljs | 5 +- .../app/main/ui/workspace/shapes/debug.cljs | 99 +++++++++++++++++++ .../app/main/ui/workspace/shapes/frame.cljs | 6 +- .../app/main/ui/workspace/shapes/group.cljs | 5 +- .../app/main/ui/workspace/shapes/path.cljs | 5 +- .../app/main/ui/workspace/shapes/svg_raw.cljs | 5 +- .../app/main/ui/workspace/shapes/text.cljs | 56 +---------- .../app/main/ui/workspace/sidebar/debug.cljs | 2 +- frontend/src/app/util/color.cljs | 9 ++ 11 files changed, 148 insertions(+), 71 deletions(-) create mode 100644 frontend/src/app/main/ui/workspace/shapes/debug.cljs diff --git a/frontend/src/app/main.cljs b/frontend/src/app/main.cljs index 7ed4bd059..868d4b9ea 100644 --- a/frontend/src/app/main.cljs +++ b/frontend/src/app/main.cljs @@ -117,17 +117,17 @@ (st/emit! (initialize))) (defn ^:export reinit - [] - ;; NOTE: in cases of some strange behavior after hot-reload, - ;; uncomment this lines; they make a hard-rerender instead - ;; soft-rerender. - ;; - ;; (mf/unmount! app-root) - ;; (mf/unmount! modal-root) - ;; (set! app-root (mf/create-root (dom/get-element "app"))) - ;; (set! modal-root (mf/create-root (dom/get-element "modal"))) - (st/emit! (ev/initialize)) - (init-ui)) + ([] + (reinit false)) + ([hard?] + ;; The hard flag will force to unmount the whole UI and will redraw every component + (when hard? + (mf/unmount! app-root) + (mf/unmount! modal-root) + (set! app-root (mf/create-root (dom/get-element "app"))) + (set! modal-root (mf/create-root (dom/get-element "modal")))) + (st/emit! (ev/initialize)) + (init-ui))) (defn ^:dev/after-load after-load [] diff --git a/frontend/src/app/main/ui/workspace/shapes/bool.cljs b/frontend/src/app/main/ui/workspace/shapes/bool.cljs index 6a07eeed8..a3080b704 100644 --- a/frontend/src/app/main/ui/workspace/shapes/bool.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/bool.cljs @@ -11,6 +11,7 @@ [app.main.ui.shapes.bool :as bool] [app.main.ui.shapes.shape :refer [shape-container]] [app.main.ui.workspace.shapes.common :refer [check-shape-props]] + [app.main.ui.workspace.shapes.debug :as wsd] [rumext.v2 :as mf])) (defn bool-wrapper-factory @@ -38,5 +39,7 @@ [:> shape-container {:shape shape} [:& bool-shape {:shape shape - :childs childs}]])))) + :childs childs}] + (when *assert* + [:& wsd/shape-debug {:shape shape}])])))) diff --git a/frontend/src/app/main/ui/workspace/shapes/common.cljs b/frontend/src/app/main/ui/workspace/shapes/common.cljs index c407b8db8..4af042e87 100644 --- a/frontend/src/app/main/ui/workspace/shapes/common.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/common.cljs @@ -8,6 +8,7 @@ (:require [app.common.record :as cr] [app.main.ui.shapes.shape :refer [shape-container]] + [app.main.ui.workspace.shapes.debug :as wsd] [rumext.v2 :as mf])) (def ^:private excluded-attrs @@ -34,4 +35,6 @@ [props] (let [shape (unchecked-get props "shape")] [:> shape-container {:shape shape} - [:& component {:shape shape}]]))) + [:& component {:shape shape}] + (when *assert* + [:& wsd/shape-debug {:shape shape}])]))) diff --git a/frontend/src/app/main/ui/workspace/shapes/debug.cljs b/frontend/src/app/main/ui/workspace/shapes/debug.cljs new file mode 100644 index 000000000..71cec7d26 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/shapes/debug.cljs @@ -0,0 +1,99 @@ +;; 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) KALEIDOS INC + +(ns app.main.ui.workspace.shapes.debug + (:require + [app.common.data :as d] + [app.common.data.macros :as dm] + [app.common.files.helpers :as cfh] + [app.common.geom.shapes :as gsh] + [app.common.geom.shapes.text :as gst] + [app.common.math :as mth] + [app.main.refs :as refs] + [app.util.color :as uc] + [app.util.debug :as dbg] + [app.util.dom :as dom] + [cuerdas.core :as str] + [rumext.v2 :as mf])) + +(mf/defc debug-bounding-boxes + [{:keys [shape]}] + (let [points (->> (:points shape) + (map #(dm/fmt "%,%" (dm/get-prop % :x) (dm/get-prop % :y))) + (str/join " ")) + color (mf/use-memo #(uc/random-color)) + sr (:selrect shape)] + [:g.debug-bounding-boxes + [:rect {:transform (gsh/transform-str shape) + :x (:x sr) + :y (:y sr) + :width (:width sr) + :height (:height sr) + :fill color + :opacity 0.2}] + (for [p (:points shape)] + [:circle {:cx (dm/get-prop p :x) + :cy (dm/get-prop p :y) + :r 2 + :fill color}]) + [:polygon {:points points + :stroke-width 1 + :stroke color}]])) + +(mf/defc debug-text-bounds + {::mf/wrap-props false} + [props] + (let [shape (unchecked-get props "shape") + zoom (mf/deref refs/selected-zoom) + bounding-box (gst/shape->rect shape) + ctx (js* "document.createElement(\"canvas\").getContext(\"2d\")")] + [:g {:transform (gsh/transform-str shape)} + [:rect {:x (:x bounding-box) + :y (:y bounding-box) + :width (:width bounding-box) + :height (:height bounding-box) + :style {:fill "none" + :stroke "orange" + :stroke-width (/ 1 zoom)}}] + + (for [[index data] (d/enumerate (:position-data shape))] + (let [{:keys [x y width height]} data + res (dom/measure-text ctx (:font-size data) (:font-family data) (:text data))] + [:g {:key (dm/str index)} + ;; Text fragment bounding box + [:rect {:x x + :y (- y height) + :width width + :height height + :style {:fill "none" + :stroke "red" + :stroke-width (/ 1 zoom)}}] + + ;; Text baseline + [:line {:x1 (mth/round x) + :y1 (mth/round (- (:y data) (:height data))) + :x2 (mth/round (+ x width)) + :y2 (mth/round (- (:y data) (:height data))) + :style {:stroke "blue" + :stroke-width (/ 1 zoom)}}] + + [:line {:x1 (:x data) + :y1 (- (:y data) (:descent res)) + :x2 (+ (:x data) (:width data)) + :y2 (- (:y data) (:descent res)) + :style {:stroke "green" + :stroke-width (/ 2 zoom)}}]]))])) + +(mf/defc shape-debug + [{:keys [shape]}] + [:* + (when ^boolean (dbg/enabled? :bounding-boxes) + [:& debug-bounding-boxes]) + + (when (and ^boolean (cfh/text-shape? shape) + ^boolean (dbg/enabled? :text-outline) + ^boolean (seq (:position-data shape))) + [:& debug-text-bounds {:shape shape}])]) diff --git a/frontend/src/app/main/ui/workspace/shapes/frame.cljs b/frontend/src/app/main/ui/workspace/shapes/frame.cljs index 2dc7e3289..9ecddd1e2 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame.cljs @@ -20,6 +20,7 @@ [app.main.ui.shapes.frame :as frame] [app.main.ui.shapes.shape :refer [shape-container]] [app.main.ui.workspace.shapes.common :refer [check-shape-props]] + [app.main.ui.workspace.shapes.debug :as wsd] [app.main.ui.workspace.shapes.frame.dynamic-modifiers :as fdm] [app.util.debug :as dbg] [app.util.dom :as dom] @@ -193,5 +194,8 @@ [:g.frame-content {:id (dm/str "frame-content-" frame-id) :ref container-ref} - [:& frame-shape {:shape shape :ref content-ref}]])]])))) + [:& frame-shape {:shape shape :ref content-ref}]])] + + (when *assert* + [:& wsd/shape-debug {:shape shape}])])))) diff --git a/frontend/src/app/main/ui/workspace/shapes/group.cljs b/frontend/src/app/main/ui/workspace/shapes/group.cljs index 5fbd2ad16..d98d58a2f 100644 --- a/frontend/src/app/main/ui/workspace/shapes/group.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/group.cljs @@ -11,6 +11,7 @@ [app.main.ui.shapes.group :as group] [app.main.ui.shapes.shape :refer [shape-container]] [app.main.ui.workspace.shapes.common :refer [check-shape-props]] + [app.main.ui.workspace.shapes.debug :as wsd] [rumext.v2 :as mf])) (defn group-wrapper-factory @@ -30,5 +31,7 @@ [:> shape-container {:shape shape} [:& group-shape {:shape shape - :childs childs}]])))) + :childs childs}] + (when *assert* + [:& wsd/shape-debug {:shape shape}])])))) diff --git a/frontend/src/app/main/ui/workspace/shapes/path.cljs b/frontend/src/app/main/ui/workspace/shapes/path.cljs index b20f65b1d..110238be4 100644 --- a/frontend/src/app/main/ui/workspace/shapes/path.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/path.cljs @@ -11,6 +11,7 @@ [app.main.refs :as refs] [app.main.ui.shapes.path :as path] [app.main.ui.shapes.shape :refer [shape-container]] + [app.main.ui.workspace.shapes.debug :as wsd] [app.main.ui.workspace.shapes.path.common :as pc] [rumext.v2 :as mf])) @@ -38,4 +39,6 @@ [:> shape-container {:shape shape :pointer-events (when editing? "none")} - [:& path/path-shape {:shape shape}]])) + [:& path/path-shape {:shape shape}] + (when *assert* + [:& wsd/shape-debug {:shape shape}])])) diff --git a/frontend/src/app/main/ui/workspace/shapes/svg_raw.cljs b/frontend/src/app/main/ui/workspace/shapes/svg_raw.cljs index 774fa61fa..de1701e01 100644 --- a/frontend/src/app/main/ui/workspace/shapes/svg_raw.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/svg_raw.cljs @@ -10,6 +10,7 @@ [app.main.refs :as refs] [app.main.ui.shapes.shape :refer [shape-container]] [app.main.ui.shapes.svg-raw :as svg-raw] + [app.main.ui.workspace.shapes.debug :as wsd] [rumext.v2 :as mf])) (defn svg-raw-wrapper-factory @@ -26,7 +27,9 @@ (if (contains? csvg/svg-group-safe-tags svg-tag) [:> shape-container {:shape shape} [:& svg-raw-shape {:shape shape - :childs childs}]] + :childs childs}] + (when *assert* + [:& wsd/shape-debug {:shape shape}])] [:& svg-raw-shape {:shape shape :childs childs}]))))) diff --git a/frontend/src/app/main/ui/workspace/shapes/text.cljs b/frontend/src/app/main/ui/workspace/shapes/text.cljs index eb9b5e68a..cdaeda400 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text.cljs @@ -6,63 +6,14 @@ (ns app.main.ui.workspace.shapes.text (:require - [app.common.data :as d] [app.common.data.macros :as dm] - [app.common.geom.shapes :as gsh] - [app.common.geom.shapes.text :as gst] - [app.common.math :as mth] [app.main.data.workspace.texts :as dwt] [app.main.refs :as refs] [app.main.ui.shapes.shape :refer [shape-container]] [app.main.ui.shapes.text :as text] - [app.util.debug :as dbg] - [app.util.dom :as dom] + [app.main.ui.workspace.shapes.debug :as wsd] [rumext.v2 :as mf])) -(mf/defc debug-text-bounds - {::mf/wrap-props false} - [props] - (let [shape (unchecked-get props "shape") - zoom (mf/deref refs/selected-zoom) - bounding-box (gst/shape->rect shape) - ctx (js* "document.createElement(\"canvas\").getContext(\"2d\")")] - [:g {:transform (gsh/transform-str shape)} - [:rect {:x (:x bounding-box) - :y (:y bounding-box) - :width (:width bounding-box) - :height (:height bounding-box) - :style {:fill "none" - :stroke "orange" - :stroke-width (/ 1 zoom)}}] - - (for [[index data] (d/enumerate (:position-data shape))] - (let [{:keys [x y width height]} data - res (dom/measure-text ctx (:font-size data) (:font-family data) (:text data))] - [:g {:key (dm/str index)} - ;; Text fragment bounding box - [:rect {:x x - :y (- y height) - :width width - :height height - :style {:fill "none" - :stroke "red" - :stroke-width (/ 1 zoom)}}] - - ;; Text baseline - [:line {:x1 (mth/round x) - :y1 (mth/round (- (:y data) (:height data))) - :x2 (mth/round (+ x width)) - :y2 (mth/round (- (:y data) (:height data))) - :style {:stroke "blue" - :stroke-width (/ 1 zoom)}}] - - [:line {:x1 (:x data) - :y1 (- (:y data) (:descent res)) - :x2 (+ (:x data) (:width data)) - :y2 (- (:y data) (:descent res)) - :style {:stroke "green" - :stroke-width (/ 2 zoom)}}]]))])) - ;; --- Text Wrapper for workspace (mf/defc text-wrapper {::mf/wrap-props false} @@ -84,6 +35,5 @@ [:g.text-shape {:key (dm/str shape-id)} [:& text/text-shape {:shape shape}]] - (when (and ^boolean (dbg/enabled? :text-outline) - ^boolean (seq (:position-data shape))) - [:& debug-text-bounds {:shape shape}])])) + (when *assert* + [:& wsd/shape-debug {:shape shape}])])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/debug.cljs b/frontend/src/app/main/ui/workspace/sidebar/debug.cljs index 1168e8dc2..b49371e5f 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/debug.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/debug.cljs @@ -24,7 +24,7 @@ (dom/prevent-default event) (dom/stop-propagation event) (dbg/toggle! option) - (js* "app.main.reinit()"))) + (js* "app.main.reinit(true)"))) handle-close (mf/use-fn diff --git a/frontend/src/app/util/color.cljs b/frontend/src/app/util/color.cljs index d7fc4a2bd..79989b389 100644 --- a/frontend/src/app/util/color.cljs +++ b/frontend/src/app/util/color.cljs @@ -12,6 +12,8 @@ (:require [app.common.colors :as cc] [app.common.data :as d] + [app.common.data.macros :as dm] + [app.common.math :as mth] [app.util.i18n :as i18n :refer [tr]] [cuerdas.core :as str])) @@ -82,3 +84,10 @@ (:name color) (:color color) (gradient-type->string (:type (:gradient color))))) + +(defn random-color + [] + (dm/fmt "rgb(%, %, %)" + (mth/floor (* (js/Math.random) 256)) + (mth/floor (* (js/Math.random) 256)) + (mth/floor (* (js/Math.random) 256)))) From ce675097b16b1906c2d3ff12f75db0428fa518ac Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Wed, 17 Jan 2024 12:25:38 +0100 Subject: [PATCH 2/9] :bug: Fix problem with group selrect --- common/src/app/common/geom/shapes/transforms.cljc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/common/src/app/common/geom/shapes/transforms.cljc b/common/src/app/common/geom/shapes/transforms.cljc index 6adf456cd..f9be02ada 100644 --- a/common/src/app/common/geom/shapes/transforms.cljc +++ b/common/src/app/common/geom/shapes/transforms.cljc @@ -414,7 +414,11 @@ (gco/transform-points shape-center (:transform group (gmt/matrix)))) ;; Calculate the new selrect - new-selrect (grc/points->rect base-points)] + sr-transform (gmt/transform-in (gco/points->center new-points) (:transform-inverse group (gmt/matrix))) + new-selrect + (-> new-points + (gco/transform-points sr-transform) + (grc/points->rect))] ;; Updates the shape and the applytransform-rect will update the other properties (-> group From 843a3f7f6ef18ea930a5b6a4952a5add5f2f9648 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Wed, 17 Jan 2024 14:06:21 +0100 Subject: [PATCH 3/9] :bug: Fix problem with fix when scrolling --- .../src/app/main/ui/viewer/interactions.cljs | 39 ++++++++++--------- .../src/app/main/ui/viewer/interactions.scss | 9 +++++ 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/frontend/src/app/main/ui/viewer/interactions.cljs b/frontend/src/app/main/ui/viewer/interactions.cljs index c304d0545..60f1928c4 100644 --- a/frontend/src/app/main/ui/viewer/interactions.cljs +++ b/frontend/src/app/main/ui/viewer/interactions.cljs @@ -113,24 +113,27 @@ [:& (mf/provider shapes/base-frame-ctx) {:value base} [:& (mf/provider shapes/frame-offset-ctx) {:value offset} ;; We have two different svgs for fixed and not fixed elements so we can emulate the sticky css attribute in svg - [:svg.not-fixed {:view-box vbox - :width (:width size) - :height (:height size) - :version "1.1" - :xmlnsXlink "http://www.w3.org/1999/xlink" - :xmlns "http://www.w3.org/2000/svg" - :fill "none"} - [:& wrapper-not-fixed {:shape frame :view-box vbox}]] - [:svg.fixed {:view-box vbox - :width (:width size) - :height (:height size) - :version "1.1" - :xmlnsXlink "http://www.w3.org/1999/xlink" - :xmlns "http://www.w3.org/2000/svg" - :fill "none" - :style {:width (:width size) - :height (:height size)}} - [:& wrapper-fixed {:shape fixed-frame :view-box vbox}]]]])) + [:svg {:class (stl/css :fixed) + :view-box vbox + :width (:width size) + :height (:height size) + :version "1.1" + :xmlnsXlink "http://www.w3.org/1999/xlink" + :xmlns "http://www.w3.org/2000/svg" + :fill "none" + :style {:width (:width size) + :height (:height size)}} + [:& wrapper-fixed {:shape fixed-frame :view-box vbox}]] + + [:svg {:class (stl/css :not-fixed) + :view-box vbox + :width (:width size) + :height (:height size) + :version "1.1" + :xmlnsXlink "http://www.w3.org/1999/xlink" + :xmlns "http://www.w3.org/2000/svg" + :fill "none"} + [:& wrapper-not-fixed {:shape frame :view-box vbox}]]]])) (mf/defc viewport {::mf/wrap [mf/memo] diff --git a/frontend/src/app/main/ui/viewer/interactions.scss b/frontend/src/app/main/ui/viewer/interactions.scss index 07b12e3af..656bc7c93 100644 --- a/frontend/src/app/main/ui/viewer/interactions.scss +++ b/frontend/src/app/main/ui/viewer/interactions.scss @@ -78,3 +78,12 @@ } // breakpoint 1013px + +.fixed { + position: fixed; + pointer-events: none; + + :global(.frame-children) g { + pointer-events: auto; + } +} From e193261d7f758505092213231e802a7c7fb60e99 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Wed, 17 Jan 2024 18:06:34 +0100 Subject: [PATCH 4/9] :sparkles: Reduce handlers for the flex layout gaps and paddings --- CHANGES.md | 1 + frontend/src/app/main/ui/flex_controls.cljs | 16 + .../src/app/main/ui/flex_controls/common.cljs | 35 + .../src/app/main/ui/flex_controls/gap.cljs | 311 +++++++++ .../src/app/main/ui/flex_controls/margin.cljs | 185 ++++++ .../app/main/ui/flex_controls/padding.cljs | 223 +++++++ frontend/src/app/main/ui/measurements.cljs | 612 ------------------ .../src/app/main/ui/workspace/viewport.cljs | 66 +- .../main/ui/workspace/viewport/actions.cljs | 2 +- 9 files changed, 808 insertions(+), 643 deletions(-) create mode 100644 frontend/src/app/main/ui/flex_controls.cljs create mode 100644 frontend/src/app/main/ui/flex_controls/common.cljs create mode 100644 frontend/src/app/main/ui/flex_controls/gap.cljs create mode 100644 frontend/src/app/main/ui/flex_controls/margin.cljs create mode 100644 frontend/src/app/main/ui/flex_controls/padding.cljs diff --git a/CHANGES.md b/CHANGES.md index 738b261ea..7e1a1760b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -20,6 +20,7 @@ - Select through stroke only rectangle [Taiga #5484](https://tree.taiga.io/project/penpot/issue/5484) - Override browser Ctrl+ and Ctrl- zoom with Penpot Zoom [Taiga #3200](https://tree.taiga.io/project/penpot/us/3200) +- Reduce handlers for the flex layout gaps and paddings ### :bug: Bugs fixed diff --git a/frontend/src/app/main/ui/flex_controls.cljs b/frontend/src/app/main/ui/flex_controls.cljs new file mode 100644 index 000000000..a21c6b9d9 --- /dev/null +++ b/frontend/src/app/main/ui/flex_controls.cljs @@ -0,0 +1,16 @@ +;; 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) KALEIDOS INC + +(ns app.main.ui.flex-controls + (:require + [app.common.data.macros :as dm] + [app.main.ui.flex-controls.gap :as fcg] + [app.main.ui.flex-controls.margin :as fcm] + [app.main.ui.flex-controls.padding :as fcp])) + +(dm/export fcg/gap-control) +(dm/export fcm/margin-control) +(dm/export fcp/padding-control) diff --git a/frontend/src/app/main/ui/flex_controls/common.cljs b/frontend/src/app/main/ui/flex_controls/common.cljs new file mode 100644 index 000000000..052e501ee --- /dev/null +++ b/frontend/src/app/main/ui/flex_controls/common.cljs @@ -0,0 +1,35 @@ +(ns app.main.ui.flex-controls.common + (:require + [app.main.ui.formats :as fmt] + [rumext.v2 :as mf])) + +;; ------------------------------------------------ +;; CONSTANTS +;; ------------------------------------------------ + +(def font-size 11) +(def distance-color "var(--color-distance)") +(def distance-text-color "var(--color-white)") +(def warning-color "var(--color-warning)") +(def flex-display-pill-width 40) +(def flex-display-pill-height 20) +(def flex-display-pill-border-radius 4) + +(mf/defc flex-display-pill + [{:keys [x y width height font-size border-radius value color]}] + [:g.distance-pill + [:rect {:x x + :y y + :width width + :height height + :rx border-radius + :ry border-radius + :style {:fill color}}] + + [:text {:x (+ x (/ width 2)) + :y (+ y (/ height 2)) + :text-anchor "middle" + :dominant-baseline "central" + :style {:fill distance-text-color + :font-size font-size}} + (fmt/format-number (or value 0))]]) diff --git a/frontend/src/app/main/ui/flex_controls/gap.cljs b/frontend/src/app/main/ui/flex_controls/gap.cljs new file mode 100644 index 000000000..bd38eba10 --- /dev/null +++ b/frontend/src/app/main/ui/flex_controls/gap.cljs @@ -0,0 +1,311 @@ +;; 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) KALEIDOS INC + +(ns app.main.ui.flex-controls.gap + (:require + [app.common.data :as d] + [app.common.files.helpers :as cfh] + [app.common.geom.point :as gpt] + [app.common.geom.shapes :as gsh] + [app.common.geom.shapes.flex-layout :as gsl] + [app.common.geom.shapes.points :as gpo] + [app.common.types.modifiers :as ctm] + [app.common.types.shape.layout :as ctl] + [app.main.data.workspace.modifiers :as dwm] + [app.main.data.workspace.state-helpers :as wsh] + [app.main.refs :as refs] + [app.main.store :as st] + [app.main.ui.css-cursors :as cur] + [app.main.ui.flex-controls.common :as fcc] + [app.main.ui.workspace.viewport.viewport-ref :refer [point->viewport]] + [app.util.dom :as dom] + [rumext.v2 :as mf])) + +(mf/defc gap-display + [{:keys [frame-id zoom gap-type gap on-pointer-enter on-pointer-leave + rect-data hover? selected? mouse-pos hover-value + on-move-selected on-context-menu]}] + (let [resizing (mf/use-var nil) + start (mf/use-var nil) + original-value (mf/use-var 0) + negate? (:resize-negate? rect-data) + axis (:resize-axis rect-data) + + on-pointer-down + (mf/use-fn + (mf/deps frame-id gap-type gap) + (fn [event] + (dom/capture-pointer event) + (reset! resizing gap-type) + (reset! start (dom/get-client-position event)) + (reset! original-value (:initial-value rect-data)))) + + on-lost-pointer-capture + (mf/use-fn + (mf/deps frame-id gap-type gap) + (fn [event] + (dom/release-pointer event) + (reset! resizing nil) + (reset! start nil) + (reset! original-value 0) + (st/emit! (dwm/apply-modifiers)))) + + on-pointer-move + (mf/use-fn + (mf/deps frame-id gap-type gap) + (fn [event] + (let [pos (dom/get-client-position event)] + (reset! mouse-pos (point->viewport pos)) + (when (= @resizing gap-type) + (let [delta (-> (gpt/to-vec @start pos) + (cond-> negate? gpt/negate) + (get axis)) + val (int (max (+ @original-value (/ delta zoom)) 0)) + layout-gap (assoc gap gap-type val) + modifiers (dwm/create-modif-tree [frame-id] (ctm/change-property (ctm/empty) :layout-gap layout-gap))] + + (reset! hover-value val) + (st/emit! (dwm/set-modifiers modifiers)))))))] + + [:g.gap-rect + [:rect.info-area + {:x (:x rect-data) + :y (:y rect-data) + :width (:width rect-data) + :height (:height rect-data) + :on-pointer-enter on-pointer-enter + :on-pointer-leave on-pointer-leave + :on-pointer-move on-pointer-move + :on-pointer-down on-move-selected + :on-context-menu on-context-menu + + :style {:fill (if (or hover? selected?) fcc/distance-color "none") + :opacity (if selected? 0.5 0.25)}}] + + (let [handle-width + (if (= axis :x) + (/ 2 zoom) + (min (* (:width rect-data) 0.5) (/ 20 zoom))) + + handle-height + (if (= axis :y) + (/ 2 zoom) + (min (* (:height rect-data) 0.5) (/ 30 zoom)))] + [:rect.handle + {:x (+ (:x rect-data) (/ (- (:width rect-data) handle-width) 2)) + :y (+ (:y rect-data) (/ (- (:height rect-data) handle-height) 2)) + :width handle-width + :height handle-height + :on-pointer-enter on-pointer-enter + :on-pointer-leave on-pointer-leave + :on-pointer-down on-pointer-down + :on-lost-pointer-capture on-lost-pointer-capture + :on-pointer-move on-pointer-move + :on-context-menu on-context-menu + :class (when (or hover? selected?) + (if (= (:resize-axis rect-data) :x) (cur/get-dynamic "resize-ew" 0) (cur/get-dynamic "resize-ew" 90))) + :style {:fill (if (or hover? selected?) fcc/distance-color "none") + :opacity (if selected? 0 1)}}])])) + +(mf/defc gap-rects + [{:keys [frame zoom on-move-selected on-context-menu]}] + (let [frame-id (:id frame) + saved-dir (:layout-flex-dir frame) + is-col? (or (= :column saved-dir) (= :column-reverse saved-dir)) + flip-x (:flip-x frame) + flip-y (:flip-y frame) + pill-width (/ fcc/flex-display-pill-width zoom) + pill-height (/ fcc/flex-display-pill-height zoom) + workspace-modifiers (mf/deref refs/workspace-modifiers) + gap-selected (mf/deref refs/workspace-gap-selected) + hover (mf/use-var nil) + hover-value (mf/use-var 0) + mouse-pos (mf/use-var nil) + padding (:layout-padding frame) + gap (:layout-gap frame) + {:keys [width height x1 y1]} (:selrect frame) + on-pointer-enter (fn [hover-type val] + (reset! hover hover-type) + (reset! hover-value val)) + + on-pointer-leave #(reset! hover nil) + negate {:column-gap (if flip-x true false) + :row-gap (if flip-y true false)} + + objects (wsh/lookup-page-objects @st/state) + children (->> (cfh/get-immediate-children objects frame-id) + (remove ctl/position-absolute?)) + + children-to-display (if (or (= :row-reverse saved-dir) + (= :column-reverse saved-dir)) + (drop-last children) + (rest children)) + children-to-display (->> children-to-display + (map #(gsh/transform-shape % (get-in workspace-modifiers [(:id %) :modifiers])))) + + wrap-blocks + (let [block-children (->> children + (map #(vector (gpo/parent-coords-bounds (:points %) (:points frame)) %))) + bounds (d/lazy-map (keys objects) #(gsh/shape->points (get objects %))) + + layout-data (gsl/calc-layout-data frame (:points frame) block-children bounds objects) + layout-bounds (:layout-bounds layout-data) + xv #(gpo/start-hv layout-bounds %) + yv #(gpo/start-vv layout-bounds %)] + (for [{:keys [start-p line-width line-height layout-gap-row layout-gap-col num-children]} (:layout-lines layout-data)] + (let [line-width (if is-col? line-width (+ line-width (* (dec num-children) layout-gap-row))) + line-height (if is-col? (+ line-height (* (dec num-children) layout-gap-col)) line-height) + end-p (-> start-p (gpt/add (xv line-width)) (gpt/add (yv line-height)))] + {:x1 (min (:x start-p) (:x end-p)) + :y1 (min (:y start-p) (:y end-p)) + :x2 (max (:x start-p) (:x end-p)) + :y2 (max (:y start-p) (:y end-p))}))) + + block-contains + (fn [x y block] + (if is-col? + (<= (:x1 block) x (:x2 block)) + (<= (:y1 block) y (:y2 block)))) + + get-container-block + (fn [shape] + (let [selrect (:selrect shape) + x (/ (+ (:x1 selrect) (:x2 selrect)) 2) + y (/ (+ (:y1 selrect) (:y2 selrect)) 2)] + (->> wrap-blocks + (filter #(block-contains x y %)) + first))) + + create-cgdd + (fn [shape] + (let [block (get-container-block shape) + x (if flip-x + (- (:x1 (:selrect shape)) + (get-in shape [:layout-item-margin :m2]) + (:column-gap gap)) + (+ (:x2 (:selrect shape)) (get-in shape [:layout-item-margin :m2]))) + y (:y1 block) + h (- (:y2 block) (:y1 block))] + {:x x + :y y + :height h + :width (:column-gap gap) + :initial-value (:column-gap gap) + :resize-type :left + :resize-axis :x + :resize-negate? (:column-gap negate) + :gap-type (if is-col? :row-gap :column-gap)})) + + create-cgdd-block + (fn [block] + (let [x (if flip-x + (- (:x1 block) (:column-gap gap)) + (:x2 block)) + y (if flip-y + (+ y1 (:p3 padding)) + (+ y1 (:p1 padding))) + h (- height (+ (:p1 padding) (:p3 padding)))] + {:x x + :y y + :width (:column-gap gap) + :height h + :initial-value (:column-gap gap) + :resize-type :left + :resize-axis :x + :resize-negate? (:column-gap negate) + :gap-type (if is-col? :column-gap :row-gap)})) + + create-rgdd + (fn [shape] + (let [block (get-container-block shape) + x (:x1 block) + y (if flip-y + (- (:y1 (:selrect shape)) + (get-in shape [:layout-item-margin :m3]) + (:row-gap gap)) + (+ (:y2 (:selrect shape)) (get-in shape [:layout-item-margin :m3]))) + w (- (:x2 block) (:x1 block))] + {:x x + :y y + :width w + :height (:row-gap gap) + :initial-value (:row-gap gap) + :resize-type :bottom + :resize-axis :y + :resize-negate? (:row-gap negate) + :gap-type (if is-col? :row-gap :column-gap)})) + + create-rgdd-block + (fn [block] + (let [x (if flip-x + (+ x1 (:p2 padding)) + (+ x1 (:p4 padding))) + y (if flip-y + (- (:y1 block) (:row-gap gap)) + (:y2 block)) + w (- width (+ (:p2 padding) (:p4 padding)))] + {:x x + :y y + :width w + :height (:row-gap gap) + :initial-value (:row-gap gap) + :resize-type :bottom + :resize-axis :y + :resize-negate? (:row-gap negate) + :gap-type (if is-col? :column-gap :row-gap)})) + + display-blocks (if is-col? + (->> (drop-last wrap-blocks) + (map create-cgdd-block)) + (->> (drop-last wrap-blocks) + (map create-rgdd-block))) + + display-children (if is-col? + (->> children-to-display + (map create-rgdd)) + (->> children-to-display + (map create-cgdd)))] + + [:g.gaps {:pointer-events "visible"} + (for [[index display-item] (d/enumerate (concat display-blocks display-children))] + (let [gap-type (:gap-type display-item)] + [:& gap-display {:key (str frame-id index) + :frame-id frame-id + :zoom zoom + :gap-type gap-type + :gap gap + :on-pointer-enter (partial on-pointer-enter gap-type (get gap gap-type)) + :on-pointer-leave on-pointer-leave + :on-move-selected on-move-selected + :on-context-menu on-context-menu + :rect-data display-item + :hover? (= @hover gap-type) + :selected? (= gap-selected gap-type) + :mouse-pos mouse-pos + :hover-value hover-value}])) + + (when @hover + [:& fcc/flex-display-pill + {:height pill-height + :width pill-width + :font-size (/ fcc/font-size zoom) + :border-radius (/ fcc/flex-display-pill-border-radius zoom) + :color fcc/distance-color + :x (:x @mouse-pos) + :y (- (:y @mouse-pos) pill-width) + :value @hover-value}])])) + + +(mf/defc gap-control + [{:keys [frame zoom on-move-selected on-context-menu]}] + (when frame + [:g.measurement-gaps {:pointer-events "none"} + [:g.hover-shapes + [:& gap-rects + {:frame frame + :zoom zoom + :on-move-selected on-move-selected + :on-context-menu on-context-menu}]]])) diff --git a/frontend/src/app/main/ui/flex_controls/margin.cljs b/frontend/src/app/main/ui/flex_controls/margin.cljs new file mode 100644 index 000000000..5ee7d33c5 --- /dev/null +++ b/frontend/src/app/main/ui/flex_controls/margin.cljs @@ -0,0 +1,185 @@ +;; 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) KALEIDOS INC + +(ns app.main.ui.flex-controls.margin + (:require + [app.common.geom.point :as gpt] + [app.common.types.modifiers :as ctm] + [app.main.data.workspace.modifiers :as dwm] + [app.main.refs :as refs] + [app.main.store :as st] + [app.main.ui.css-cursors :as cur] + [app.main.ui.flex-controls.common :as fcc] + [app.main.ui.workspace.viewport.viewport-ref :refer [point->viewport]] + [app.util.dom :as dom] + [rumext.v2 :as mf])) + +(mf/defc margin-display [{:keys [shape-id zoom hover-all? hover-v? hover-h? margin-num margin on-pointer-enter on-pointer-leave + rect-data hover? selected? mouse-pos hover-value]}] + (let [resizing? (mf/use-var false) + start (mf/use-var nil) + original-value (mf/use-var 0) + negate? (true? (:resize-negate? rect-data)) + axis (:resize-axis rect-data) + + on-pointer-down + (mf/use-fn + (mf/deps shape-id margin-num margin) + (fn [event] + (dom/capture-pointer event) + (reset! resizing? true) + (reset! start (dom/get-client-position event)) + (reset! original-value (:initial-value rect-data)))) + + on-lost-pointer-capture + (mf/use-fn + (mf/deps shape-id margin-num margin) + (fn [event] + (dom/release-pointer event) + (reset! resizing? false) + (reset! start nil) + (reset! original-value 0) + (st/emit! (dwm/apply-modifiers)))) + + on-pointer-move + (mf/use-fn + (mf/deps shape-id margin-num margin hover-all? hover-v? hover-h?) + (fn [event] + (let [pos (dom/get-client-position event)] + (reset! mouse-pos (point->viewport pos)) + (when @resizing? + (let [delta (-> (gpt/to-vec @start pos) + (cond-> negate? gpt/negate) + (get axis)) + val (int (max (+ @original-value (/ delta zoom)) 0)) + layout-item-margin (cond + hover-all? (assoc margin :m1 val :m2 val :m3 val :m4 val) + hover-v? (assoc margin :m1 val :m3 val) + hover-h? (assoc margin :m2 val :m4 val) + :else (assoc margin margin-num val)) + layout-item-margin-type (if (= (:m1 margin) (:m2 margin) (:m3 margin) (:m4 margin)) :simple :multiple) + modifiers (dwm/create-modif-tree [shape-id] + (-> (ctm/empty) + (ctm/change-property :layout-item-margin layout-item-margin) + (ctm/change-property :layout-item-margin-type layout-item-margin-type)))] + (reset! hover-value val) + (st/emit! (dwm/set-modifiers modifiers)))))))] + + [:rect.margin-rect + {:x (:x rect-data) + :y (:y rect-data) + :width (:width rect-data) + :height (:height rect-data) + :on-pointer-enter on-pointer-enter + :on-pointer-leave on-pointer-leave + :on-pointer-down on-pointer-down + :on-lost-pointer-capture on-lost-pointer-capture + :on-pointer-move on-pointer-move + :class (when (or hover? selected?) + (if (= (:resize-axis rect-data) :x) (cur/get-dynamic "resize-ew" 0) (cur/get-dynamic "resize-ew" 90))) + :style {:fill (if (or hover? selected?) fcc/warning-color "none") + :opacity (if selected? 0.5 0.25)}}])) + + +(mf/defc margin-rects [{:keys [shape frame zoom alt? shift?]}] + (let [shape-id (:id shape) + pill-width (/ fcc/flex-display-pill-width zoom) + pill-height (/ fcc/flex-display-pill-height zoom) + margins-selected (mf/deref refs/workspace-margins-selected) + hover-value (mf/use-var 0) + mouse-pos (mf/use-var nil) + hover (mf/use-var nil) + hover-all? (and (not (nil? @hover)) alt?) + hover-v? (and (or (= @hover :m1) (= @hover :m3)) shift?) + hover-h? (and (or (= @hover :m2) (= @hover :m4)) shift?) + margin (:layout-item-margin shape) + {:keys [width height x1 x2 y1 y2]} (:selrect shape) + on-pointer-enter (fn [hover-type val] + (reset! hover hover-type) + (reset! hover-value val)) + on-pointer-leave #(reset! hover nil) + hover? #(or hover-all? + (and (or (= % :m1) (= % :m3)) hover-v?) + (and (or (= % :m2) (= % :m4)) hover-h?) + (= @hover %)) + margin-display-data {:m1 {:key (str shape-id "-m1") + :x x1 + :y (if (:flip-y frame) y2 (- y1 (:m1 margin))) + :width width + :height (:m1 margin) + :initial-value (:m1 margin) + :resize-type :top + :resize-axis :y + :resize-negate? (:flip-y frame)} + :m2 {:key (str shape-id "-m2") + :x (if (:flip-x frame) (- x1 (:m2 margin)) x2) + :y y1 + :width (:m2 margin) + :height height + :initial-value (:m2 margin) + :resize-type :left + :resize-axis :x + :resize-negate? (:flip-x frame)} + :m3 {:key (str shape-id "-m3") + :x x1 + :y (if (:flip-y frame) (- y1 (:m3 margin)) y2) + :width width + :height (:m3 margin) + :initial-value (:m3 margin) + :resize-type :top + :resize-axis :y + :resize-negate? (:flip-y frame)} + :m4 {:key (str shape-id "-m4") + :x (if (:flip-x frame) x2 (- x1 (:m4 margin))) + :y y1 + :width (:m4 margin) + :height height + :initial-value (:m4 margin) + :resize-type :left + :resize-axis :x + :resize-negate? (:flip-x frame)}}] + + [:g.margins {:pointer-events "visible"} + (for [[margin-num rect-data] margin-display-data] + [:& margin-display + {:key (:key rect-data) + :shape-id shape-id + :zoom zoom + :hover-all? hover-all? + :hover-v? hover-v? + :hover-h? hover-h? + :margin-num margin-num + :margin margin + :on-pointer-enter (partial on-pointer-enter margin-num (get margin margin-num)) + :on-pointer-leave on-pointer-leave + :rect-data rect-data + :hover? (hover? margin-num) + :selected? (get margins-selected margin-num) + :mouse-pos mouse-pos + :hover-value hover-value}]) + + (when @hover + [:& fcc/flex-display-pill + {:height pill-height + :width pill-width + :font-size (/ fcc/font-size zoom) + :border-radius (/ fcc/flex-display-pill-border-radius zoom) + :color fcc/warning-color + :x (:x @mouse-pos) + :y (- (:y @mouse-pos) pill-width) + :value @hover-value}])])) + +(mf/defc margin-control + [{:keys [shape parent zoom alt? shift?]}] + (when shape + [:g.measurement-gaps {:pointer-events "none"} + [:g.hover-shapes + [:& margin-rects + {:shape shape + :frame parent + :zoom zoom + :alt? alt? + :shift? shift?}]]])) diff --git a/frontend/src/app/main/ui/flex_controls/padding.cljs b/frontend/src/app/main/ui/flex_controls/padding.cljs new file mode 100644 index 000000000..5b64c94fb --- /dev/null +++ b/frontend/src/app/main/ui/flex_controls/padding.cljs @@ -0,0 +1,223 @@ +;; 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) KALEIDOS INC + +(ns app.main.ui.flex-controls.padding + (:require + [app.common.geom.point :as gpt] + [app.common.types.modifiers :as ctm] + [app.main.data.workspace.modifiers :as dwm] + [app.main.refs :as refs] + [app.main.store :as st] + [app.main.ui.css-cursors :as cur] + [app.main.ui.flex-controls.common :as fcc] + [app.main.ui.workspace.viewport.viewport-ref :refer [point->viewport]] + [app.util.dom :as dom] + [rumext.v2 :as mf])) + +(mf/defc padding-display + [{:keys [frame-id zoom hover-all? hover-v? hover-h? padding-num padding on-pointer-enter on-pointer-leave + rect-data hover? selected? mouse-pos hover-value on-move-selected on-context-menu]}] + (let [resizing? (mf/use-var false) + start (mf/use-var nil) + original-value (mf/use-var 0) + negate? (true? (:resize-negate? rect-data)) + axis (:resize-axis rect-data) + + on-pointer-down + (mf/use-fn + (mf/deps frame-id rect-data padding-num) + (fn [event] + (dom/capture-pointer event) + (reset! resizing? true) + (reset! start (dom/get-client-position event)) + (reset! original-value (:initial-value rect-data)))) + + on-lost-pointer-capture + (mf/use-fn + (mf/deps frame-id padding-num padding) + (fn [event] + (dom/release-pointer event) + (reset! resizing? false) + (reset! start nil) + (reset! original-value 0) + (st/emit! (dwm/apply-modifiers)))) + + on-pointer-move + (mf/use-fn + (mf/deps frame-id padding-num padding hover-all? hover-v? hover-h?) + (fn [event] + (let [pos (dom/get-client-position event)] + (reset! mouse-pos (point->viewport pos)) + (when @resizing? + (let [delta (-> (gpt/to-vec @start pos) + (cond-> negate? gpt/negate) + (get axis)) + val (int (max (+ @original-value (/ delta zoom)) 0)) + layout-padding (cond + hover-all? (assoc padding :p1 val :p2 val :p3 val :p4 val) + hover-v? (assoc padding :p1 val :p3 val) + hover-h? (assoc padding :p2 val :p4 val) + :else (assoc padding padding-num val)) + + + layout-padding-type (if (= (:p1 padding) (:p2 padding) (:p3 padding) (:p4 padding)) :simple :multiple) + modifiers (dwm/create-modif-tree [frame-id] + (-> (ctm/empty) + (ctm/change-property :layout-padding layout-padding) + (ctm/change-property :layout-padding-type layout-padding-type)))] + (reset! hover-value val) + (st/emit! (dwm/set-modifiers modifiers)))))))] + + [:g.padding-rect + [:rect.info-area + {:x (:x rect-data) + :y (:y rect-data) + :width (max 0 (:width rect-data)) + :height (max 0 (:height rect-data)) + + :on-pointer-enter on-pointer-enter + :on-pointer-leave on-pointer-leave + :on-pointer-move on-pointer-move + :on-pointer-down on-move-selected + :on-context-menu on-context-menu + :style {:fill (if (or hover? selected?) fcc/distance-color "none") + :opacity (if selected? 0.5 0.25)}}] + + (let [handle-width + (if (= axis :x) + (/ 2 zoom) + (min (* (:width rect-data) 0.5) (/ 20 zoom))) + + handle-height + (if (= axis :y) + (/ 2 zoom) + (min (* (:height rect-data) 0.5) (/ 30 zoom)))] + [:rect.handle + {:x (+ (:x rect-data) (/ (- (:width rect-data) handle-width) 2)) + :y (+ (:y rect-data) (/ (- (:height rect-data) handle-height) 2)) + :width handle-width + :height handle-height + :on-pointer-enter on-pointer-enter + :on-pointer-leave on-pointer-leave + :on-pointer-down on-pointer-down + :on-lost-pointer-capture on-lost-pointer-capture + :on-pointer-move on-pointer-move + :on-context-menu on-context-menu + :class (when (or hover? selected?) + (if (= (:resize-axis rect-data) :x) (cur/get-dynamic "resize-ew" 0) (cur/get-dynamic "resize-ew" 90))) + :style {:fill (if (or hover? selected?) fcc/distance-color "none") + :opacity (if selected? 0 1)}}])])) + +(mf/defc padding-rects + [{:keys [frame zoom alt? shift? on-move-selected on-context-menu]}] + (let [frame-id (:id frame) + paddings-selected (mf/deref refs/workspace-paddings-selected) + hover-value (mf/use-var 0) + mouse-pos (mf/use-var nil) + hover (mf/use-var nil) + hover-all? (and (not (nil? @hover)) alt?) + hover-v? (and (or (= @hover :p1) (= @hover :p3)) shift?) + hover-h? (and (or (= @hover :p2) (= @hover :p4)) shift?) + padding (:layout-padding frame) + {:keys [width height x1 x2 y1 y2]} (:selrect frame) + on-pointer-enter (fn [hover-type val] + (reset! hover hover-type) + (reset! hover-value val)) + on-pointer-leave #(reset! hover nil) + pill-width (/ fcc/flex-display-pill-width zoom) + pill-height (/ fcc/flex-display-pill-height zoom) + hover? #(or hover-all? + (and (or (= % :p1) (= % :p3)) hover-v?) + (and (or (= % :p2) (= % :p4)) hover-h?) + (= @hover %)) + negate {:p1 (if (:flip-y frame) true false) + :p2 (if (:flip-x frame) true false) + :p3 (if (:flip-y frame) true false) + :p4 (if (:flip-x frame) true false)} + negate (cond-> negate + (not= :auto (:layout-item-h-sizing frame)) (assoc :p2 (not (:p2 negate))) + (not= :auto (:layout-item-v-sizing frame)) (assoc :p3 (not (:p3 negate)))) + + padding-rect-data {:p1 {:key (str frame-id "-p1") + :x x1 + :y (if (:flip-y frame) (- y2 (:p1 padding)) y1) + :width width + :height (:p1 padding) + :initial-value (:p1 padding) + :resize-type (if (:flip-y frame) :bottom :top) + :resize-axis :y + :resize-negate? (:p1 negate)} + :p2 {:key (str frame-id "-p2") + :x (if (:flip-x frame) x1 (- x2 (:p2 padding))) + :y y1 + :width (:p2 padding) + :height height + :initial-value (:p2 padding) + :resize-type :left + :resize-axis :x + :resize-negate? (:p2 negate)} + :p3 {:key (str frame-id "-p3") + :x x1 + :y (if (:flip-y frame) y1 (- y2 (:p3 padding))) + :width width + :height (:p3 padding) + :initial-value (:p3 padding) + :resize-type :bottom + :resize-axis :y + :resize-negate? (:p3 negate)} + :p4 {:key (str frame-id "-p4") + :x (if (:flip-x frame) (- x2 (:p4 padding)) x1) + :y y1 + :width (:p4 padding) + :height height + :initial-value (:p4 padding) + :resize-type (if (:flip-x frame) :right :left) + :resize-axis :x + :resize-negate? (:p4 negate)}}] + + [:g.paddings {:pointer-events "visible"} + (for [[padding-num rect-data] padding-rect-data] + [:& padding-display + {:key (:key rect-data) + :frame-id frame-id + :zoom zoom + :hover-all? hover-all? + :hover-v? hover-v? + :hover-h? hover-h? + :padding padding + :mouse-pos mouse-pos + :hover-value hover-value + :padding-num padding-num + :on-pointer-enter (partial on-pointer-enter padding-num (get padding padding-num)) + :on-pointer-leave on-pointer-leave + :on-move-selected on-move-selected + :on-context-menu on-context-menu + :hover? (hover? padding-num) + :selected? (get paddings-selected padding-num) + :rect-data rect-data}]) + (when @hover + [:& fcc/flex-display-pill + {:height pill-height + :width pill-width + :font-size (/ fcc/font-size zoom) + :border-radius (/ fcc/flex-display-pill-border-radius zoom) + :color fcc/distance-color + :x (:x @mouse-pos) + :y (- (:y @mouse-pos) pill-width) + :value @hover-value}])])) + +(mf/defc padding-control + [{:keys [frame zoom alt? shift? on-move-selected on-context-menu]}] + (when frame + [:g.measurement-gaps {:pointer-events "none"} + [:g.hover-shapes + [:& padding-rects + {:frame frame + :zoom zoom + :alt? alt? + :shift? shift? + :on-move-selected on-move-selected + :on-context-menu on-context-menu}]]])) diff --git a/frontend/src/app/main/ui/measurements.cljs b/frontend/src/app/main/ui/measurements.cljs index 057794e30..9c4f9719c 100644 --- a/frontend/src/app/main/ui/measurements.cljs +++ b/frontend/src/app/main/ui/measurements.cljs @@ -8,24 +8,12 @@ (:require [app.common.data :as d] [app.common.data.macros :as dm] - [app.common.files.helpers :as cfh] [app.common.geom.point :as gpt] [app.common.geom.rect :as grc] [app.common.geom.shapes :as gsh] - [app.common.geom.shapes.flex-layout :as gsl] - [app.common.geom.shapes.points :as gpo] [app.common.math :as mth] - [app.common.types.modifiers :as ctm] - [app.common.types.shape.layout :as ctl] [app.common.uuid :as uuid] - [app.main.data.workspace.modifiers :as dwm] - [app.main.data.workspace.state-helpers :as wsh] - [app.main.refs :as refs] - [app.main.store :as st] - [app.main.ui.css-cursors :as cur] [app.main.ui.formats :as fmt] - [app.main.ui.workspace.viewport.viewport-ref :refer [point->viewport]] - [app.util.dom :as dom] [rumext.v2 :as mf])) ;; ------------------------------------------------ @@ -57,9 +45,6 @@ (def distance-pill-height 16) (def distance-line-stroke 1) (def warning-color "var(--color-warning)") -(def flex-display-pill-width 40) -(def flex-display-pill-height 20) -(def flex-display-pill-border-radius 4) ;; ------------------------------------------------ ;; HELPERS @@ -277,600 +262,3 @@ [:& size-display {:selrect hover-selrect :zoom zoom}] [:& distance-display {:from hover-selrect :to selected-selrect :zoom zoom :bounds bounds-selrect}]])]))) - - -(mf/defc flex-display-pill [{:keys [x y width height font-size border-radius value color]}] - [:g.distance-pill - [:rect {:x x - :y y - :width width - :height height - :rx border-radius - :ry border-radius - :style {:fill color}}] - - [:text {:x (+ x (/ width 2)) - :y (+ y (/ height 2)) - :text-anchor "middle" - :dominant-baseline "central" - :style {:fill distance-text-color - :font-size font-size}} - (fmt/format-number (or value 0))]]) - - -(mf/defc padding-display [{:keys [frame-id zoom hover-all? hover-v? hover-h? padding-num padding on-pointer-enter on-pointer-leave - rect-data hover? selected? mouse-pos hover-value]}] - (let [resizing? (mf/use-var false) - start (mf/use-var nil) - original-value (mf/use-var 0) - negate? (true? (:resize-negate? rect-data)) - axis (:resize-axis rect-data) - - on-pointer-down - (mf/use-fn - (mf/deps frame-id rect-data padding-num) - (fn [event] - (dom/capture-pointer event) - (reset! resizing? true) - (reset! start (dom/get-client-position event)) - (reset! original-value (:initial-value rect-data)))) - - on-lost-pointer-capture - (mf/use-fn - (mf/deps frame-id padding-num padding) - (fn [event] - (dom/release-pointer event) - (reset! resizing? false) - (reset! start nil) - (reset! original-value 0) - (st/emit! (dwm/apply-modifiers)))) - - on-pointer-move - (mf/use-fn - (mf/deps frame-id padding-num padding hover-all? hover-v? hover-h?) - (fn [event] - (let [pos (dom/get-client-position event)] - (reset! mouse-pos (point->viewport pos)) - (when @resizing? - (let [delta (-> (gpt/to-vec @start pos) - (cond-> negate? gpt/negate) - (get axis)) - val (int (max (+ @original-value (/ delta zoom)) 0)) - layout-padding (cond - hover-all? (assoc padding :p1 val :p2 val :p3 val :p4 val) - hover-v? (assoc padding :p1 val :p3 val) - hover-h? (assoc padding :p2 val :p4 val) - :else (assoc padding padding-num val)) - - - layout-padding-type (if (= (:p1 padding) (:p2 padding) (:p3 padding) (:p4 padding)) :simple :multiple) - modifiers (dwm/create-modif-tree [frame-id] - (-> (ctm/empty) - (ctm/change-property :layout-padding layout-padding) - (ctm/change-property :layout-padding-type layout-padding-type)))] - (reset! hover-value val) - (st/emit! (dwm/set-modifiers modifiers)))))))] - - [:rect.padding-rect {:x (:x rect-data) - :y (:y rect-data) - :width (max 0 (:width rect-data)) - :height (max 0 (:height rect-data)) - :on-pointer-enter on-pointer-enter - :on-pointer-leave on-pointer-leave - :on-pointer-down on-pointer-down - :on-lost-pointer-capture on-lost-pointer-capture - :on-pointer-move on-pointer-move - :class (when (or hover? selected?) - (if (= (:resize-axis rect-data) :x) (cur/get-dynamic "resize-ew" 0) (cur/get-dynamic "resize-ew" 90))) - :style {:fill (if (or hover? selected?) distance-color "none") - :opacity (if selected? 0.5 0.25)}}])) - -(mf/defc padding-rects [{:keys [frame zoom alt? shift?]}] - (let [frame-id (:id frame) - paddings-selected (mf/deref refs/workspace-paddings-selected) - hover-value (mf/use-var 0) - mouse-pos (mf/use-var nil) - hover (mf/use-var nil) - hover-all? (and (not (nil? @hover)) alt?) - hover-v? (and (or (= @hover :p1) (= @hover :p3)) shift?) - hover-h? (and (or (= @hover :p2) (= @hover :p4)) shift?) - padding (:layout-padding frame) - {:keys [width height x1 x2 y1 y2]} (:selrect frame) - on-pointer-enter (fn [hover-type val] - (reset! hover hover-type) - (reset! hover-value val)) - on-pointer-leave #(reset! hover nil) - pill-width (/ flex-display-pill-width zoom) - pill-height (/ flex-display-pill-height zoom) - hover? #(or hover-all? - (and (or (= % :p1) (= % :p3)) hover-v?) - (and (or (= % :p2) (= % :p4)) hover-h?) - (= @hover %)) - negate {:p1 (if (:flip-y frame) true false) - :p2 (if (:flip-x frame) true false) - :p3 (if (:flip-y frame) true false) - :p4 (if (:flip-x frame) true false)} - negate (cond-> negate - (not= :auto (:layout-item-h-sizing frame)) (assoc :p2 (not (:p2 negate))) - (not= :auto (:layout-item-v-sizing frame)) (assoc :p3 (not (:p3 negate)))) - - padding-rect-data {:p1 {:key (str frame-id "-p1") - :x x1 - :y (if (:flip-y frame) (- y2 (:p1 padding)) y1) - :width width - :height (:p1 padding) - :initial-value (:p1 padding) - :resize-type (if (:flip-y frame) :bottom :top) - :resize-axis :y - :resize-negate? (:p1 negate)} - :p2 {:key (str frame-id "-p2") - :x (if (:flip-x frame) x1 (- x2 (:p2 padding))) - :y y1 - :width (:p2 padding) - :height height - :initial-value (:p2 padding) - :resize-type :left - :resize-axis :x - :resize-negate? (:p2 negate)} - :p3 {:key (str frame-id "-p3") - :x x1 - :y (if (:flip-y frame) y1 (- y2 (:p3 padding))) - :width width - :height (:p3 padding) - :initial-value (:p3 padding) - :resize-type :bottom - :resize-axis :y - :resize-negate? (:p3 negate)} - :p4 {:key (str frame-id "-p4") - :x (if (:flip-x frame) (- x2 (:p4 padding)) x1) - :y y1 - :width (:p4 padding) - :height height - :initial-value (:p4 padding) - :resize-type (if (:flip-x frame) :right :left) - :resize-axis :x - :resize-negate? (:p4 negate)}}] - - [:g.paddings {:pointer-events "visible"} - (for [[padding-num rect-data] padding-rect-data] - [:& padding-display {:key (:key rect-data) - :frame-id frame-id - :zoom zoom - :hover-all? hover-all? - :hover-v? hover-v? - :hover-h? hover-h? - :padding padding - :mouse-pos mouse-pos - :hover-value hover-value - :padding-num padding-num - :on-pointer-enter (partial on-pointer-enter padding-num (get padding padding-num)) - :on-pointer-leave on-pointer-leave - :hover? (hover? padding-num) - :selected? (get paddings-selected padding-num) - :rect-data rect-data}]) - (when @hover - [:& flex-display-pill {:height pill-height - :width pill-width - :font-size (/ font-size zoom) - :border-radius (/ flex-display-pill-border-radius zoom) - :color distance-color - :x (:x @mouse-pos) - :y (- (:y @mouse-pos) pill-width) - :value @hover-value}])])) - -(mf/defc margin-display [{:keys [shape-id zoom hover-all? hover-v? hover-h? margin-num margin on-pointer-enter on-pointer-leave - rect-data hover? selected? mouse-pos hover-value]}] - (let [resizing? (mf/use-var false) - start (mf/use-var nil) - original-value (mf/use-var 0) - negate? (true? (:resize-negate? rect-data)) - axis (:resize-axis rect-data) - - on-pointer-down - (mf/use-fn - (mf/deps shape-id margin-num margin) - (fn [event] - (dom/capture-pointer event) - (reset! resizing? true) - (reset! start (dom/get-client-position event)) - (reset! original-value (:initial-value rect-data)))) - - on-lost-pointer-capture - (mf/use-fn - (mf/deps shape-id margin-num margin) - (fn [event] - (dom/release-pointer event) - (reset! resizing? false) - (reset! start nil) - (reset! original-value 0) - (st/emit! (dwm/apply-modifiers)))) - - on-pointer-move - (mf/use-fn - (mf/deps shape-id margin-num margin hover-all? hover-v? hover-h?) - (fn [event] - (let [pos (dom/get-client-position event)] - (reset! mouse-pos (point->viewport pos)) - (when @resizing? - (let [delta (-> (gpt/to-vec @start pos) - (cond-> negate? gpt/negate) - (get axis)) - val (int (max (+ @original-value (/ delta zoom)) 0)) - layout-item-margin (cond - hover-all? (assoc margin :m1 val :m2 val :m3 val :m4 val) - hover-v? (assoc margin :m1 val :m3 val) - hover-h? (assoc margin :m2 val :m4 val) - :else (assoc margin margin-num val)) - layout-item-margin-type (if (= (:m1 margin) (:m2 margin) (:m3 margin) (:m4 margin)) :simple :multiple) - modifiers (dwm/create-modif-tree [shape-id] - (-> (ctm/empty) - (ctm/change-property :layout-item-margin layout-item-margin) - (ctm/change-property :layout-item-margin-type layout-item-margin-type)))] - (reset! hover-value val) - (st/emit! (dwm/set-modifiers modifiers)))))))] - - [:rect.margin-rect {:x (:x rect-data) - :y (:y rect-data) - :width (:width rect-data) - :height (:height rect-data) - :on-pointer-enter on-pointer-enter - :on-pointer-leave on-pointer-leave - :on-pointer-down on-pointer-down - :on-lost-pointer-capture on-lost-pointer-capture - :on-pointer-move on-pointer-move - :class (when (or hover? selected?) - (if (= (:resize-axis rect-data) :x) (cur/get-dynamic "resize-ew" 0) (cur/get-dynamic "resize-ew" 90))) - :style {:fill (if (or hover? selected?) warning-color "none") - :opacity (if selected? 0.5 0.25)}}])) - -(mf/defc margin-rects [{:keys [shape frame zoom alt? shift?]}] - (let [shape-id (:id shape) - pill-width (/ flex-display-pill-width zoom) - pill-height (/ flex-display-pill-height zoom) - margins-selected (mf/deref refs/workspace-margins-selected) - hover-value (mf/use-var 0) - mouse-pos (mf/use-var nil) - hover (mf/use-var nil) - hover-all? (and (not (nil? @hover)) alt?) - hover-v? (and (or (= @hover :m1) (= @hover :m3)) shift?) - hover-h? (and (or (= @hover :m2) (= @hover :m4)) shift?) - margin (:layout-item-margin shape) - {:keys [width height x1 x2 y1 y2]} (:selrect shape) - on-pointer-enter (fn [hover-type val] - (reset! hover hover-type) - (reset! hover-value val)) - on-pointer-leave #(reset! hover nil) - hover? #(or hover-all? - (and (or (= % :m1) (= % :m3)) hover-v?) - (and (or (= % :m2) (= % :m4)) hover-h?) - (= @hover %)) - margin-display-data {:m1 {:key (str shape-id "-m1") - :x x1 - :y (if (:flip-y frame) y2 (- y1 (:m1 margin))) - :width width - :height (:m1 margin) - :initial-value (:m1 margin) - :resize-type :top - :resize-axis :y - :resize-negate? (:flip-y frame)} - :m2 {:key (str shape-id "-m2") - :x (if (:flip-x frame) (- x1 (:m2 margin)) x2) - :y y1 - :width (:m2 margin) - :height height - :initial-value (:m2 margin) - :resize-type :left - :resize-axis :x - :resize-negate? (:flip-x frame)} - :m3 {:key (str shape-id "-m3") - :x x1 - :y (if (:flip-y frame) (- y1 (:m3 margin)) y2) - :width width - :height (:m3 margin) - :initial-value (:m3 margin) - :resize-type :top - :resize-axis :y - :resize-negate? (:flip-y frame)} - :m4 {:key (str shape-id "-m4") - :x (if (:flip-x frame) x2 (- x1 (:m4 margin))) - :y y1 - :width (:m4 margin) - :height height - :initial-value (:m4 margin) - :resize-type :left - :resize-axis :x - :resize-negate? (:flip-x frame)}}] - - [:g.margins {:pointer-events "visible"} - (for [[margin-num rect-data] margin-display-data] - [:& margin-display - {:key (:key rect-data) - :shape-id shape-id - :zoom zoom - :hover-all? hover-all? - :hover-v? hover-v? - :hover-h? hover-h? - :margin-num margin-num - :margin margin - :on-pointer-enter (partial on-pointer-enter margin-num (get margin margin-num)) - :on-pointer-leave on-pointer-leave - :rect-data rect-data - :hover? (hover? margin-num) - :selected? (get margins-selected margin-num) - :mouse-pos mouse-pos - :hover-value hover-value}]) - - (when @hover - [:& flex-display-pill {:height pill-height - :width pill-width - :font-size (/ font-size zoom) - :border-radius (/ flex-display-pill-border-radius zoom) - :color warning-color - :x (:x @mouse-pos) - :y (- (:y @mouse-pos) pill-width) - :value @hover-value}])])) - -(mf/defc gap-display [{:keys [frame-id zoom gap-type gap on-pointer-enter on-pointer-leave - rect-data hover? selected? mouse-pos hover-value]}] - (let [resizing (mf/use-var nil) - start (mf/use-var nil) - original-value (mf/use-var 0) - negate? (:resize-negate? rect-data) - axis (:resize-axis rect-data) - - on-pointer-down - (mf/use-fn - (mf/deps frame-id gap-type gap) - (fn [event] - (dom/capture-pointer event) - (reset! resizing gap-type) - (reset! start (dom/get-client-position event)) - (reset! original-value (:initial-value rect-data)))) - - on-lost-pointer-capture - (mf/use-fn - (mf/deps frame-id gap-type gap) - (fn [event] - (dom/release-pointer event) - (reset! resizing nil) - (reset! start nil) - (reset! original-value 0) - (st/emit! (dwm/apply-modifiers)))) - - on-pointer-move - (mf/use-fn - (mf/deps frame-id gap-type gap) - (fn [event] - (let [pos (dom/get-client-position event)] - (reset! mouse-pos (point->viewport pos)) - (when (= @resizing gap-type) - (let [delta (-> (gpt/to-vec @start pos) - (cond-> negate? gpt/negate) - (get axis)) - val (int (max (+ @original-value (/ delta zoom)) 0)) - layout-gap (assoc gap gap-type val) - modifiers (dwm/create-modif-tree [frame-id] (ctm/change-property (ctm/empty) :layout-gap layout-gap))] - - (reset! hover-value val) - (st/emit! (dwm/set-modifiers modifiers)))))))] - - [:rect.gap-rect {:x (:x rect-data) - :y (:y rect-data) - :width (:width rect-data) - :height (:height rect-data) - :on-pointer-enter on-pointer-enter - :on-pointer-leave on-pointer-leave - :on-pointer-down on-pointer-down - :on-lost-pointer-capture on-lost-pointer-capture - :on-pointer-move on-pointer-move - :class (when (or hover? selected?) - (if (= (:resize-axis rect-data) :x) (cur/get-dynamic "resize-ew" 0) (cur/get-dynamic "resize-ew" 90))) - :style {:fill (if (or hover? selected?) distance-color "none") - :opacity (if selected? 0.5 0.25)}}])) - -(mf/defc gap-rects [{:keys [frame zoom]}] - (let [frame-id (:id frame) - saved-dir (:layout-flex-dir frame) - is-col? (or (= :column saved-dir) (= :column-reverse saved-dir)) - flip-x (:flip-x frame) - flip-y (:flip-y frame) - pill-width (/ flex-display-pill-width zoom) - pill-height (/ flex-display-pill-height zoom) - workspace-modifiers (mf/deref refs/workspace-modifiers) - gap-selected (mf/deref refs/workspace-gap-selected) - hover (mf/use-var nil) - hover-value (mf/use-var 0) - mouse-pos (mf/use-var nil) - padding (:layout-padding frame) - gap (:layout-gap frame) - {:keys [width height x1 y1]} (:selrect frame) - on-pointer-enter (fn [hover-type val] - (reset! hover hover-type) - (reset! hover-value val)) - - on-pointer-leave #(reset! hover nil) - negate {:column-gap (if flip-x true false) - :row-gap (if flip-y true false)} - - objects (wsh/lookup-page-objects @st/state) - children (->> (cfh/get-immediate-children objects frame-id) - (remove ctl/position-absolute?)) - - children-to-display (if (or (= :row-reverse saved-dir) - (= :column-reverse saved-dir)) - (drop-last children) - (rest children)) - children-to-display (->> children-to-display - (map #(gsh/transform-shape % (get-in workspace-modifiers [(:id %) :modifiers])))) - - wrap-blocks - (let [block-children (->> children - (map #(vector (gpo/parent-coords-bounds (:points %) (:points frame)) %))) - bounds (d/lazy-map (keys objects) #(gsh/shape->points (get objects %))) - - layout-data (gsl/calc-layout-data frame (:points frame) block-children bounds objects) - layout-bounds (:layout-bounds layout-data) - xv #(gpo/start-hv layout-bounds %) - yv #(gpo/start-vv layout-bounds %)] - (for [{:keys [start-p line-width line-height layout-gap-row layout-gap-col num-children]} (:layout-lines layout-data)] - (let [line-width (if is-col? line-width (+ line-width (* (dec num-children) layout-gap-row))) - line-height (if is-col? (+ line-height (* (dec num-children) layout-gap-col)) line-height) - end-p (-> start-p (gpt/add (xv line-width)) (gpt/add (yv line-height)))] - {:x1 (min (:x start-p) (:x end-p)) - :y1 (min (:y start-p) (:y end-p)) - :x2 (max (:x start-p) (:x end-p)) - :y2 (max (:y start-p) (:y end-p))}))) - - block-contains - (fn [x y block] - (if is-col? - (<= (:x1 block) x (:x2 block)) - (<= (:y1 block) y (:y2 block)))) - - get-container-block - (fn [shape] - (let [selrect (:selrect shape) - x (/ (+ (:x1 selrect) (:x2 selrect)) 2) - y (/ (+ (:y1 selrect) (:y2 selrect)) 2)] - (->> wrap-blocks - (filter #(block-contains x y %)) - first))) - - create-cgdd - (fn [shape] - (let [block (get-container-block shape) - x (if flip-x - (- (:x1 (:selrect shape)) - (get-in shape [:layout-item-margin :m2]) - (:column-gap gap)) - (+ (:x2 (:selrect shape)) (get-in shape [:layout-item-margin :m2]))) - y (:y1 block) - h (- (:y2 block) (:y1 block))] - {:x x - :y y - :height h - :width (:column-gap gap) - :initial-value (:column-gap gap) - :resize-type :left - :resize-axis :x - :resize-negate? (:column-gap negate) - :gap-type (if is-col? :row-gap :column-gap)})) - - create-cgdd-block - (fn [block] - (let [x (if flip-x - (- (:x1 block) (:column-gap gap)) - (:x2 block)) - y (if flip-y - (+ y1 (:p3 padding)) - (+ y1 (:p1 padding))) - h (- height (+ (:p1 padding) (:p3 padding)))] - {:x x - :y y - :width (:column-gap gap) - :height h - :initial-value (:column-gap gap) - :resize-type :left - :resize-axis :x - :resize-negate? (:column-gap negate) - :gap-type (if is-col? :column-gap :row-gap)})) - - create-rgdd - (fn [shape] - (let [block (get-container-block shape) - x (:x1 block) - y (if flip-y - (- (:y1 (:selrect shape)) - (get-in shape [:layout-item-margin :m3]) - (:row-gap gap)) - (+ (:y2 (:selrect shape)) (get-in shape [:layout-item-margin :m3]))) - w (- (:x2 block) (:x1 block))] - {:x x - :y y - :width w - :height (:row-gap gap) - :initial-value (:row-gap gap) - :resize-type :bottom - :resize-axis :y - :resize-negate? (:row-gap negate) - :gap-type (if is-col? :row-gap :column-gap)})) - - create-rgdd-block - (fn [block] - (let [x (if flip-x - (+ x1 (:p2 padding)) - (+ x1 (:p4 padding))) - y (if flip-y - (- (:y1 block) (:row-gap gap)) - (:y2 block)) - w (- width (+ (:p2 padding) (:p4 padding)))] - {:x x - :y y - :width w - :height (:row-gap gap) - :initial-value (:row-gap gap) - :resize-type :bottom - :resize-axis :y - :resize-negate? (:row-gap negate) - :gap-type (if is-col? :column-gap :row-gap)})) - - display-blocks (if is-col? - (->> (drop-last wrap-blocks) - (map create-cgdd-block)) - (->> (drop-last wrap-blocks) - (map create-rgdd-block))) - - display-children (if is-col? - (->> children-to-display - (map create-rgdd)) - (->> children-to-display - (map create-cgdd)))] - - [:g.gaps {:pointer-events "visible"} - (for [[index display-item] (d/enumerate (concat display-blocks display-children))] - (let [gap-type (:gap-type display-item)] - [:& gap-display {:key (str frame-id index) - :frame-id frame-id - :zoom zoom - :gap-type gap-type - :gap gap - :on-pointer-enter (partial on-pointer-enter gap-type (get gap gap-type)) - :on-pointer-leave on-pointer-leave - :rect-data display-item - :hover? (= @hover gap-type) - :selected? (= gap-selected gap-type) - :mouse-pos mouse-pos - :hover-value hover-value}])) - - (when @hover - [:& flex-display-pill {:height pill-height - :width pill-width - :font-size (/ font-size zoom) - :border-radius (/ flex-display-pill-border-radius zoom) - :color distance-color - :x (:x @mouse-pos) - :y (- (:y @mouse-pos) pill-width) - :value @hover-value}])])) - -(mf/defc padding - [{:keys [frame zoom alt? shift?]}] - (when frame - [:g.measurement-gaps {:pointer-events "none"} - [:g.hover-shapes - [:& padding-rects {:frame frame :zoom zoom :alt? alt? :shift? shift?}]]])) - -(mf/defc gap - [{:keys [frame zoom]}] - (when frame - [:g.measurement-gaps {:pointer-events "none"} - [:g.hover-shapes - [:& gap-rects {:frame frame :zoom zoom}]]])) - -(mf/defc margin - [{:keys [shape parent zoom alt? shift?]}] - (when shape - [:g.measurement-gaps {:pointer-events "none"} - [:g.hover-shapes - [:& margin-rects {:shape shape :frame parent :zoom zoom :alt? alt? :shift? shift?}]]])) - - diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index 35cb52be7..7fdc82f6c 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -16,6 +16,7 @@ [app.main.data.workspace.modifiers :as dwm] [app.main.refs :as refs] [app.main.ui.context :as ctx] + [app.main.ui.flex-controls :as mfc] [app.main.ui.hooks :as ui-hooks] [app.main.ui.measurements :as msr] [app.main.ui.shapes.export :as use] @@ -232,31 +233,32 @@ disabled-guides? (or drawing-tool transform drawing-path? node-editing?) - one-selected-shape? (= (count selected-shapes) 1) + single-select? (= (count selected-shapes) 1) - show-padding? (and (nil? transform) - one-selected-shape? - (= (:type (first selected-shapes)) :frame) - (= (:layout (first selected-shapes)) :flex) - (zero? (:rotation (first selected-shapes)))) + first-shape (first selected-shapes) + show-padding? + (and (nil? transform) + single-select? + (= (:type first-shape) :frame) + (= (:layout first-shape) :flex) + (zero? (:rotation first-shape))) - show-margin? (and (nil? transform) - one-selected-shape? - (= (:layout selected-frame) :flex) - (zero? (:rotation (first selected-shapes)))) + show-margin? + (and (nil? transform) + single-select? + (= (:layout selected-frame) :flex) + (zero? (:rotation first-shape))) - first-selected-shape (first selected-shapes) - selecting-first-level-frame? (and one-selected-shape? - (cfh/root-frame? first-selected-shape)) + selecting-first-level-frame? (and single-select? (cfh/root-frame? first-shape)) offset-x (if selecting-first-level-frame? - (:x first-selected-shape) + (:x first-shape) (:x selected-frame)) offset-y (if selecting-first-level-frame? - (:y (first selected-shapes)) + (:y first-shape) (:y selected-frame)) rule-area-size (/ rules/rule-area-size zoom)] @@ -439,24 +441,28 @@ :zoom zoom}]) (when show-padding? - [:* - [:& msr/padding - {:frame (first selected-shapes) - :hover @frame-hover - :zoom zoom - :alt? @alt? - :shift? @shift?}] + [:& mfc/padding-control + {:frame first-shape + :hover @frame-hover + :zoom zoom + :alt? @alt? + :shift? @shift? + :on-move-selected on-move-selected + :on-context-menu on-menu-selected}]) - [:& msr/gap - {:frame (first selected-shapes) - :hover @frame-hover - :zoom zoom - :alt? @alt? - :shift? @shift?}]]) + (when show-padding? + [:& mfc/gap-control + {:frame first-shape + :hover @frame-hover + :zoom zoom + :alt? @alt? + :shift? @shift? + :on-move-selected on-move-selected + :on-context-menu on-menu-selected}]) (when show-margin? - [:& msr/margin - {:shape (first selected-shapes) + [:& mfc/margin-control + {:shape first-shape :parent selected-frame :hover @frame-hover :zoom zoom diff --git a/frontend/src/app/main/ui/workspace/viewport/actions.cljs b/frontend/src/app/main/ui/workspace/viewport/actions.cljs index 64fe00e77..3a9174a9d 100644 --- a/frontend/src/app/main/ui/workspace/viewport/actions.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/actions.cljs @@ -43,7 +43,6 @@ (mf/deps id blocked hidden type selected edition drawing-tool text-editing? node-editing? grid-editing? drawing-path? create-comment? @z? @space? panning workspace-read-only?) - (fn [bevent] ;; We need to handle editor related stuff here because ;; handling on editor dom node does not works properly. @@ -127,6 +126,7 @@ (not mod?) (not shift?) (not @space?)) + (dom/prevent-default bevent) (dom/stop-propagation bevent) (when-not (or workspace-read-only? @z?) From abb3a3302132038833e5ec019355567dbc7ed56a Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 18 Jan 2024 11:37:26 +0100 Subject: [PATCH 5/9] :lipstick: Change rules styles --- .../app/main/ui/workspace/viewport/rules.cljs | 45 +++++++++++-------- 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/viewport/rules.cljs b/frontend/src/app/main/ui/workspace/viewport/rules.cljs index c3af821de..21db6a320 100644 --- a/frontend/src/app/main/ui/workspace/viewport/rules.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/rules.cljs @@ -23,8 +23,9 @@ (def rules-background "var(--panel-background-color)") (def selection-area-color "var(--color-primary)") (def selection-area-opacity 0.3) -(def over-number-size 50) -(def over-number-opacity 0.7) +(def over-number-size 100) +(def over-number-opacity 0.8) +(def over-number-percent 0.75) (def font-size 12) (def font-family "worksans") @@ -204,7 +205,29 @@ ;; When using the format-number callls we consider if the guide is associated to a frame and we show the position relative to it with the offset (let [rules-background rules-background] [:g.selection-area + [:defs + [:linearGradient {:id "selection-gradient-start"} + [:stop {:offset "0%" :stop-color rules-background :stop-opacity 0}] + [:stop {:offset "40%" :stop-color rules-background :stop-opacity 1}] + [:stop {:offset "100%" :stop-color rules-background :stop-opacity 1}]] + + [:linearGradient {:id "selection-gradient-end"} + [:stop {:offset "0%" :stop-color rules-background :stop-opacity 1}] + [:stop {:offset "60%" :stop-color rules-background :stop-opacity 1}] + [:stop {:offset "100%" :stop-color rules-background :stop-opacity 0}]]] [:g + [:rect {:x (- (:x selection-rect) (* (* over-number-size over-number-percent) zoom-inverse)) + :y (:y vbox) + :width (* over-number-size zoom-inverse) + :height (* rule-area-size zoom-inverse) + :fill "url('#selection-gradient-start')"}] + + [:rect {:x (- (:x2 selection-rect) (* over-number-size (- 1 over-number-percent))) + :y (:y vbox) + :width (* over-number-size zoom-inverse) + :height (* rule-area-size zoom-inverse) + :fill "url('#selection-gradient-end')"}] + [:rect {:x (:x selection-rect) :y (:y vbox) :width (:width selection-rect) @@ -212,15 +235,8 @@ :style {:fill selection-area-color :fill-opacity selection-area-opacity}}] - [:rect {:x (- (:x selection-rect) (* over-number-size zoom-inverse)) - :y (:y vbox) - :width (* over-number-size zoom-inverse) - :height (* rule-area-size zoom-inverse) - :style {:fill rules-background - :fill-opacity over-number-opacity}}] - [:text {:x (- (:x1 selection-rect) (* 4 zoom-inverse)) - :y (+ (:y vbox) (* 12 zoom-inverse)) + :y (+ (:y vbox) (* 10.6 zoom-inverse)) :text-anchor "end" :dominant-baseline "middle" :style {:font-size (* font-size zoom-inverse) @@ -228,15 +244,8 @@ :fill selection-area-color}} (fmt/format-number (- (:x1 selection-rect) offset-x))] - [:rect {:x (:x2 selection-rect) - :y (:y vbox) - :width (* over-number-size zoom-inverse) - :height (* rule-area-size zoom-inverse) - :style {:fill rules-background - :fill-opacity over-number-opacity}}] - [:text {:x (+ (:x2 selection-rect) (* 4 zoom-inverse)) - :y (+ (:y vbox) (* 12 zoom-inverse)) + :y (+ (:y vbox) (* 10.6 zoom-inverse)) :text-anchor "start" :dominant-baseline "middle" :style {:font-size (* font-size zoom-inverse) From 800d35a42c7f74d62a630e97ea7bfab26e307000 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 18 Jan 2024 12:06:51 +0100 Subject: [PATCH 6/9] :sparkles: Update text name on edit --- .../src/app/main/data/workspace/texts.cljs | 46 +++++++++++-------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/frontend/src/app/main/data/workspace/texts.cljs b/frontend/src/app/main/data/workspace/texts.cljs index 5f7bfb499..c3e98739b 100644 --- a/frontend/src/app/main/data/workspace/texts.cljs +++ b/frontend/src/app/main/data/workspace/texts.cljs @@ -52,6 +52,14 @@ (when-let [editor (:workspace-editor state)] (ts/schedule #(.focus ^js editor)))))) +(defn gen-name + [editor] + (when (some? editor) + (let [result + (-> (ted/get-editor-current-plain-text editor) + (txt/generate-shape-name))] + (when (not= result "") result)))) + (defn update-editor-state [{:keys [id] :as shape} editor-state] (ptk/reify ::update-editor-state @@ -62,7 +70,7 @@ (update state :workspace-editor-state dissoc id))))) (defn finalize-editor-state - [id] + [id update-name?] (ptk/reify ::finalize-editor-state ptk/WatchEvent (watch [_ state _] @@ -72,8 +80,8 @@ editor-state (get-in state [:workspace-editor-state id]) content (-> editor-state (ted/get-editor-current-content)) - text (-> (ted/get-editor-current-plain-text editor-state) - (txt/generate-shape-name)) + name (gen-name editor-state) + new-shape? (nil? (:content shape))] (if (ted/content-has-text? content) (let [content (d/merge (ted/export-content content) @@ -93,8 +101,8 @@ (assoc :content content) (cond-> position-data (assoc :position-data position-data)) - (cond-> new-shape? - (assoc :name text)) + (cond-> (and update-name? (some? name)) + (assoc :name name)) (cond-> (or (some? width) (some? height)) (gsh/transform-shape (ctm/change-size shape width height)))))) {:undo-group (when new-shape? id)}))))) @@ -104,29 +112,31 @@ (dwsh/delete-shapes #{id}))))))))) (defn initialize-editor-state - [{:keys [id content] :as shape} decorator] + [{:keys [id name content] :as shape} decorator] (ptk/reify ::initialize-editor-state ptk/UpdateEvent (update [_ state] - (let [text-state (some->> content ted/import-content) - attrs (d/merge txt/default-text-attrs - (get-in state [:workspace-global :default-font])) - editor (cond-> (ted/create-editor-state text-state decorator) - (and (nil? content) (some? attrs)) - (ted/update-editor-current-block-data attrs))] + (let [text-state (some->> content ted/import-content) + attrs (d/merge txt/default-text-attrs + (get-in state [:workspace-global :default-font])) + editor (cond-> (ted/create-editor-state text-state decorator) + (and (nil? content) (some? attrs)) + (ted/update-editor-current-block-data attrs))] (-> state (assoc-in [:workspace-editor-state id] editor)))) ptk/WatchEvent - (watch [_ _ stream] + (watch [_ state stream] ;; We need to finalize editor on two main events: (1) when user ;; explicitly navigates to other section or page; (2) when user ;; leaves the editor. - (->> (rx/merge - (rx/filter (ptk/type? ::rt/navigate) stream) - (rx/filter #(= ::finalize-editor-state %) stream)) - (rx/take 1) - (rx/map #(finalize-editor-state id)))))) + (let [editor (dm/get-in state [:workspace-editor-state id]) + update-name? (or (nil? content) (= name (gen-name editor)))] + (->> (rx/merge + (rx/filter (ptk/type? ::rt/navigate) stream) + (rx/filter #(= ::finalize-editor-state %) stream)) + (rx/take 1) + (rx/map #(finalize-editor-state id update-name?))))))) (defn select-all "Select all content of the current editor. When not editor found this From 4c7e565f6ad4a8167481da3d8fd1963a9c031ffc Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 18 Jan 2024 12:45:48 +0100 Subject: [PATCH 7/9] :bug: Fix keep layout-item properties after swap --- common/src/app/common/types/component.cljc | 23 ++++++++++--------- .../app/main/data/workspace/libraries.cljs | 18 +++++++++++---- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/common/src/app/common/types/component.cljc b/common/src/app/common/types/component.cljc index 9dd0a8c80..67ffd2414 100644 --- a/common/src/app/common/types/component.cljc +++ b/common/src/app/common/types/component.cljc @@ -83,18 +83,19 @@ :layout-grid-dir :layout-container :layout-grid-rows :layout-container :layout-grid-columns :layout-container - :layout-grid-cells :layout-container - - :layout-item-margin :layout-item - :layout-item-margin-type :layout-item - :layout-item-h-sizing :layout-item - :layout-item-v-sizing :layout-item - :layout-item-max-h :layout-item - :layout-item-min-h :layout-item - :layout-item-max-w :layout-item - :layout-item-min-w :layout-item - :layout-item-align-self :layout-item}) + :layout-grid-cells :layout-container}) +(def swap-keep-attrs + [: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-absolute + :layout-item-z-index]) (defn instance-root? "Check if this shape is the head of a top instance." diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index 30106467d..2a7b7984d 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -829,7 +829,7 @@ 0))))) (defn- add-component-for-swap - [shape file-id id-new-component index target-cell] + [shape file-id id-new-component index target-cell keep-props-values] (dm/assert! (uuid? id-new-component)) (dm/assert! (uuid? file-id)) (ptk/reify ::add-component-for-swap @@ -856,8 +856,13 @@ (:parent-id shape) (:frame-id shape)) - ;; We need to set the same index as the original shape - changes (pcb/change-parent changes (:parent-id shape) [new-shape] index {:component-swap true})] + changes + (-> changes + ;; Restore the properties + (pcb/update-shapes [(:id new-shape)] #(d/patch-object % keep-props-values)) + + ;; We need to set the same index as the original shape + (pcb/change-parent (:parent-id shape) [new-shape] index {:component-swap true}))] ;; First delete so we don't break the grid layout cells (rx/of (dch/commit-changes changes) @@ -880,9 +885,12 @@ target-cell (when (ctl/grid-layout? parent) (ctl/get-cell-by-shape-id parent (:id shape))) - index (find-shape-index objects (:parent-id shape) (:id shape))] + index (find-shape-index objects (:parent-id shape) (:id shape)) + + ;; Store the properties that need to be maintained when the component is swapped + keep-props-values (select-keys shape ctk/swap-keep-attrs)] (rx/of (dwsh/delete-shapes nil (d/ordered-set (:id shape)) {:component-swap true}) - (add-component-for-swap shape file-id id-new-component index target-cell) + (add-component-for-swap shape file-id id-new-component index target-cell keep-props-values) (ptk/data-event :layout/update [(:parent-id shape)])))))) (defn component-multi-swap From 26af5c78471097c82e7fa93d8ddb9c2cf90df8df Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 18 Jan 2024 17:16:16 +0100 Subject: [PATCH 8/9] :bug: Fix keep cells when create component inside grid layout --- .../src/app/common/files/shapes_helpers.cljc | 49 ++++++++++++++----- common/src/app/common/types/container.cljc | 4 +- common/src/app/common/types/shape/layout.cljc | 12 +++++ .../app/main/data/workspace/libraries.cljs | 16 +++--- .../app/main/ui/workspace/context_menu.cljs | 5 +- .../options/menus/layout_container.cljs | 3 +- 6 files changed, 68 insertions(+), 21 deletions(-) diff --git a/common/src/app/common/files/shapes_helpers.cljc b/common/src/app/common/files/shapes_helpers.cljc index 8a656886f..03e3e89c1 100644 --- a/common/src/app/common/files/shapes_helpers.cljc +++ b/common/src/app/common/files/shapes_helpers.cljc @@ -71,6 +71,23 @@ parent-id (or parent-id (dm/get-in objects [selected-id :parent-id])) base-parent (get objects parent-id) + layout-props + (when (and (= 1 (count selected)) + (ctl/any-layout? base-parent)) + (let [shape (get objects selected-id)] + (select-keys shape ctl/layout-item-props))) + + target-cell-id + (if (and (nil? target-cell-id) + (ctl/grid-layout? objects parent-id)) + ;; Find the top-left grid cell of the selected elements + (let [ncols (count (:layout-grid-columns base-parent))] + (->> selected + (map #(ctl/get-cell-by-shape-id base-parent %)) + (apply min-key (fn [{:keys [row column]}] (+ (* ncols row) column))) + :id)) + target-cell-id) + attrs {:type :frame :x (:x srect) :y (:y srect) @@ -90,12 +107,14 @@ :parent-id parent-id :shapes (into [] selected)) - :always - (with-meta {:index new-index}) + (some? layout-props) + (d/patch-object layout-props) (or (not= frame-id uuid/zero) without-fill?) (assoc :fills [] :hide-in-viewer true))) + shape (with-meta shape {:index new-index}) + [shape changes] (prepare-add-shape changes shape objects) @@ -105,15 +124,23 @@ changes (cond-> changes (ctl/grid-layout? objects (:parent-id shape)) - (-> (cond-> (some? target-cell-id) - (pcb/update-shapes - [(:parent-id shape)] - (fn [parent] - (-> parent - (assoc :layout-grid-cells (:layout-grid-cells base-parent)) - (assoc-in [:layout-grid-cells target-cell-id :shapes] [id]) - (assoc :position :auto))))) - (pcb/update-shapes [(:parent-id shape)] ctl/assign-cells {:with-objects? true}) + (-> (pcb/update-shapes + [(:parent-id shape)] + (fn [parent objects] + ;; This restores the grid layout before adding and moving the shapes + ;; this is done because the add+move could have altered the layout and we + ;; want to do it after both operations are completed. Also here we could + ;; asign the new element to a target-cell + (-> parent + (assoc :layout-grid-cells (:layout-grid-cells base-parent)) + (assoc :layout-grid-rows (:layout-grid-rows base-parent)) + (assoc :layout-grid-columns (:layout-grid-columns base-parent)) + + (cond-> (some? target-cell-id) + (assoc-in [:layout-grid-cells target-cell-id :shapes] [(:id shape)])) + (ctl/assign-cells objects))) + {:with-objects? true}) + (pcb/reorder-grid-children [(:parent-id shape)])))] [shape changes]))))) diff --git a/common/src/app/common/types/container.cljc b/common/src/app/common/types/container.cljc index f70705230..b00a41b1a 100644 --- a/common/src/app/common/types/container.cljc +++ b/common/src/app/common/types/container.cljc @@ -6,6 +6,7 @@ (ns app.common.types.container (:require + [app.common.data :as d] [app.common.data.macros :as dm] [app.common.files.helpers :as cfh] [app.common.geom.point :as gpt] @@ -287,7 +288,8 @@ component-shape (if components-v2 (-> (get-shape component-page (:main-instance-id component)) (assoc :parent-id nil) ;; On v2 we force parent-id to nil in order to behave like v1 - (assoc :frame-id uuid/zero)) + (assoc :frame-id uuid/zero) + (d/without-keys ctk/swap-keep-attrs)) (get-shape component (:id component))) orig-pos (gpt/point (:x component-shape) (:y component-shape)) diff --git a/common/src/app/common/types/shape/layout.cljc b/common/src/app/common/types/shape/layout.cljc index 5a48aa016..104fb9bc7 100644 --- a/common/src/app/common/types/shape/layout.cljc +++ b/common/src/app/common/types/shape/layout.cljc @@ -73,6 +73,18 @@ (def justify-items-types #{:start :end :center :stretch}) +(def layout-item-props + [: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-absolute + :layout-item-z-index]) + (sm/def! ::layout-attrs [:map {:title "LayoutAttrs"} [:layout {:optional true} [::sm/one-of layout-types]] diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index 2a7b7984d..54047f0e3 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -1010,12 +1010,16 @@ (rx/of (dch/commit-changes (assoc changes ;; TODO a ver qué pasa con esto :file-id file-id)))) (when-not (empty? updated-frames) - (->> (rx/from updated-frames) - (rx/mapcat (fn [shape] - (rx/of - (dwt/clear-thumbnail file-id (:page-id shape) (:id shape) "frame") - (when-not (= (:frame-id shape) uuid/zero) - (dwt/clear-thumbnail file-id (:page-id shape) (:frame-id shape) "frame"))))))) + (rx/merge + (rx/of (ptk/data-event :layout/update (map :id updated-frames))) + (->> (rx/from updated-frames) + (rx/mapcat + (fn [shape] + (rx/of + (dwt/clear-thumbnail file-id (:page-id shape) (:id shape) "frame") + (when-not (= (:frame-id shape) uuid/zero) + (dwt/clear-thumbnail file-id (:page-id shape) (:frame-id shape) "frame")))))))) + (when (not= file-id library-id) ;; When we have just updated the library file, give some time for the ;; update to finish, before marking this file as synced. diff --git a/frontend/src/app/main/ui/workspace/context_menu.cljs b/frontend/src/app/main/ui/workspace/context_menu.cljs index cb5e635f8..da56b6338 100644 --- a/frontend/src/app/main/ui/workspace/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/context_menu.cljs @@ -106,8 +106,9 @@ [:span {:class (stl/css :title)} title] (when shortcut [:span {:class (stl/css :shortcut)} - (for [sc (scd/split-sc shortcut)] - [:span {:class (stl/css :shortcut-key)} sc])]) + (for [[idx sc] (d/enumerate (scd/split-sc shortcut))] + [:span {:key (dm/str shortcut "-" idx) + :class (stl/css :shortcut-key)} sc])]) (when (> (count children) 1) [:span {:class (stl/css :submenu-icon)} i/arrow-refactor]) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs index dff6d0888..fd5e42324 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs @@ -555,7 +555,8 @@ :on-change #(set-justify % type) :name (dm/str "grid-justify-items-" (d/name type))} (for [justify [:start :center :end :space-around :space-between :stretch]] - [:& radio-button {:value (d/name justify) + [:& radio-button {:key (dm/str "justify-item-" (d/name justify)) + :value (d/name justify) :icon (get-layout-grid-icon-refactor :justify-items justify is-col?) :title (dm/str "Justify items " (d/name justify)) :id (dm/str "justify-items-" (d/name justify) "-" (d/name type))}])])) From e36dce372a5750a0abe43c49f48dad78dbd70562 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Fri, 19 Jan 2024 09:52:33 +0100 Subject: [PATCH 9/9] :bug: Make create component children scale --- common/src/app/common/files/libraries_helpers.cljc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/common/src/app/common/files/libraries_helpers.cljc b/common/src/app/common/files/libraries_helpers.cljc index cf4b637c4..bd750dee5 100644 --- a/common/src/app/common/files/libraries_helpers.cljc +++ b/common/src/app/common/files/libraries_helpers.cljc @@ -39,6 +39,7 @@ [it shapes objects page-id file-id components-v2 prepare-create-group prepare-create-board] (let [changes (pcb/empty-changes it page-id) + from-singe-frame? (and (= 1 (count shapes)) (-> shapes first cfh/frame-shape?)) [root changes old-root-ids] (if (and (= (count shapes) 1) (or (and (= (:type (first shapes)) :group) (not components-v2)) @@ -72,6 +73,15 @@ [root changes (map :id shapes)])) + changes + (cond-> changes + (not from-singe-frame?) + (pcb/update-shapes + (:shapes root) + (fn [shape] + (-> shape + (assoc :constraints-h :scale :constraints-v :scale))))) + objects' (assoc objects (:id root) root) [root-shape changes] (generate-add-component-changes changes root objects' file-id page-id components-v2)