From 495ba6e4a495b438dbf8864c2e007c7d7e0d80a5 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Mon, 11 Sep 2023 09:51:39 +0200 Subject: [PATCH] :sparkles: Reorder grid tracks --- common/src/app/common/types/shape/layout.cljc | 19 +++ .../partials/sidebar-element-options.scss | 17 +++ .../data/workspace/grid_layout/editor.cljs | 7 + .../app/main/data/workspace/shape_layout.cljs | 32 +++++ .../sidebar/options/menus/grid_cell.cljs | 21 ++- .../options/menus/layout_container.cljs | 124 +++++++++++++----- .../viewport/grid_layout_editor.cljs | 41 ++++-- 7 files changed, 220 insertions(+), 41 deletions(-) diff --git a/common/src/app/common/types/shape/layout.cljc b/common/src/app/common/types/shape/layout.cljc index bb4017a431..c361747390 100644 --- a/common/src/app/common/types/shape/layout.cljc +++ b/common/src/app/common/types/shape/layout.cljc @@ -695,6 +695,25 @@ (update :layout-grid-cells update-cells) (assign-cells)))) +(defn- reorder-grid-track + [prop parent from-index to-index] + (-> parent + (update prop + (fn [tracks] + (let [tr (nth tracks from-index)] + (-> tracks + (assoc from-index nil) + (d/insert-at-index (inc to-index) [tr]) + (d/vec-without-nils))))))) + +(defn reorder-grid-column + [parent from-index to-index] + (reorder-grid-track :layout-grid-columns parent from-index to-index)) + +(defn reorder-grid-row + [parent from-index to-index] + (reorder-grid-track :layout-grid-rows parent from-index to-index)) + (defn get-cells ([parent] (get-cells parent nil)) diff --git a/frontend/resources/styles/main/partials/sidebar-element-options.scss b/frontend/resources/styles/main/partials/sidebar-element-options.scss index 961adc2d5f..d6cce261e8 100644 --- a/frontend/resources/styles/main/partials/sidebar-element-options.scss +++ b/frontend/resources/styles/main/partials/sidebar-element-options.scss @@ -1880,6 +1880,16 @@ } } } + + &.single-button { + display: flex; + justify-content: end; + height: 1.5rem; + + .btn-wrapper { + width: initial; + } + } } .no-wrap { display: flex; @@ -2262,6 +2272,13 @@ grid-template-columns: 35px 1fr 1fr auto; background-color: $color-gray-60; padding: 3px; + border-radius: 3px; + border: 1px solid transparent; + + &:hover { + border: 1px solid $color-primary; + } + &:not(:first-child) { margin-top: 3px; } diff --git a/frontend/src/app/main/data/workspace/grid_layout/editor.cljs b/frontend/src/app/main/data/workspace/grid_layout/editor.cljs index f4dce997f4..bbcef7e9d7 100644 --- a/frontend/src/app/main/data/workspace/grid_layout/editor.cljs +++ b/frontend/src/app/main/data/workspace/grid_layout/editor.cljs @@ -38,6 +38,13 @@ (update [_ state] (update-in state [:workspace-grid-edition grid-id] dissoc :selected)))) +(defn clear-selection + [grid-id] + (ptk/reify ::clear-selection + ptk/UpdateEvent + (update [_ state] + (update-in state [:workspace-grid-edition grid-id] dissoc :selected)))) + (defn stop-grid-layout-editing [grid-id] (ptk/reify ::stop-grid-layout-editing diff --git a/frontend/src/app/main/data/workspace/shape_layout.cljs b/frontend/src/app/main/data/workspace/shape_layout.cljs index 2cf6841a03..1451f5937a 100644 --- a/frontend/src/app/main/data/workspace/shape_layout.cljs +++ b/frontend/src/app/main/data/workspace/shape_layout.cljs @@ -407,6 +407,38 @@ (ptk/data-event :layout/update ids) (dwu/commit-undo-transaction undo-id)))))) +(defn reorder-layout-track + [ids type from-index to-index] + (assert (#{:row :column} type)) + + (ptk/reify ::reorder-layout-track + ptk/WatchEvent + (watch [_ _ _] + (let [undo-id (js/Symbol)] + (rx/of (dwu/start-undo-transaction undo-id) + (dwc/update-shapes + ids + (fn [shape] + (case type + :row (ctl/reorder-grid-row shape from-index to-index) + :column (ctl/reorder-grid-column shape from-index to-index)))) + (ptk/data-event :layout/update ids) + (dwu/commit-undo-transaction undo-id)))))) + +(defn hover-layout-track + [ids type index hover?] + (assert (#{:row :column} type)) + + (ptk/reify ::hover-layout-track + ptk/UpdateEvent + (update [_ state] + (cond-> state + hover? + (update-in [:workspace-grid-edition (first ids) :hover-track] (fnil conj #{}) [type index]) + + (not hover?) + (update-in [:workspace-grid-edition (first ids) :hover-track] (fnil disj #{}) [type index]))))) + (defn change-layout-track [ids type index props] (assert (#{:row :column} type)) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs index c2f03f1d7a..bd81743b86 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/grid_cell.cljs @@ -8,6 +8,7 @@ (:require [app.common.data :as d] [app.common.data.macros :as dm] + [app.main.data.workspace.grid-layout.editor :as dwge] [app.main.data.workspace.shape-layout :as dwsl] [app.main.store :as st] [app.main.ui.components.numeric-input :refer [numeric-input*]] @@ -99,7 +100,13 @@ (let [props (cond-> {:mode mode} (not= mode :area) (assoc :area-name nil))] - (st/emit! (dwsl/update-grid-cell (:id shape) (:id cell) props)))))] + (st/emit! (dwsl/update-grid-cell (:id shape) (:id cell) props))))) + + toggle-edit-mode + (mf/use-fn + (mf/deps (:id shape)) + (fn [] + (st/emit! (dwge/remove-selection (:id shape)))))] [:div.element-set [:div.element-set-title @@ -191,4 +198,14 @@ [:div.btn-wrapper [:& set-self-alignment {:is-col? true :alignment justify-self - :set-alignment set-justify-self}]]]]])) + :set-alignment set-justify-self}]]] + + [:div.layout-row.single-button + [:div.btn-wrapper + [:div.edit-mode + [:button.tooltip.tooltip-bottom-left + {:alt "Grid edit mode" + :on-click toggle-edit-mode + :style {:padding 0}} + "Edit grid" + i/grid-layout-mode]]]]]])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs index 7dbdc17dad..48c06ce1bf 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs @@ -21,6 +21,7 @@ [app.main.ui.components.select :refer [select]] [app.main.ui.components.title-bar :refer [title-bar]] [app.main.ui.context :as ctx] + [app.main.ui.hooks :as h] [app.main.ui.icons :as i] [app.util.dom :as dom] [cuerdas.core :as str] @@ -871,8 +872,68 @@ :fixed (dm/str value "px") value)) +(mf/defc grid-track-info + [{:keys [is-col? type index column set-column-value set-column-type remove-element reorder-track hover-track]}] + (let [drop-track + (mf/use-callback + (mf/deps type reorder-track index) + (fn [drop-position data] + (reorder-track type (:index data) (if (= :top drop-position) (dec index) index)))) + + pointer-enter + (mf/use-callback + (mf/deps type hover-track index) + (fn [] + (hover-track type index true))) + + pointer-leave + (mf/use-callback + (mf/deps type hover-track index) + (fn [] + (hover-track type index false))) + + [dprops dref] + (h/use-sortable + :data-type "penpot/layer" + :on-drop drop-track + :data {:is-col? is-col? + :index index + :column column} + :draggable? true)] + [:div.column-info + {:ref dref + :class (dom/classnames + :dnd-over-top (or (= (:over dprops) :top) + (= (:over dprops) :center)) + :dnd-over-bot (= (:over dprops) :bot)) + :on-pointer-enter pointer-enter + :on-pointer-leave pointer-leave} + [:div.direction-grid-icon + (if is-col? + i/layout-rows + i/layout-columns)] + + [:div.grid-column-value + [:> numeric-input* {:no-validate true + :value (:value column) + :on-change #(set-column-value type index %) + :placeholder "--" + :disabled (= :auto (:type column))}]] + [:div.grid-column-unit + [:& select + {:class "grid-column-unit-selector" + :default-value (:type column) + :options [{:value :flex :label "FR"} + {:value :auto :label "AUTO"} + {:value :fixed :label "PX"} + {:value :percent :label "%"}] + :on-change #(set-column-type type index %)}]] + [:button.remove-grid-column + {:on-click #(remove-element type index)} + i/minus]])) + (mf/defc grid-columns-row - [{:keys [is-col? expanded? column-values toggle add-new-element set-column-value set-column-type remove-element] :as props}] + [{:keys [is-col? expanded? column-values toggle add-new-element set-column-value set-column-type remove-element reorder-track hover-track] :as props}] (let [column-num (count column-values) direction (if (> column-num 1) (if is-col? "Columns " "Rows ") @@ -894,32 +955,19 @@ (add-new-element type ctl/default-track-value))} i/plus]] (when expanded? - [:div.columns-info-wrapper - (for [[index column] (d/enumerate column-values)] - [:div.column-info {:key (dm/str index "-" (name type) "-" column)} - [:div.direction-grid-icon - (if is-col? - i/layout-rows - i/layout-columns)] - - [:div.grid-column-value - [:> numeric-input* {:no-validate true - :value (:value column) - :on-change #(set-column-value type index %) - :placeholder "--" - :disabled (= :auto (:type column))}]] - [:div.grid-column-unit - [:& select - {:class "grid-column-unit-selector" - :default-value (:type column) - :options [{:value :flex :label "FR"} - {:value :auto :label "AUTO"} - {:value :fixed :label "PX"} - {:value :percent :label "%"}] - :on-change #(set-column-type type index %)}]] - [:button.remove-grid-column - {:on-click #(remove-element type index)} - i/minus]])])])) + [:& h/sortable-container {} + [:div.columns-info-wrapper + (for [[index column] (d/enumerate column-values)] + [:& grid-track-info {:key (dm/str index "-" (name type) "-" column) + :type type + :is-col? is-col? + :index index + :column column + :set-column-value set-column-value + :set-column-type set-column-type + :remove-element remove-element + :reorder-track reorder-track + :hover-track hover-track}])]])])) ;; LAYOUT COMPONENT @@ -1352,7 +1400,7 @@ {::mf/wrap [#(mf/memo' % (mf/check-props ["ids" "values"]))]} [{:keys [ids values] :as props}] (let [;; Gap - gap-selected? (mf/use-state :none) + gap-selected? (mf/use-state :none) saved-grid-dir (:layout-grid-dir values) set-direction @@ -1431,6 +1479,18 @@ (fn [type index] (st/emit! (dwsl/remove-layout-track ids type index)))) + reorder-track + (mf/use-fn + (mf/deps ids) + (fn [type from-index to-index] + (st/emit! (dwsl/reorder-layout-track ids type from-index to-index)))) + + hover-track + (mf/use-fn + (mf/deps ids) + (fn [type index hover?] + (st/emit! (dwsl/hover-layout-track ids type index hover?)))) + set-column-value (mf/use-fn (mf/deps ids) @@ -1496,7 +1556,9 @@ :add-new-element add-new-element :set-column-value set-column-value :set-column-type set-column-type - :remove-element remove-element}] + :remove-element remove-element + :reorder-track reorder-track + :hover-track hover-track}] [:& grid-columns-row {:is-col? false :expanded? @grid-rows-open? @@ -1505,7 +1567,9 @@ :add-new-element add-new-element :set-column-value set-column-value :set-column-type set-column-type - :remove-element remove-element}] + :remove-element remove-element + :reorder-track reorder-track + :hover-track hover-track}] [:& gap-section {:gap-selected? gap-selected? :on-change set-gap diff --git a/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.cljs b/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.cljs index 194a838dc8..6c4aef79a2 100644 --- a/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/grid_layout_editor.cljs @@ -531,16 +531,17 @@ (dm/str value)]])) (mf/defc track - {::mf/wrap [#(mf/memo' % (mf/check-props ["shape" "zoom" "index" "type" "track-data" "layout-data"]))] + {::mf/wrap [#(mf/memo' % (mf/check-props ["shape" "zoom" "index" "type" "track-data" "layout-data" "hovering?"]))] ::mf/wrap-props false} [props] - (let [shape (unchecked-get props "shape") - zoom (unchecked-get props "zoom") - type (unchecked-get props "type") - index (unchecked-get props "index") + (let [shape (unchecked-get props "shape") + zoom (unchecked-get props "zoom") + type (unchecked-get props "type") + index (unchecked-get props "index") snap-pixel? (unchecked-get props "snap-pixel?") - track-data (unchecked-get props "track-data") + track-data (unchecked-get props "track-data") layout-data (unchecked-get props "layout-data") + hovering? (unchecked-get props "hovering?") track-input-ref (mf/use-ref) [layout-gap-row layout-gap-col] (ctl/gaps shape) @@ -621,6 +622,15 @@ [:g {:transform (if (= type :column) (dm/str (gmt/transform-in text-p (:transform shape))) (dm/str (gmt/transform-in text-p (gmt/rotate (:transform shape) -90))))} + + (when hovering? + [:rect {:x (+ text-x (/ 5 zoom)) + :y text-y + :width (- text-width (/ 10 zoom)) + :height (- text-height (/ 5 zoom)) + :rx (/ 3 zoom) + :fill "#DB00FF" + :opacity 0.2}]) [:foreignObject {:x text-x :y text-y :width text-width :height text-height} [:input {:ref track-input-ref @@ -684,6 +694,18 @@ hover-cells (:hover grid-edition) selected-cells (:selected grid-edition) + hover-columns + (->> (:hover-track grid-edition) + (filter (fn [[t _]] (= t :column))) + (map (fn [[_ idx]] idx)) + (into #{})) + + hover-rows + (->> (:hover-track grid-edition) + (filter (fn [[t _]] (= t :row))) + (map (fn [[_ idx]] idx)) + (into #{})) + children (mf/use-memo (mf/deps shape modifiers) @@ -759,7 +781,8 @@ :index idx :layout-data layout-data :snap-pixel? snap-pixel? - :track-data column-data}]) + :track-data column-data + :hovering? (contains? hover-columns idx)}]) ;; Last track resize handler (when-not (empty? column-tracks) @@ -796,8 +819,8 @@ :snap-pixel? snap-pixel? :track-data row-data :type :row - :zoom zoom}]) - + :zoom zoom + :hovering? (contains? hover-rows idx)}]) (when-not (empty? row-tracks) (let [last-track (last row-tracks) start-p (:start-p last-track)