From eb425dc4f275d73d2864cd0b9a1e1f611e3aa13c Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Mon, 20 Feb 2023 15:43:54 +0100 Subject: [PATCH] :sparkles: Edit cell panel --- .../data/workspace/grid_layout/editor.cljs | 44 +++ .../app/main/data/workspace/shape_layout.cljs | 2 - frontend/src/app/main/refs.cljs | 9 + .../main/ui/workspace/sidebar/options.cljs | 15 +- .../sidebar/options/shapes/grid_cell.cljs | 22 ++ .../src/app/main/ui/workspace/viewport.cljs | 15 +- .../viewport/grid_layout_editor.cljs | 334 +++++++++++------- 7 files changed, 299 insertions(+), 142 deletions(-) create mode 100644 frontend/src/app/main/data/workspace/grid_layout/editor.cljs create mode 100644 frontend/src/app/main/ui/workspace/sidebar/options/shapes/grid_cell.cljs diff --git a/frontend/src/app/main/data/workspace/grid_layout/editor.cljs b/frontend/src/app/main/data/workspace/grid_layout/editor.cljs new file mode 100644 index 000000000..7cd464a05 --- /dev/null +++ b/frontend/src/app/main/data/workspace/grid_layout/editor.cljs @@ -0,0 +1,44 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns app.main.data.workspace.grid-layout.editor + (:require + [potok.core :as ptk])) + +(defn hover-grid-cell + [grid-id row column add-to-set] + (ptk/reify ::hover-grid-cell + ptk/UpdateEvent + (update [_ state] + (update-in + state + [:workspace-grid-edition grid-id :hover] + (fn [hover-set] + (let [hover-set (or hover-set #{})] + (if add-to-set + (conj hover-set [row column]) + (disj hover-set [row column])))))))) + +(defn select-grid-cell + [grid-id row column] + (ptk/reify ::hover-grid-cell + ptk/UpdateEvent + (update [_ state] + (assoc-in state [:workspace-grid-edition grid-id :selected] [row column])))) + +(defn remove-selection + [grid-id] + (ptk/reify ::hover-grid-cell + 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 + ptk/UpdateEvent + (update [_ state] + (update state :workspace-grid-edition dissoc grid-id)))) diff --git a/frontend/src/app/main/data/workspace/shape_layout.cljs b/frontend/src/app/main/data/workspace/shape_layout.cljs index e201ad54c..f3317e214 100644 --- a/frontend/src/app/main/data/workspace/shape_layout.cljs +++ b/frontend/src/app/main/data/workspace/shape_layout.cljs @@ -68,7 +68,6 @@ (defn get-layout-initializer [type from-frame?] - (prn "??type" type) (let [initial-layout-data (case type :flex initial-flex-layout @@ -157,7 +156,6 @@ (defn create-layout-from-id [ids type from-frame?] - (.trace js/console "create-layout-from-id" type) (ptk/reify ::create-layout-from-id ptk/WatchEvent (watch [_ state _] diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index 7e7bc193a..49f1b4d13 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -522,3 +522,12 @@ (def colorpicker (l/derived :colorpicker st/state)) + + +(def workspace-grid-edition + (l/derived :workspace-grid-edition st/state)) + +(defn workspace-grid-edition-id + [id] + (l/derived #(get % id) workspace-grid-edition)) + diff --git a/frontend/src/app/main/ui/workspace/sidebar/options.cljs b/frontend/src/app/main/ui/workspace/sidebar/options.cljs index 80d4fa796..1847962c0 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options.cljs @@ -6,6 +6,7 @@ (ns app.main.ui.workspace.sidebar.options (:require + [app.main.ui.workspace.sidebar.options.shapes.grid-cell :as grid-cell] [app.common.data :as d] [app.common.geom.shapes :as gsh] [app.common.pages.helpers :as cph] @@ -67,9 +68,18 @@ (let [drawing (mf/deref refs/workspace-drawing) objects (mf/deref refs/workspace-page-objects) shared-libs (mf/deref refs/workspace-libraries) + grid-edition (mf/deref refs/workspace-grid-edition) selected-shapes (into [] (keep (d/getf objects)) selected) first-selected-shape (first selected-shapes) shape-parent-frame (cph/get-frame objects (:frame-id first-selected-shape)) + + [grid-id {[row-selected col-selected] :selected}] + (d/seek (fn [[grid-id {:keys [selected]}]] + (some? selected)) + grid-edition) + + grid-cell-selected? (and (some? grid-id) (some? row-selected) (some? col-selected)) + on-change-tab (fn [options-mode] (st/emit! (udw/set-options-mode options-mode) @@ -87,6 +97,10 @@ [:& align-options] [:& bool-options] (cond + grid-cell-selected? [:& grid-cell/options {:shape (get objects grid-id) + :row row-selected + :column col-selected}] + (d/not-empty? drawing) [:& shape-options {:shape (:object drawing) :page-id page-id :file-id file-id @@ -138,4 +152,3 @@ :file-id file-id :page-id page-id :section section}])) - diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/grid_cell.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/grid_cell.cljs new file mode 100644 index 000000000..7f46e045b --- /dev/null +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/grid_cell.cljs @@ -0,0 +1,22 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns app.main.ui.workspace.sidebar.options.shapes.grid-cell + (:require + [rumext.v2 :as mf])) + +(mf/defc options + {::mf/wrap [mf/memo]} + [{:keys [shape row column] :as props}] + + [:div.element-set + [:div.element-set-title + [:span "Grid Cell"]] + + [:div.element-set-content.layout-item-menu + [:div.layout-row + [:div.row-title.sizing "Position"] + [:div (str row "," column)]]]]) diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index d9102d9f9..007754eb7 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -477,11 +477,6 @@ :disabled-guides? disabled-guides? :modifiers modifiers}]) - [:& grid-layout/editor - {:zoom zoom - :objects base-objects - :shape (first selected-shapes)}] - ;; DEBUG LAYOUT DROP-ZONES (when (debug? :layout-drop-zones) [:& wvd/debug-drop-zones {:selected-shapes selected-shapes @@ -543,4 +538,12 @@ (when show-gradient-handlers? [:& gradients/gradient-handlers {:id (first selected) - :zoom zoom}])]]])) + :zoom zoom}]) + + (when-let [selected (first selected-shapes)] + (when (ctl/grid-layout? selected) + [:& grid-layout/editor + {:zoom zoom + :objects base-objects + :shape selected}])) + ]]])) 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 77732e1a6..48ba04343 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 @@ -6,21 +6,23 @@ (ns app.main.ui.workspace.viewport.grid-layout-editor (:require - [app.main.ui.icons :as i] - [app.common.geom.shapes.grid-layout.layout-data :refer [set-sample-data] ] - - [cuerdas.core :as str] - [app.common.geom.point :as gpt] + [app.main.data.workspace.grid-layout.editor :as dwge] [app.common.data :as d] [app.common.data.macros :as dm] + [app.common.geom.point :as gpt] [app.common.geom.shapes.grid-layout :as gsg] + [app.common.geom.shapes.grid-layout.layout-data :refer [set-sample-data] ] [app.common.geom.shapes.points :as gpo] [app.common.pages.helpers :as cph] [app.common.types.shape.layout :as ctl] [app.main.refs :as refs] [app.main.store :as st] + [app.main.ui.icons :as i] + [cuerdas.core :as str] [rumext.v2 :as mf])) +(defn apply-to-point [result next-fn] + (conj result (next-fn (last result)))) (mf/defc track-marker {::mf/wrap-props false} @@ -30,32 +32,25 @@ value (unchecked-get props "value") zoom (unchecked-get props "zoom") - p1 (-> center - (update :x - (/ 13 zoom)) - (update :y - (/ 16 zoom))) - - p2 (-> p1 - (update :x + (/ 26 zoom))) - - p3 (-> p2 - (update :y + (/ 24 zoom))) - - p4 (-> p3 - (update :x - (/ 13 zoom)) - (update :y + (/ 8 zoom))) - - p5 (-> p4 - (update :x - (/ 13 zoom)) - (update :y - (/ 8 zoom))) + marker-points + (reduce + apply-to-point + [(gpt/subtract center + (gpt/point (/ 13 zoom) (/ 16 zoom)))] + [#(gpt/add % (gpt/point (/ 26 zoom) 0)) + #(gpt/add % (gpt/point 0 (/ 24 zoom))) + #(gpt/add % (gpt/point (- (/ 13 zoom)) (/ 8 zoom))) + #(gpt/subtract % (gpt/point (/ 13 zoom) (/ 8 zoom)))]) text-x (:x center) text-y (:y center)] + [:g.grid-track-marker - [:polygon {:points (->> [p1 p2 p3 p4 p5] + [:polygon {:points (->> marker-points (map #(dm/fmt "%,%" (:x %) (:y %))) (str/join " ")) - :style {:fill "#DB00FF" + :style {:fill "var(--color-distance)" :fill-opacity 0.3}}] [:text {:x text-x :y text-y @@ -64,9 +59,122 @@ :font-size (/ 16 zoom) :text-anchor "middle" :dominant-baseline "middle" - :style {:fill "#DB00FF"}} + :style {:fill "var(--color-distance)"}} (dm/str value)]])) +(mf/defc grid-editor-frame + {::mf/wrap-props false} + [props] + + (let [bounds (unchecked-get props "bounds") + zoom (unchecked-get props "zoom") + hv #(gpo/start-hv bounds %) + vv #(gpo/start-vv bounds %) + width (gpo/width-points bounds) + height (gpo/height-points bounds) + origin (gpo/origin bounds) + + frame-points + (reduce + apply-to-point + [origin] + [#(gpt/add % (hv width)) + #(gpt/subtract % (vv (/ 40 zoom))) + #(gpt/subtract % (hv (+ width (/ 40 zoom)))) + #(gpt/add % (vv (+ height (/ 40 zoom)))) + #(gpt/add % (hv (/ 40 zoom)))])] + + [:polygon {:points (->> frame-points + (map #(dm/fmt "%,%" (:x %) (:y %))) + (str/join " ")) + :style {:stroke "var(--color-distance)" + :stroke-width (/ 1 zoom)}}])) + +(mf/defc plus-btn + {::mf/wrap-props false} + [props] + + (let [start-p (unchecked-get props "start-p") + zoom (unchecked-get props "zoom") + type (unchecked-get props "type") + + [rect-x rect-y icon-x icon-y] + (if (= type :column) + [(:x start-p) + (- (:y start-p) (/ 40 zoom)) + (+ (:x start-p) (/ 12 zoom)) + (- (:y start-p) (/ 28 zoom))] + + [(- (:x start-p) (/ 40 zoom)) + (:y start-p) + (- (:x start-p) (/ 28 zoom)) + (+ (:y start-p) (/ 12 zoom))])] + + [:g.plus-button + [:rect {:x rect-x + :y rect-y + :width (/ 40 zoom) + :height (/ 40 zoom) + :style {:fill "var(--color-distance)" + :stroke "var(--color-distance)" + :stroke-width (/ 1 zoom)}}] + + [:use {:x icon-x + :y icon-y + :width (/ 16 zoom) + :height (/ 16 zoom) + :href (dm/str "#icon-plus") + :fill "white"}]])) + +(mf/defc grid-cell + {::mf/wrap-props false} + [props] + (let [shape (unchecked-get props "shape") + {:keys [row-tracks column-tracks]} (unchecked-get props "layout-data") + bounds (unchecked-get props "bounds") + zoom (unchecked-get props "zoom") + + hover? (unchecked-get props "hover?") + selected? (unchecked-get props "selected?") + + row (unchecked-get props "row") + column (unchecked-get props "column") + + column-track (nth column-tracks (dec column) nil) + row-track (nth row-tracks (dec row) nil) + + + origin (gpo/origin bounds) + hv #(gpo/start-hv bounds %) + vv #(gpo/start-vv bounds %) + + start-p (-> origin + (gpt/add (hv (:distance column-track))) + (gpt/add (vv (:distance row-track)))) + + end-p (-> start-p + (gpt/add (hv (:value column-track))) + (gpt/add (vv (:value row-track))))] + + [:rect.cell-editor + {:x (:x start-p) + :y (:y start-p) + :width (- (:x end-p) (:x start-p)) + :height (- (:y end-p) (:y start-p)) + + :on-pointer-enter #(st/emit! (dwge/hover-grid-cell (:id shape) row column true)) + :on-pointer-leave #(st/emit! (dwge/hover-grid-cell (:id shape) row column false)) + + :on-click #(st/emit! (dwge/select-grid-cell (:id shape) row column)) + + :style {:fill "transparent" + :stroke "var(--color-distance)" + :stroke-dasharray (when-not (or hover? selected?) + (str/join " " (map #(/ % zoom) [0 8]) )) + :stroke-linecap "round" + :stroke-width (/ 2 zoom)}}]) + ) + (mf/defc editor {::mf/wrap-props false} [props] @@ -74,124 +182,84 @@ (let [shape (unchecked-get props "shape") objects (unchecked-get props "objects") zoom (unchecked-get props "zoom") - bounds (:points shape)] + bounds (:points shape) - (when (ctl/grid-layout? shape) - (let [children (->> (cph/get-immediate-children objects (:id shape)) - (remove :hidden) - (map #(vector (gpo/parent-coords-bounds (:points %) (:points shape)) %))) + grid-edition-id-ref (mf/use-memo #(refs/workspace-grid-edition-id (:id shape))) + grid-edition (mf/deref grid-edition-id-ref) - hv #(gpo/start-hv bounds %) - vv #(gpo/start-vv bounds %) + hover-cells (:hover grid-edition) + selected-cells (:selected grid-edition) - width (gpo/width-points bounds) - height (gpo/height-points bounds) - origin (gpo/origin bounds) + children (->> (cph/get-immediate-children objects (:id shape)) + (remove :hidden) + (map #(vector (gpo/parent-coords-bounds (:points %) (:points shape)) %))) - {:keys [row-tracks column-tracks shape-cells]} - (gsg/calc-layout-data shape children bounds) + hv #(gpo/start-hv bounds %) + vv #(gpo/start-vv bounds %) + width (gpo/width-points bounds) + height (gpo/height-points bounds) + origin (gpo/origin bounds) - [shape children] (set-sample-data shape children)] + {:keys [row-tracks column-tracks shape-cells] :as layout-data} + (gsg/calc-layout-data shape children bounds) - [:g.grid-editor - [:polygon {:points (->> [origin - (-> origin - (gpt/add (hv width))) - (-> origin - (gpt/add (hv width)) - (gpt/subtract (vv (/ 40 zoom)))) + [shape children] (set-sample-data shape children)] - (-> origin - (gpt/add (hv width)) - (gpt/subtract (vv (/ 40 zoom))) - (gpt/subtract (hv (+ width (/ 40 zoom))))) + (mf/use-effect + (fn [] + #(st/emit! (dwge/stop-grid-layout-editing (:id shape))))) - (-> origin - (gpt/add (hv width)) - (gpt/subtract (vv (/ 40 zoom))) - (gpt/subtract (hv (+ width (/ 40 zoom)))) - (gpt/add (vv (+ height (/ 40 zoom))))) - (-> origin - (gpt/add (hv width)) - (gpt/subtract (vv (/ 40 zoom))) - (gpt/subtract (hv (+ width (/ 40 zoom)))) - (gpt/add (vv (+ height (/ 40 zoom)))) - (gpt/add (hv (/ 40 zoom))))] - (map #(dm/fmt "%,%" (:x %) (:y %))) - (str/join " ")) - :style {:stroke "#DB00FF" - :stroke-width (/ 1 zoom)}}] + [:g.grid-editor + [:& grid-editor-frame {:zoom zoom + :bounds bounds}] + (let [start-p (-> origin (gpt/add (hv width)))] + [:& plus-btn {:start-p start-p + :zoom zoom + :type :column}]) - (let [start-p (-> origin (gpt/add (hv width)))] - [:* - [:rect {:x (:x start-p) - :y (- (:y start-p) (/ 40 zoom)) - :width (/ 40 zoom) - :height (/ 40 zoom) - :style {:fill "#DB00FF" - :stroke "#DB00FF" - :stroke-width (/ 1 zoom)}}] + (let [start-p (-> origin (gpt/add (vv height)))] + [:& plus-btn {:start-p start-p + :zoom zoom + :type :row}]) - [:use {:x (+ (:x start-p) (/ 12 zoom)) - :y (- (:y start-p) (/ 28 zoom)) - :width (/ 16 zoom) - :height (/ 16 zoom) - :href (dm/str "#icon-plus") - :fill "white"}]]) + (for [[_ {:keys [column row]}] (:layout-grid-cells shape)] + (let [] + [:& grid-cell {:shape shape + :layout-data layout-data + :row row + :column column + :bounds bounds + :zoom zoom + :hover? (contains? hover-cells [row column]) + :selected? (= selected-cells [row column]) + }])) - (let [start-p (-> origin (gpt/add (vv height)))] - [:rect {:x (- (:x start-p) (/ 40 zoom)) - :y (:y start-p) - :width (/ 40 zoom) - :height (/ 40 zoom) - :style {:fill "#DB00FF" - :stroke "#DB00FF" - :stroke-width (/ 1 zoom)}}]) + (for [[idx column-data] (d/enumerate column-tracks)] + (let [start-p (-> origin (gpt/add (hv (:distance column-data)))) + marker-p (-> start-p (gpt/subtract (vv (/ 20 zoom))))] + [:* + [:& track-marker {:center marker-p + :value (dm/str (inc idx)) + :zoom zoom}] + [:rect.resize-handler + {:x (- (:x start-p) (/ 8 zoom)) + :y (:y start-p) + :height height + :width (/ 16 zoom) + :style {:fill "transparent"}}]])) - (for [[idx column-data] (d/enumerate column-tracks)] - (let [start-p (-> origin - (gpt/add (hv (:distance column-data))) - (gpt/subtract (vv (/ 20 zoom))))] - [:& track-marker {:center start-p - :value (dm/str (inc idx)) - :zoom zoom}])) + (for [[idx row-data] (d/enumerate row-tracks)] + (let [start-p (-> origin (gpt/add (vv (:distance row-data)))) + marker-p (-> start-p (gpt/subtract (hv (/ 20 zoom))))] + [:* + [:g {:transform (dm/fmt "rotate(-90 % %)" (:x marker-p) (:y marker-p))} + [:& track-marker {:center marker-p + :value (dm/str (inc idx)) + :zoom zoom}]] - (for [[idx row-data] (d/enumerate row-tracks)] - (let [start-p (-> origin - (gpt/add (vv (:distance row-data))) - (gpt/subtract (hv (/ 20 zoom))))] - [:g {:transform (dm/fmt "rotate(-90 % %)" (:x start-p) (:y start-p))} - [:& track-marker {:center start-p - :value (dm/str (inc idx)) - :zoom zoom}]])) - - (for [[_ grid-cell] (:layout-grid-cells shape)] - (let [column (nth column-tracks (dec (:column grid-cell)) nil) - row (nth row-tracks (dec (:row grid-cell)) nil) - - start-p (-> origin - (gpt/add (hv (:distance column))) - (gpt/add (vv (:distance row)))) - - end-p (-> start-p - (gpt/add (hv (:value column))) - (gpt/add (vv (:value row))))] - - [:* - #_[:rect {:x (:x start-p) - :y (- (:y start-p) (/ 32 zoom) (/ 8 zoom)) - :width (/ 26.26 zoom) - :height (/ 32 zoom) - :style {:fill "#DB00FF" - :fill-opacity 0.3} - }] - - [:rect.cell-editor {:x (:x start-p) - :y (:y start-p) - :width (- (:x end-p) (:x start-p)) - :height (- (:y end-p) (:y start-p)) - :style {:stroke "#DB00FF" - :stroke-dasharray (str/join " " (map #(/ % zoom) [0 8]) ) - :stroke-linecap "round" - :stroke-width (/ 2 zoom)} - }]]))])))) + [:rect.resize-handler + {:x (:x start-p) + :y (- (:y start-p) (/ 8 zoom)) + :height (/ 16 zoom) + :width width + :style {:fill "transparent"}}]]))]))