diff --git a/common/src/app/common/geom/shapes.cljc b/common/src/app/common/geom/shapes.cljc index f0776af5a..1bf496b99 100644 --- a/common/src/app/common/geom/shapes.cljc +++ b/common/src/app/common/geom/shapes.cljc @@ -205,3 +205,6 @@ ;; Modifiers (dm/export gsm/set-objects-modifiers) + +;; Rect +(dm/export grc/rect->points) diff --git a/common/src/app/common/geom/shapes/bounds.cljc b/common/src/app/common/geom/shapes/bounds.cljc index 4b35744d3..3fc8342a9 100644 --- a/common/src/app/common/geom/shapes/bounds.cljc +++ b/common/src/app/common/geom/shapes/bounds.cljc @@ -107,9 +107,7 @@ margin (if ignore-margin? 0 - (->> strokes - (map #(shape-stroke-margin % stroke-width)) - (reduce d/max 0))) + (shape-stroke-margin shape stroke-width)) shadow-width (->> (:shadow shape) @@ -125,8 +123,8 @@ 0)) (reduce d/max 0))] - {:horizontal (+ stroke-width margin shadow-width) - :vertical (+ stroke-width margin shadow-height)}))) + {:horizontal (mth/ceil (+ stroke-width margin shadow-width)) + :vertical (mth/ceil (+ stroke-width margin shadow-height))}))) (defn- add-padding [bounds padding] diff --git a/common/src/app/common/types/container.cljc b/common/src/app/common/types/container.cljc index ef7bc7657..35ff81f06 100644 --- a/common/src/app/common/types/container.cljc +++ b/common/src/app/common/types/container.cljc @@ -16,6 +16,7 @@ [app.common.types.components-list :as ctkl] [app.common.types.pages-list :as ctpl] [app.common.types.shape-tree :as ctst] + [app.common.types.shape.layout :as ctl] [app.common.uuid :as uuid])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -265,7 +266,7 @@ (gpt/add orig-pos delta) {:skip-components? true :bottom-frames? true})) - frame-ids-map (volatile! {}) + ids-map (volatile! {}) update-new-shape (fn [new-shape original-shape] @@ -275,8 +276,7 @@ (when (nil? (:parent-id original-shape)) (vswap! unames conj new-name)) - (when (= (:type original-shape) :frame) - (vswap! frame-ids-map assoc (:id original-shape) (:id new-shape))) + (vswap! ids-map assoc (:id original-shape) (:id new-shape)) (cond-> new-shape :always @@ -307,24 +307,29 @@ (dissoc :component-root)))) [new-shape new-shapes _] - (ctst/clone-object component-shape - nil - (if components-v2 (:objects component-page) (:objects component)) - update-new-shape - (fn [object _] object) - force-id - keep-ids?) + (ctst/clone-object + component-shape + nil + (if components-v2 (:objects component-page) (:objects component)) + update-new-shape + (fn [object _] object) + force-id + keep-ids?) - ;; If frame-id points to a shape inside the component, remap it to the - ;; corresponding new frame shape. If not, set it to the destination frame. - ;; Also fix empty parent-id. - remap-frame-id (fn [shape] - (as-> shape $ - (update $ :frame-id #(get @frame-ids-map % frame-id)) - (update $ :parent-id #(or % (:frame-id $)))))] + ;; If frame-id points to a shape inside the component, remap it to the + ;; corresponding new frame shape. If not, set it to the destination frame. + ;; Also fix empty parent-id. + remap-ids + (fn [shape] + (as-> shape $ + (update $ :frame-id #(get @ids-map % frame-id)) + (update $ :parent-id #(or % (:frame-id $))) + (cond-> $ + (ctl/grid-layout? shape) + (ctl/remap-grid-cells @ids-map))))] - [(remap-frame-id new-shape) - (map remap-frame-id new-shapes)]))) + [(remap-ids new-shape) + (map remap-ids new-shapes)]))) (defn get-top-instance "The case of having an instance that contains another instances. diff --git a/common/src/app/common/types/shape/layout.cljc b/common/src/app/common/types/shape/layout.cljc index 20617a80f..9abd71970 100644 --- a/common/src/app/common/types/shape/layout.cljc +++ b/common/src/app/common/types/shape/layout.cljc @@ -1177,3 +1177,15 @@ (for [r (range first-row (inc last-row)) c (range first-column (inc last-column))] [r c])))) + +(defn remap-grid-cells + "Remaps the shapes inside the cells" + [shape ids-map] + (let [do-remap-cells + (fn [cell] + (-> cell + (update :shapes #(mapv ids-map %)))) + shape + (-> shape + (update :layout-grid-cells update-vals do-remap-cells))] + shape)) diff --git a/common/test/common_tests/types_shape_interactions_test.cljc b/common/test/common_tests/types_shape_interactions_test.cljc index a9dc8a2d6..289ca7544 100644 --- a/common/test/common_tests/types_shape_interactions_test.cljc +++ b/common/test/common_tests/types_shape_interactions_test.cljc @@ -6,6 +6,7 @@ (ns common-tests.types-shape-interactions-test (:require + [app.common.math :as mth] [app.common.exceptions :as ex] [app.common.geom.point :as gpt] [app.common.geom.rect :as grc] @@ -332,56 +333,56 @@ (t/testing "Overlay top-center relative to auto" (let [i2 (ctsi/set-overlay-pos-type interaction-auto :top-center base-frame objects) [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] - (t/is (= (:x overlay-pos) 35)) - (t/is (= (:y overlay-pos) 0)) + (t/is (mth/close? (:x overlay-pos) 35)) + (t/is (mth/close? (:y overlay-pos) 0)) (t/is (= snap-v :top)) (t/is (= snap-h :center)))) (t/testing "Overlay top-right relative to auto" (let [i2 (ctsi/set-overlay-pos-type interaction-auto :top-right base-frame objects) [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] - (t/is (= (:x overlay-pos) 70)) - (t/is (= (:y overlay-pos) 0)) + (t/is (mth/close? (:x overlay-pos) 70)) + (t/is (mth/close? (:y overlay-pos) 0)) (t/is (= snap-v :top)) (t/is (= snap-h :right)))) (t/testing "Overlay bottom-left relative to auto" (let [i2 (ctsi/set-overlay-pos-type interaction-auto :bottom-left base-frame objects) [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] - (t/is (= (:x overlay-pos) 0)) - (t/is (= (:y overlay-pos) 80)) + (t/is (mth/close? (:x overlay-pos) 0)) + (t/is (mth/close? (:y overlay-pos) 80)) (t/is (= snap-v :bottom)) (t/is (= snap-h :left)))) (t/testing "Overlay bottom-center relative to auto" (let [i2 (ctsi/set-overlay-pos-type interaction-auto :bottom-center base-frame objects) [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] - (t/is (= (:x overlay-pos) 35)) - (t/is (= (:y overlay-pos) 80)) + (t/is (mth/close? (:x overlay-pos) 35)) + (t/is (mth/close? (:y overlay-pos) 80)) (t/is (= snap-v :bottom)) (t/is (= snap-h :center)))) (t/testing "Overlay bottom-right relative to auto" (let [i2 (ctsi/set-overlay-pos-type interaction-auto :bottom-right base-frame objects) [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] - (t/is (= (:x overlay-pos) 70)) - (t/is (= (:y overlay-pos) 80)) + (t/is (mth/close? (:x overlay-pos) 70)) + (t/is (mth/close? (:y overlay-pos) 80)) (t/is (= snap-v :bottom)) (t/is (= snap-h :right)))) (t/testing "Overlay center relative to auto" (let [i2 (ctsi/set-overlay-pos-type interaction-auto :center base-frame objects) [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] - (t/is (= (:x overlay-pos) 35)) - (t/is (= (:y overlay-pos) 40)) + (t/is (mth/close? (:x overlay-pos) 35)) + (t/is (mth/close? (:y overlay-pos) 40)) (t/is (= snap-v :center)) (t/is (= snap-h :center)))) (t/testing "Overlay manual relative to auto" (let [i2 (ctsi/set-overlay-pos-type interaction-auto :center base-frame objects) [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] - (t/is (= (:x overlay-pos) 35)) - (t/is (= (:y overlay-pos) 40)) + (t/is (mth/close? (:x overlay-pos) 35)) + (t/is (mth/close? (:y overlay-pos) 40)) (t/is (= snap-v :center)) (t/is (= snap-h :center)))) @@ -390,64 +391,64 @@ (ctsi/set-overlay-pos-type :manual base-frame objects) (ctsi/set-overlay-position (gpt/point 12 62))) [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] - (t/is (= (:x overlay-pos) 17)) - (t/is (= (:y overlay-pos) 67)) + (t/is (mth/close? (:x overlay-pos) 17)) + (t/is (mth/close? (:y overlay-pos) 67)) (t/is (= snap-v :top)) (t/is (= snap-h :left)))) (t/testing "Overlay top-left relative to base-frame" (let [i2 (ctsi/set-overlay-pos-type interaction-base-frame :top-left base-frame objects) [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] - (t/is (= (:x overlay-pos) 5)) - (t/is (= (:y overlay-pos) 5)) + (t/is (mth/close? (:x overlay-pos) 5)) + (t/is (mth/close? (:y overlay-pos) 5)) (t/is (= snap-v :top)) (t/is (= snap-h :left)))) (t/testing "Overlay top-center relative to base-frame" (let [i2 (ctsi/set-overlay-pos-type interaction-base-frame :top-center base-frame objects) [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] - (t/is (= (:x overlay-pos) 40)) - (t/is (= (:y overlay-pos) 5)) + (t/is (mth/close? (:x overlay-pos) 40)) + (t/is (mth/close? (:y overlay-pos) 5)) (t/is (= snap-v :top)) (t/is (= snap-h :center)))) (t/testing "Overlay top-right relative to base-frame" (let [i2 (ctsi/set-overlay-pos-type interaction-base-frame :top-right base-frame objects) [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] - (t/is (= (:x overlay-pos) 75)) - (t/is (= (:y overlay-pos) 5)) + (t/is (mth/close? (:x overlay-pos) 75)) + (t/is (mth/close? (:y overlay-pos) 5)) (t/is (= snap-v :top)) (t/is (= snap-h :right)))) (t/testing "Overlay bottom-left relative to base-frame" (let [i2 (ctsi/set-overlay-pos-type interaction-base-frame :bottom-left base-frame objects) [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] - (t/is (= (:x overlay-pos) 5)) - (t/is (= (:y overlay-pos) 85)) + (t/is (mth/close? (:x overlay-pos) 5)) + (t/is (mth/close? (:y overlay-pos) 85)) (t/is (= snap-v :bottom)) (t/is (= snap-h :left)))) (t/testing "Overlay bottom-center relative to base-frame" (let [i2 (ctsi/set-overlay-pos-type interaction-base-frame :bottom-center base-frame objects) [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] - (t/is (= (:x overlay-pos) 40)) - (t/is (= (:y overlay-pos) 85)) + (t/is (mth/close? (:x overlay-pos) 40)) + (t/is (mth/close? (:y overlay-pos) 85)) (t/is (= snap-v :bottom)) (t/is (= snap-h :center)))) (t/testing "Overlay bottom-right relative to base-frame" (let [i2 (ctsi/set-overlay-pos-type interaction-base-frame :bottom-right base-frame objects) [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] - (t/is (= (:x overlay-pos) 75)) - (t/is (= (:y overlay-pos) 85)) + (t/is (mth/close? (:x overlay-pos) 75)) + (t/is (mth/close? (:y overlay-pos) 85)) (t/is (= snap-v :bottom)) (t/is (= snap-h :right)))) (t/testing "Overlay center relative to base-frame" (let [i2 (ctsi/set-overlay-pos-type interaction-base-frame :center base-frame objects) [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] - (t/is (= (:x overlay-pos) 40)) - (t/is (= (:y overlay-pos) 45)) + (t/is (mth/close? (:x overlay-pos) 40)) + (t/is (mth/close? (:y overlay-pos) 45)) (t/is (= snap-v :center)) (t/is (= snap-h :center)))) @@ -456,64 +457,64 @@ (ctsi/set-overlay-pos-type :manual base-frame objects) (ctsi/set-overlay-position (gpt/point 12 62))) [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] - (t/is (= (:x overlay-pos) 17)) - (t/is (= (:y overlay-pos) 67)) + (t/is (mth/close? (:x overlay-pos) 17)) + (t/is (mth/close? (:y overlay-pos) 67)) (t/is (= snap-v :top)) (t/is (= snap-h :left)))) (t/testing "Overlay top-left relative to popup" (let [i2 (ctsi/set-overlay-pos-type interaction-popup :top-left base-frame objects) [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects popup base-frame overlay-frame frame-offset)] - (t/is (= (:x overlay-pos) 15)) - (t/is (= (:y overlay-pos) 15)) + (t/is (mth/close? (:x overlay-pos) 15)) + (t/is (mth/close? (:y overlay-pos) 15)) (t/is (= snap-v :top)) (t/is (= snap-h :left)))) (t/testing "Overlay top-center relative to popup" (let [i2 (ctsi/set-overlay-pos-type interaction-popup :top-center base-frame objects) [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects popup base-frame overlay-frame frame-offset)] - (t/is (= (:x overlay-pos) 25)) - (t/is (= (:y overlay-pos) 15)) + (t/is (mth/close? (:x overlay-pos) 25)) + (t/is (mth/close? (:y overlay-pos) 15)) (t/is (= snap-v :top)) (t/is (= snap-h :center)))) (t/testing "Overlay top-right relative to popup" (let [i2 (ctsi/set-overlay-pos-type interaction-popup :top-right base-frame objects) [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects popup base-frame overlay-frame frame-offset)] - (t/is (= (:x overlay-pos) 35)) - (t/is (= (:y overlay-pos) 15)) + (t/is (mth/close? (:x overlay-pos) 35)) + (t/is (mth/close? (:y overlay-pos) 15)) (t/is (= snap-v :top)) (t/is (= snap-h :right)))) (t/testing "Overlay bottom-left relative to popup" (let [i2 (ctsi/set-overlay-pos-type interaction-popup :bottom-left base-frame objects) [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects popup base-frame overlay-frame frame-offset)] - (t/is (= (:x overlay-pos) 15)) - (t/is (= (:y overlay-pos) 45)) + (t/is (mth/close? (:x overlay-pos) 15)) + (t/is (mth/close? (:y overlay-pos) 45)) (t/is (= snap-v :bottom)) (t/is (= snap-h :left)))) (t/testing "Overlay bottom-center relative to popup" (let [i2 (ctsi/set-overlay-pos-type interaction-popup :bottom-center base-frame objects) [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects popup base-frame overlay-frame frame-offset)] - (t/is (= (:x overlay-pos) 25)) - (t/is (= (:y overlay-pos) 45)) + (t/is (mth/close? (:x overlay-pos) 25)) + (t/is (mth/close? (:y overlay-pos) 45)) (t/is (= snap-v :bottom)) (t/is (= snap-h :center)))) (t/testing "Overlay bottom-right relative to popup" (let [i2 (ctsi/set-overlay-pos-type interaction-popup :bottom-right base-frame objects) [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects popup base-frame overlay-frame frame-offset)] - (t/is (= (:x overlay-pos) 35)) - (t/is (= (:y overlay-pos) 45)) + (t/is (mth/close? (:x overlay-pos) 35)) + (t/is (mth/close? (:y overlay-pos) 45)) (t/is (= snap-v :bottom)) (t/is (= snap-h :right)))) (t/testing "Overlay center relative to popup" (let [i2 (ctsi/set-overlay-pos-type interaction-popup :center base-frame objects) [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects popup base-frame overlay-frame frame-offset)] - (t/is (= (:x overlay-pos) 25)) - (t/is (= (:y overlay-pos) 30)) + (t/is (mth/close? (:x overlay-pos) 25)) + (t/is (mth/close? (:y overlay-pos) 30)) (t/is (= snap-v :center)) (t/is (= snap-h :center)))) @@ -522,64 +523,64 @@ (ctsi/set-overlay-pos-type :manual base-frame objects) (ctsi/set-overlay-position (gpt/point 12 62))) [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects popup base-frame overlay-frame frame-offset)] - (t/is (= (:x overlay-pos) 27)) - (t/is (= (:y overlay-pos) 77)) + (t/is (mth/close? (:x overlay-pos) 27)) + (t/is (mth/close? (:y overlay-pos) 77)) (t/is (= snap-v :top)) (t/is (= snap-h :left)))) (t/testing "Overlay top-left relative to popup" (let [i2 (ctsi/set-overlay-pos-type interaction-popup :top-left base-frame objects) [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects popup base-frame overlay-frame frame-offset)] - (t/is (= (:x overlay-pos) 15)) - (t/is (= (:y overlay-pos) 15)) + (t/is (mth/close? (:x overlay-pos) 15)) + (t/is (mth/close? (:y overlay-pos) 15)) (t/is (= snap-v :top)) (t/is (= snap-h :left)))) (t/testing "Overlay top-center relative to rect" (let [i2 (ctsi/set-overlay-pos-type interaction-rect :top-center base-frame objects) [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects rect base-frame overlay-frame frame-offset)] - (t/is (= (:x overlay-pos) 25)) - (t/is (= (:y overlay-pos) 15)) + (t/is (mth/close? (:x overlay-pos) 25)) + (t/is (mth/close? (:y overlay-pos) 15)) (t/is (= snap-v :top)) (t/is (= snap-h :center)))) (t/testing "Overlay top-right relative to rect" (let [i2 (ctsi/set-overlay-pos-type interaction-rect :top-right base-frame objects) [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects rect base-frame overlay-frame frame-offset)] - (t/is (= (:x overlay-pos) 35)) - (t/is (= (:y overlay-pos) 15)) + (t/is (mth/close? (:x overlay-pos) 35)) + (t/is (mth/close? (:y overlay-pos) 15)) (t/is (= snap-v :top)) (t/is (= snap-h :right)))) (t/testing "Overlay bottom-left relative to rect" (let [i2 (ctsi/set-overlay-pos-type interaction-rect :bottom-left base-frame objects) [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects rect base-frame overlay-frame frame-offset)] - (t/is (= (:x overlay-pos) 15)) - (t/is (= (:y overlay-pos) 45)) + (t/is (mth/close? (:x overlay-pos) 15)) + (t/is (mth/close? (:y overlay-pos) 45)) (t/is (= snap-v :bottom)) (t/is (= snap-h :left)))) (t/testing "Overlay bottom-center relative to rect" (let [i2 (ctsi/set-overlay-pos-type interaction-rect :bottom-center base-frame objects) [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects rect base-frame overlay-frame frame-offset)] - (t/is (= (:x overlay-pos) 25)) - (t/is (= (:y overlay-pos) 45)) + (t/is (mth/close? (:x overlay-pos) 25)) + (t/is (mth/close? (:y overlay-pos) 45)) (t/is (= snap-v :bottom)) (t/is (= snap-h :center)))) (t/testing "Overlay bottom-right relative to rect" (let [i2 (ctsi/set-overlay-pos-type interaction-rect :bottom-right base-frame objects) [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects rect base-frame overlay-frame frame-offset)] - (t/is (= (:x overlay-pos) 35)) - (t/is (= (:y overlay-pos) 45)) + (t/is (mth/close? (:x overlay-pos) 35)) + (t/is (mth/close? (:y overlay-pos) 45)) (t/is (= snap-v :bottom)) (t/is (= snap-h :right)))) (t/testing "Overlay center relative to rect" (let [i2 (ctsi/set-overlay-pos-type interaction-rect :center base-frame objects) [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects rect base-frame overlay-frame frame-offset)] - (t/is (= (:x overlay-pos) 25)) - (t/is (= (:y overlay-pos) 30)) + (t/is (mth/close? (:x overlay-pos) 25)) + (t/is (mth/close? (:y overlay-pos) 30)) (t/is (= snap-v :center)) (t/is (= snap-h :center)))) @@ -588,8 +589,8 @@ (ctsi/set-overlay-pos-type :manual base-frame objects) (ctsi/set-overlay-position (gpt/point 12 62))) [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects rect base-frame overlay-frame frame-offset)] - (t/is (= (:x overlay-pos) 17)) - (t/is (= (:y overlay-pos) 67)) + (t/is (mth/close? (:x overlay-pos) 17)) + (t/is (mth/close? (:y overlay-pos) 67)) (t/is (= snap-v :top)) (t/is (= snap-h :left)))))) diff --git a/frontend/src/app/main/data/workspace/selection.cljs b/frontend/src/app/main/data/workspace/selection.cljs index d6949f84e..6a463f2c6 100644 --- a/frontend/src/app/main/data/workspace/selection.cljs +++ b/frontend/src/app/main/data/workspace/selection.cljs @@ -330,17 +330,6 @@ (when selected (rx/of (select-shape (:id selected)))))))) -(defn remap-grid-cells - "Remaps the shapes inside the cells" - [shape ids-map] - - (let [do-remap-cells - (fn [cell] - (-> cell - (update :shapes #(mapv ids-map %))))] - - (update shape :layout-grid-cells update-vals do-remap-cells))) - ;; --- Duplicate Shapes (declare prepare-duplicate-shape-change) (declare prepare-duplicate-flows) @@ -463,8 +452,7 @@ (d/update-when :interactions #(ctsi/remap-interactions % ids-map objects)) (cond-> (ctl/grid-layout? obj) - (-> (ctl/check-deassigned-cells) - (remap-grid-cells ids-map)))) + (ctl/remap-grid-cells ids-map))) new-obj (cond-> new-obj (not duplicating-component?) @@ -474,7 +462,7 @@ changes (-> (pcb/add-object changes new-obj {:ignore-touched (and duplicating-component? child?)}) (pcb/amend-last-change #(assoc % :old-id (:id obj))) (cond-> (ctl/grid-layout? objects (:parent-id obj)) - (-> (pcb/update-shapes [(:parent-id obj)] ctl/assign-cells) + (-> (pcb/update-shapes [(:parent-id obj)] (fn [shape] (-> shape ctl/assign-cells ctl/check-deassigned-cells))) (pcb/reorder-grid-children [(:parent-id obj)])))) changes (cond-> changes diff --git a/frontend/src/app/main/ui/frame_preview.cljs b/frontend/src/app/main/ui/frame_preview.cljs index 805b73a1e..12f3a0054 100644 --- a/frontend/src/app/main/ui/frame_preview.cljs +++ b/frontend/src/app/main/ui/frame_preview.cljs @@ -25,12 +25,11 @@ handle-load (mf/use-callback (fn [data width height] - (prn "handle-load" data) (reset! last-data* data) (let [iframe-dom (mf/ref-val iframe-ref)] (when iframe-dom - (-> iframe-dom (aset "width" width)) - (-> iframe-dom (aset "height" height)) + (-> iframe-dom (aset "width" (+ width 64))) + (-> iframe-dom (aset "height" (+ height 64))) (-> iframe-dom .-contentWindow .-document .open) (-> iframe-dom .-contentWindow .-document (.write data)) (-> iframe-dom .-contentWindow .-document .close))))) @@ -59,13 +58,13 @@ [:div {:style {:display "flex" :width "100%" :height "100%" :flex-direction "column" :overflow "auto" :align-items "center"}} [:input {:id "zoom-input" :ref zoom-ref - :type "range" :min 1 :max 200 :default-value 100 + :type "range" :min 1 :max 400 :default-value 100 :on-change change-zoom :style {:max-width "500px"}}] [:div {:style {:width "100%" :height "100%" :overflow "auto"}} [:iframe {:ref load-ref - :frameborder "0" + :frame-border "0" :scrolling "no" - :style {:transform-origin "top center" + :style {:transform-origin "top left" :transform (str "scale(" zoom ")")}}]]])) diff --git a/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.cljs b/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.cljs index 34334ff66..70c935b82 100644 --- a/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.cljs @@ -34,6 +34,9 @@ [cuerdas.core :as str] [rumext.v2 :as mf])) +(def small-size-limit 60) +(def medium-size-limit 110) + (defn apply-to-point [result next-fn] (conj result (next-fn (last result)))) @@ -630,6 +633,10 @@ [(:x text-p) (- (:y text-p) (/ 36 zoom)) (max 0 (:size track-data)) (/ 36 zoom)] [(- (:x text-p) (max 0 (:size track-data))) (- (:y text-p) (/ 36 zoom)) (max 0 (:size track-data)) (/ 36 zoom)]) + trackwidth (* text-width zoom) + medium? (and (>= trackwidth small-size-limit) (< trackwidth medium-size-limit)) + small? (< trackwidth small-size-limit) + track-before (get-in layout-data [track-list-prop (dec index)])] (mf/use-effect @@ -644,28 +651,29 @@ (dm/str (gmt/transform-in text-p (:transform shape))) (dm/str (gmt/transform-in text-p (gmt/rotate (:transform shape) -90))))} - (when hovering? - [:rect {:x (+ text-x (/ 5 zoom)) + (when (and hovering? (not small?)) + [:rect {:x (+ text-x (/ 18 zoom)) :y text-y - :width (- text-width (/ 10 zoom)) + :width (- text-width (/ 36 zoom)) :height (- text-height (/ 5 zoom)) :rx (/ 3 zoom) :fill "var(--color-distance)" :opacity 0.2}]) - [:foreignObject {:x text-x :y text-y :width text-width :height text-height} - [:div {:class (css :grid-editor-wrapper)} - [:input - {:ref track-input-ref - :style {} - :class (css :grid-editor-label) - :type "text" - :default-value (format-size track-data) - :data-default-value (format-size track-data) - :on-key-down handle-keydown-track-input - :on-blur handle-blur-track-input}] - (when hovering? - [:button {:class (css :grid-editor-button) - :on-click handle-remove-track} i/trash])]]] + (when (not small?) + [:foreignObject {:x text-x :y text-y :width text-width :height text-height} + [:div {:class (css :grid-editor-wrapper)} + [:input + {:ref track-input-ref + :style {} + :class (css :grid-editor-label) + :type "text" + :default-value (format-size track-data) + :data-default-value (format-size track-data) + :on-key-down handle-keydown-track-input + :on-blur handle-blur-track-input}] + (when (and hovering? (not medium?) (not small?)) + [:button {:class (css :grid-editor-button) + :on-click handle-remove-track} i/trash])]])] [:g {:transform (when (= type :row) (dm/fmt "rotate(-90 % %)" (:x marker-p) (:y marker-p)))} [:& track-marker @@ -782,106 +790,107 @@ (fn [] #(st/emit! (dwge/stop-grid-layout-editing (:id shape))))) - [:g.grid-editor {:pointer-events (when view-only "none") - :on-pointer-down handle-pointer-down} - [:g.cells - (for [cell (ctl/get-cells shape {:sort? true})] - [:& grid-cell {:key (dm/str "cell-" (:id cell)) - :shape base-shape - :layout-data layout-data - :cell cell + (when (and (not (:hidden shape)) (not (:blocked shape))) + [:g.grid-editor {:pointer-events (when view-only "none") + :on-pointer-down handle-pointer-down} + [:g.cells + (for [cell (ctl/get-cells shape {:sort? true})] + [:& grid-cell {:key (dm/str "cell-" (:id cell)) + :shape base-shape + :layout-data layout-data + :cell cell + :zoom zoom + :hover? (contains? hover-cells (:id cell)) + :selected? (contains? selected-cells (:id cell))}])] + (when-not view-only + [:* + [:& grid-editor-frame {:zoom zoom + :bounds bounds}] + (let [start-p (-> origin (gpt/add (hv width)))] + [:g {:transform (dm/str (gmt/transform-in start-p (:transform shape)))} + [:& plus-btn {:start-p start-p + :zoom zoom + :type :column + :on-click handle-add-column}]]) + + (let [start-p (-> origin (gpt/add (vv height)))] + [:g {:transform (dm/str (gmt/transform-in start-p (:transform shape)))} + [:& plus-btn {:start-p start-p + :zoom zoom + :type :row + :on-click handle-add-row}]]) + + (for [[idx column-data] (d/enumerate column-tracks)] + [:& track {:key (dm/str "column-track-" idx) + :shape shape :zoom zoom - :hover? (contains? hover-cells (:id cell)) - :selected? (contains? selected-cells (:id cell))}])] - (when-not view-only - [:* - [:& grid-editor-frame {:zoom zoom - :bounds bounds}] - (let [start-p (-> origin (gpt/add (hv width)))] - [:g {:transform (dm/str (gmt/transform-in start-p (:transform shape)))} - [:& plus-btn {:start-p start-p - :zoom zoom - :type :column - :on-click handle-add-column}]]) + :type :column + :index idx + :layout-data layout-data + :snap-pixel? snap-pixel? + :track-data column-data + :hovering? (contains? hover-columns idx)}]) - (let [start-p (-> origin (gpt/add (vv height)))] - [:g {:transform (dm/str (gmt/transform-in start-p (:transform shape)))} - [:& plus-btn {:start-p start-p - :zoom zoom - :type :row - :on-click handle-add-row}]]) + ;; Last track resize handler + (when-not (empty? column-tracks) + (let [last-track (last column-tracks) + start-p (:start-p last-track) + end-p (gpt/add start-p (hv (:size last-track))) + marker-p (-> (gpo/project-point bounds :h end-p) + (gpt/subtract (vv (/ 20 zoom))))] + [:g.track + [:& track-marker {:center marker-p + :index (count column-tracks) + :shape shape + :snap-pixel? snap-pixel? + :track-before (last column-tracks) + :type :column + :value (dm/str (inc (count column-tracks))) + :zoom zoom}] + [:& resize-track-handler + {:index (count column-tracks) + :last? true + :shape shape + :layout-data layout-data + :snap-pixel? snap-pixel? + :start-p end-p + :type :column + :track-before (last column-tracks) + :zoom zoom}]])) - (for [[idx column-data] (d/enumerate column-tracks)] - [:& track {:key (dm/str "column-track-" idx) - :shape shape - :zoom zoom - :type :column - :index idx - :layout-data layout-data - :snap-pixel? snap-pixel? - :track-data column-data - :hovering? (contains? hover-columns idx)}]) - - ;; Last track resize handler - (when-not (empty? column-tracks) - (let [last-track (last column-tracks) - start-p (:start-p last-track) - end-p (gpt/add start-p (hv (:size last-track))) - marker-p (-> (gpo/project-point bounds :h end-p) - (gpt/subtract (vv (/ 20 zoom))))] - [:g.track - [:& track-marker {:center marker-p - :index (count column-tracks) - :shape shape - :snap-pixel? snap-pixel? - :track-before (last column-tracks) - :type :column - :value (dm/str (inc (count column-tracks))) - :zoom zoom}] - [:& resize-track-handler - {:index (count column-tracks) - :last? true - :shape shape - :layout-data layout-data - :snap-pixel? snap-pixel? - :start-p end-p - :type :column - :track-before (last column-tracks) - :zoom zoom}]])) - - (for [[idx row-data] (d/enumerate row-tracks)] - [:& track {:index idx - :key (dm/str "row-track-" idx) - :layout-data layout-data - :shape shape - :snap-pixel? snap-pixel? - :track-data row-data - :type :row - :zoom zoom - :hovering? (contains? hover-rows idx)}]) - (when-not (empty? row-tracks) - (let [last-track (last row-tracks) - start-p (:start-p last-track) - end-p (gpt/add start-p (vv (:size last-track))) - marker-p (-> (gpo/project-point bounds :v end-p) - (gpt/subtract (hv (/ 20 zoom))))] - [:g.track - [:g {:transform (dm/fmt "rotate(-90 % %)" (:x marker-p) (:y marker-p))} - [:& track-marker {:center marker-p - :index (count row-tracks) - :shape shape - :snap-pixel? snap-pixel? - :track-before (last row-tracks) - :type :row - :value (dm/str (inc (count row-tracks))) - :zoom zoom}]] - [:& resize-track-handler - {:index (count row-tracks) - :last? true - :shape shape - :layout-data layout-data - :start-p end-p - :type :row - :track-before (last row-tracks) - :snap-pixel? snap-pixel? - :zoom zoom}]]))])])) + (for [[idx row-data] (d/enumerate row-tracks)] + [:& track {:index idx + :key (dm/str "row-track-" idx) + :layout-data layout-data + :shape shape + :snap-pixel? snap-pixel? + :track-data row-data + :type :row + :zoom zoom + :hovering? (contains? hover-rows idx)}]) + (when-not (empty? row-tracks) + (let [last-track (last row-tracks) + start-p (:start-p last-track) + end-p (gpt/add start-p (vv (:size last-track))) + marker-p (-> (gpo/project-point bounds :v end-p) + (gpt/subtract (hv (/ 20 zoom))))] + [:g.track + [:g {:transform (dm/fmt "rotate(-90 % %)" (:x marker-p) (:y marker-p))} + [:& track-marker {:center marker-p + :index (count row-tracks) + :shape shape + :snap-pixel? snap-pixel? + :track-before (last row-tracks) + :type :row + :value (dm/str (inc (count row-tracks))) + :zoom zoom}]] + [:& resize-track-handler + {:index (count row-tracks) + :last? true + :shape shape + :layout-data layout-data + :start-p end-p + :type :row + :track-before (last row-tracks) + :snap-pixel? snap-pixel? + :zoom zoom}]]))])]))) diff --git a/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.scss b/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.scss index a8043abb3..db53c71cf 100644 --- a/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.scss +++ b/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.scss @@ -46,7 +46,7 @@ margin: 0; padding: 0; position: absolute; - right: calc(10px / var(--zoom)); + right: calc(20px / var(--zoom)); width: calc(20px / var(--zoom)); height: calc(20px / var(--zoom)); @@ -80,12 +80,12 @@ .grid-cell-outline { fill: transparent; stroke: var(--color-distance); - stroke-linecap: round; - stroke-width: calc(2 / var(--zoom)); - stroke-dasharray: 0 calc(8 / var(--zoom)); + stroke-opacity: 0.5; + stroke-width: calc(1 / var(--zoom)); &.hover, &.selected { - stroke-dasharray: initial; + stroke-opacity: 1; + stroke-width: calc(2 / var(--zoom)); } } diff --git a/frontend/src/app/util/code_gen/common.cljs b/frontend/src/app/util/code_gen/common.cljs index 5abbe40fb..4a9bc1f99 100644 --- a/frontend/src/app/util/code_gen/common.cljs +++ b/frontend/src/app/util/code_gen/common.cljs @@ -58,5 +58,6 @@ [objects shape] ;; Layout children with a transform should be wrapped (and (ctl/any-layout-immediate-child? objects shape) + (not (ctl/layout-absolute? shape)) (not (gmt/unit? (:transform shape))))) diff --git a/frontend/src/app/util/code_gen/markup_html.cljs b/frontend/src/app/util/code_gen/markup_html.cljs index 3229bf70a..028cbf220 100644 --- a/frontend/src/app/util/code_gen/markup_html.cljs +++ b/frontend/src/app/util/code_gen/markup_html.cljs @@ -23,68 +23,69 @@ (generate-html objects shape 0)) ([objects shape level] - (let [indent (str/repeat " " level) - maybe-reverse (if (ctl/any-layout? shape) reverse identity) + (when (and (some? shape) (some? (:selrect shape))) + (let [indent (str/repeat " " level) + maybe-reverse (if (ctl/any-layout? shape) reverse identity) - shape-html - (cond - (cgc/svg-markup? shape) - (let [svg-markup (generate-svg objects shape)] + shape-html + (cond + (cgc/svg-markup? shape) + (let [svg-markup (generate-svg objects shape)] + (dm/fmt "%