diff --git a/common/src/app/common/geom/shapes.cljc b/common/src/app/common/geom/shapes.cljc index 28bc5dcef0..31c1037795 100644 --- a/common/src/app/common/geom/shapes.cljc +++ b/common/src/app/common/geom/shapes.cljc @@ -14,6 +14,8 @@ [app.common.geom.shapes.constraints :as gct] [app.common.geom.shapes.corners :as gsc] [app.common.geom.shapes.intersect :as gin] + [app.common.geom.shapes.layout :as gcl] + [app.common.geom.shapes.modifiers :as gsm] [app.common.geom.shapes.path :as gsp] [app.common.geom.shapes.rect :as gpr] [app.common.geom.shapes.transforms :as gtr] @@ -170,10 +172,15 @@ (dm/export gtr/modifiers->transform) (dm/export gtr/empty-modifiers?) (dm/export gtr/move-position-data) +(dm/export gtr/apply-transform) ;; Constratins (dm/export gct/calc-child-modifiers) +;; Layout +(dm/export gcl/calc-layout-data) +(dm/export gcl/calc-layout-modifiers) + ;; PATHS (dm/export gsp/content->selrect) (dm/export gsp/transform-content) @@ -196,3 +203,6 @@ ;; Corners (dm/export gsc/shape-corners-1) (dm/export gsc/shape-corners-4) + +;; Modifiers +(dm/export gsm/set-objects-modifiers) diff --git a/common/src/app/common/geom/shapes/constraints.cljc b/common/src/app/common/geom/shapes/constraints.cljc index 1565392f80..ee5a213d21 100644 --- a/common/src/app/common/geom/shapes/constraints.cljc +++ b/common/src/app/common/geom/shapes/constraints.cljc @@ -197,6 +197,9 @@ ;; Build final child modifiers. Apply transform again to the result, to get the ;; real modifiers that need to be applied to the child, including rotation as needed. (cond-> {} + (some? (:displacement-after modifiers)) + (assoc :displacement-after (:displacement-after modifiers)) + (or (contains? modifiers-h :displacement) (contains? modifiers-v :displacement)) (assoc :displacement (cond-> (gpt/point (get-in modifiers-h [:displacement :x] 0) diff --git a/common/src/app/common/geom/shapes/layout.cljc b/common/src/app/common/geom/shapes/layout.cljc new file mode 100644 index 0000000000..8020fbef1a --- /dev/null +++ b/common/src/app/common/geom/shapes/layout.cljc @@ -0,0 +1,327 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) UXBOX Labs SL + +(ns app.common.geom.shapes.layout + (:require + [app.common.geom.matrix :as gmt] + [app.common.geom.point :as gpt] + [app.common.geom.shapes.rect :as gre])) + +;; :layout ;; true if active, false if not +;; :layout-dir ;; :right, :left, :top, :bottom +;; :layout-gap ;; number could be negative +;; :layout-type ;; :packed, :space-between, :space-around +;; :layout-wrap-type ;; :wrap, :no-wrap +;; :layout-padding-type ;; :simple, :multiple +;; :layout-padding ;; {:p1 num :p2 num :p3 num :p4 num} number could be negative +;; :layout-h-orientation ;; :top, :center, :bottom +;; :layout-v-orientation ;; :left, :center, :right + +(defn col? + [{:keys [layout-dir]}] + (or (= :right layout-dir) (= :left layout-dir))) + +(defn row? + [{:keys [layout-dir]}] + (or (= :top layout-dir) (= :bottom layout-dir))) + +(defn h-start? + [{:keys [layout-h-orientation]}] + (= layout-h-orientation :left)) + +(defn h-center? + [{:keys [layout-h-orientation]}] + (= layout-h-orientation :center)) + +(defn h-end? + [{:keys [layout-h-orientation]}] + (= layout-h-orientation :right)) + +(defn v-start? + [{:keys [layout-v-orientation]}] + (= layout-v-orientation :top)) + +(defn v-center? + [{:keys [layout-v-orientation]}] + (= layout-v-orientation :center)) + +(defn v-end? + [{:keys [layout-v-orientation]}] + (= layout-v-orientation :bottom)) + +(defn add-padding [transformed-rect {:keys [layout-padding-type layout-padding]}] + (let [{:keys [p1 p2 p3 p4]} layout-padding + [p1 p2 p3 p4] + (if (= layout-padding-type :multiple) + [p1 p2 p3 p4] + [p1 p1 p1 p1])] + + (-> transformed-rect + (update :y + p1) + (update :width - p2 p3) + (update :x + p3) + (update :height - p1 p4)))) + +(defn calc-layout-lines + [{:keys [layout-gap layout-wrap-type] :as shape} children {:keys [width height] :as layout-bounds}] + + (let [wrap? (= layout-wrap-type :wrap) + + reduce-fn + (fn [[{:keys [line-width line-height num-children] :as line-data} result] child] + (let [child-bounds (-> child :points gre/points->rect) + next-width (-> child-bounds :width) + next-height (-> child-bounds :height)] + + (if (and (some? line-data) + (or (not wrap?) + (and (col? shape) (<= (+ line-width next-width (* layout-gap num-children)) width)) + (and (row? shape) (<= (+ line-height next-height (* layout-gap num-children)) height)))) + + [{:line-width (if (col? shape) (+ line-width next-width) (max line-width next-width)) + :line-height (if (row? shape) (+ line-height next-height) (max line-height next-height)) + :num-children (inc num-children)} + result] + + [{:line-width next-width + :line-height next-height + :num-children 1} + (cond-> result (some? line-data) (conj line-data))]))) + + [line-data layout-lines] (reduce reduce-fn [nil []] children)] + + (cond-> layout-lines (some? line-data) (conj line-data)))) + +(defn calc-layout-lines-position + [{:keys [layout-gap layout-type] :as shape} {:keys [x y width height]} layout-lines] + + (letfn [(get-base-line + [total-width total-height] + + (let [base-x + (cond + (and (row? shape) (h-center? shape)) + (+ x (/ (- width total-width) 2)) + + (and (row? shape) (h-end? shape)) + (+ x width (- total-width)) + + :else x) + + base-y + (cond + (and (col? shape) (v-center? shape)) + (+ y (/ (- height total-height) 2)) + + (and (col? shape) (v-end? shape)) + (+ y height (- total-height)) + + :else y)] + + [base-x base-y])) + + (get-start-line + [{:keys [line-width line-height num-children]} base-x base-y] + + (let [children-gap (* layout-gap (dec num-children)) + + start-x + (cond + (or (and (col? shape) (= :space-between layout-type)) + (and (col? shape) (= :space-around layout-type))) + x + + (and (col? shape) (h-center? shape)) + (- (+ x (/ width 2)) (/ (+ line-width children-gap) 2)) + + (and (col? shape) (h-end? shape)) + (- (+ x width) (+ line-width children-gap)) + + (and (row? shape) (h-center? shape)) + (+ base-x (/ line-width 2)) + + (and (row? shape) (h-end? shape)) + (+ base-x line-width) + + (row? shape) + base-x + + :else + x) + + start-y + (cond + (or (and (row? shape) (= :space-between layout-type)) + (and (row? shape) (= :space-around layout-type))) + y + + (and (row? shape) (v-center? shape)) + (- (+ y (/ height 2)) (/ (+ line-height children-gap) 2)) + + (and (row? shape) (v-end? shape)) + (- (+ y height) (+ line-height children-gap)) + + (and (col? shape) (v-center? shape)) + (+ base-y (/ line-height 2)) + + (and (col? shape) (v-end? shape)) + (+ base-y line-height) + + (col? shape) + base-y + + :else + y)] + [start-x start-y])) + + (get-next-line + [{:keys [line-width line-height]} base-x base-y] + (let [next-x (if (col? shape) base-x (+ base-x line-width layout-gap)) + next-y (if (row? shape) base-y (+ base-y line-height layout-gap))] + [next-x next-y])) + + (add-lines [[total-width total-height] {:keys [line-width line-height]}] + [(+ total-width line-width) + (+ total-height line-height)]) + + (add-starts [[result base-x base-y] layout-line] + (let [[start-x start-y] (get-start-line layout-line base-x base-y) + [next-x next-y] (get-next-line layout-line base-x base-y)] + [(conj result + (assoc layout-line + :start-x start-x + :start-y start-y)) + next-x + next-y]))] + + (let [[total-width total-height] + (->> layout-lines (reduce add-lines [0 0])) + + total-width (+ total-width (* layout-gap (dec (count layout-lines)))) + total-height (+ total-height (* layout-gap (dec (count layout-lines)))) + + [base-x base-y] + (get-base-line total-width total-height) + + [layout-lines _ _ _ _] + (reduce add-starts [[] base-x base-y] layout-lines)] + layout-lines))) + +(defn calc-layout-line-data + [{:keys [layout-type layout-gap] :as shape} + {:keys [width height] :as layout-bounds} + {:keys [num-children line-width line-height] :as line-data}] + + (let [layout-gap + (cond + (= :packed layout-type) + layout-gap + + (= :space-around layout-type) + 0 + + (and (col? shape) (= :space-between layout-type)) + (/ (- width line-width) (dec num-children)) + + (and (row? shape) (= :space-between layout-type)) + (/ (- height line-height) (dec num-children))) + + margin-x + (if (and (col? shape) (= :space-around layout-type)) + (/ (- width line-width) (inc num-children) ) + 0) + + margin-y + (if (and (row? shape) (= :space-around layout-type)) + (/ (- height line-height) (inc num-children)) + 0)] + + (assoc line-data + :layout-gap layout-gap + :margin-x margin-x + :margin-y margin-y))) + + +(defn calc-layout-data + "Digest the layout data to pass it to the constrains" + [{:keys [layout-dir] :as shape} children layout-bounds] + + (let [reverse? (or (= :left layout-dir) (= :bottom layout-dir)) + layout-bounds (-> layout-bounds (add-padding shape)) + children (cond->> children reverse? reverse) + layout-lines + (->> (calc-layout-lines shape children layout-bounds) + (calc-layout-lines-position shape layout-bounds) + (map (partial calc-layout-line-data shape layout-bounds)))] + + {:layout-lines layout-lines + :reverse? reverse?})) + +(defn next-p + "Calculates the position for the current shape given the layout-data context" + [shape + {:keys [width height]} + {:keys [start-x start-y layout-gap margin-x margin-y] :as layout-data}] + + (let [pos-x + (cond + (and (row? shape) (h-center? shape)) + (- start-x (/ width 2)) + + (and (row? shape) (h-end? shape)) + (- start-x width) + + :else + start-x) + + pos-y + (cond + (and (col? shape) (v-center? shape)) + (- start-y (/ height 2)) + + (and (col? shape) (v-end? shape)) + (- start-y height) + + :else + start-y) + + pos-x (cond-> pos-x (some? margin-x) (+ margin-x)) + pos-y (cond-> pos-y (some? margin-y) (+ margin-y)) + + corner-p (gpt/point pos-x pos-y) + + next-x + (if (col? shape) + (+ start-x width layout-gap) + start-x) + + next-y + (if (row? shape) + (+ start-y height layout-gap) + start-y) + + next-x (cond-> next-x (some? margin-x) (+ margin-x)) + next-y (cond-> next-y (some? margin-y) (+ margin-y)) + + layout-data + (assoc layout-data :start-x next-x :start-y next-y)] + [corner-p layout-data])) + +(defn calc-layout-modifiers + "Calculates the modifiers for the layout" + [parent transform child layout-data] + + (let [bounds (-> child :points gre/points->selrect) + + [corner-p layout-data] (next-p parent bounds layout-data) + + delta-p (-> corner-p + (gpt/subtract (gpt/point bounds)) + (cond-> (some? transform) (gpt/transform transform))) + + modifiers {:displacement-after (gmt/translate-matrix delta-p)}] + + [modifiers layout-data])) diff --git a/common/src/app/common/geom/shapes/modifiers.cljc b/common/src/app/common/geom/shapes/modifiers.cljc new file mode 100644 index 0000000000..cd8a1ab021 --- /dev/null +++ b/common/src/app/common/geom/shapes/modifiers.cljc @@ -0,0 +1,286 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) UXBOX Labs SL + +(ns app.common.geom.shapes.modifiers + (:require + [app.common.data :as d] + [app.common.geom.matrix :as gmt] + [app.common.geom.point :as gpt] + [app.common.geom.shapes.common :as gco] + [app.common.geom.shapes.constraints :as gct] + [app.common.geom.shapes.layout :as gcl] + [app.common.geom.shapes.rect :as gpr] + [app.common.geom.shapes.transforms :as gtr] + [app.common.math :as mth] + [app.common.uuid :as uuid])) + +(defn set-pixel-precision + "Adjust modifiers so they adjust to the pixel grid" + [modifiers shape] + + (if (some? (:resize-transform modifiers)) + ;; If we're working with a rotation we don't handle pixel precision because + ;; the transformation won't have the precision anyway + modifiers + + (let [center (gco/center-shape shape) + base-bounds (-> (:points shape) (gpr/points->rect)) + + raw-bounds + (-> (gtr/transform-bounds (:points shape) center modifiers) + (gpr/points->rect)) + + flip-x? (neg? (get-in modifiers [:resize-vector :x])) + flip-y? (or (neg? (get-in modifiers [:resize-vector :y])) + (neg? (get-in modifiers [:resize-vector-2 :y]))) + + path? (= :path (:type shape)) + vertical-line? (and path? (<= (:width raw-bounds) 0.01)) + horizontal-line? (and path? (<= (:height raw-bounds) 0.01)) + + target-width (if vertical-line? + (:width raw-bounds) + (max 1 (mth/round (:width raw-bounds)))) + + target-height (if horizontal-line? + (:height raw-bounds) + (max 1 (mth/round (:height raw-bounds)))) + + target-p (cond-> (gpt/round (gpt/point raw-bounds)) + flip-x? + (update :x + target-width) + + flip-y? + (update :y + target-height)) + + ratio-width (/ target-width (:width raw-bounds)) + ratio-height (/ target-height (:height raw-bounds)) + + modifiers + (-> modifiers + (d/without-nils) + (d/update-in-when + [:resize-vector :x] #(* % ratio-width)) + + ;; If the resize-vector-2 modifier arrives means the resize-vector + ;; will only resize on the x axis + (cond-> (nil? (:resize-vector-2 modifiers)) + (d/update-in-when + [:resize-vector :y] #(* % ratio-height))) + + (d/update-in-when + [:resize-vector-2 :y] #(* % ratio-height))) + + origin (get modifiers :resize-origin) + origin-2 (get modifiers :resize-origin-2) + + resize-v (get modifiers :resize-vector) + resize-v-2 (get modifiers :resize-vector-2) + displacement (get modifiers :displacement) + + target-p-inv + (-> target-p + (gpt/transform + (cond-> (gmt/matrix) + (some? displacement) + (gmt/multiply (gmt/inverse displacement)) + + (and (some? resize-v) (some? origin)) + (gmt/scale (gpt/inverse resize-v) origin) + + (and (some? resize-v-2) (some? origin-2)) + (gmt/scale (gpt/inverse resize-v-2) origin-2)))) + + delta-v (gpt/subtract target-p-inv (gpt/point base-bounds)) + + modifiers + (-> modifiers + (d/update-when :displacement #(gmt/multiply (gmt/translate-matrix delta-v) %)) + (cond-> (nil? (:displacement modifiers)) + (assoc :displacement (gmt/translate-matrix delta-v))))] + modifiers))) + + +(defn set-children-modifiers + [modif-tree shape objects ignore-constraints snap-pixel?] + (letfn [(set-child [transformed-rect snap-pixel? modif-tree child] + (let [modifiers (get-in modif-tree [(:id shape) :modifiers]) + child-modifiers (gct/calc-child-modifiers shape child modifiers ignore-constraints transformed-rect) + child-modifiers (cond-> child-modifiers snap-pixel? (set-pixel-precision child))] + (cond-> modif-tree + (not (gtr/empty-modifiers? child-modifiers)) + (update-in [(:id child) :modifiers] #(merge child-modifiers %)))))] + (let [children (map (d/getf objects) (:shapes shape)) + modifiers (get-in modif-tree [(:id shape) :modifiers]) + transformed-rect (gtr/transform-selrect (:selrect shape) modifiers) + resize-modif? (or (:resize-vector modifiers) (:resize-vector-2 modifiers))] + (reduce (partial set-child transformed-rect (and snap-pixel? resize-modif?)) modif-tree children)))) + +(defn group? [shape] + (or (= :group (:type shape)) + (= :bool (:type shape)))) + +(defn merge-modifiers + [modif-tree ids modifiers] + (reduce + (fn [modif-tree id] + (update-in modif-tree [id :modifiers] #(merge % modifiers))) + modif-tree + ids)) + +(defn set-layout-modifiers + [modif-tree objects id] + + (letfn [(transform-child [parent child] + (let [modifiers (get modif-tree (:id child)) + + child + (cond-> child + (not (group? child)) + (-> (merge modifiers) gtr/transform-shape) + + (group? child) + (gtr/apply-group-modifiers objects modif-tree)) + + child + (-> child + (gtr/apply-transform (gmt/transform-in (gco/center-shape parent) (:transform-inverse parent))))] + + child)) + + (set-layout-modifiers [parent transform [layout-data modif-tree] child] + (let [[modifiers layout-data] + (gcl/calc-layout-modifiers parent transform child layout-data) + + modif-tree + (cond-> modif-tree + (not (gtr/empty-modifiers? modifiers)) + (merge-modifiers [(:id child)] modifiers) + + (and (not (gtr/empty-modifiers? modifiers)) (group? child)) + (merge-modifiers (:shapes child) modifiers))] + + [layout-data modif-tree]))] + + (let [modifiers (get modif-tree id) + + shape (-> (get objects id) (merge modifiers) gtr/transform-shape) + + + children (->> (:shapes shape) + (map (d/getf objects)) + (map (partial transform-child shape))) + + center (gco/center-shape shape) + {:keys [transform transform-inverse]} shape + + shape + (-> shape + (gtr/apply-transform (gmt/transform-in center transform-inverse))) + + transformed-rect (:selrect shape) + + layout-data (gcl/calc-layout-data shape children transformed-rect) + children (into [] (cond-> children (:reverse? layout-data) reverse)) + + max-idx (dec (count children)) + layout-lines (:layout-lines layout-data)] + + (loop [modif-tree modif-tree + layout-line (first layout-lines) + pending (rest layout-lines) + from-idx 0] + (if (and (some? layout-line) (<= from-idx max-idx)) + (let [to-idx (+ from-idx (:num-children layout-line)) + children (subvec children from-idx to-idx) + + [_ modif-tree] + (reduce (partial set-layout-modifiers shape transform) [layout-line modif-tree] children)] + + (recur modif-tree (first pending) (rest pending) to-idx)) + + modif-tree))))) + +(defn get-first-layout + [id objects] + + (loop [current id + result id] + (let [shape (get objects current) + parent (get objects (:parent-id shape))] + (cond + (or (not shape) (= uuid/zero current)) + result + + ;; Frame found, but not layout we return the last layout found (or the id) + (and (= :frame (:type parent)) + (not (:layout parent))) + result + + ;; Layout found. We continue upward but we mark this layout + (and (= :frame (:type parent)) + (:layout parent)) + (:id parent) + + ;; If group or boolean or other type of group we continue with the last result + :else + (recur (:id parent) result))))) + +(defn resolve-layout-ids + "Given a list of ids, resolve the parent layouts that will need to update. This will go upwards + in the tree while a layout is found" + [ids objects] + + (into (d/ordered-set) + (map #(get-first-layout % objects)) + ids)) + +(defn set-objects-modifiers + [ids objects get-modifier ignore-constraints snap-pixel?] + + (let [set-modifiers + (fn [modif-tree id] + (assoc modif-tree id {:modifiers (get-modifier (get objects id))})) + + modif-tree (reduce set-modifiers {} ids) + + ids (resolve-layout-ids ids objects) + + ;; First: Calculate children modifiers (constraints, etc) + [modif-tree touched-layouts] + (loop [current (first ids) + pending (rest ids) + modif-tree modif-tree + touched-layouts (d/ordered-set)] + (if (some? current) + (let [shape (get objects current) + pending (concat pending (:shapes shape)) + + touched-layouts + (cond-> touched-layouts + (:layout shape) + (conj (:id shape))) + + modif-tree + (-> modif-tree + (set-children-modifiers shape objects ignore-constraints snap-pixel?))] + + (recur (first pending) (rest pending) modif-tree touched-layouts)) + + [modif-tree touched-layouts])) + + ;; Second: Calculate layout positioning + modif-tree + (loop [current (first touched-layouts) + pending (rest touched-layouts) + modif-tree modif-tree] + + (if (some? current) + (let [modif-tree (set-layout-modifiers modif-tree objects current)] + (recur (first pending) (rest pending) modif-tree)) + modif-tree))] + + modif-tree)) diff --git a/common/src/app/common/geom/shapes/transforms.cljc b/common/src/app/common/geom/shapes/transforms.cljc index 5adb7e988e..44c87562b4 100644 --- a/common/src/app/common/geom/shapes/transforms.cljc +++ b/common/src/app/common/geom/shapes/transforms.cljc @@ -278,7 +278,7 @@ (if transform (gmt/multiply transform matrix) matrix) (if transform-inverse (gmt/multiply matrix-inverse transform-inverse) matrix-inverse)])) -(defn- apply-transform +(defn apply-transform "Given a new set of points transformed, set up the rectangle so it keeps its properties. We adjust de x,y,width,height and create a custom transform" [shape transform-mtx] @@ -491,6 +491,7 @@ ([center modifiers] (let [displacement (:displacement modifiers) + displacement-after (:displacement-after modifiers) resize-v1 (:resize-vector modifiers) resize-v2 (:resize-vector-2 modifiers) origin-1 (:resize-origin modifiers (gpt/point)) @@ -512,6 +513,9 @@ rt-modif (:rotation modifiers)] (cond-> (gmt/matrix) + (some? displacement-after) + (gmt/multiply displacement-after) + (some? resize-1) (-> (gmt/translate origin-1) (cond-> (some? resize-transform) @@ -610,7 +614,7 @@ (dissoc :modifiers)))))) (defn transform-bounds - [points center {:keys [displacement resize-transform-inverse resize-vector resize-origin resize-vector-2 resize-origin-2]}] + [points center {:keys [displacement displacement-after resize-transform-inverse resize-vector resize-origin resize-vector-2 resize-origin-2]}] ;; FIXME: Improve Performance (let [resize-transform-inverse (or resize-transform-inverse (gmt/matrix)) @@ -624,9 +628,10 @@ resize-origin-2 (when (some? resize-origin-2) - (transform-point-center resize-origin-2 center resize-transform-inverse))] + (transform-point-center resize-origin-2 center resize-transform-inverse)) + ] - (if (and (nil? displacement) (nil? resize-origin) (nil? resize-origin-2)) + (if (and (nil? displacement) (nil? resize-origin) (nil? resize-origin-2) (nil? displacement-after)) points (cond-> points @@ -637,7 +642,10 @@ (gco/transform-points resize-origin (gmt/scale-matrix resize-vector)) (some? resize-origin-2) - (gco/transform-points resize-origin-2 (gmt/scale-matrix resize-vector-2)))))) + (gco/transform-points resize-origin-2 (gmt/scale-matrix resize-vector-2)) + + (some? displacement-after) + (gco/transform-points displacement-after))))) (defn transform-selrect [selrect modifiers] @@ -662,3 +670,17 @@ (map (comp gpr/points->selrect :points transform-shape)) (gpr/join-selrects))) +(defn apply-group-modifiers + "Apply the modifiers to the group children to calculate its selection rect" + [group objects modif-tree] + + (let [children + (->> (:shapes group) + (map (d/getf objects)) + (map (fn [shape] + (let [modifiers (get modif-tree (:id shape)) + shape (-> shape (merge modifiers) transform-shape)] + (if (= :group (:type shape)) + (apply-group-modifiers shape objects modif-tree) + shape)))))] + (update-group-selrect group children))) diff --git a/common/src/app/common/pages/helpers.cljc b/common/src/app/common/pages/helpers.cljc index 043f6a3a79..64d377b6c7 100644 --- a/common/src/app/common/pages/helpers.cljc +++ b/common/src/app/common/pages/helpers.cljc @@ -218,7 +218,7 @@ (->> (get-root-shapes objects) (mapv :id))) -(defn- get-base +(defn get-base [objects id-a id-b] (let [parents-a (reverse (get-parents-seq objects id-a)) diff --git a/common/src/app/common/types/shape.cljc b/common/src/app/common/types/shape.cljc index 317e4af786..a5b182f64e 100644 --- a/common/src/app/common/types/shape.cljc +++ b/common/src/app/common/types/shape.cljc @@ -13,6 +13,7 @@ [app.common.types.shape.blur :as ctsb] [app.common.types.shape.export :as ctse] [app.common.types.shape.interactions :as ctsi] + [app.common.types.shape.layout :as ctsl] [app.common.types.shape.radius :as ctsr] [app.common.types.shape.shadow :as ctss] [clojure.set :as set] @@ -156,68 +157,71 @@ :luminosity}) (s/def ::shape-attrs - (s/keys :opt-un [::id - ::type - ::name - ::component-id - ::component-file - ::component-root? - ::shape-ref - ::selrect - ::points - ::blocked - ::collapsed - ::fills - ::fill-color ;; TODO: remove these attributes - ::fill-opacity ;; when backward compatibility - ::fill-color-gradient ;; is no longer needed - ::fill-color-ref-file ;; - ::fill-color-ref-id ;; - ::hide-fill-on-export - ::font-family - ::font-size - ::font-style - ::font-weight - ::hidden - ::letter-spacing - ::line-height - ::locked - ::proportion - ::proportion-lock - ::constraints-h - ::constraints-v - ::fixed-scroll - ::ctsr/rx - ::ctsr/ry - ::ctsr/r1 - ::ctsr/r2 - ::ctsr/r3 - ::ctsr/r4 - ::x - ::y - ::exports - ::shapes - ::strokes - ::stroke-color ;; TODO: same thing - ::stroke-color-ref-file ;; - ::stroke-color-ref-i ;; - ::stroke-opacity ;; - ::stroke-style - ::stroke-width - ::stroke-alignment - ::stroke-cap-start - ::stroke-cap-end - ::text-align - ::transform - ::transform-inverse - ::width - ::height - ::masked-group? - ::ctsi/interactions - ::ctss/shadow - ::ctsb/blur - ::opacity - ::blend-mode])) + (s/and + ::ctsl/layout-container-props + ::ctsl/layout-child-props + (s/keys :opt-un [::id + ::type + ::name + ::component-id + ::component-file + ::component-root? + ::shape-ref + ::selrect + ::points + ::blocked + ::collapsed + ::fills + ::fill-color ;; TODO: remove these attributes + ::fill-opacity ;; when backward compatibility + ::fill-color-gradient ;; is no longer needed + ::fill-color-ref-file ;; + ::fill-color-ref-id ;; + ::hide-fill-on-export + ::font-family + ::font-size + ::font-style + ::font-weight + ::hidden + ::letter-spacing + ::line-height + ::locked + ::proportion + ::proportion-lock + ::constraints-h + ::constraints-v + ::fixed-scroll + ::ctsr/rx + ::ctsr/ry + ::ctsr/r1 + ::ctsr/r2 + ::ctsr/r3 + ::ctsr/r4 + ::x + ::y + ::exports + ::shapes + ::strokes + ::stroke-color ;; TODO: same thing + ::stroke-color-ref-file ;; + ::stroke-color-ref-i ;; + ::stroke-opacity ;; + ::stroke-style + ::stroke-width + ::stroke-alignment + ::stroke-cap-start + ::stroke-cap-end + ::text-align + ::transform + ::transform-inverse + ::width + ::height + ::masked-group? + ::ctsi/interactions + ::ctss/shadow + ::ctsb/blur + ::opacity + ::blend-mode]))) (s/def :internal.shape.text/type #{"root" "paragraph-set" "paragraph"}) (s/def :internal.shape.text/children diff --git a/common/src/app/common/types/shape/layout.cljc b/common/src/app/common/types/shape/layout.cljc new file mode 100644 index 0000000000..4e7e7059b5 --- /dev/null +++ b/common/src/app/common/types/shape/layout.cljc @@ -0,0 +1,61 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) UXBOX Labs SL + +(ns app.common.types.shape.layout + (:require + [app.common.spec :as us] + [clojure.spec.alpha :as s])) + +(s/def ::layout boolean?) +(s/def ::layout-dir #{:right :left :top :bottom}) +(s/def ::layout-gap ::us/safe-number) +(s/def ::layout-type #{:packed :space-between :space-around}) +(s/def ::layout-wrap-type #{:wrap :no-wrap}) +(s/def ::layout-padding-type #{:simple :multiple}) + +(s/def ::p1 ::us/safe-number) +(s/def ::p2 ::us/safe-number) +(s/def ::p3 ::us/safe-number) +(s/def ::p4 ::us/safe-number) + +(s/def ::layout-padding + (s/keys :req-un [::p1] + :opt-un [::p2 ::p3 ::p4])) + +(s/def ::layout-h-orientation #{:left :center :right}) +(s/def ::layout-v-orientation #{:top :center :bottom}) + +(s/def ::layout-container-props + (s/keys :opt-un [::layout + ::layout-dir + ::layout-gap + ::layout-type + ::layout-wrap-type + ::layout-padding-type + ::layout-padding + ::layout-h-orientation + ::layout-v-orientation])) + +(s/def ::layout-margin (s/keys :req-un [::m1] + :opt-un [::m2 ::m3 ::m4])) + +(s/def ::layout-margin-type #{:simple :multiple}) +(s/def ::layout-h-behavior #{:fill :fix :auto}) +(s/def ::layout-v-behavior #{:fill :fix :auto}) +(s/def ::layout-max-h ::us/safe-number) +(s/def ::layout-min-h ::us/safe-number) +(s/def ::layout-max-w ::us/safe-number) +(s/def ::layout-min-w ::us/safe-number) + +(s/def ::layout-child-props + (s/keys :opt-un [::layout-margin + ::layout-margin-type + ::layout-h-behavior + ::layout-v-behavior + ::layout-max-h + ::layout-min-h + ::layout-max-w + ::layout-min-w])) diff --git a/frontend/src/app/main/constants.cljs b/frontend/src/app/main/constants.cljs index 5f9e844862..d57c7ec16e 100644 --- a/frontend/src/app/main/constants.cljs +++ b/frontend/src/app/main/constants.cljs @@ -23,8 +23,6 @@ :grid-alignment true :background "var(--color-white)"}) -(def has-layout-item false) - (def size-presets [{:name "APPLE"} {:name "iPhone 12/12 Pro" diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 8d2abf1c85..1b734551cf 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -45,6 +45,7 @@ [app.main.data.workspace.path.shapes-to-path :as dwps] [app.main.data.workspace.persistence :as dwp] [app.main.data.workspace.selection :as dws] + [app.main.data.workspace.shape-layout :as dwsl] [app.main.data.workspace.shapes :as dwsh] [app.main.data.workspace.state-helpers :as wsh] [app.main.data.workspace.thumbnails :as dwth] @@ -799,7 +800,8 @@ ids)] (rx/of (dch/commit-changes changes) - (dwco/expand-collapse parent-id)))))) + (dwco/expand-collapse parent-id) + (dwsl/update-layout-positions [parent-id])))))) (defn relocate-selected-shapes [parent-id to-index] diff --git a/frontend/src/app/main/data/workspace/shape_layout.cljs b/frontend/src/app/main/data/workspace/shape_layout.cljs new file mode 100644 index 0000000000..3dc797b3c0 --- /dev/null +++ b/frontend/src/app/main/data/workspace/shape_layout.cljs @@ -0,0 +1,84 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) UXBOX Labs SL + +(ns app.main.data.workspace.shape-layout + (:require + [app.common.data :as d] + [app.common.pages.helpers :as cph] + [app.main.data.workspace.changes :as dwc] + [app.main.data.workspace.state-helpers :as wsh] + [app.main.data.workspace.transforms :as dwt] + [beicon.core :as rx] + [potok.core :as ptk])) + +(def layout-keys + [:layout + :layout-dir + :layout-gap + :layout-type + :layout-wrap-type + :layout-padding-type + :layout-padding + :layout-h-orientation + :layout-v-orientation]) + +(def initial-layout + {:layout true + :layout-dir :left + :layout-gap 0 + :layout-type :packed + :layout-wrap-type :wrap + :layout-padding-type :simple + :layout-padding {:p1 0 :p2 0 :p3 0 :p4 0} + :layout-h-orientation :left + :layout-v-orientation :top}) + +(defn update-layout-positions + [ids] + (ptk/reify ::update-layout-positions + ptk/WatchEvent + (watch [_ state _] + (let [objects (wsh/lookup-page-objects state) + ids (->> ids (filter #(get-in objects [% :layout])))] + (if (d/not-empty? ids) + (rx/of (dwt/set-modifiers ids) + (dwt/apply-modifiers)) + (rx/empty)))))) + +;; TODO: Remove constraints from children +(defn create-layout + [ids] + (ptk/reify ::create-layout + ptk/WatchEvent + (watch [_ _ _] + (rx/of (dwc/update-shapes ids #(merge % initial-layout)) + (update-layout-positions ids))))) + +(defn remove-layout + [ids] + (ptk/reify ::remove-layout + ptk/WatchEvent + (watch [_ _ _] + (rx/of (dwc/update-shapes ids #(apply dissoc % layout-keys)) + (update-layout-positions ids))))) + +(defn update-layout + [ids changes] + (ptk/reify ::update-layout + ptk/WatchEvent + (watch [_ _ _] + (rx/of (dwc/update-shapes ids #(d/deep-merge % changes)) + (update-layout-positions ids))))) + +(defn update-layout-child + [ids changes] + (ptk/reify ::update-layout-child + ptk/WatchEvent + (watch [_ state _] + (let [objects (wsh/lookup-page-objects state) + parent-ids (->> ids (map #(cph/get-parent-id objects %)))] + (rx/of (dwc/update-shapes ids #(d/deep-merge (or % {}) changes)) + (update-layout-positions parent-ids)))))) diff --git a/frontend/src/app/main/data/workspace/shapes.cljs b/frontend/src/app/main/data/workspace/shapes.cljs index 9694db988c..98a47522ca 100644 --- a/frontend/src/app/main/data/workspace/shapes.cljs +++ b/frontend/src/app/main/data/workspace/shapes.cljs @@ -19,6 +19,7 @@ [app.main.data.workspace.changes :as dch] [app.main.data.workspace.edition :as dwe] [app.main.data.workspace.selection :as dws] + [app.main.data.workspace.shape-layout :as dwsl] [app.main.data.workspace.state-helpers :as wsh] [app.main.streams :as ms] [app.util.names :as un] @@ -98,6 +99,7 @@ (rx/concat (rx/of (dch/commit-changes changes) + (dwsl/update-layout-positions [(:parent-id shape)]) (when-not no-select? (dws/select-shapes (d/ordered-set id)))) (when (= :text (:type attrs)) @@ -239,7 +241,8 @@ flows starting-flows)))))] - (rx/of (dch/commit-changes changes)))))) + (rx/of (dch/commit-changes changes) + (dwsl/update-layout-positions all-parents)))))) (defn- viewport-center [state] diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index 4d87ff0f6e..703a558aa4 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -110,10 +110,10 @@ ;; geometric attributes of the shapes. (declare clear-local-transform) -(declare set-objects-modifiers) + (declare get-ignore-tree) -(defn- set-modifiers +(defn set-modifiers ([ids] (set-modifiers ids nil false)) @@ -128,20 +128,16 @@ (ptk/reify ::set-modifiers ptk/UpdateEvent (update [_ state] - (let [modifiers (or modifiers (get-in state [:workspace-local :modifiers] {})) - page-id (:current-page-id state) - objects (wsh/lookup-page-objects state page-id) + (let [objects (wsh/lookup-page-objects state) ids (into #{} (remove #(get-in objects [% :blocked] false)) ids) - layout (get state :workspace-layout) - snap-pixel? (and (not ignore-snap-pixel) (contains? layout :snap-pixel-grid)) - setup-modifiers - (fn [state id] - (let [shape (get objects id)] - (update state :workspace-modifiers - #(set-objects-modifiers % objects shape modifiers ignore-constraints snap-pixel?))))] + snap-pixel? (and (not ignore-snap-pixel) + (contains? (:workspace-layout state) :snap-pixel-grid)) - (reduce setup-modifiers state ids)))))) + modif-tree + (gsh/set-objects-modifiers ids objects (constantly modifiers) ignore-constraints snap-pixel?)] + + (update state :workspace-modifiers merge modif-tree)))))) ;; Rotation use different algorithm to calculate children modifiers (and do not use child constraints). (defn- set-rotation-modifiers @@ -152,19 +148,23 @@ (ptk/reify ::set-rotation-modifiers ptk/UpdateEvent (update [_ state] - (let [objects (wsh/lookup-page-objects state) - shapes (->> shapes - (remove #(get % :blocked false)) - (mapcat #(cph/get-children objects (:id %))) - (concat shapes) - (filter #((cpc/editable-attrs (:type %)) :rotation))) + (let [objects (wsh/lookup-page-objects state) + ids + (->> shapes + (remove #(get % :blocked false)) + (mapcat #(cph/get-children objects (:id %))) + (concat shapes) + (filter #((cpc/editable-attrs (:type %)) :rotation)) + (map :id)) - update-shape - (fn [modifiers shape] - (let [rotate-modifiers (gsh/rotation-modifiers shape center angle)] - (assoc-in modifiers [(:id shape) :modifiers] rotate-modifiers)))] + get-modifier + (fn [shape] + (gsh/rotation-modifiers shape center angle)) - (update state :workspace-modifiers #(reduce update-shape % shapes))))))) + modif-tree + (gsh/set-objects-modifiers ids objects get-modifier false false)] + + (update state :workspace-modifiers merge modif-tree)))))) (defn- update-grow-type [shape old-shape] @@ -180,18 +180,20 @@ change-to-fixed? (assoc :grow-type :fixed)))) -(defn- apply-modifiers - ([ids] - (apply-modifiers ids nil)) +(defn apply-modifiers + ([] + (apply-modifiers nil)) - ([ids {:keys [undo-transation?] :or {undo-transation? true}}] - (us/verify (s/coll-of uuid?) ids) + ([{:keys [undo-transation?] :or {undo-transation? true}}] (ptk/reify ::apply-modifiers ptk/WatchEvent (watch [_ state _] (let [objects (wsh/lookup-page-objects state) - ids-with-children (into (vec ids) (mapcat #(cph/get-children-ids objects %)) ids) object-modifiers (get state :workspace-modifiers) + + ids (keys object-modifiers) + ids-with-children (into (vec ids) (mapcat #(cph/get-children-ids objects %)) ids) + shapes (map (d/getf objects) ids) ignore-tree (->> (map #(get-ignore-tree object-modifiers objects %) shapes) (reduce merge {}))] @@ -202,7 +204,7 @@ (rx/empty)) (rx/of (dwg/move-frame-guides ids-with-children) (dch/update-shapes - ids-with-children + ids (fn [shape] (let [modif (get object-modifiers (:id shape)) text-shape? (cph/text-shape? shape)] @@ -280,119 +282,9 @@ [root transformed-root ignore-geometry?])) -(defn set-pixel-precision - "Adjust modifiers so they adjust to the pixel grid" - [modifiers shape] - (if (some? (:resize-transform modifiers)) - ;; If we're working with a rotation we don't handle pixel precision because - ;; the transformation won't have the precision anyway - modifiers - (let [center (gsh/center-shape shape) - base-bounds (-> (:points shape) (gsh/points->rect)) - raw-bounds - (-> (gsh/transform-bounds (:points shape) center modifiers) - (gsh/points->rect)) - - flip-x? (neg? (get-in modifiers [:resize-vector :x])) - flip-y? (or (neg? (get-in modifiers [:resize-vector :y])) - (neg? (get-in modifiers [:resize-vector-2 :y]))) - - path? (= :path (:type shape)) - vertical-line? (and path? (<= (:width raw-bounds) 0.01)) - horizontal-line? (and path? (<= (:height raw-bounds) 0.01)) - - target-width (if vertical-line? - (:width raw-bounds) - (max 1 (mth/round (:width raw-bounds)))) - - target-height (if horizontal-line? - (:height raw-bounds) - (max 1 (mth/round (:height raw-bounds)))) - - target-p (cond-> (gpt/round (gpt/point raw-bounds)) - flip-x? - (update :x + target-width) - - flip-y? - (update :y + target-height)) - - ratio-width (/ target-width (:width raw-bounds)) - ratio-height (/ target-height (:height raw-bounds)) - - modifiers - (-> modifiers - (d/without-nils) - (d/update-in-when - [:resize-vector :x] #(* % ratio-width)) - - ;; If the resize-vector-2 modifier arrives means the resize-vector - ;; will only resize on the x axis - (cond-> (nil? (:resize-vector-2 modifiers)) - (d/update-in-when - [:resize-vector :y] #(* % ratio-height))) - - (d/update-in-when - [:resize-vector-2 :y] #(* % ratio-height))) - - origin (get modifiers :resize-origin) - origin-2 (get modifiers :resize-origin-2) - - resize-v (get modifiers :resize-vector) - resize-v-2 (get modifiers :resize-vector-2) - displacement (get modifiers :displacement) - - target-p-inv - (-> target-p - (gpt/transform - (cond-> (gmt/matrix) - (some? displacement) - (gmt/multiply (gmt/inverse displacement)) - - (and (some? resize-v) (some? origin)) - (gmt/scale (gpt/inverse resize-v) origin) - - (and (some? resize-v-2) (some? origin-2)) - (gmt/scale (gpt/inverse resize-v-2) origin-2)))) - - delta-v (gpt/subtract target-p-inv (gpt/point base-bounds)) - - modifiers - (-> modifiers - (d/update-when :displacement #(gmt/multiply (gmt/translate-matrix delta-v) %)) - (cond-> (nil? (:displacement modifiers)) - (assoc :displacement (gmt/translate-matrix delta-v))))] - modifiers))) - -(defn- set-objects-modifiers - [modif-tree objects shape modifiers ignore-constraints snap-pixel?] - (letfn [(set-modifiers-rec - [modif-tree shape modifiers] - - (let [children (map (d/getf objects) (:shapes shape)) - transformed-rect (gsh/transform-selrect (:selrect shape) modifiers) - - set-child - (fn [snap-pixel? modif-tree child] - (let [child-modifiers (gsh/calc-child-modifiers shape child modifiers ignore-constraints transformed-rect) - child-modifiers (cond-> child-modifiers snap-pixel? (set-pixel-precision child))] - (cond-> modif-tree - (not (gsh/empty-modifiers? child-modifiers)) - (set-modifiers-rec child child-modifiers)))) - - modif-tree - (-> modif-tree - (assoc-in [(:id shape) :modifiers] modifiers)) - - resize-modif? - (or (:resize-vector modifiers) (:resize-vector-2 modifiers))] - - (reduce (partial set-child (and snap-pixel? resize-modif?)) modif-tree children)))] - - (let [modifiers (cond-> modifiers snap-pixel? (set-pixel-precision shape))] - (set-modifiers-rec modif-tree shape modifiers)))) (defn- get-ignore-tree "Retrieves a map with the flag `ignore-geometry?` given a tree of modifiers" @@ -534,7 +426,7 @@ (rx/map #(conj current %))))) (rx/mapcat (partial resize shape initial-position layout)) (rx/take-until stoper)) - (rx/of (apply-modifiers ids) + (rx/of (apply-modifiers) (finish-transform)))))))) (defn update-dimensions @@ -547,22 +439,20 @@ (ptk/reify ::update-dimensions ptk/UpdateEvent (update [_ state] - (let [objects (wsh/lookup-page-objects state) - layout (get state :workspace-layout) - snap-pixel? (contains? layout :snap-pixel-grid) + (let [objects (wsh/lookup-page-objects state) + snap-pixel? (and (contains? (:workspace-layout state) :snap-pixel-grid) + (int? value)) + get-modifier + (fn [shape] (gsh/resize-modifiers shape attr value)) - update-modifiers - (fn [state id] - (let [shape (get objects id) - modifiers (gsh/resize-modifiers shape attr value)] - (-> state - (update :workspace-modifiers - #(set-objects-modifiers % objects shape modifiers false (and snap-pixel? (int? value)))))))] - (reduce update-modifiers state ids))) + modif-tree + (gsh/set-objects-modifiers ids objects get-modifier false snap-pixel?)] + + (assoc state :workspace-modifiers modif-tree))) ptk/WatchEvent (watch [_ _ _] - (rx/of (apply-modifiers ids))))) + (rx/of (apply-modifiers))))) (defn change-orientation "Change orientation of shapes, from the sidebar options form. @@ -574,21 +464,19 @@ ptk/UpdateEvent (update [_ state] (let [objects (wsh/lookup-page-objects state) - layout (get state :workspace-layout) - snap-pixel? (contains? layout :snap-pixel-grid) + snap-pixel? (contains? (get state :workspace-layout) :snap-pixel-grid) - update-modifiers - (fn [state id] - (let [shape (get objects id) - modifiers (gsh/change-orientation-modifiers shape orientation)] - (-> state - (update :workspace-modifiers - #(set-objects-modifiers % objects shape modifiers false snap-pixel?)))))] - (reduce update-modifiers state ids))) + get-modifier + (fn [shape] (gsh/change-orientation-modifiers shape orientation)) + + modif-tree + (gsh/set-objects-modifiers ids objects get-modifier false snap-pixel?)] + + (assoc state :workspace-modifiers modif-tree))) ptk/WatchEvent (watch [_ _ _] - (rx/of (apply-modifiers ids))))) + (rx/of (apply-modifiers))))) ;; -- Rotate -------------------------------------------------------- @@ -631,7 +519,7 @@ (let [delta-angle (calculate-angle pos mod? shift?)] (set-rotation-modifiers delta-angle shapes group-center)))) (rx/take-until stoper)) - (rx/of (apply-modifiers (map :id shapes)) + (rx/of (apply-modifiers) (finish-transform))))))) (defn increase-rotation @@ -648,7 +536,7 @@ (set-rotation-modifiers delta [shape])))] (rx/concat (rx/from (->> ids (map #(get objects %)) (map rotate-shape))) - (rx/of (apply-modifiers ids))))))) + (rx/of (apply-modifiers))))))) ;; -- Move ---------------------------------------------------------- @@ -772,7 +660,7 @@ (rx/of (dwu/start-undo-transaction) (calculate-frame-for-move ids) - (apply-modifiers ids {:undo-transation? false}) + (apply-modifiers {:undo-transation? false}) (finish-transform) (dwu/commit-undo-transaction))))))))) @@ -820,7 +708,7 @@ (rx/take-until stopper)) (rx/of (move-selected direction shift?))) - (rx/of (apply-modifiers selected) + (rx/of (apply-modifiers) (finish-transform)))) (rx/empty)))))) @@ -850,7 +738,7 @@ displ (gmt/translate-matrix delta)] (rx/of (set-modifiers [id] {:displacement displ} false true) - (apply-modifiers [id])))))) + (apply-modifiers)))))) (defn check-frame-move? [target-frame-id objects position shape] @@ -911,7 +799,7 @@ :resize-origin origin :displacement (gmt/translate-matrix (gpt/point (- (:width selrect)) 0))} true) - (apply-modifiers selected)))))) + (apply-modifiers)))))) (defn flip-vertical-selected [] (ptk/reify ::flip-vertical-selected @@ -928,4 +816,4 @@ :resize-origin origin :displacement (gmt/translate-matrix (gpt/point 0 (- (:height selrect))))} true) - (apply-modifiers selected)))))) + (apply-modifiers)))))) diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index 81d51b461d..ce4f0b8d54 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -410,3 +410,11 @@ (defn workspace-text-modifier-by-id [id] (l/derived #(get % id) workspace-text-modifier =)) + +(defn is-layout-child? + [ids] + (l/derived + (fn [objects] + (->> ids + (some #(-> (cph/get-parent objects %) :layout)))) + workspace-page-objects)) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs similarity index 50% rename from frontend/src/app/main/ui/workspace/sidebar/options/menus/layout.cljs rename to frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs index 54745a0edb..942a86f710 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs @@ -4,10 +4,12 @@ ;; ;; Copyright (c) UXBOX Labs SL -(ns app.main.ui.workspace.sidebar.options.menus.layout +(ns app.main.ui.workspace.sidebar.options.menus.layout-container (:require [app.common.data :as d] [app.common.data.macros :as dm] + [app.main.data.workspace.shape-layout :as dwsl] + [app.main.store :as st] [app.main.ui.components.numeric-input :refer [numeric-input]] [app.main.ui.icons :as i] [app.util.dom :as dom] @@ -15,16 +17,16 @@ [cuerdas.core :as str] [rumext.alpha :as mf])) -(def layout-attrs - [:layout ;; true if active, false if not - :layout-dir ;; :right, :left, :top, :bottom - :gap ;; number could be negative - :layout-type ;; :packed, :space-between, :space-around - :wrap-type ;; :wrap, :no-wrap - :padding-type ;; :simple, :multiple - :padding ;; {:p1 num :p2 num :p3 num :p4 num} number could be negative - :h-orientation ;; :top, :center, :bottom - :v-orientation]) ;; :left, :center, :right +(def layout-container-attrs + [:layout ;; true if active, false if not + :layout-dir ;; :right, :left, :top, :bottom + :layout-gap ;; number could be negative + :layout-type ;; :packed, :space-between, :space-around + :layout-wrap-type ;; :wrap, :no-wrap + :layout-padding-type ;; :simple, :multiple + :layout-padding ;; {:p1 num :p2 num :p3 num :p4 num} number could be negative + :layout-h-orientation ;; :left, :center, :right + :layout-v-orientation]) ;; :top, :center, :bottom (def grid-pos [[:top :left] [:top :center] @@ -35,35 +37,65 @@ [:bottom :left] [:bottom :center] [:bottom :right]]) + (def grid-rows [:top :center :bottom]) (def grid-cols [:left :center :right]) +(defn- get-layout-icon + [dir layout-type v h] + (let [row? (or (= dir :right) (= dir :left)) + manage-text-icon + (if row? + (case v + :top i/text-align-left + :center i/text-align-center + :bottom i/text-align-right + i/text-align-center) + (case h + :left i/text-align-left + :center i/text-align-center + :right i/text-align-right + i/text-align-center))] + (case layout-type + :packed manage-text-icon + :space-around i/space-around + :space-between i/space-between))) + (mf/defc direction-row [{:keys [dir saved-dir set-direction] :as props}] - [:button.dir.tooltip.tooltip-bottom - {:class (dom/classnames :active (= saved-dir dir) - :left (= :left dir) - :right (= :right dir) - :top (= :top dir) - :bottom (= :bottom dir)) - :key (dm/str "direction-" dir) - :alt (tr (dm/str "workspace.options.layout.direction." (d/name dir))) - :on-click #(set-direction dir)} - i/auto-direction]) + (let [handle-on-click + (mf/use-callback + (mf/deps set-direction dir) + (fn [] + (when (some? set-direction) + (set-direction dir))))] + + [:button.dir.tooltip.tooltip-bottom + {:class (dom/classnames :active (= saved-dir dir) + :left (= :left dir) + :right (= :right dir) + :top (= :top dir) + :bottom (= :bottom dir)) + :key (dm/str "direction-" dir) + :alt (tr (dm/str "workspace.options.layout.direction." (d/name dir))) + :on-click handle-on-click} + i/auto-direction])) (mf/defc orientation-grid - [{:keys [manage-orientation test-values get-icon] :as props}] - (let [dir (:layout-dir @test-values) - type (:layout-type @test-values) + [{:keys [on-change-orientation values] :as props}] + (let [dir (:layout-dir values) + type (:layout-type values) is-col? (or (= dir :top) (= dir :bottom)) - saved-pos [(:h-orientation @test-values) (:v-orientation @test-values)]] + saved-pos [(:layout-v-orientation values) + (:layout-h-orientation values)]] + (if (= type :packed) [:div.orientation-grid [:div.button-wrapper (for [[pv ph] grid-pos] [:button.orientation - {:on-click (partial manage-orientation pv ph type) + {:on-click (partial on-change-orientation pv ph type) :class (dom/classnames :active (= [pv ph] saved-pos) :top (= :top pv) @@ -75,35 +107,14 @@ :key (dm/str pv ph)} [:span.icon {:class (dom/classnames - :rotated is-col?)} - (get-icon dir type pv ph)]])]] - - (if is-col? - [:div.orientation-grid.col - [:div.button-wrapper - (for [col grid-cols] - [:button.orientation - {:on-click (partial manage-orientation :top col type) - :class (dom/classnames - :active (= col (second saved-pos)) - :top (= :left col) - :centered (= :center col) - :bottom (= :right col))} - [:span.icon - {:class (dom/classnames :rotated is-col?)} - (get-icon dir type nil col)] - [:span.icon - {:class (dom/classnames :rotated is-col?)} - (get-icon dir type nil col)] - [:span.icon - {:class (dom/classnames :rotated is-col?)} - (get-icon dir type nil col)]])]] - + :rotated (not is-col?))} + (get-layout-icon dir type pv ph)]])]] + (if (not is-col?) [:div.orientation-grid.row [:div.button-wrapper (for [row grid-rows] [:button.orientation - {:on-click (partial manage-orientation row :left type) + {:on-click (partial on-change-orientation row :left type) :class (dom/classnames :active (= row (first saved-pos)) :top (= :top row) @@ -111,30 +122,51 @@ :bottom (= :bottom row))} [:span.icon {:class (dom/classnames :rotated is-col?)} - (get-icon dir type row nil)] + (get-layout-icon dir type nil row)] [:span.icon {:class (dom/classnames :rotated is-col?)} - (get-icon dir type row nil)] + (get-layout-icon dir type nil row)] [:span.icon {:class (dom/classnames :rotated is-col?)} - (get-icon dir type row nil)]])]])))) + (get-layout-icon dir type nil row)]])]] + + [:div.orientation-grid.col + [:div.button-wrapper + (for [[idx col] (d/enumerate grid-cols)] + [:button.orientation + {:key (dm/str idx col) + :on-click (partial on-change-orientation :top col type) + :class (dom/classnames + :active (= col (second saved-pos)) + :top (= :left col) + :centered (= :center col) + :bottom (= :right col))} + [:span.icon + {:class (dom/classnames :rotated is-col?)} + (get-layout-icon dir type col nil)] + [:span.icon + {:class (dom/classnames :rotated is-col?)} + (get-layout-icon dir type col nil)] + [:span.icon + {:class (dom/classnames :rotated is-col?)} + (get-layout-icon dir type col nil)]])]])))) (mf/defc padding-section - [{:keys [test-values change-padding-style select-all on-padding-change] :as props}] + [{:keys [values on-change-style on-change] :as props}] - (let [padding-type (:padding-type @test-values)] + (let [padding-type (:layout-padding-type values)] [:div.row-flex [:div.padding-options [:div.padding-icon.tooltip.tooltip-bottom {:class (dom/classnames :selected (= padding-type :simple)) :alt (tr "workspace.options.layout.padding-simple") - :on-click #(change-padding-style :simple)} + :on-click #(on-change-style :simple)} i/auto-padding] [:div.padding-icon.tooltip.tooltip-bottom {:class (dom/classnames :selected (= padding-type :multiple)) :alt (tr "workspace.options.layout.padding") - :on-click #(change-padding-style :multiple)} + :on-click #(on-change-style :multiple)} i/auto-padding-side]] (cond @@ -145,9 +177,9 @@ [:> numeric-input {:placeholder "--" - :on-click select-all - :on-change (partial on-padding-change :simple) - :value (:p1 (:padding @test-values))}]]] + :on-click #(dom/select-target %) + :on-change (partial on-change :simple) + :value (:p1 (:layout-padding values))}]]] (= padding-type :multiple) (for [num [:p1 :p2 :p3 :p4]] @@ -161,82 +193,55 @@ [:div.input-element.mini [:> numeric-input {:placeholder "--" - :on-click select-all - :on-change (partial on-padding-change num) - :value (num (:padding @test-values))}]]]))])) + :on-click #(dom/select-target %) + :on-change (partial on-change num) + :value (num (:layout-padding values))}]]]))])) - - -(mf/defc layout-menu +(mf/defc layout-container-menu {::mf/wrap [#(mf/memo' % (mf/check-props ["ids" "values" "type"]))]} - [{:keys [_ids _type _values] :as props}] - (let [test-values (mf/use-state {:layout false - :layout-dir nil - :gap 0 - :layout-type nil - :wrap-type nil - :padding-type nil - :padding {:p1 0 :p2 0 :p3 0 :p4 0} - :h-orientation nil - :v-orientation nil}) - - open? (mf/use-state false) + [{:keys [ids _type values] :as props}] + (let [open? (mf/use-state false) gap-selected? (mf/use-state false) toggle-open (fn [] (swap! open? not)) on-add-layout (fn [_] - (reset! test-values {:layout true - :layout-dir :left - :gap 0 - :layout-type :packed - :wrap-type :wrap - :padding-type :simple - :padding {:p1 0 :p2 0 :p3 0 :p4 0} - :h-orientation :top - :v-orientation :left})) + (st/emit! (dwsl/create-layout ids))) + on-remove-layout (fn [_] - (reset! test-values {:layout false - :layout-dir nil - :gap 0 - :layout-type nil - :wrap-type nil - :padding-type nil - :padding {:p1 0 :p2 0 :p3 0 :p4 0} - :h-orientation nil - :v-orientation nil}) + (st/emit! (dwsl/remove-layout ids)) (reset! open? false)) set-direction (fn [dir] - (swap! test-values assoc :layout-dir dir)) + (st/emit! (dwsl/update-layout ids {:layout-dir dir}))) set-gap - (fn [event] - (swap! test-values assoc :gap event)) - + (fn [gap] + (st/emit! (dwsl/update-layout ids {:layout-gap gap}))) + change-padding-style (fn [type] - (swap! test-values assoc :padding-type type)) + (st/emit! (dwsl/update-layout ids {:layout-padding-type type}))) - select-all #(dom/select-target %) - - select-all-gap #(do (reset! gap-selected? true) - (dom/select-target %)) + select-all-gap + (fn [event] + (reset! gap-selected? true) + (dom/select-target event)) on-padding-change (fn [type val] (if (= type :simple) - (swap! test-values assoc :padding {:p1 val :p2 val :p3 val :p4 val}) - (swap! test-values assoc-in [:padding type] val))) + (st/emit! (dwsl/update-layout ids {:layout-padding {:p1 val :p2 val :p3 val :p4 val}})) + (st/emit! (dwsl/update-layout ids {:layout-padding {type val}})))) handle-change-type (fn [event] (let [target (dom/get-target event) value (dom/get-value target) value (keyword value)] - (swap! test-values assoc :layout-type value))) + (st/emit! (dwsl/update-layout ids {:layout-type value})))) handle-wrap-type (mf/use-callback @@ -244,47 +249,30 @@ (let [target (dom/get-target event) value (dom/get-value target) value (keyword value)] - (swap! test-values assoc :wrap-type value)))) + (st/emit! (dwsl/update-layout ids {:layout-wrap-type value}))))) - manage-orientation - (fn [h v] - (swap! test-values assoc :h-orientation h :v-orientation v)) - - get-icon - (fn [dir layout-type v h] - (let [col? (= dir (or :left :right)) - manage-text-icon - (if col? - (case h - :left i/text-align-left - :center i/text-align-center - :right i/text-align-right - i/text-align-center) - - (case v - :top i/text-align-left - :center i/text-align-center - :bottom i/text-align-right - i/text-align-center))] - (case layout-type - :packed manage-text-icon - :space-around i/space-around - :space-between i/space-between))) + handle-change-orientation + (fn [v-orientation h-orientation] + (st/emit! (dwsl/update-layout ids {:layout-h-orientation h-orientation :layout-v-orientation v-orientation}))) layout-info (fn [] - (let [type (:layout-type @test-values) - dir (:layout-dir @test-values) - is-col? (or (= dir :top) - (= dir :bottom)) - h (:v-orientation @test-values) - v (:h-orientation @test-values) - wrap (:wrap-type @test-values) - orientation (if (= type :packed) - (dm/str (tr (dm/str "workspace.options.layout.v." (d/name v))) ", " (tr (dm/str "workspace.options.layout.h." (d/name h))) ", ") - (if is-col? - (dm/str (tr (dm/str "workspace.options.layout.h." (d/name h))) ", ") - (dm/str (tr (dm/str "workspace.options.layout.v." (d/name v))) ", ")))] + (let [type (:layout-type values) + dir (:layout-dir values) + is-col? (or (= dir :top) (= dir :bottom)) + h (:layout-h-orientation values) + v (:layout-v-orientation values) + + wrap (:layout-wrap-type values) + + orientation + (if (= type :packed) + (dm/str (tr (dm/str "workspace.options.layout.v." (d/name v))) ", " + (tr (dm/str "workspace.options.layout.h." (d/name h))) ", ") + + (if is-col? + (dm/str (tr (dm/str "workspace.options.layout.h." (d/name h))) ", ") + (dm/str (tr (dm/str "workspace.options.layout.v." (d/name v))) ", ")))] (dm/str orientation (str/replace (tr (dm/str "workspace.options.layout." (d/name type))) "-" " ") ", " @@ -294,26 +282,27 @@ [:div.element-set-title [:* [:span (tr "workspace.options.layout.title")] - (if (= true (:layout @test-values)) + (if (:layout values) [:div.add-page {:on-click on-remove-layout} i/minus] [:div.add-page {:on-click on-add-layout} i/close])]] - (when (= true (:layout @test-values)) + (when (:layout values) [:div.element-set-content.layout-menu ;; DIRECTION-GAP [:div.direction-gap [:div.direction [:* (for [dir [:left :right :bottom :top]] - [:& direction-row {:dir dir - :saved-dir (:layout-dir @test-values) + [:& direction-row {:key (d/name dir) + :dir dir + :saved-dir (:layout-dir values) :set-direction set-direction}])]] [:div.gap.tooltip.tooltip-bottom-left {:alt (tr "workspace.options.layout.gap")} [:span.icon {:class (dom/classnames - :rotated (or (= (:layout-dir @test-values) :top) - (= (:layout-dir @test-values) :bottom)) + :rotated (or (= (:layout-dir values) :top) + (= (:layout-dir values) :bottom)) :activated (= @gap-selected? true))} i/auto-gap] [:> numeric-input {:no-validate true @@ -321,7 +310,7 @@ :on-click select-all-gap :on-change set-gap :on-blur #(reset! gap-selected? false) - :value (:gap @test-values)}]]] + :value (:layout-gap values)}]]] ;; LAYOUT FLEX [:div.layout-container @@ -329,25 +318,24 @@ {:on-click toggle-open :alt (layout-info)} [:div.element-set-actions-button i/actions] - [:div.layout-info - (layout-info)]] - (when (= true @open?) + [:div.layout-info (layout-info)]] + + (when @open? [:div.layout-body - [:& orientation-grid {:manage-orientation manage-orientation :test-values test-values :get-icon get-icon}] + [:& orientation-grid {:on-change-orientation handle-change-orientation :values values}] [:div.selects-wrapper - [:select.input-select {:value (d/name (:layout-type @test-values)) + [:select.input-select {:value (d/name (:layout-type values)) :on-change handle-change-type} [:option {:value "packed" :label (tr "workspace.options.layout.packed")}] [:option {:value "space-between" :label (tr "workspace.options.layout.space-between")}] [:option {:value "space-around" :label (tr "workspace.options.layout.space-around")}]] - [:select.input-select {:value (d/name (:wrap-type @test-values)) + [:select.input-select {:value (d/name (:layout-wrap-type values)) :on-change handle-wrap-type} [:option {:value "wrap" :label (tr "workspace.options.layout.wrap")}] [:option {:value "no-wrap" :label (tr "workspace.options.layout.no-wrap")}]]]])] - [:& padding-section {:test-values test-values - :change-padding-style change-padding-style - :select-all select-all - :on-padding-change on-padding-change}]])])) + [:& padding-section {:values values + :on-change-style change-padding-style + :on-change on-padding-change}]])])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs index 448211b1fa..59d955b601 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs @@ -8,6 +8,8 @@ (:require [app.common.data :as d] [app.common.data.macros :as dm] + [app.main.data.workspace.shape-layout :as dwsl] + [app.main.store :as st] [app.main.ui.components.numeric-input :refer [numeric-input]] [app.main.ui.icons :as i] [app.util.dom :as dom] @@ -15,18 +17,19 @@ [rumext.alpha :as mf])) (def layout-item-attrs - [:margin ;; {:m1 0 :m2 0 :m3 0 :m4 0} - :margin-type ;; :simple :multiple - :h-behavior ;; :fill :fix :auto - :v-behavior ;; :fill :fix :auto - :max-h ;; num - :min-h ;; num - :max-w ;; num - :min-w ]) ;; num -(mf/defc margin-section - [{:keys [test-values change-margin-style select-all on-margin-change] :as props}] + [:layout-margin ;; {:m1 0 :m2 0 :m3 0 :m4 0} + :layout-margin-type ;; :simple :multiple + :layout-h-behavior ;; :fill :fix :auto + :layout-v-behavior ;; :fill :fix :auto + :layout-max-h ;; num + :layout-min-h ;; num + :layout-max-w ;; num + :layout-min-w ]) ;; num - (let [margin-type (:margin-type @test-values)] +(mf/defc margin-section + [{:keys [values change-margin-style on-margin-change] :as props}] + + (let [margin-type (or (:layout-margin-type values) :simple)] [:div.row-flex [:div.margin-options @@ -49,9 +52,9 @@ [:> numeric-input {:placeholder "--" - :on-click select-all + :on-click #(dom/select-target %) :on-change (partial on-margin-change :simple) - :value (:m1 (:margin @test-values))}]]] + :value (or (-> values :layout-margin :m1) 0)}]]] (= margin-type :multiple) [:* @@ -67,101 +70,93 @@ [:div.input-element.mini [:> numeric-input {:placeholder "--" - :on-click select-all + :on-click #(dom/select-target %) :on-change (partial on-margin-change num) - :value (num (:margin @test-values))}]]])])])) + :value (or (-> values :layout-margin num) 0)}]]])])])) (mf/defc element-behavior - [{:keys [is-layout-container? is-layout-item? h-behavior v-behavior on-change-behavior] :as props}] - (let [auto? is-layout-container? - fill? (and (= true is-layout-item?) (not= true is-layout-container?))] + [{:keys [is-layout-container? is-layout-child? layout-h-behavior layout-v-behavior on-change-behavior] :as props}] + (let [fill? is-layout-child? + auto? is-layout-container?] [:div.layout-behavior [:div.button-wrapper.horizontal [:button.behavior-btn.tooltip.tooltip-bottom {:alt "horizontal fix" - :class (dom/classnames :activated (= h-behavior :fix)) + :class (dom/classnames :activated (= layout-h-behavior :fix)) :on-click #(on-change-behavior :h :fix)} [:span.icon i/auto-fix-layout]] (when fill? [:button.behavior-btn.tooltip.tooltip-bottom {:alt "horizontal fill" - :class (dom/classnames :activated (= h-behavior :fill)) + :class (dom/classnames :activated (= layout-h-behavior :fill)) :on-click #(on-change-behavior :h :fill)} [:span.icon i/auto-fill]]) (when auto? [:button.behavior-btn.tooltip.tooltip-bottom {:alt "horizontal auto" - :class (dom/classnames :activated (= h-behavior :auto)) + :class (dom/classnames :activated (= layout-v-behavior :auto)) :on-click #(on-change-behavior :h :auto)} [:span.icon i/auto-hug]])] + [:div.button-wrapper [:button.behavior-btn.tooltip.tooltip-bottom {:alt "vertical fix" - :class (dom/classnames :activated (= v-behavior :fix)) + :class (dom/classnames :activated (= layout-v-behavior :fix)) :on-click #(on-change-behavior :v :fix)} [:span.icon i/auto-fix-layout]] (when fill? [:button.behavior-btn.tooltip.tooltip-bottom {:alt "vertical fill" - :class (dom/classnames :activated (= v-behavior :fill)) + :class (dom/classnames :activated (= layout-v-behavior :fill)) :on-click #(on-change-behavior :v :fill)} [:span.icon i/auto-fill]]) (when auto? [:button.behavior-btn.tooltip.tooltip-bottom {:alt "vertical auto" - :class (dom/classnames :activated (= v-behavior :auto)) + :class (dom/classnames :activated (= layout-v-behavior :auto)) :on-click #(on-change-behavior :v :auto)} [:span.icon i/auto-hug]])]])) (mf/defc layout-item-menu {::mf/wrap [#(mf/memo' % (mf/check-props ["ids" "values" "type"]))]} - [{:keys [_ids _type _values] :as props}] - (let [test-values (mf/use-state {:margin {:m1 0 :m2 0 :m3 0 :m4 0} - :margin-type :simple - :h-behavior :fill - :v-behavior :fill - :max-h 100 - :min-h 100 - :max-w 100 - :min-w 100}) - open? (mf/use-state false) + [{:keys [ids _type values is-layout-child? is-layout-container?] :as props}] + (let [open? (mf/use-state false) toggle-open (fn [] (swap! open? not)) - is-layout-container? true - is-layout-item? true + change-margin-style (fn [type] - (swap! test-values assoc :margin-type type)) - - select-all #(dom/select-target %) + (st/emit! (dwsl/update-layout-child ids {:layout-margin-type type}))) on-margin-change (fn [type val] (if (= type :simple) - (swap! test-values assoc :margin {:m1 val :m2 val :m3 val :m4 val}) - (swap! test-values assoc-in [:margin type] val))) + (st/emit! (dwsl/update-layout-child ids {:layout-margin {:m1 val :m2 val :m3 val :m4 val}})) + (st/emit! (dwsl/update-layout-child ids {:layout-margin {type val}})))) on-change-behavior (fn [dir value] (if (= dir :h) - (swap! test-values assoc :h-behavior value) - (swap! test-values assoc :v-behavior value))) + (st/emit! (dwsl/update-layout-child ids {:layout-h-behavior value})) + (st/emit! (dwsl/update-layout-child ids {:layout-v-behavior value})))) on-size-change (fn [measure value] - (swap! test-values assoc measure value))] + (st/emit! (dwsl/update-layout-child ids {measure value})))] + [:div.element-set [:div.element-set-title [:span (tr "workspace.options.layout-item.title")]] + [:div.element-set-content.layout-item-menu - [:& element-behavior {:is-layout-container? is-layout-container? - :is-layout-item? is-layout-item? - :v-behavior (:v-behavior @test-values) - :h-behavior (:h-behavior @test-values) + [:& element-behavior {:is-layout-child? is-layout-child? + :is-layout-container? is-layout-container? + :layout-v-behavior (or (:layout-v-behavior values) :fix) + :layout-h-behavior (or (:layout-h-behavior values) :fix) :on-change-behavior on-change-behavior}] - [:div.margin [:& margin-section {:test-values test-values + + [:div.margin [:& margin-section {:values values :change-margin-style change-margin-style - :select-all select-all :on-margin-change on-margin-change}]] [:div.advanced-ops-container [:div.advanced-ops.toltip.tooltip-bottom @@ -169,22 +164,24 @@ :alt (tr "workspace.options.layout-item.advanced-ops")} [:div.element-set-actions-button i/actions] [:span (tr "workspace.options.layout-item.advanced-ops")]]] - (when (= true @open?) + + (when @open? [:div.advanced-ops-body - (for [item [:max-h :min-h :max-w :min-w]] + (for [item [:layout-max-h :layout-min-h :layout-max-w :layout-min-w]] [:div.input-element - {:alt (tr (dm/str "workspace.options.layout-item." (d/name item))) + {:key (d/name item) + :alt (tr (dm/str "workspace.options.layout-item." (d/name item))) :title (tr (dm/str "workspace.options.layout-item." (d/name item))) - :class (dom/classnames "maxH" (= item :max-h) - "minH" (= item :min-h) - "maxW" (= item :max-w) - "minW" (= item :min-w)) - :key item} + :class (dom/classnames "maxH" (= item :layout-max-h) + "minH" (= item :layout-min-h) + "maxW" (= item :layout-max-w) + "minW" (= item :layout-min-w))} + [:> numeric-input {:no-validate true :min 0 :data-wrap true :placeholder "--" - :on-click select-all + :on-click #(dom/select-target %) :on-change (partial on-size-change item) - :value (item @test-values)}]])])]])) + :value (get values item)}]])])]])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/bool.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/bool.cljs index 69517bad68..6333c0d185 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/bool.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/bool.cljs @@ -6,7 +6,7 @@ (ns app.main.ui.workspace.sidebar.options.shapes.bool (:require - [app.main.constants :refer [has-layout-item]] + [app.main.refs :as refs] [app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]] [app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]] [app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]] @@ -25,17 +25,22 @@ stroke-values (select-keys shape stroke-attrs) layer-values (select-keys shape layer-attrs) constraint-values (select-keys shape constraint-attrs) - layout-item-values (select-keys shape layout-item-attrs)] + layout-item-values (select-keys shape layout-item-attrs) + + is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids)) + is-layout-child? (mf/deref is-layout-child-ref)] [:* [:& measures-menu {:ids ids :type type :values measure-values :shape shape}] - (when has-layout-item - [:& layout-item-menu {:ids ids - :type type - :values layout-item-values - :shape shape}]) + (when is-layout-child? + [:& layout-item-menu + {:ids ids + :type type + :values layout-item-values + :is-layout-child? true + :shape shape}]) [:& constraints-menu {:ids ids :values constraint-values}] [:& layer-menu {:ids ids diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/circle.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/circle.cljs index 845db69616..a818eba8fa 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/circle.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/circle.cljs @@ -6,7 +6,7 @@ (ns app.main.ui.workspace.sidebar.options.shapes.circle (:require - [app.main.constants :refer [has-layout-item]] + [app.main.refs :as refs] [app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]] [app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]] [app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]] @@ -22,20 +22,26 @@ [{:keys [shape] :as props}] (let [ids [(:id shape)] type (:type shape) + measure-values (select-keys shape measure-attrs) stroke-values (select-keys shape stroke-attrs) layer-values (select-keys shape layer-attrs) constraint-values (select-keys shape constraint-attrs) - layout-item-values (select-keys shape layout-item-attrs)] + layout-item-values (select-keys shape layout-item-attrs) + + is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids)) + is-layout-child? (mf/deref is-layout-child-ref)] [:* [:& measures-menu {:ids ids :type type :values measure-values :shape shape}] - (when has-layout-item + (when is-layout-child? [:& layout-item-menu {:ids ids :type type :values layout-item-values + :is-layout-child? true + :is-layout-container? false :shape shape}]) [:& constraints-menu {:ids ids :values constraint-values}] diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs index 13aa47a60e..6a6957c255 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs @@ -6,13 +6,14 @@ (ns app.main.ui.workspace.sidebar.options.shapes.frame (:require - [app.main.constants :refer [has-layout-item]] + [app.main.refs :as refs] + [app.main.ui.features :as features] [app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]] [app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]] [app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs-shape fill-menu]] [app.main.ui.workspace.sidebar.options.menus.frame-grid :refer [frame-grid]] [app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]] - [app.main.ui.workspace.sidebar.options.menus.layout :refer [layout-attrs layout-menu]] + [app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-attrs layout-container-menu]] [app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-attrs layout-item-menu]] [app.main.ui.workspace.sidebar.options.menus.measures :refer [measure-attrs measures-menu]] [app.main.ui.workspace.sidebar.options.menus.shadow :refer [shadow-menu]] @@ -23,12 +24,18 @@ [{:keys [shape] :as props}] (let [ids [(:id shape)] type (:type shape) + + layout-active? (features/use-feature :auto-layout) + stroke-values (select-keys shape stroke-attrs) layer-values (select-keys shape layer-attrs) measure-values (select-keys shape measure-attrs) constraint-values (select-keys shape constraint-attrs) - layout-values (select-keys shape layout-attrs) - layout-item-values (select-keys shape layout-item-attrs)] + layout-container-values (select-keys shape layout-container-attrs) + layout-item-values (select-keys shape layout-item-attrs) + + is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids)) + is-layout-child? (mf/deref is-layout-child-ref)] [:* [:& measures-menu {:ids [(:id shape)] :values measure-values @@ -36,14 +43,19 @@ :shape shape}] [:& constraints-menu {:ids ids :values constraint-values}] - (when has-layout-item - [:& layout-menu {:type type :ids [(:id shape)] :values layout-values}]) - - (when has-layout-item - [:& layout-item-menu {:ids ids - :type type - :values layout-item-values - :shape shape}]) + (when layout-active? + [:* + [:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values}] + + (when (or (:layout shape) is-layout-child?) + [:& layout-item-menu + {:ids ids + :type type + :values layout-item-values + :is-layout-child? is-layout-child? + :is-layout-container? (:layout shape) + :shape shape}])]) + [:& layer-menu {:ids ids :type type :values layer-values}] diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/group.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/group.cljs index 3a4e92055a..b8697be199 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/group.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/group.cljs @@ -7,14 +7,13 @@ (ns app.main.ui.workspace.sidebar.options.shapes.group (:require [app.common.data :as d] - [app.main.constants :refer [has-layout-item]] + [app.main.refs :as refs] [app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]] [app.main.ui.workspace.sidebar.options.menus.color-selection :refer [color-selection-menu]] [app.main.ui.workspace.sidebar.options.menus.component :refer [component-attrs component-menu]] [app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraints-menu]] [app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-menu]] [app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-menu]] - [app.main.ui.workspace.sidebar.options.menus.layout :refer [layout-menu]] [app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-menu]] [app.main.ui.workspace.sidebar.options.menus.measures :refer [measures-menu]] [app.main.ui.workspace.sidebar.options.menus.shadow :refer [shadow-menu]] @@ -34,6 +33,10 @@ objects (->> shape-with-children (group-by :id) (d/mapm (fn [_ v] (first v)))) file-id (unchecked-get props "file-id") + ids [(:id shape)] + is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids)) + is-layout-child? (mf/deref is-layout-child-ref) + type :group [measure-ids measure-values] (get-attrs [shape] objects :measure) [layer-ids layer-values] (get-attrs [shape] objects :layer) @@ -45,17 +48,20 @@ [text-ids text-values] (get-attrs [shape] objects :text) [svg-ids svg-values] [[(:id shape)] (select-keys shape [:svg-attrs])] [comp-ids comp-values] [[(:id shape)] (select-keys shape component-attrs)] - [layout-ids layout-values] (get-attrs [shape] objects :layout) - [layout-item-ids layout-item-values] (get-attrs [shape] objects :layout-item) - ] + [layout-item-ids layout-item-values] (get-attrs [shape] objects :layout-item)] [:div.options [:& measures-menu {:type type :ids measure-ids :values measure-values :shape shape}] [:& component-menu {:ids comp-ids :values comp-values :shape-name (:name shape)}] - (when-not (empty? layout-ids) - [:& layout-menu {:type type :ids layout-ids :values layout-values}]) - (when has-layout-item - [:& layout-item-menu {:type type :ids layout-item-ids :values layout-item-values}]) + + (when is-layout-child? + [:& layout-item-menu + {:type type + :ids layout-item-ids + :is-layout-child? true + :is-layout-container? false + :values layout-item-values}]) + [:& constraints-menu {:ids constraint-ids :values constraint-values}] [:& layer-menu {:type type :ids layer-ids :values layer-values}] diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/image.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/image.cljs index b6d37e3a53..667bbb5551 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/image.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/image.cljs @@ -6,7 +6,7 @@ (ns app.main.ui.workspace.sidebar.options.shapes.image (:require - [app.main.constants :refer [has-layout-item]] + [app.main.refs :as refs] [app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]] [app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]] [app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]] @@ -21,22 +21,28 @@ [{:keys [shape] :as props}] (let [ids [(:id shape)] type (:type shape) + measure-values (select-keys shape measure-attrs) layer-values (select-keys shape layer-attrs) constraint-values (select-keys shape constraint-attrs) fill-values (select-keys shape fill-attrs) stroke-values (select-keys shape stroke-attrs) - layout-item-values (select-keys shape layout-item-attrs)] + layout-item-values (select-keys shape layout-item-attrs) + + is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids)) + is-layout-child? (mf/deref is-layout-child-ref)] [:* [:& measures-menu {:ids ids :type type :values measure-values :shape shape}] - (when has-layout-item - [:& layout-item-menu {:ids ids - :type type - :values layout-item-values - :shape shape}]) + (when is-layout-child? + [:& layout-item-menu + {:ids ids + :type type + :values layout-item-values + :is-layout-child? true + :shape shape}]) [:& constraints-menu {:ids ids :values constraint-values}] diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs index 782e33a564..0b1896be34 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs @@ -11,7 +11,7 @@ [app.common.geom.shapes :as gsh] [app.common.pages.common :as cpc] [app.common.text :as txt] - [app.main.constants :refer [has-layout-item]] + [app.main.refs :as refs] [app.main.ui.hooks :as hooks] [app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-attrs blur-menu]] [app.main.ui.workspace.sidebar.options.menus.color-selection :refer [color-selection-menu]] @@ -19,7 +19,7 @@ [app.main.ui.workspace.sidebar.options.menus.exports :refer [exports-attrs exports-menu]] [app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]] [app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]] - [app.main.ui.workspace.sidebar.options.menus.layout :refer [layout-attrs layout-menu]] + [app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-attrs layout-container-menu]] [app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-attrs layout-item-menu]] [app.main.ui.workspace.sidebar.options.menus.measures :refer [measure-attrs measures-menu]] [app.main.ui.workspace.sidebar.options.menus.shadow :refer [shadow-attrs shadow-menu]] @@ -142,7 +142,7 @@ :stroke stroke-attrs :text ot/attrs :exports exports-attrs - :layout layout-attrs + :layout layout-container-attrs :layout-item layout-item-attrs}) (def shadow-keys [:style :color :offset-x :offset-y :blur :spread]) @@ -247,6 +247,10 @@ type :multiple all-types (into #{} (map :type shapes)) + ids (->> shapes (map :id)) + is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids)) + is-layout-child? (mf/deref is-layout-child-ref) + has-text? (contains? all-types :text) [measure-ids measure-values] (get-attrs shapes objects :measure) @@ -260,7 +264,7 @@ stroke-ids stroke-values text-ids text-values exports-ids exports-values - layout-ids layout-values + layout-ids layout-container-values layout-item-ids layout-item-values] (mf/use-memo (mf/deps objects-no-measures) @@ -284,11 +288,16 @@ (when-not (empty? measure-ids) [:& measures-menu {:type type :all-types all-types :ids measure-ids :values measure-values :shape shapes}]) - (when-not (empty? layout-ids) - [:& layout-menu {:type type :ids layout-ids :values layout-values}]) + (when (:layout layout-container-values) + [:& layout-container-menu {:type type :ids layout-ids :values layout-container-values}]) - (when has-layout-item - [:& layout-item-menu {:type type :ids layout-item-ids :values layout-item-values}]) + (when is-layout-child? + [:& layout-item-menu + {:type type + :ids layout-item-ids + :is-layout-child? true + :is-layout-container? true + :values layout-item-values}]) (when-not (empty? constraint-ids) [:& constraints-menu {:ids constraint-ids :values constraint-values}]) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/path.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/path.cljs index b7f4265fae..247b550ba1 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/path.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/path.cljs @@ -6,7 +6,7 @@ (ns app.main.ui.workspace.sidebar.options.shapes.path (:require - [app.main.constants :refer [has-layout-item]] + [app.main.refs :as refs] [app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]] [app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]] [app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]] @@ -22,20 +22,26 @@ [{:keys [shape] :as props}] (let [ids [(:id shape)] type (:type shape) + measure-values (select-keys shape measure-attrs) stroke-values (select-keys shape stroke-attrs) layer-values (select-keys shape layer-attrs) constraint-values (select-keys shape constraint-attrs) - layout-item-values (select-keys shape layout-item-attrs)] + layout-item-values (select-keys shape layout-item-attrs) + + is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids)) + is-layout-child? (mf/deref is-layout-child-ref)] [:* [:& measures-menu {:ids ids :type type :values measure-values :shape shape}] - (when has-layout-item + (when is-layout-child? [:& layout-item-menu {:ids ids :type type :values layout-item-values + :is-layout-child? true + :is-layout-container? false :shape shape}]) [:& constraints-menu {:ids ids :values constraint-values}] diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/rect.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/rect.cljs index 49773a0698..4c16b9fe0e 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/rect.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/rect.cljs @@ -6,7 +6,7 @@ (ns app.main.ui.workspace.sidebar.options.shapes.rect (:require - [app.main.constants :refer [has-layout-item]] + [app.main.refs :as refs] [app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]] [app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]] [app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]] @@ -23,22 +23,28 @@ [{:keys [shape] :as props}] (let [ids [(:id shape)] type (:type shape) + measure-values (select-keys shape measure-attrs) layer-values (select-keys shape layer-attrs) constraint-values (select-keys shape constraint-attrs) fill-values (select-keys shape fill-attrs) stroke-values (select-keys shape stroke-attrs) - layout-item-values (select-keys shape layout-item-attrs)] + layout-item-values (select-keys shape layout-item-attrs) + + is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids)) + is-layout-child? (mf/deref is-layout-child-ref)] [:* [:& measures-menu {:ids ids :type type :values measure-values :shape shape}] - (when has-layout-item - [:& layout-item-menu {:ids ids - :type type - :values layout-item-values - :shape shape}]) + (when is-layout-child? + [:& layout-item-menu + {:ids ids + :type type + :values layout-item-values + :is-layout-child? true + :shape shape}]) [:& constraints-menu {:ids ids :values constraint-values}] diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/svg_raw.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/svg_raw.cljs index 331cc6d558..be7fa1ca17 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/svg_raw.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/svg_raw.cljs @@ -8,7 +8,7 @@ (:require [app.common.colors :as clr] [app.common.data :as d] - [app.main.constants :refer [has-layout-item]] + [app.main.refs :as refs] [app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]] [app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]] [app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]] @@ -95,12 +95,16 @@ (let [ids [(:id shape)] type (:type shape) + {:keys [tag] :as content} (:content shape) measure-values (select-keys shape measure-attrs) constraint-values (select-keys shape constraint-attrs) fill-values (get-fill-values shape) stroke-values (get-stroke-values shape) - layout-item-values (select-keys shape layout-item-attrs)] + layout-item-values (select-keys shape layout-item-attrs) + + is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids)) + is-layout-child? (mf/deref is-layout-child-ref)] (when (contains? svg-elements tag) [:* @@ -108,11 +112,13 @@ :type type :values measure-values :shape shape}] - (when has-layout-item - [:& layout-item-menu {:ids ids - :type type - :values layout-item-values - :shape shape}]) + (when is-layout-child? + [:& layout-item-menu + {:ids ids + :type type + :values layout-item-values + :is-layout-child? true + :shape shape}]) [:& constraints-menu {:ids ids :values constraint-values}] diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/text.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/text.cljs index 98e2e7849a..8288a21f9c 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/text.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/text.cljs @@ -7,7 +7,6 @@ (ns app.main.ui.workspace.sidebar.options.shapes.text (:require [app.common.data :as d] - [app.main.constants :refer [has-layout-item]] [app.main.data.workspace.texts :as dwt] [app.main.refs :as refs] [app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]] @@ -27,6 +26,8 @@ (let [ids [(:id shape)] type (:type shape) + is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids)) + is-layout-child? (mf/deref is-layout-child-ref) state-map (mf/deref refs/workspace-editor-state) shared-libs (mf/deref refs/workspace-libraries) @@ -69,11 +70,14 @@ :type type :values (select-keys shape measure-attrs) :shape shape}] - (when has-layout-item - [:& layout-item-menu {:ids ids - :type type - :values layout-item-values - :shape shape}]) + + (when is-layout-child? + [:& layout-item-menu + {:ids ids + :type type + :values layout-item-values + :is-layout-child? true + :shape shape}]) [:& constraints-menu {:ids ids :values (select-keys shape constraint-attrs)}] diff --git a/frontend/src/app/util/dom.cljs b/frontend/src/app/util/dom.cljs index dd02530596..532df7a618 100644 --- a/frontend/src/app/util/dom.cljs +++ b/frontend/src/app/util/dom.cljs @@ -106,9 +106,7 @@ "Extract the target from event instance and select it" [^js event] (when (some? event) - (-> event - (.-target) - (.-select)))) + (-> event (.-target) (.select)))) (defn select-node "Select element by node" diff --git a/frontend/src/debug.cljs b/frontend/src/debug.cljs index ccf1bd49b9..bef2d105d9 100644 --- a/frontend/src/debug.cljs +++ b/frontend/src/debug.cljs @@ -353,3 +353,10 @@ (let [root-node (dom/query ".viewport .render-shapes") num-nodes (->> (dom/seq-nodes root-node) count)] #js {:number num-nodes})) + +#_(defn modif->js + [modif-tree objects] + (clj->js (into {} + (map (fn [[k v]] + [(get-in objects [k :name]) v])) + modif-tree))) diff --git a/frontend/translations/en.po b/frontend/translations/en.po index ad5bec27f9..e9bfc9dd3d 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -3503,19 +3503,19 @@ msgid "workspace.options.layout-item.advanced-ops" msgstr "Advanced options" #: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs -msgid "workspace.options.layout-item.max-h" +msgid "workspace.options.layout-item.layout-max-h" msgstr "Max.Height" #: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs -msgid "workspace.options.layout-item.max-w" +msgid "workspace.options.layout-item.layout-max-w" msgstr "Max.Width" #: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs -msgid "workspace.options.layout-item.min-h" +msgid "workspace.options.layout-item.layout-min-h" msgstr "Min.Height" #: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs -msgid "workspace.options.layout-item.min-w" +msgid "workspace.options.layout-item.layout-min-w" msgstr "Min.Width" #: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs @@ -3523,19 +3523,19 @@ msgid "workspace.options.layout-item.title" msgstr "Element resizing" #: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs -msgid "workspace.options.layout-item.title.max-h" +msgid "workspace.options.layout-item.title.layout-max-h" msgstr "Maximum height" #: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs -msgid "workspace.options.layout-item.title.max-w" +msgid "workspace.options.layout-item.title.layout-max-w" msgstr "Maximum width" #: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs -msgid "workspace.options.layout-item.title.min-h" +msgid "workspace.options.layout-item.title.layout-min-h" msgstr "Minimum height" #: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs -msgid "workspace.options.layout-item.title.min-w" +msgid "workspace.options.layout-item.title.layout-min-w" msgstr "Minimum width" #: src/app/main/ui/workspace/sidebar/options/menus/layout.cljs @@ -4368,4 +4368,4 @@ msgid "workspace.updates.update" msgstr "Update" msgid "workspace.viewport.click-to-close-path" -msgstr "Click to close the path" \ No newline at end of file +msgstr "Click to close the path" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index f7c366b6a6..736e0766a5 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -3661,19 +3661,19 @@ msgid "workspace.options.layout-item.advanced-ops" msgstr "Opciones avanzadas" #: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs -msgid "workspace.options.layout-item.max-h" +msgid "workspace.options.layout-item.layout-max-h" msgstr "AlturaMax." #: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs -msgid "workspace.options.layout-item.max-w" +msgid "workspace.options.layout-item.layout-max-w" msgstr "AnchoMax." #: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs -msgid "workspace.options.layout-item.min-h" +msgid "workspace.options.layout-item.layout-min-h" msgstr "AlturaMin." #: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs -msgid "workspace.options.layout-item.min-w" +msgid "workspace.options.layout-item.layout-min-w" msgstr "AnchoMin." #: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs @@ -3681,19 +3681,19 @@ msgid "workspace.options.layout-item.title" msgstr "Redimensionado de elemento" #: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs -msgid "workspace.options.layout-item.title.max-h" +msgid "workspace.options.layout-item.title.layout-max-h" msgstr "Altura máxima" #: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs -msgid "workspace.options.layout-item.title.max-w" +msgid "workspace.options.layout-item.title.layout-max-w" msgstr "Ancho máximo" #: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs -msgid "workspace.options.layout-item.title.min-h" +msgid "workspace.options.layout-item.title.layout-min-h" msgstr "Altura mínima" #: src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs -msgid "workspace.options.layout-item.title.min-w" +msgid "workspace.options.layout-item.title.layout-min-w" msgstr "Ancho mínimo" #: src/app/main/ui/workspace/sidebar/options/menus/layout.cljs @@ -4547,4 +4547,4 @@ msgid "workspace.updates.update" msgstr "Actualizar" msgid "workspace.viewport.click-to-close-path" -msgstr "Pulsar para cerrar la ruta" \ No newline at end of file +msgstr "Pulsar para cerrar la ruta"