From 40da1c302adde502416275f54092ea44a20c3a83 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Wed, 28 Dec 2022 12:41:46 +0100 Subject: [PATCH 1/3] :sparkles: Support hidden elements in flex layout --- .../geom/shapes/flex_layout/drop_area.cljc | 1 + .../src/app/common/geom/shapes/modifiers.cljc | 9 +++-- .../src/app/main/data/workspace/changes.cljs | 35 +++++++++++++------ .../app/main/ui/workspace/viewport/debug.cljs | 10 ++++-- 4 files changed, 38 insertions(+), 17 deletions(-) diff --git a/common/src/app/common/geom/shapes/flex_layout/drop_area.cljc b/common/src/app/common/geom/shapes/flex_layout/drop_area.cljc index 8a5c7a5621..3c11174479 100644 --- a/common/src/app/common/geom/shapes/flex_layout/drop_area.cljc +++ b/common/src/app/common/geom/shapes/flex_layout/drop_area.cljc @@ -184,6 +184,7 @@ (let [frame (get objects frame-id) position (gmt/transform-point-center position (gco/center-shape frame) (:transform-inverse frame)) children (->> (cph/get-immediate-children objects frame-id) + (remove :hidden) (map #(vector (gpo/parent-coords-bounds (:points %) (:points frame)) %))) layout-data (fli/calc-layout-data frame children (:points frame)) drop-areas (layout-drop-areas frame layout-data children) diff --git a/common/src/app/common/geom/shapes/modifiers.cljc b/common/src/app/common/geom/shapes/modifiers.cljc index 1966fc03fc..b1761aac54 100644 --- a/common/src/app/common/geom/shapes/modifiers.cljc +++ b/common/src/app/common/geom/shapes/modifiers.cljc @@ -169,8 +169,9 @@ [layout-line modif-tree]))] - (let [children (->> (:shapes parent) - (map (comp apply-modifiers (d/getf objects)))) + (let [children (->> (cph/get-immediate-children objects (:id parent)) + (remove :hidden) + (map apply-modifiers)) layout-data (gcl/calc-layout-data parent children @transformed-parent-bounds) children (into [] (cond-> children (not (:reverse? layout-data)) reverse)) max-idx (dec (count children)) @@ -209,7 +210,9 @@ (-> modifiers (ctm/resize-parent (gpt/point 1 scale-height) origin (:transform parent) (:transform-inverse parent))))) - children (->> parent :shapes (map (d/getf objects))) + children (->> (cph/get-immediate-children objects parent-id) + (remove :hidden)) + content-bounds (when (and (d/not-empty? children) (or (ctl/auto-height? parent) (ctl/auto-width? parent))) (gcl/layout-content-bounds bounds parent children)) diff --git a/frontend/src/app/main/data/workspace/changes.cljs b/frontend/src/app/main/data/workspace/changes.cljs index b00c2904e7..15d1c9b7e5 100644 --- a/frontend/src/app/main/data/workspace/changes.cljs +++ b/frontend/src/app/main/data/workspace/changes.cljs @@ -30,6 +30,7 @@ (s/every ::us/uuid)) (defonce page-change? #{:add-page :mod-page :del-page :mov-page}) +(defonce update-layout-attr? #{:hidden}) (declare commit-changes) @@ -50,18 +51,30 @@ objects (wsh/lookup-page-objects state page-id) ids (into [] (filter some?) ids) - changes (reduce - (fn [changes id] - (let [opts {:attrs attrs :ignore-geometry? (get ignore-tree id)}] - (pcb/update-shapes changes [id] update-fn opts))) - (-> (pcb/empty-changes it page-id) - (pcb/set-save-undo? save-undo?) - (pcb/with-objects objects)) - ids)] + update-layout-ids + (->> ids + (map (d/getf objects)) + (filter #(some update-layout-attr? (pcb/changed-attrs % update-fn {:attrs attrs}))) + (map :id)) - (when (seq (:redo-changes changes)) - (let [changes (cond-> changes reg-objects? (pcb/resize-parents ids))] - (rx/of (commit-changes changes))))))))) + changes (reduce + (fn [changes id] + (let [opts {:attrs attrs :ignore-geometry? (get ignore-tree id)}] + (pcb/update-shapes changes [id] update-fn opts))) + (-> (pcb/empty-changes it page-id) + (pcb/set-save-undo? save-undo?) + (pcb/with-objects objects)) + ids)] + (rx/concat + (if (seq (:redo-changes changes)) + (let [changes (cond-> changes reg-objects? (pcb/resize-parents ids))] + (rx/of (commit-changes changes))) + (rx/empty)) + + ;; Update layouts for properties marked + (if (d/not-empty? update-layout-ids) + (rx/of (ptk/data-event :layout/update update-layout-ids)) + (rx/empty)))))))) (defn send-update-indices [] diff --git a/frontend/src/app/main/ui/workspace/viewport/debug.cljs b/frontend/src/app/main/ui/workspace/viewport/debug.cljs index 845e38ca41..21b5aecf31 100644 --- a/frontend/src/app/main/ui/workspace/viewport/debug.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/debug.cljs @@ -35,7 +35,8 @@ shape (or selected-frame (get objects hover-top-frame-id))] (when (and shape (:layout shape)) - (let [children (cph/get-immediate-children objects (:id shape)) + (let [children (->> (cph/get-immediate-children objects (:id shape)) + (remove :hidden)) layout-bounds (gsl/layout-content-bounds (d/lazy-map (keys objects) #(dm/get-in objects [% :points])) shape children)] [:g.debug-layout {:pointer-events "none"} [:polygon {:points (->> layout-bounds (map #(dm/fmt "%, %" (:x %) (:y %))) (str/join " ")) @@ -61,7 +62,8 @@ (let [row? (ctl/row? shape) col? (ctl/col? shape) - children (cph/get-immediate-children objects (:id shape)) + children (->> (cph/get-immediate-children objects (:id shape)) + (remove :hidden)) layout-data (gsl/calc-layout-data shape children (:points shape)) layout-bounds (:layout-bounds layout-data) @@ -99,6 +101,7 @@ (when (and shape (:layout shape)) (let [children (->> (cph/get-immediate-children objects (:id shape)) + (remove :hidden) (map #(vector (gpo/parent-coords-bounds (:points %) (:points shape)) %))) layout-data (gsl/calc-layout-data shape children (:points shape)) drop-areas (gsl/layout-drop-areas shape layout-data children)] @@ -164,7 +167,8 @@ parent-bounds (:points parent)] (when (and (some? parent) (not= uuid/zero (:id parent))) - (let [children (cph/get-immediate-children objects (:id parent))] + (let [children (->> (cph/get-immediate-children objects (:id parent)) + (remove :hidden))] [:g.debug-parent-bounds {:pointer-events "none"} (for [[idx child] (d/enumerate children)] [:* From e926b11fefcf4ce75c5bdde86f69be96d09ebe5b Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Wed, 28 Dec 2022 16:31:54 +0100 Subject: [PATCH 2/3] :sparkles: Changes to the margin-item and min/max width/height --- common/src/app/common/types/shape/layout.cljc | 2 +- .../images/icons/auto-margin-both-sides.svg | 3 + .../partials/sidebar-element-options.scss | 19 +- frontend/src/app/main/ui/icons.cljs | 1 + .../sidebar/options/menus/layout_item.cljs | 165 ++++++++++-------- 5 files changed, 108 insertions(+), 82 deletions(-) create mode 100644 frontend/resources/images/icons/auto-margin-both-sides.svg diff --git a/common/src/app/common/types/shape/layout.cljc b/common/src/app/common/types/shape/layout.cljc index bfe99ed200..5a9ffcf97a 100644 --- a/common/src/app/common/types/shape/layout.cljc +++ b/common/src/app/common/types/shape/layout.cljc @@ -193,7 +193,7 @@ m4 (or m4 0)] (if (= layout-item-margin-type :multiple) [m1 m2 m3 m4] - [m1 m1 m1 m1]))) + [m1 m2 m1 m2]))) (defn child-height-margin [child] diff --git a/frontend/resources/images/icons/auto-margin-both-sides.svg b/frontend/resources/images/icons/auto-margin-both-sides.svg new file mode 100644 index 0000000000..841f73206c --- /dev/null +++ b/frontend/resources/images/icons/auto-margin-both-sides.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/styles/main/partials/sidebar-element-options.scss b/frontend/resources/styles/main/partials/sidebar-element-options.scss index fb3f29de71..a79ea921d0 100644 --- a/frontend/resources/styles/main/partials/sidebar-element-options.scss +++ b/frontend/resources/styles/main/partials/sidebar-element-options.scss @@ -1717,13 +1717,17 @@ padding: 1px; } - .gap-group { + .gap-group, + .margin-item-group { display: flex; margin-top: 3px; margin-bottom: 8px; height: 26px; + .gap-row, .gap-column, + .margin-item-row, + .margin-item-column, .padding-row { display: flex; justify-content: center; @@ -1789,23 +1793,28 @@ grid-template-columns: auto 30px; } + .margin-item-row { + grid-template-columns: auto 30px; + margin-bottom: 1rem; + } + .margin-row { grid-template-columns: 65px auto; } .padding-row, - .margin-row { + .margin-item-row { display: grid; height: 26px; .padding-icons, - .margin-icons { + .margin-item-icons { display: flex; padding: 0; border: 1px solid $color-gray-60; border-radius: 3px; .padding-icon, - .margin-icon { + .margin-item-icon { display: flex; justify-content: center; align-items: center; @@ -1832,7 +1841,7 @@ } } - .padding-icons { + .padding-icons, .margin-item-icons { margin-bottom: 8px; margin-top: 3px; margin-right: 1px; diff --git a/frontend/src/app/main/ui/icons.cljs b/frontend/src/app/main/ui/icons.cljs index a8dec1705d..f73424ecd2 100644 --- a/frontend/src/app/main/ui/icons.cljs +++ b/frontend/src/app/main/ui/icons.cljs @@ -65,6 +65,7 @@ (def auto-height (icon-xref :auto-height)) (def auto-hug (icon-xref :auto-hug)) (def auto-margin-side (icon-xref :auto-margin-side)) +(def auto-margin-both-sides (icon-xref :auto-margin-both-sides)) (def auto-margin (icon-xref :auto-margin)) (def auto-padding (icon-xref :auto-padding)) (def auto-padding-side (icon-xref :auto-padding-side)) 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 4ae113c202..8403c0b5cb 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 @@ -35,39 +35,43 @@ [{:keys [values change-margin-style on-margin-change] :as props}] (let [margin-type (or (:layout-item-margin-type values) :simple) - margins (if (nil? (:layout-item-margin values)) - {:m1 0 :m2 0 :m3 0 :m4 0} - (:layout-item-margin values)) - rx (if (or (= :multiple margins) (not (apply = (vals margins)))) - "--" - (:m1 margins))] + m1 (if (and (not (= :multiple (:layout-item-margin values))) + (= (dm/get-in values [:layout-item-margin :m1]) + (dm/get-in values [:layout-item-margin :m3]))) + (dm/get-in values [:layout-item-margin :m1]) + "--") - [:div.margin-row - [:div.margin-icons - [:div.margin-icon.tooltip.tooltip-bottom - {:class (dom/classnames :selected (= margin-type :simple)) - :alt (tr "workspace.options.layout.margin-simple") - :on-click #(change-margin-style :simple)} - i/auto-margin] - [:div.margin-icon.tooltip.tooltip-bottom - {:class (dom/classnames :selected (= margin-type :multiple)) - :alt (tr "workspace.options.layout.margin") - :on-click #(change-margin-style :multiple)} - i/auto-margin-side]] - [:div.wrapper - (cond - (= margin-type :simple) - [:div.tooltip.tooltip-bottom - {:alt (tr "workspace.options.layout.margin-all")} - [:div.input-element.mini + m2 (if (and (not (= :multiple (:layout-item-margin values))) + (= (dm/get-in values [:layout-item-margin :m2]) + (dm/get-in values [:layout-item-margin :m4]))) + (dm/get-in values [:layout-item-margin :m2]) + "--")] - [:> numeric-input - {:placeholder "--" - :on-click #(dom/select-target %) - :on-change (partial on-margin-change :simple) - :value rx}]]] + [:div.layout-row.margin-item-row + (cond + (= margin-type :simple) - (= margin-type :multiple) + [:div.margin-item-group + [:div.margin-item-row.tooltip.tooltip-bottom-left + {:alt "Vertical margin"} + [:span.icon i/auto-margin-both-sides] + [:> numeric-input + {:placeholder "--" + :on-click #(dom/select-target %) + :on-change (partial on-margin-change :simple :m1) + :value m1}]] + + [:div.margin-item-column.tooltip.tooltip-bottom-left + {:alt "Horizontal margin"} + [:span.icon.rotated i/auto-margin-both-sides] + [:> numeric-input + {:placeholder "--" + :on-click #(dom/select-target %) + :on-change (partial on-margin-change :simple :m2) + :value m2}]]] + + (= margin-type :multiple) + [:div.wrapper (for [num [:m1 :m2 :m3 :m4]] [:div.tooltip.tooltip-bottom {:key (dm/str "margin-" (d/name num)) @@ -76,12 +80,19 @@ :m2 "Right" :m3 "Bottom" :m4 "Left")} - [:div.input-element.mini + [:div.input-element.auto [:> numeric-input {:placeholder "--" :on-click #(dom/select-target %) - :on-change (partial on-margin-change num) - :value (or (-> values :layout-item-margin num) 0)}]]]))]])) + :on-change (partial on-margin-change :multiple num) + :value (num (:layout-item-margin values))}]]])]) + + [:div.margin-item-icons + [:div.margin-item-icon.tooltip.tooltip-bottom + {:class (dom/classnames :selected (= margin-type :multiple)) + :alt "Margin - multiple" + :on-click #(change-margin-style (if (= margin-type :multiple) :simple :multiple))} + i/auto-margin]]])) (mf/defc element-behavior [{:keys [is-layout-container? is-layout-child? layout-item-h-sizing layout-item-v-sizing on-change-behavior] :as props}] @@ -146,10 +157,7 @@ {::mf/wrap [#(mf/memo' % (mf/check-props ["ids" "values" "type" "is-layout-child?"]))]} [{:keys [ids values is-layout-child? is-layout-container?] :as props}] - (let [open? (mf/use-state false) - toggle-open (fn [] (swap! open? not)) - - selection-parents-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids)) + (let [selection-parents-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids)) selection-parents (mf/deref selection-parents-ref) change-margin-style @@ -165,10 +173,16 @@ is-col? (every? ctl/col? selection-parents) on-margin-change - (fn [type val] - (if (= type :simple) - (st/emit! (dwsl/update-layout-child ids {:layout-item-margin {:m1 val :m2 val :m3 val :m4 val}})) - (st/emit! (dwsl/update-layout-child ids {:layout-item-margin {type val}})))) + (fn [type prop val] + (cond + (and (= type :simple) (= prop :m1)) + (st/emit! (dwsl/update-layout-child ids {:layout-item-margin {:m1 val :m3 val}})) + + (and (= type :simple) (= prop :m2)) + (st/emit! (dwsl/update-layout-child ids {:layout-item-margin {:m2 val :m4 val}})) + + :else + (st/emit! (dwsl/update-layout-child ids {:layout-item-margin {prop val}})))) on-change-behavior (fn [dir value] @@ -199,39 +213,38 @@ :change-margin-style change-margin-style :on-margin-change on-margin-change}]) - [:div.advanced-ops-container - [:button.advanced-ops.toltip.tooltip-bottom - {:on-click toggle-open - :alt (tr "workspace.options.layout-item.advanced-ops")} - [:span.icon i/actions] - [:span (tr "workspace.options.layout-item.advanced-ops")]]] + (when is-layout-child? + [:div.layout-row + [:div.row-title "Align"] + [:div.btn-wrapper + [:& align-self-row {:is-col? is-col? + :align-self align-self + :set-align-self set-align-self}]]]) - (when @open? - [:div.advanced-ops-body - (when is-layout-child? - [:div.layout-row - [:div.direction-wrap.row-title "Align"] - [:div.btn-wrapper - [:& align-self-row {:is-col? is-col? - :align-self align-self - :set-align-self set-align-self}]]]) - [:div.input-wrapper - (for [item [:layout-item-max-h :layout-item-min-h :layout-item-max-w :layout-item-min-w]] - [:div.tooltip.tooltip-bottom - {:key (d/name item) - :alt (tr (dm/str "workspace.options.layout-item.title." (d/name item))) - :class (dom/classnames "maxH" (= item :layout-item-max-h) - "minH" (= item :layout-item-min-h) - "maxW" (= item :layout-item-max-w) - "minW" (= item :layout-item-min-w))} - [:div.input-element - {:alt (tr (dm/str "workspace.options.layout-item." (d/name item)))} - [:> numeric-input - {:no-validate true - :min 0 - :data-wrap true - :placeholder "--" - :on-click #(dom/select-target %) - :on-change (partial on-size-change item) - :value (get values item) - :nillable true}]]])]])]])) + [:div.advanced-ops-body + [:div.input-wrapper + (for [item (cond-> [] + (= (:layout-item-h-sizing values) :fill) + (conj :layout-item-min-w :layout-item-max-w) + + (= (:layout-item-v-sizing values) :fill) + (conj :layout-item-min-h :layout-item-max-h))] + + [:div.tooltip.tooltip-bottom + {:key (d/name item) + :alt (tr (dm/str "workspace.options.layout-item.title." (d/name item))) + :class (dom/classnames "maxH" (= item :layout-item-max-h) + "minH" (= item :layout-item-min-h) + "maxW" (= item :layout-item-max-w) + "minW" (= item :layout-item-min-w))} + [:div.input-element + {:alt (tr (dm/str "workspace.options.layout-item." (d/name item)))} + [:> numeric-input + {:no-validate true + :min 0 + :data-wrap true + :placeholder "--" + :on-click #(dom/select-target %) + :on-change (partial on-size-change item) + :value (get values item) + :nillable true}]]])]]]])) From 644854a6514406862a3bf8765fbc20149b6dfba3 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 29 Dec 2022 11:11:08 +0100 Subject: [PATCH 3/3] :sparkles: Reorder layers through keys in flex layout --- .../partials/sidebar-element-options.scss | 3 +- .../app/main/data/workspace/transforms.cljs | 85 +++++++++++++++++-- 2 files changed, 81 insertions(+), 7 deletions(-) diff --git a/frontend/resources/styles/main/partials/sidebar-element-options.scss b/frontend/resources/styles/main/partials/sidebar-element-options.scss index a79ea921d0..3174aa77e9 100644 --- a/frontend/resources/styles/main/partials/sidebar-element-options.scss +++ b/frontend/resources/styles/main/partials/sidebar-element-options.scss @@ -1841,7 +1841,8 @@ } } - .padding-icons, .margin-item-icons { + .padding-icons, + .margin-item-icons { margin-bottom: 8px; margin-top: 3px; margin-right: 1px; diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index 0b71b00da8..8483799019 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -8,6 +8,7 @@ "Events related with shapes transformations" (:require [app.common.data :as d] + [app.common.data.macros :as dm] [app.common.geom.matrix :as gmt] [app.common.geom.point :as gpt] [app.common.geom.shapes :as gsh] @@ -516,14 +517,70 @@ (s/def ::direction #{:up :down :right :left}) -(defn move-selected +(defn reorder-selected-layout-child + [direction] + (ptk/reify ::reorder-layout-child + ptk/WatchEvent + (watch [it state _] + (let [selected (wsh/lookup-selected state {:omit-blocked? true}) + objects (wsh/lookup-page-objects state) + page-id (:current-page-id state) + + get-new-position + (fn [parent-id position] + (let [parent (get objects parent-id)] + (when (ctl/layout? parent) + (if (or + (and (ctl/reverse? parent) + (or (= direction :left) + (= direction :up))) + (and (not (ctl/reverse? parent)) + (or (= direction :right) + (= direction :down)))) + (dec position) + (+ position 2))))) + + add-children-position + (fn [[parent-id children]] + (let [children+position + (->> children + (keep #(let [new-position (get-new-position + parent-id + (cph/get-position-on-parent objects %))] + (when new-position + (vector % new-position)))) + (sort-by second >))] + [parent-id children+position])) + + change-parents-and-position + (->> selected + (group-by #(dm/get-in objects [% :parent-id])) + (map add-children-position) + (into {})) + + changes + (->> change-parents-and-position + (reduce + (fn [changes [parent-id children]] + (->> children + (reduce + (fn [changes [child-id index]] + (pcb/change-parent changes parent-id + [(get objects child-id)] + index)) + changes))) + (-> (pcb/empty-changes it page-id) + (pcb/with-objects objects))))] + + (rx/of (dch/commit-changes changes) + (ptk/data-event :layout/update selected)))))) + +(defn nudge-selected-shapes "Move shapes a fixed increment in one direction, from a keyboard action." [direction shift?] - (us/verify ::direction direction) - (us/verify boolean? shift?) (let [same-event (js/Symbol "same-event")] - (ptk/reify ::move-selected + (ptk/reify ::nudge-selected-shapes IDeref (-deref [_] direction) @@ -541,7 +598,7 @@ (let [selected (wsh/lookup-selected state {:omit-blocked? true}) nudge (get-in state [:profile :props :nudge] {:big 10 :small 1}) move-events (->> stream - (rx/filter (ptk/type? ::move-selected)) + (rx/filter (ptk/type? ::nudge-selected-shapes)) (rx/filter #(= direction (deref %)))) stopper (->> move-events (rx/debounce 100) @@ -556,12 +613,28 @@ (rx/map #(dwm/create-modif-tree selected (ctm/move-modifiers %))) (rx/map (partial dwm/set-modifiers)) (rx/take-until stopper)) - (rx/of (move-selected direction shift?))) + (rx/of (nudge-selected-shapes direction shift?))) (rx/of (dwm/apply-modifiers) (finish-transform)))) (rx/empty)))))) +(defn move-selected + "Move shapes a fixed increment in one direction, from a keyboard action." + [direction shift?] + (us/verify ::direction direction) + (us/verify boolean? shift?) + + (ptk/reify ::move-selected + ptk/WatchEvent + (watch [_ state _] + (let [objects (wsh/lookup-page-objects state) + selected (wsh/lookup-selected state {:omit-blocked? true}) + selected-shapes (->> selected (map (d/getf objects)))] + (if (every? (partial ctl/layout-child? objects) selected-shapes) + (rx/of (reorder-selected-layout-child direction)) + (rx/of (nudge-selected-shapes direction shift?))))))) + (s/def ::x number?) (s/def ::y number?) (s/def ::position