Merge pull request #2996 from penpot/alotor-grid-layout

Partial merge of the grid layout infrastructure
This commit is contained in:
Alejandro 2023-03-03 11:15:32 +01:00 committed by GitHub
commit c8360b1994
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
59 changed files with 2388 additions and 322 deletions

View file

@ -660,7 +660,7 @@
(pcb/with-objects objects)
;; Remove layout-item properties when moving a shape outside a layout
(cond-> (not (ctl/layout? objects parent-id))
(cond-> (not (ctl/any-layout? objects parent-id))
(pcb/update-shapes ordered-indexes ctl/remove-layout-item-data))
;; Move the shapes
@ -714,7 +714,7 @@
;; Fix the sizing when moving a shape
(pcb/update-shapes parents
(fn [parent]
(if (ctl/layout? parent)
(if (ctl/flex-layout? parent)
(cond-> parent
(ctl/change-h-sizing? (:id parent) objects (:shapes parent))
(assoc :layout-item-h-sizing :fix)

View file

@ -82,8 +82,9 @@
focus (:workspace-focus-selected state)
fid (ctst/top-nested-frame objects initial)
layout? (ctl/layout? objects fid)
drop-index (when layout? (gsl/get-drop-index fid objects initial))
flex-layout? (ctl/flex-layout? objects fid)
drop-index (when flex-layout? (gsl/get-drop-index fid objects initial))
shape (get-in state [:workspace-drawing :object])
shape (-> shape

View file

@ -47,12 +47,12 @@
ptk/UpdateEvent
(update [_ state]
(let [objects (wsh/lookup-page-objects state)
content (get-in state [:workspace-drawing :object :content] [])
position (gpt/point (get-in content [0 :params] nil))
frame-id (ctst/top-nested-frame objects position)
layout? (ctl/layout? objects frame-id)
drop-index (when layout? (gsl/get-drop-index frame-id objects position))]
(let [objects (wsh/lookup-page-objects state)
content (get-in state [:workspace-drawing :object :content] [])
position (gpt/point (get-in content [0 :params] nil))
frame-id (ctst/top-nested-frame objects position)
flex-layout? (ctl/flex-layout? objects frame-id)
drop-index (when flex-layout? (gsl/get-drop-index frame-id objects position))]
(-> state
(assoc-in [:workspace-drawing :object :frame-id] frame-id)
(cond-> (some? drop-index)

View file

@ -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))))

View file

@ -144,7 +144,7 @@
(-> (pcb/empty-changes it page-id)
(pcb/with-objects objects)
(cond-> (ctl/layout? frame)
(cond-> (ctl/any-layout? frame)
(pcb/update-shapes (:shapes frame) ctl/remove-layout-item-data))
(pcb/change-parent parent-id children idx-in-parent)
(pcb/remove-objects [(:id frame)]))))

View file

@ -179,8 +179,8 @@
(let [origin-frame-ids (->> selected (group-by #(get-in objects [% :frame-id])))
child-set (set (get-in objects [target-frame-id :shapes]))
target-frame (get objects target-frame-id)
target-layout? (ctl/layout? target-frame)
target-frame (get objects target-frame-id)
target-flex-layout? (ctl/flex-layout? target-frame)
children-ids (concat (:shapes target-frame) selected)
@ -201,7 +201,7 @@
(fn [modif-tree [original-frame shapes]]
(let [shapes (->> shapes (d/removev #(= target-frame-id %)))
shapes (cond->> shapes
(and target-layout? (= original-frame target-frame-id))
(and target-flex-layout? (= original-frame target-frame-id))
;; When movining inside a layout frame remove the shapes that are not immediate children
(filterv #(contains? child-set %)))
children-ids (->> (dm/get-in objects [original-frame :shapes])
@ -219,7 +219,7 @@
(cond-> v-sizing?
(update-in [original-frame :modifiers] ctm/change-property :layout-item-v-sizing :fix)))
(and target-layout? (= original-frame target-frame-id))
(and target-flex-layout? (= original-frame target-frame-id))
(update-in [target-frame-id :modifiers] ctm/add-children shapes drop-index))))]
(as-> modif-tree $

View file

@ -240,12 +240,12 @@
(ptk/reify ::setup-frame-path
ptk/UpdateEvent
(update [_ state]
(let [objects (wsh/lookup-page-objects state)
content (get-in state [:workspace-drawing :object :content] [])
position (gpt/point (get-in content [0 :params] nil))
frame-id (ctst/top-nested-frame objects position)
layout? (ctl/layout? objects frame-id)
drop-index (when layout? (gsl/get-drop-index frame-id objects position))]
(let [objects (wsh/lookup-page-objects state)
content (get-in state [:workspace-drawing :object :content] [])
position (gpt/point (get-in content [0 :params] nil))
frame-id (ctst/top-nested-frame objects position)
flex-layout? (ctl/flex-layout? objects frame-id)
drop-index (when flex-layout? (gsl/get-drop-index frame-id objects position))]
(-> state
(assoc-in [:workspace-drawing :object :frame-id] frame-id)
(cond-> (some? drop-index)

View file

@ -1,4 +1,4 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
; 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/.
;;
@ -53,14 +53,29 @@
:layout-padding {:p1 0 :p2 0 :p3 0 :p4 0}})
(def initial-grid-layout ;; TODO
{:layout :grid})
{:layout :grid
:layout-grid-dir :row
:layout-gap-type :multiple
:layout-gap {:row-gap 0 :column-gap 0}
:layout-align-items :start
:layout-align-content :stretch
:layout-justify-items :start
:layout-justify-content :start
:layout-padding-type :simple
:layout-padding {:p1 0 :p2 0 :p3 0 :p4 0}
:layout-grid-rows []
:layout-grid-columns []})
(defn get-layout-initializer
[type from-frame?]
(let [initial-layout-data (if (= type :flex) initial-flex-layout initial-grid-layout)]
(let [initial-layout-data
(case type
:flex initial-flex-layout
:grid initial-grid-layout)]
(fn [shape]
(-> shape
(merge initial-layout-data)
(cond-> (= type :grid) ctl/assign-cells)
;; If the original shape is not a frame we set clip content and show-viewer to false
(cond-> (not from-frame?)
(assoc :show-content true :hide-in-viewer true))))))
@ -140,6 +155,13 @@
:layout-align-items :center
:layout-gap layout-gap)))))
(defn shapes->grid-params
"Given the shapes calculate its flex parameters (horizontal vs vertical, gaps, etc)"
([objects shapes]
(shapes->flex-params objects shapes nil))
([_objects _shapes _parent]
{}))
(defn create-layout-from-id
[ids type from-frame?]
(ptk/reify ::create-layout-from-id
@ -149,8 +171,10 @@
children-ids (into [] (mapcat #(get-in objects [% :shapes])) ids)
children-shapes (map (d/getf objects) children-ids)
parent (get objects (first ids))
flex-params (when (d/not-empty? children-shapes)
(shapes->flex-params objects children-shapes parent))
layout-params (when (d/not-empty? children-shapes)
(case type
:flex (shapes->flex-params objects children-shapes parent)
:grid (shapes->grid-params objects children-shapes parent)))
undo-id (js/Symbol)]
(rx/of (dwu/start-undo-transaction undo-id)
(dwc/update-shapes ids (get-layout-initializer type from-frame?))
@ -161,7 +185,7 @@
(not from-frame?)
(-> (assoc :layout-item-h-sizing :auto
:layout-item-v-sizing :auto)
(merge flex-params)))))
(merge layout-params)))))
(ptk/data-event :layout/update ids)
(dwc/update-shapes children-ids #(dissoc % :constraints-h :constraints-v))
(dwu/commit-undo-transaction undo-id))))))
@ -288,7 +312,7 @@
(dwu/commit-undo-transaction undo-id))))))
(defn create-layout
[]
[type]
(ptk/reify ::create-layout
ptk/WatchEvent
(watch [_ state _]
@ -300,15 +324,12 @@
is-frame? (= :frame (:type (first selected-shapes)))
undo-id (js/Symbol)]
(if (and single? is-frame?)
(rx/of
(dwu/start-undo-transaction undo-id)
(create-layout-from-id [(first selected)] :flex true)
(dwu/commit-undo-transaction undo-id))
(rx/of
(dwu/start-undo-transaction undo-id)
(create-layout-from-selection :flex)
(dwu/commit-undo-transaction undo-id)))))))
(rx/of
(dwu/start-undo-transaction undo-id)
(if (and single? is-frame?)
(create-layout-from-id [(first selected)] type true)
(create-layout-from-selection type))
(dwu/commit-undo-transaction undo-id))))))
(defn toggle-layout-flex
[]
@ -320,12 +341,12 @@
selected (wsh/lookup-selected state)
selected-shapes (map (d/getf objects) selected)
single? (= (count selected-shapes) 1)
has-flex-layout? (and single? (ctl/layout? objects (:id (first selected-shapes))))]
has-flex-layout? (and single? (ctl/flex-layout? objects (:id (first selected-shapes))))]
(when (not= 0 (count selected))
(if has-flex-layout?
(rx/of (remove-layout selected))
(rx/of (create-layout))))))))
(rx/of (create-layout :flex))))))))
(defn update-layout
[ids changes]
@ -338,6 +359,101 @@
(ptk/data-event :layout/update ids)
(dwu/commit-undo-transaction undo-id))))))
#_(defn update-grid-cells
[parent objects]
(let [children (cph/get-immediate-children objects (:id parent))
layout-grid-rows (:layout-grid-rows parent)
layout-grid-columns (:layout-grid-columns parent)
num-rows (count layout-grid-columns)
num-columns (count layout-grid-columns)
layout-grid-cells (:layout-grid-cells parent)
allocated-shapes
(into #{} (mapcat :shapes) (:layout-grid-cells parent))
no-cell-shapes
(->> children (:shapes parent) (remove allocated-shapes))
layout-grid-cells
(for [[row-idx row] (d/enumerate layout-grid-rows)
[col-idx col] (d/enumerate layout-grid-columns)]
(let [shape (nth children (+ (* row-idx num-columns) col-idx) nil)
cell-data {:id (uuid/next)
:row (inc row-idx)
:column (inc col-idx)
:row-span 1
:col-span 1
:shapes (when shape [(:id shape)])}]
[(:id cell-data) cell-data]))]
(assoc parent :layout-grid-cells (into {} layout-grid-cells))))
#_(defn check-grid-cells-update
[ids]
(ptk/reify ::check-grid-cells-update
ptk/WatchEvent
(watch [_ state _]
(let [objects (wsh/lookup-page-objects state)
undo-id (js/Symbol)]
(rx/of (dwc/update-shapes
ids
(fn [shape]
(-> shape
(update-grid-cells objects)))))))))
(defn add-layout-track
[ids type value]
(assert (#{:row :column} type))
(ptk/reify ::add-layout-column
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/add-grid-row shape value)
:column (ctl/add-grid-column shape value))))
(ptk/data-event :layout/update ids)
(dwu/commit-undo-transaction undo-id))))))
(defn remove-layout-track
[ids type index]
(assert (#{:row :column} type))
(ptk/reify ::remove-layout-column
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/remove-grid-row shape index)
:column (ctl/remove-grid-column shape index))))
(ptk/data-event :layout/update ids)
(dwu/commit-undo-transaction undo-id))))))
(defn change-layout-track
[ids type index props]
(assert (#{:row :column} type))
(ptk/reify ::change-layout-column
ptk/WatchEvent
(watch [_ _ _]
(let [undo-id (js/Symbol)
property (case :row :layout-grid-rows
:column :layout-grid-columns)]
(rx/of (dwu/start-undo-transaction undo-id)
(dwc/update-shapes
ids
(fn [shape]
(-> shape
(update-in [property index] merge props))))
(ptk/data-event :layout/update ids)
(dwu/commit-undo-transaction undo-id))))))
(defn fix-child-sizing
[objects parent-changes shape]

View file

@ -95,6 +95,7 @@
selected)
index (:index (meta attrs))
changes (-> (pcb/empty-changes it page-id)
(pcb/with-objects objects)
(cond-> (some? index)
@ -102,7 +103,10 @@
(cond-> (nil? index)
(pcb/add-object shape))
(cond-> (some? (:parent-id attrs))
(pcb/change-parent (:parent-id attrs) [shape])))
(pcb/change-parent (:parent-id attrs) [shape]))
(cond-> (ctl/grid-layout? objects (:parent-id shape))
(pcb/update-shapes [(:parent-id shape)] ctl/assign-cells))
)
undo-id (js/Symbol)]
(rx/concat
@ -131,9 +135,11 @@
(when (d/not-empty? to-move-shapes)
(-> (pcb/empty-changes it page-id)
(pcb/with-objects objects)
(cond-> (not (ctl/layout? objects frame-id))
(cond-> (not (ctl/any-layout? objects frame-id))
(pcb/update-shapes ordered-indexes ctl/remove-layout-item-data))
(pcb/change-parent frame-id to-move-shapes 0)))]
(pcb/change-parent frame-id to-move-shapes 0)
(cond-> (ctl/grid-layout? objects frame-id)
(pcb/update-shapes [frame-id] ctl/assign-cells))))]
(if (some? changes)
(rx/of (dch/commit-changes changes))
@ -194,10 +200,6 @@
[file page objects ids it components-v2]
(let [lookup (d/getf objects)
layout-ids (->> ids
(mapcat (partial cph/get-parent-ids objects))
(filter (partial ctl/layout? objects)))
groups-to-unmask
(reduce (fn [group-ids id]
;; When the shape to delete is the mask of a masked group,
@ -319,7 +321,6 @@
(dc/detach-comment-thread ids)
(ptk/data-event :layout/update all-parents)
(dch/commit-changes changes)
(ptk/data-event :layout/update layout-ids)
(dwu/commit-undo-transaction undo-id))))
(defn create-and-add-shape

View file

@ -12,7 +12,8 @@
[app.common.geom.matrix :as gmt]
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh]
[app.common.geom.shapes.flex-layout :as gsl]
[app.common.geom.shapes.flex-layout :as gslf]
[app.common.geom.shapes.grid-layout :as gslg]
[app.common.math :as mth]
[app.common.pages.changes-builder :as pcb]
[app.common.pages.helpers :as cph]
@ -440,7 +441,7 @@
exclude-frames-siblings
(into exclude-frames
(comp (mapcat (partial cph/get-siblings-ids objects))
(filter (partial ctl/layout-immediate-child-id? objects)))
(filter (partial ctl/any-layout-immediate-child-id? objects)))
selected)
position (->> ms/mouse-position
@ -469,11 +470,14 @@
(rx/map
(fn [[move-vector mod?]]
(let [position (gpt/add from-position move-vector)
(let [position (gpt/add from-position move-vector)
exclude-frames (if mod? exclude-frames exclude-frames-siblings)
target-frame (ctst/top-nested-frame objects position exclude-frames)
layout? (ctl/layout? objects target-frame)
drop-index (when layout? (gsl/get-drop-index target-frame objects position))]
target-frame (ctst/top-nested-frame objects position exclude-frames)
flex-layout? (ctl/flex-layout? objects target-frame)
grid-layout? (ctl/grid-layout? objects target-frame)
drop-index (cond
flex-layout? (gslf/get-drop-index target-frame objects position)
grid-layout? (gslg/get-drop-index target-frame objects position))]
[move-vector target-frame drop-index])))
(rx/take-until stopper))]
@ -529,7 +533,8 @@
get-new-position
(fn [parent-id position]
(let [parent (get objects parent-id)]
(when (ctl/layout? parent)
(cond
(ctl/flex-layout? parent)
(if (or
(and (ctl/reverse? parent)
(or (= direction :left)
@ -538,7 +543,12 @@
(or (= direction :right)
(= direction :down))))
(dec position)
(+ position 2)))))
(+ position 2))
;; TODO: GRID
(ctl/grid-layout? parent)
nil
)))
add-children-position
(fn [[parent-id children]]
@ -643,7 +653,7 @@
(let [objects (wsh/lookup-page-objects state)
selected (wsh/lookup-selected state {:omit-blocked? true})
selected-shapes (->> selected (map (d/getf objects)))]
(if (every? #(and (ctl/layout-immediate-child? objects %)
(if (every? #(and (ctl/any-layout-immediate-child? objects %)
(not (ctl/layout-absolute? %)))
selected-shapes)
(rx/of (reorder-selected-layout-child direction))
@ -732,7 +742,7 @@
(-> (pcb/empty-changes it page-id)
(pcb/with-objects objects)
;; Remove layout-item properties when moving a shape outside a layout
(cond-> (not (ctl/layout? objects frame-id))
(cond-> (not (ctl/any-layout? objects frame-id))
(pcb/update-shapes (map :id moving-shapes) ctl/remove-layout-item-data))
(pcb/change-parent frame-id moving-shapes drop-index)
(pcb/remove-objects empty-parents))]

View file

@ -405,7 +405,7 @@
(let [objects (wsh/lookup-page-objects state)]
(into []
(comp (map (d/getf objects))
(filter (partial ctl/layout-immediate-child? objects)))
(filter (partial ctl/flex-layout-immediate-child? objects)))
ids)))
st/state =))
@ -478,22 +478,22 @@
(defn workspace-text-modifier-by-id [id]
(l/derived #(get % id) workspace-text-modifier =))
(defn is-layout-child?
(defn is-flex-layout-child?
[ids]
(l/derived
(fn [objects]
(->> ids
(map (d/getf objects))
(some (partial ctl/layout-immediate-child? objects))))
(some (partial ctl/flex-layout-immediate-child? objects))))
workspace-page-objects))
(defn all-layout-child?
(defn all-flex-layout-child?
[ids]
(l/derived
(fn [objects]
(->> ids
(map (d/getf objects))
(every? (partial ctl/layout-immediate-child? objects))))
(every? (partial ctl/flex-layout-immediate-child? objects))))
workspace-page-objects))
(defn get-flex-child-viewer
@ -503,7 +503,7 @@
(let [objects (wsh/lookup-viewer-objects state page-id)]
(into []
(comp (map (d/getf objects))
(filter (partial ctl/layout-immediate-child? objects)))
(filter (partial ctl/flex-layout-immediate-child? objects)))
ids)))
st/state =))
@ -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))

View file

@ -20,12 +20,14 @@
i/component-copy)
(case (:type shape)
:frame (cond
(and (ctl/layout? shape) (ctl/col? shape))
(and (ctl/flex-layout? shape) (ctl/col? shape))
i/layout-columns
(and (ctl/layout? shape) (ctl/row? shape))
(and (ctl/flex-layout? shape) (ctl/row? shape))
i/layout-rows
;; TODO: GRID ICON
:else
i/artboard)
:image i/image

View file

@ -113,6 +113,17 @@
(def full-screen (icon-xref :full-screen))
(def full-screen-off (icon-xref :full-screen-off))
(def grid (icon-xref :grid))
(def grid-justify-content-column-around (icon-xref :grid-justify-content-column-around))
(def grid-justify-content-column-between (icon-xref :grid-justify-content-column-between))
(def grid-justify-content-column-center (icon-xref :grid-justify-content-column-center))
(def grid-justify-content-column-end (icon-xref :grid-justify-content-column-end))
(def grid-justify-content-column-start (icon-xref :grid-justify-content-column-start))
(def grid-justify-content-row-around (icon-xref :grid-justify-content-row-around))
(def grid-justify-content-row-between (icon-xref :grid-justify-content-row-between))
(def grid-justify-content-row-center (icon-xref :grid-justify-content-row-center))
(def grid-justify-content-row-end (icon-xref :grid-justify-content-row-end))
(def grid-justify-content-row-start (icon-xref :grid-justify-content-row-start))
(def grid-layout-mode (icon-xref :grid-layout-mode))
(def grid-snap (icon-xref :grid-snap))
(def go-next (icon-xref :go-next))
(def go-prev (icon-xref :go-prev))

View file

@ -129,7 +129,7 @@
(let [shape (unchecked-get props "shape")
childs (unchecked-get props "childs")
childs (cond-> childs
(ctl/layout? shape)
(ctl/any-layout? shape)
(cph/sort-layout-children-z-index))]
[:> frame-container props
[:g.frame-children {:opacity (:opacity shape)}

View file

@ -23,6 +23,7 @@
[app.main.ui.workspace.sidebar.options.shapes.bool :as bool]
[app.main.ui.workspace.sidebar.options.shapes.circle :as circle]
[app.main.ui.workspace.sidebar.options.shapes.frame :as frame]
[app.main.ui.workspace.sidebar.options.shapes.grid-cell :as grid-cell]
[app.main.ui.workspace.sidebar.options.shapes.group :as group]
[app.main.ui.workspace.sidebar.options.shapes.image :as image]
[app.main.ui.workspace.sidebar.options.shapes.multiple :as multiple]
@ -67,9 +68,16 @@
(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 [[_ {: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 +95,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 +150,3 @@
:file-id file-id
:page-id page-id
:section section}]))

View file

@ -5,97 +5,138 @@
;; Copyright (c) KALEIDOS INC
(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 :as udw]
[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]
[cuerdas.core :as str]
[rumext.v2 :as mf]))
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.main.data.workspace :as udw]
[app.main.data.workspace.shape-layout :as dwsl]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.components.numeric-input :refer [numeric-input]]
[app.main.ui.components.select :refer [select]]
[app.main.ui.icons :as i]
[app.util.dom :as dom]
[cuerdas.core :as str]
[rumext.v2 :as mf]))
(def layout-container-flex-attrs
[:layout ;; :flex, :grid in the future
:layout-flex-dir ;; :row, :row-reverse, :column, :column-reverse
:layout-gap-type ;; :simple, :multiple
:layout-gap ;; {:row-gap number , :column-gap number}
:layout-align-items ;; :start :end :center :stretch
:layout-justify-content ;; :start :center :end :space-between :space-around :space-evenly
:layout-align-content ;; :start :center :end :space-between :space-around :space-evenly :stretch (by default)
:layout-wrap-type ;; :wrap, :nowrap
:layout-padding-type ;; :simple, :multiple
:layout-padding ;; {:p1 num :p2 num :p3 num :p4 num} number could be negative
])
:layout-grid-dir ;; :row :column
:layout-justify-items
:layout-grid-columns
:layout-grid-rows])
(defn get-layout-flex-icon
[type val is-col?]
(case type
:align-items (if is-col?
(case val
:start i/align-items-column-start
:end i/align-items-column-end
:center i/align-items-column-center
:stretch i/align-items-column-strech
:baseline i/align-items-column-baseline)
(case val
:start i/align-items-row-start
:end i/align-items-row-end
:center i/align-items-row-center
:stretch i/align-items-row-strech
:baseline i/align-items-row-baseline))
:justify-content (if is-col?
(case val
:start i/justify-content-column-start
:end i/justify-content-column-end
:center i/justify-content-column-center
:space-around i/justify-content-column-around
:space-evenly i/justify-content-column-evenly
:space-between i/justify-content-column-between)
(case val
:start i/justify-content-row-start
:end i/justify-content-row-end
:center i/justify-content-row-center
:space-around i/justify-content-row-around
:space-evenly i/justify-content-row-evenly
:space-between i/justify-content-row-between))
:align-items
(if is-col?
(case val
:start i/align-items-column-start
:end i/align-items-column-end
:center i/align-items-column-center
:stretch i/align-items-column-strech
:baseline i/align-items-column-baseline)
(case val
:start i/align-items-row-start
:end i/align-items-row-end
:center i/align-items-row-center
:stretch i/align-items-row-strech
:baseline i/align-items-row-baseline))
:align-content (if is-col?
(case val
:start i/align-content-column-start
:end i/align-content-column-end
:center i/align-content-column-center
:space-around i/align-content-column-around
:space-evenly i/align-content-column-evenly
:space-between i/align-content-column-between
:stretch nil)
:justify-content
(if is-col?
(case val
:start i/justify-content-column-start
:end i/justify-content-column-end
:center i/justify-content-column-center
:space-around i/justify-content-column-around
:space-evenly i/justify-content-column-evenly
:space-between i/justify-content-column-between)
(case val
:start i/justify-content-row-start
:end i/justify-content-row-end
:center i/justify-content-row-center
:space-around i/justify-content-row-around
:space-evenly i/justify-content-row-evenly
:space-between i/justify-content-row-between))
(case val
:start i/align-content-row-start
:end i/align-content-row-end
:center i/align-content-row-center
:space-around i/align-content-row-around
:space-evenly i/align-content-row-evenly
:space-between i/align-content-row-between
:stretch nil))
:align-content
(if is-col?
(case val
:start i/align-content-column-start
:end i/align-content-column-end
:center i/align-content-column-center
:space-around i/align-content-column-around
:space-evenly i/align-content-column-evenly
:space-between i/align-content-column-between
:stretch nil)
:align-self (if is-col?
(case val
:start i/align-self-row-left
:end i/align-self-row-right
:center i/align-self-row-center
:stretch i/align-self-row-strech
:baseline i/align-self-row-baseline)
(case val
:start i/align-self-column-top
:end i/align-self-column-bottom
:center i/align-self-column-center
:stretch i/align-self-column-strech
:baseline i/align-self-column-baseline))))
(case val
:start i/align-content-row-start
:end i/align-content-row-end
:center i/align-content-row-center
:space-around i/align-content-row-around
:space-evenly i/align-content-row-evenly
:space-between i/align-content-row-between
:stretch nil))
(case val
:start i/align-content-row-start
:end i/align-content-row-end
:center i/align-content-row-center
:space-around i/align-content-row-around
:space-between i/align-content-row-between
:stretch nil)
:align-self
(if is-col?
(case val
:auto i/minus
:start i/align-self-row-left
:end i/align-self-row-right
:center i/align-self-row-center
:stretch i/align-self-row-strech
:baseline i/align-self-row-baseline)
(case val
:auto i/minus
:start i/align-self-column-top
:end i/align-self-column-bottom
:center i/align-self-column-center
:stretch i/align-self-column-strech
:baseline i/align-self-column-baseline))))
(defn get-layout-grid-icon
[type val is-col?]
(case type
:justify-items
(if is-col?
(case val
:start i/grid-justify-content-column-start
:end i/grid-justify-content-column-end
:center i/grid-justify-content-column-center
:space-around i/grid-justify-content-column-around
:space-between i/grid-justify-content-column-between)
(case val
:start i/grid-justify-content-row-start
:end i/grid-justify-content-row-end
:center i/grid-justify-content-row-center
:space-around i/grid-justify-content-row-around
:space-between i/grid-justify-content-row-between))))
(mf/defc direction-btn
[{:keys [dir saved-dir set-direction] :as props}]
[{:keys [dir saved-dir set-direction icon?] :as props}]
(let [handle-on-click
(mf/use-callback
(mf/deps set-direction dir)
@ -112,7 +153,9 @@
:key (dm/str "direction-" dir)
:alt (str/replace (str/capital (d/name dir)) "-" " ")
:on-click handle-on-click}
i/auto-direction]))
(if icon?
i/auto-direction
(str/replace (str/capital (d/name dir)) "-" " "))]))
(mf/defc wrap-row
[{:keys [wrap-type set-wrap] :as props}]
@ -299,6 +342,111 @@
:value (:row-gap gap-value)
:disabled (and (= :nowrap wrap-type) (not is-col?))}]]]])
(mf/defc grid-edit-mode
[{:keys [id] :as props}]
(let [edition (mf/deref refs/selected-edition)
active? (= id edition)
toggle-edit-mode
(mf/use-callback
(mf/deps id edition)
(fn []
(if-not active?
(st/emit! (udw/start-edition-mode id))
(st/emit! :interrupt))))]
[:button.tooltip.tooltip-bottom-left
{:class (dom/classnames :active active?)
:alt "Grid edit mode"
:on-click #(toggle-edit-mode)
:style {:padding 0}}
i/grid-layout-mode]))
(mf/defc align-grid-row
[{:keys [is-col? align-items set-align] :as props}]
(let [type (if is-col? :column :row)]
[:div.align-items-style
(for [align [:start :center :end :stretch :baseline]]
[:button.align-start.tooltip
{:class (dom/classnames :active (= align-items align)
:tooltip-bottom-left (not= align :start)
:tooltip-bottom (= align :start))
:alt (dm/str "Align items " (d/name align))
:on-click #(set-align align type)
:key (dm/str "align-items" (d/name align))}
(get-layout-flex-icon :align-items align is-col?)])]))
(mf/defc justify-grid-row
[{:keys [is-col? justify-items set-justify] :as props}]
(let [type (if is-col? :column :row)]
[:div.justify-content-style
(for [align [:start :center :end :space-around :space-between]]
[:button.align-start.tooltip
{:class (dom/classnames :active (= justify-items align)
:tooltip-bottom-left (not= align :start)
:tooltip-bottom (= align :start))
:alt (dm/str "Justify content " (d/name align))
:on-click #(set-justify align type)
:key (dm/str "justify-content" (d/name align))}
(get-layout-grid-icon :justify-items align is-col?)])]))
(defn manage-values [{:keys [value type]}]
(case type
:auto "auto"
:percent (dm/str value "%")
:flex (dm/str value "fr")
:fixed (dm/str value "px")
value))
(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}]
(let [column-num (count column-values)
direction (if (> column-num 1)
(if is-col? "Columns " "Rows ")
(if is-col? "Column " "Row "))
column-vals (str/join ", " (map manage-values column-values))
generated-name (dm/str direction (if (= column-num 0) " - empty" (dm/str column-num " (" column-vals ")")))
type (if is-col? :column :row)]
[:div.grid-columns
[:div.grid-columns-header
[:button.expand-icon
{:on-click toggle} i/actions]
[:div.columns-info {:title generated-name
:on-click toggle} generated-name]
[:button.add-column {:on-click #(do
(when-not expanded? (toggle))
(add-new-element type {:type :fixed :value 100}))} i/plus]]
(when expanded?
[:div.columns-info-wrapper
(for [[index column] (d/enumerate column-values)]
[:div.column-info
[: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 "--"}]]
[: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 layout-container-menu
{::mf/wrap [#(mf/memo' % (mf/check-props ["ids" "values" "type" "multiple"]))]}
[{:keys [ids _type values multiple] :as props}]
@ -308,8 +456,8 @@
layout-type (:layout values)
on-add-layout
(fn [_]
(st/emit! (dwsl/create-layout))
(fn [type]
(st/emit! (dwsl/create-layout type))
(reset! open? true))
@ -318,22 +466,20 @@
(st/emit! (dwsl/remove-layout ids))
(reset! open? false))
;; Uncomment when activating the grid options
;; set-flex (fn []
;; (st/emit! (dwsl/remove-layout ids))
;; (on-add-layout :flex))
;;
;; set-grid (fn []
;; (st/emit! (dwsl/remove-layout ids))
;; (on-add-layout :grid))
_set-flex
(fn []
(st/emit! (dwsl/remove-layout ids))
(on-add-layout :flex))
_set-grid
(fn []
(st/emit! (dwsl/remove-layout ids))
(on-add-layout :grid))
;; Flex-direction
saved-dir (:layout-flex-dir values)
is-col? (or (= :column saved-dir) (= :column-reverse saved-dir))
set-direction
(fn [dir]
(st/emit! (dwsl/update-layout ids {:layout-flex-dir dir})))
;; Wrap type
@ -386,7 +532,79 @@
(st/emit! (dwsl/update-layout ids {:layout-padding {:p2 val :p4 val}}))
:else
(st/emit! (dwsl/update-layout ids {:layout-padding {prop val}}))))]
(st/emit! (dwsl/update-layout ids {:layout-padding {prop val}}))))
;; Grid-direction
saved-grid-dir (:layout-grid-dir values)
set-direction
(fn [dir type]
(if (= :flex type)
(st/emit! (dwsl/update-layout ids {:layout-flex-dir dir}))
(st/emit! (dwsl/update-layout ids {:layout-grid-dir dir}))))
;; Align grid
align-items-row (:layout-align-items values)
align-items-column (:layout-justify-items values)
set-align-grid
(fn [value type]
(if (= type :row)
(st/emit! (dwsl/update-layout ids {:layout-align-items value}))
(st/emit! (dwsl/update-layout ids {:layout-justify-items value}))))
;; Justify grid
grid-justify-content-row (:layout-align-content values)
grid-justify-content-column (:layout-justify-content values)
set-justify-grid
(mf/use-callback
(mf/deps ids)
(fn [value type]
(if (= type :row)
(st/emit! (dwsl/update-layout ids {:layout-align-content value}))
(st/emit! (dwsl/update-layout ids {:layout-justify-content value})))))
;;Grid columns
column-grid-values (:layout-grid-columns values)
grid-columns-open? (mf/use-state false)
toggle-columns-info (mf/use-callback
(fn [_]
(swap! grid-columns-open? not)))
; Grid rows / columns
rows-grid-values (:layout-grid-rows values)
grid-rows-open? (mf/use-state false)
toggle-rows-info
(mf/use-callback
(fn [_]
(swap! grid-rows-open? not)))
add-new-element
(mf/use-callback
(mf/deps ids)
(fn [type value]
(st/emit! (dwsl/add-layout-track ids type value))))
remove-element
(mf/use-callback
(mf/deps ids)
(fn [type index]
(st/emit! (dwsl/remove-layout-track ids type index))))
set-column-value
(mf/use-callback
(mf/deps ids)
(fn [type index value]
(st/emit! (dwsl/change-layout-track ids type index {:value value}))))
set-column-type
(mf/use-callback
(mf/deps ids)
(fn [type index track-type]
(st/emit! (dwsl/change-layout-track ids type index {:type track-type}))))]
[:div.element-set
[:div.element-set-title
@ -395,19 +613,21 @@
(if (and (not multiple) (:layout values))
[:div.title-actions
#_[:div.layout-btns
[:button {:on-click set-flex
:class (dom/classnames
:active (= :flex layout-type))} "Flex"]
[:button {:on-click set-grid
:class (dom/classnames
:active (= :grid layout-type))} "Grid"]]
[:button {:on-click set-flex
:class (dom/classnames
:active (= :flex layout-type))} "Flex"]
[:button {:on-click set-grid
:class (dom/classnames
:active (= :grid layout-type))} "Grid"]]
[:button.remove-layout {:on-click on-remove-layout} i/minus]]
[:button.add-page {:on-click on-add-layout} i/close])]]
[:button.add-page {:on-click #(on-add-layout :flex)} i/close])]]
(when (:layout values)
(when (not= :multiple layout-type)
(if (= :flex layout-type)
(case layout-type
:flex
[:div.element-set-content.layout-menu
[:div.layout-row
[:div.direction-wrap.row-title "Direction"]
@ -418,7 +638,8 @@
[:& direction-btn {:key (d/name dir)
:dir dir
:saved-dir saved-dir
:set-direction set-direction}])]]
:set-direction #(set-direction dir :flex)
:icon? true}])]]
[:div.wrap-type
[:& wrap-row {:wrap-type wrap-type
@ -456,4 +677,74 @@
:on-change-style change-padding-type
:on-change on-padding-change}]]
[:div "GRID TO COME"])))]))
:grid
[:div.element-set-content.layout-menu
[:div.layout-row
[:div.direction-wrap.row-title "Direction"]
[:div.btn-wrapper
[:div.direction
[:*
(for [dir [:row :column]]
[:& direction-btn {:key (d/name dir)
:dir dir
:saved-dir saved-grid-dir
:set-direction #(set-direction dir :grid)
:icon? false}])]]
(when (= 1 (count ids))
[:div.edit-mode
[:& grid-edit-mode {:id (first ids)}]])]]
[:div.layout-row
[:div.align-items-grid.row-title "Align"]
[:div.btn-wrapper.align-grid
[:& align-grid-row {:is-col? false
:align-items align-items-row
:set-align set-align-grid}]
[:& align-grid-row {:is-col? true
:align-items align-items-column
:set-align set-align-grid}]]]
[:div.layout-row
[:div.jusfiy-content-grid.row-title "Justify"]
[:div.btn-wrapper.align-grid
[:& justify-grid-row {:is-col? true
:justify-items grid-justify-content-column
:set-justify set-justify-grid}]
[:& justify-grid-row {:is-col? false
:justify-items grid-justify-content-row
:set-justify set-justify-grid}]]]
[:& grid-columns-row {:is-col? true
:expanded? @grid-columns-open?
:toggle toggle-columns-info
:column-values column-grid-values
:add-new-element add-new-element
:set-column-value set-column-value
:set-column-type set-column-type
:remove-element remove-element}]
[:& grid-columns-row {:is-col? false
:expanded? @grid-rows-open?
:toggle toggle-rows-info
:column-values rows-grid-values
:add-new-element add-new-element
:set-column-value set-column-value
:set-column-type set-column-type
:remove-element remove-element}]
[:& gap-section {:is-col? is-col?
:wrap-type wrap-type
:gap-selected? gap-selected?
:set-gap set-gap
:gap-value (:layout-gap values)}]
[:& padding-section {:values values
:on-change-style change-padding-type
:on-change on-padding-change}]]
;; Default if not grid or flex
nil)))]))

View file

@ -83,9 +83,9 @@
selection-parents-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))
selection-parents (mf/deref selection-parents-ref)
flex-child? (->> selection-parents (some ctl/layout?))
flex-child? (->> selection-parents (some ctl/flex-layout?))
absolute? (ctl/layout-absolute? shape)
flex-container? (ctl/layout? shape)
flex-container? (ctl/flex-layout? shape)
flex-auto-width? (ctl/auto-width? shape)
flex-fill-width? (ctl/fill-width? shape)
flex-auto-height? (ctl/auto-height? shape)

View file

@ -30,8 +30,8 @@
layout-item-values (select-keys shape layout-item-attrs)
layout-container-values (select-keys shape layout-container-flex-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)
is-flex-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-flex-layout-child? ids))
is-flex-layout-child? (mf/deref is-flex-layout-child-ref)
is-layout-child-absolute? (ctl/layout-absolute? shape)]
[:*
@ -41,7 +41,7 @@
:shape shape}]
[:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values :multiple false}]
(when is-layout-child?
(when is-flex-layout-child?
[:& layout-item-menu
{:ids ids
:type type
@ -49,7 +49,7 @@
:is-layout-child? true
:shape shape}])
(when (or (not is-layout-child?) is-layout-child-absolute?)
(when (or (not is-flex-layout-child?) is-layout-child-absolute?)
[:& constraints-menu {:ids ids
:values constraint-values}])
[:& layer-menu {:ids ids

View file

@ -32,9 +32,8 @@
layout-item-values (select-keys shape layout-item-attrs)
layout-container-values (select-keys shape layout-container-flex-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)
is-flex-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-flex-layout-child? ids))
is-flex-layout-child? (mf/deref is-flex-layout-child-ref)
is-layout-child-absolute? (ctl/layout-absolute? shape)]
[:*
[:& measures-menu {:ids ids
@ -43,14 +42,14 @@
:shape shape}]
[:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values :multiple false}]
(when is-layout-child?
(when is-flex-layout-child?
[:& layout-item-menu {:ids ids
:type type
:values layout-item-values
:is-layout-child? true
:is-layout-container? false
:shape shape}])
(when (or (not is-layout-child?) is-layout-child-absolute?)
(when (or (not is-flex-layout-child?) is-layout-child-absolute?)
[:& constraints-menu {:ids ids
:values constraint-values}])
[:& layer-menu {:ids ids

View file

@ -36,9 +36,9 @@
layout-item-values (select-keys shape layout-item-attrs)
[comp-ids comp-values] [[(:id shape)] (select-keys shape component-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)
is-layout-container? (ctl/layout? shape)
is-flex-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-flex-layout-child? ids))
is-flex-layout-child? (mf/deref is-flex-layout-child-ref)
is-flex-layout-container? (ctl/flex-layout? shape)
is-layout-child-absolute? (ctl/layout-absolute? shape)]
[:*
[:& measures-menu {:ids [(:id shape)]
@ -48,18 +48,18 @@
[:& component-menu {:ids comp-ids
:values comp-values
:shape-name (:name shape)}]
(when (or (not is-layout-child?) is-layout-child-absolute?)
(when (or (not is-flex-layout-child?) is-layout-child-absolute?)
[:& constraints-menu {:ids ids
:values constraint-values}])
[:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values :multiple false}]
(when (or is-layout-child? is-layout-container?)
(when (or is-flex-layout-child? is-flex-layout-container?)
[:& layout-item-menu
{:ids ids
:type type
:values layout-item-values
:is-layout-child? is-layout-child?
:is-layout-container? is-layout-container?
:is-layout-child? is-flex-layout-child?
:is-layout-container? is-flex-layout-container?
:shape shape}])
[:& layer-menu {:ids ids

View file

@ -0,0 +1,171 @@
;; 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
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.main.ui.components.numeric-input :refer [numeric-input]]
[app.main.ui.icons :as i]
[app.main.ui.workspace.sidebar.options.menus.layout-container :as lyc]
[app.util.dom :as dom]
[rumext.v2 :as mf]))
(mf/defc set-self-alignment
[{:keys [is-col? alignment set-alignment] :as props}]
(let [dir-v [:auto :start :center :end :stretch #_:baseline]]
[:div.align-self-style
(for [align dir-v]
[:button.align-self.tooltip.tooltip-bottom
{:class (dom/classnames :active (= alignment align)
:tooltip-bottom-left (not= align :start)
:tooltip-bottom (= align :start))
:alt (dm/str "Align self " (d/name align)) ;; TODO fix this tooltip
:on-click #(set-alignment align)
:key (str "align-self" align)}
(lyc/get-layout-flex-icon :align-self align is-col?)])]))
(mf/defc options
{::mf/wrap [mf/memo]}
[{:keys [_shape row column] :as props}]
(let [position-mode (mf/use-state :auto) ;; TODO this should come from shape
set-position-mode (fn [mode]
(reset! position-mode mode))
align-self (mf/use-state :auto) ;; TODO this should come from shape
justify-alignment (mf/use-state :auto) ;; TODO this should come from shape
set-alignment (fn [value]
(reset! align-self value)
#_(if (= align-self value)
(st/emit! (dwsl/update-layout-child ids {:layout-item-align-self nil}))
(st/emit! (dwsl/update-layout-child ids {:layout-item-align-self value}))))
set-justify-self (fn [value]
(reset! justify-alignment value)
#_(if (= align-self value)
(st/emit! (dwsl/update-layout-child ids {:layout-item-align-self nil}))
(st/emit! (dwsl/update-layout-child ids {:layout-item-align-self value}))))
column-start column
column-end (inc column)
row-start row
row-end (inc row)
on-change
(fn [_side _orientation _value]
;; TODO
#_(if (= orientation :column)
(case side
:all ((reset! column-start value)
(reset! column-end value))
:start (reset! column-start value)
:end (reset! column-end value))
(case side
:all ((reset! row-start value)
(reset! row-end value))
:start (reset! row-start value)
:end (reset! row-end value))))
area-name (mf/use-state "header") ;; TODO this should come from shape
on-area-name-change (fn [value]
(reset! area-name value))
on-key-press (fn [_event])]
[:div.element-set
[:div.element-set-title
[:span "Grid Cell"]]
[:div.element-set-content.layout-grid-item-menu
[:div.layout-row
[:div.row-title.sizing "Position"]
[:div.position-wrapper
[:button.position-btn
{:on-click #(set-position-mode :auto)
:class (dom/classnames :active (= :auto @position-mode))} "Auto"]
[:button.position-btn
{:on-click #(set-position-mode :manual)
:class (dom/classnames :active (= :manual @position-mode))} "Manual"]
[:button.position-btn
{:on-click #(set-position-mode :area)
:class (dom/classnames :active (= :area @position-mode))} "Area"]]]
[:div.manage-grid-columns
(when (= :auto @position-mode)
[:div.grid-auto
[:div.grid-columns-auto
[:spam.icon i/layout-rows]
[:div.input-wrapper
[:> numeric-input
{:placeholder "--"
:on-click #(dom/select-target %)
:on-change (partial on-change :all :column) ;; TODO cambiar este on-change y el value
:value column-start}]]]
[:div.grid-rows-auto
[:spam.icon i/layout-columns]
[:div.input-wrapper
[:> numeric-input
{:placeholder "--"
:on-click #(dom/select-target %)
:on-change (partial on-change :all :row) ;; TODO cambiar este on-change y el value
:value row-start}]]]])
(when (= :area @position-mode)
[:div.input-wrapper
[:input.input-text
{:key "grid-area-name"
:id "grid-area-name"
:type "text"
:aria-label "grid-area-name"
:placeholder "--"
:default-value @area-name
:auto-complete "off"
:on-change on-area-name-change
:on-key-press on-key-press}]])
(when (or (= :manual @position-mode) (= :area @position-mode))
[:div.grid-manual
[:div.grid-columns-auto
[:spam.icon i/layout-rows]
[:div.input-wrapper
[:> numeric-input
{:placeholder "--"
:on-click #(dom/select-target %)
:on-change (partial on-change :start :column)
:value column-start}]
[:> numeric-input
{:placeholder "--"
:on-click #(dom/select-target %)
:on-change (partial on-change :end :column)
:value column-end}]]]
[:div.grid-rows-auto
[:spam.icon i/layout-columns]
[:div.input-wrapper
[:> numeric-input
{:placeholder "--"
:on-click #(dom/select-target %)
:on-change (partial on-change :start :row)
:value row-start}]
[:> numeric-input
{:placeholder "--"
:on-click #(dom/select-target %)
:on-change (partial on-change :end :row)
:value row-end}]]]])]
[:div.layout-row
[:div.row-title "Align"]
[:div.btn-wrapper
[:& set-self-alignment {:is-col? false
:alignment @align-self
:set-alignment set-alignment}]]]
[:div.layout-row
[:div.row-title "Justify"]
[:div.btn-wrapper
[:& set-self-alignment {:is-col? true
:alignment @justify-alignment
:set-alignment set-justify-self}]]]]]))

View file

@ -29,15 +29,15 @@
{::mf/wrap [mf/memo]
::mf/wrap-props false}
[props]
(let [shape (unchecked-get props "shape")
shape-with-children (unchecked-get props "shape-with-children")
shared-libs (unchecked-get props "shared-libs")
objects (->> shape-with-children (group-by :id) (d/mapm (fn [_ v] (first v))))
file-id (unchecked-get props "file-id")
layout-container-values (select-keys shape layout-container-flex-attrs)
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)
(let [shape (unchecked-get props "shape")
shape-with-children (unchecked-get props "shape-with-children")
shared-libs (unchecked-get props "shared-libs")
objects (->> shape-with-children (group-by :id) (d/mapm (fn [_ v] (first v))))
file-id (unchecked-get props "file-id")
layout-container-values (select-keys shape layout-container-flex-attrs)
ids [(:id shape)]
is-flex-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-flex-layout-child? ids))
is-flex-layout-child? (mf/deref is-flex-layout-child-ref)
is-layout-child-absolute? (ctl/layout-absolute? shape)
type :group
@ -58,7 +58,7 @@
[:& component-menu {:ids comp-ids :values comp-values :shape-name (:name shape)}]
[:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values :multiple false}]
(when is-layout-child?
(when is-flex-layout-child?
[:& layout-item-menu
{:type type
:ids layout-item-ids
@ -66,7 +66,7 @@
:is-layout-container? false
:values layout-item-values}])
(when (or (not is-layout-child?) is-layout-child-absolute?)
(when (or (not is-flex-layout-child?) is-layout-child-absolute?)
[:& constraints-menu {:ids constraint-ids :values constraint-values}])
[:& layer-menu {:type type :ids layer-ids :values layer-values}]

View file

@ -32,8 +32,8 @@
layout-item-values (select-keys shape layout-item-attrs)
layout-container-values (select-keys shape layout-container-flex-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)
is-flex-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-flex-layout-child? ids))
is-flex-layout-child? (mf/deref is-flex-layout-child-ref)
is-layout-child-absolute? (ctl/layout-absolute? shape)]
[:*
[:& measures-menu {:ids ids
@ -42,7 +42,7 @@
:shape shape}]
[:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values :multiple false}]
(when is-layout-child?
(when is-flex-layout-child?
[:& layout-item-menu
{:ids ids
:type type
@ -50,7 +50,7 @@
:is-layout-child? true
:shape shape}])
(when (or (not is-layout-child?) is-layout-child-absolute?)
(when (or (not is-flex-layout-child?) is-layout-child-absolute?)
[:& constraints-menu {:ids ids
:values constraint-values}])

View file

@ -294,17 +294,17 @@
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)
is-flex-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-flex-layout-child? ids))
is-flex-layout-child? (mf/deref is-flex-layout-child-ref)
has-text? (contains? all-types :text)
has-layout-container? (->> shapes (some ctl/layout?))
has-flex-layout-container? (->> shapes (some ctl/flex-layout?))
all-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/all-layout-child? ids))
all-layout-child? (mf/deref all-layout-child-ref)
all-flex-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/all-flex-layout-child? ids))
all-flex-layout-child? (mf/deref all-flex-layout-child-ref)
all-layout-container? (->> shapes (every? ctl/layout?))
all-flex-layout-container? (->> shapes (every? ctl/flex-layout?))
[measure-ids measure-values] (get-attrs shapes objects :measure)
@ -342,15 +342,15 @@
[:& layout-container-menu {:type type :ids layout-container-ids :values layout-container-values :multiple true}]
(when (or is-layout-child? has-layout-container?)
(when (or is-flex-layout-child? has-flex-layout-container?)
[:& layout-item-menu
{:type type
:ids layout-item-ids
:is-layout-child? all-layout-child?
:is-layout-container? all-layout-container?
:is-layout-child? all-flex-layout-child?
:is-layout-container? all-flex-layout-container?
:values layout-item-values}])
(when-not (or (empty? constraint-ids) is-layout-child?)
(when-not (or (empty? constraint-ids) is-flex-layout-child?)
[:& constraints-menu {:ids constraint-ids :values constraint-values}])
(when-not (empty? layer-ids)

View file

@ -32,8 +32,8 @@
layout-item-values (select-keys shape layout-item-attrs)
layout-container-values (select-keys shape layout-container-flex-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)
is-flex-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-flex-layout-child? ids))
is-flex-layout-child? (mf/deref is-flex-layout-child-ref)
is-layout-child-absolute? (ctl/layout-absolute? shape)]
[:*
[:& measures-menu {:ids ids
@ -42,14 +42,14 @@
:shape shape}]
[:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values :multiple false}]
(when is-layout-child?
(when is-flex-layout-child?
[:& layout-item-menu {:ids ids
:type type
:values layout-item-values
:is-layout-child? true
:is-layout-container? false
:shape shape}])
(when (or (not is-layout-child?) is-layout-child-absolute?)
(when (or (not is-flex-layout-child?) is-layout-child-absolute?)
[:& constraints-menu {:ids ids
:values constraint-values}])
[:& layer-menu {:ids ids

View file

@ -32,8 +32,8 @@
stroke-values (select-keys shape stroke-attrs)
layout-item-values (select-keys shape layout-item-attrs)
layout-container-values (select-keys shape layout-container-flex-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)
is-flex-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-flex-layout-child? ids))
is-flex-layout-child? (mf/deref is-flex-layout-child-ref)
is-layout-child-absolute? (ctl/layout-absolute? shape)]
[:*
[:& measures-menu {:ids ids
@ -41,7 +41,7 @@
:values measure-values
:shape shape}]
[:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values :multiple false}]
(when is-layout-child?
(when is-flex-layout-child?
[:& layout-item-menu
{:ids ids
:type type
@ -49,7 +49,7 @@
:is-layout-child? true
:shape shape}])
(when (or (not is-layout-child?) is-layout-child-absolute?)
(when (or (not is-flex-layout-child?) is-layout-child-absolute?)
[:& constraints-menu {:ids ids
:values constraint-values}])

View file

@ -106,8 +106,8 @@
layout-item-values (select-keys shape layout-item-attrs)
layout-container-values (select-keys shape layout-container-flex-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)
is-flex-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-flex-layout-child? ids))
is-flex-layout-child? (mf/deref is-flex-layout-child-ref)
is-layout-child-absolute? (ctl/layout-absolute? shape)]
(when (contains? svg-elements tag)
@ -118,7 +118,7 @@
:shape shape}]
[:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values :multiple false}]
(when is-layout-child?
(when is-flex-layout-child?
[:& layout-item-menu
{:ids ids
:type type
@ -126,7 +126,7 @@
:is-layout-child? true
:shape shape}])
(when (or (not is-layout-child?) is-layout-child-absolute?)
(when (or (not is-flex-layout-child?) is-layout-child-absolute?)
[:& constraints-menu {:ids ids
:values constraint-values}])

View file

@ -28,8 +28,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)
is-flex-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-flex-layout-child? ids))
is-flex-layout-child? (mf/deref is-flex-layout-child-ref)
layout-container-values (select-keys shape layout-container-flex-attrs)
is-layout-child-absolute? (ctl/layout-absolute? shape)
state-map (mf/deref refs/workspace-editor-state)
@ -76,7 +76,7 @@
:shape shape}]
[:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values :multiple false}]
(when is-layout-child?
(when is-flex-layout-child?
[:& layout-item-menu
{:ids ids
:type type
@ -84,7 +84,7 @@
:is-layout-child? true
:shape shape}])
(when (or (not is-layout-child?) is-layout-child-absolute?)
(when (or (not is-flex-layout-child?) is-layout-child-absolute?)
[:& constraints-menu
{:ids ids
:values (select-keys shape constraint-attrs)}])

View file

@ -29,6 +29,7 @@
[app.main.ui.workspace.viewport.drawarea :as drawarea]
[app.main.ui.workspace.viewport.frame-grid :as frame-grid]
[app.main.ui.workspace.viewport.gradients :as gradients]
[app.main.ui.workspace.viewport.grid-layout-editor :as grid-layout]
[app.main.ui.workspace.viewport.guides :as guides]
[app.main.ui.workspace.viewport.hooks :as hooks]
[app.main.ui.workspace.viewport.interactions :as interactions]
@ -51,17 +52,20 @@
(defn apply-modifiers-to-selected
[selected objects text-modifiers modifiers]
(into []
(comp
(keep (d/getf objects))
(map (fn [{:keys [id] :as shape}]
(cond-> shape
(and (cph/text-shape? shape) (contains? text-modifiers id))
(dwm/apply-text-modifier (get text-modifiers id))
(reduce
(fn [objects id]
(update
objects id
(fn [shape]
(cond-> shape
(and (cph/text-shape? shape) (contains? text-modifiers id))
(dwm/apply-text-modifier (get text-modifiers id))
(contains? modifiers id)
(gsh/transform-shape (dm/get-in modifiers [id :modifiers]))))))
selected))
(contains? modifiers id)
(gsh/transform-shape (dm/get-in modifiers [id :modifiers]))))))
objects
selected))
(mf/defc viewport
[{:keys [wlocal wglobal selected layout file] :as props}]
@ -97,8 +101,11 @@
modifiers (mf/deref refs/workspace-modifiers)
text-modifiers (mf/deref refs/workspace-text-modifier)
objects-modified (mf/with-memo [base-objects modifiers]
(gsh/apply-objects-modifiers base-objects modifiers selected))
objects-modified (mf/with-memo
[base-objects text-modifiers modifiers]
(apply-modifiers-to-selected selected base-objects text-modifiers modifiers))
selected-shapes (->> selected (keep (d/getf objects-modified)))
background (get options :background clr/canvas)
@ -138,7 +145,6 @@
drawing-tool (:tool drawing)
drawing-obj (:object drawing)
selected-shapes (apply-modifiers-to-selected selected base-objects text-modifiers modifiers)
selected-frames (into #{} (map :frame-id) selected-shapes)
@ -152,6 +158,7 @@
(and (some? drawing-obj) (= :path (:type drawing-obj))))
node-editing? (and edition (not= :text (get-in base-objects [edition :type])))
text-editing? (and edition (= :text (get-in base-objects [edition :type])))
grid-editing? (and edition (ctl/grid-layout? base-objects edition))
workspace-read-only? (mf/use-ctx ctx/workspace-read-only?)
mode-inspect? (= options-mode :inspect)
@ -162,7 +169,7 @@
on-drag-enter (actions/on-drag-enter)
on-drag-over (actions/on-drag-over)
on-drop (actions/on-drop file)
on-mouse-down (actions/on-mouse-down @hover selected edition drawing-tool text-editing? node-editing?
on-mouse-down (actions/on-mouse-down @hover selected edition drawing-tool text-editing? node-editing? grid-editing?
drawing-path? create-comment? space? panning z? workspace-read-only?)
on-mouse-up (actions/on-mouse-up disable-paste)
on-pointer-down (actions/on-pointer-down)
@ -193,6 +200,7 @@
show-pixel-grid? (and (contains? layout :show-pixel-grid)
(>= zoom 8))
show-text-editor? (and editing-shape (= :text (:type editing-shape)))
show-grid-editor? (and editing-shape (ctl/grid-layout? editing-shape))
show-presence? page-id
show-prototypes? (= options-mode :prototype)
show-selection-handlers? (and (seq selected) (not show-text-editor?))
@ -333,7 +341,7 @@
:zoom zoom
:modifiers modifiers}]
(when (ctl/layout? outlined-frame)
(when (ctl/any-layout? outlined-frame)
[:g.ghost-outline
[:& outline/shape-outlines
{:objects base-objects
@ -496,6 +504,12 @@
:hover-top-frame-id @hover-top-frame-id
:zoom zoom}])
(when (debug? :grid-layout)
[:& wvd/debug-grid-layout {:selected-shapes selected-shapes
:objects base-objects
:hover-top-frame-id @hover-top-frame-id
:zoom zoom}])
(when show-selection-handlers?
[:g.selection-handlers {:clipPath "url(#clip-handlers)"}
[:defs
@ -526,4 +540,11 @@
(when show-gradient-handlers?
[:& gradients/gradient-handlers
{:id (first selected)
:zoom zoom}])]]]))
:zoom zoom}])
(when show-grid-editor?
[:& grid-layout/editor
{:zoom zoom
:objects base-objects
:shape (get base-objects edition)}])
]]]))

View file

@ -34,10 +34,10 @@
(defn on-mouse-down
[{:keys [id blocked hidden type]} selected edition drawing-tool text-editing?
node-editing? drawing-path? create-comment? space? panning z? workspace-read-only?]
node-editing? grid-editing? drawing-path? create-comment? space? panning z? workspace-read-only?]
(mf/use-callback
(mf/deps id blocked hidden type selected edition drawing-tool text-editing?
node-editing? drawing-path? create-comment? @z? @space?
node-editing? grid-editing? drawing-path? create-comment? @z? @space?
panning workspace-read-only?)
(fn [bevent]
(when (or (dom/class? (dom/get-target bevent) "viewport-controls")
@ -70,7 +70,7 @@
(do
(st/emit! (ms/->MouseEvent :down ctrl? shift? alt? meta?))
(when (and (not= edition id) text-editing?)
(when (and (not= edition id) (or text-editing? grid-editing?))
(st/emit! dw/clear-edition-mode))
(when (and (not text-editing?)

View file

@ -11,6 +11,7 @@
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh]
[app.common.geom.shapes.flex-layout :as gsl]
[app.common.geom.shapes.grid-layout :as gsg]
[app.common.geom.shapes.points :as gpo]
[app.common.pages.helpers :as cph]
[app.common.types.shape.layout :as ctl]
@ -68,7 +69,7 @@
shape (or selected-frame (get objects hover-top-frame-id))]
(when (and shape (ctl/layout? shape))
(when (and shape (ctl/flex-layout? shape))
(let [row? (ctl/row? shape)
col? (ctl/col? shape)
@ -195,3 +196,55 @@
:cy (:y point)
:r (/ 2 zoom)
:style {:fill "red"}}]))])]))))
(mf/defc debug-grid-layout
{::mf/wrap-props false}
[props]
(let [objects (unchecked-get props "objects")
zoom (unchecked-get props "zoom")
selected-shapes (unchecked-get props "selected-shapes")
hover-top-frame-id (unchecked-get props "hover-top-frame-id")
selected-frame
(when (and (= (count selected-shapes) 1) (= :frame (-> selected-shapes first :type)))
(first selected-shapes))
parent (or selected-frame (get objects hover-top-frame-id))
parent-bounds (:points parent)]
(when (and (some? parent) (not= uuid/zero (:id parent)))
(let [children (->> (cph/get-immediate-children objects (:id parent))
(remove :hidden)
(map #(vector (gpo/parent-coords-bounds (:points %) (:points parent)) %)))
hv #(gpo/start-hv parent-bounds %)
vv #(gpo/start-vv parent-bounds %)
width (gpo/width-points parent-bounds)
height (gpo/height-points parent-bounds)
origin (gpo/origin parent-bounds)
{:keys [row-tracks column-tracks]}
(gsg/calc-layout-data parent children parent-bounds)]
[:*
(for [row-data row-tracks]
(let [start-p (gpt/add origin (vv (:distance row-data)))
end-p (gpt/add start-p (hv width))]
[:line {:x1 (:x start-p)
:y1 (:y start-p)
:x2 (:x end-p)
:y2 (:y end-p)
:style {:stroke "red"
:stroke-width (/ 1 zoom)}}]))
(for [column-data column-tracks]
(let [start-p (gpt/add origin (hv (:distance column-data)))
end-p (gpt/add start-p (vv height))]
[:line {:x1 (:x start-p)
:y1 (:y start-p)
:x2 (:x end-p)
:y2 (:y end-p)
:style {:stroke "red"
:stroke-width (/ 1 zoom)}}]))]))))

View file

@ -0,0 +1,324 @@
;; 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.viewport.grid-layout-editor
(:require
[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.points :as gpo]
[app.common.pages.helpers :as cph]
[app.main.data.workspace.grid-layout.editor :as dwge]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.cursors :as cur]
[app.util.dom :as dom]
[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}
[props]
(let [center (unchecked-get props "center")
value (unchecked-get props "value")
zoom (unchecked-get props "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 (->> marker-points
(map #(dm/fmt "%,%" (:x %) (:y %)))
(str/join " "))
:style {:fill "var(--color-distance)"
:fill-opacity 0.3}}]
[:text {:x text-x
:y text-y
:width (/ 26.26 zoom)
:height (/ 32 zoom)
:font-size (/ 16 zoom)
:text-anchor "middle"
:dominant-baseline "middle"
: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 resize-handler
{::mf/wrap-props false}
[props]
(let [start-p (unchecked-get props "start-p")
type (unchecked-get props "type")
bounds (unchecked-get props "bounds")
zoom (unchecked-get props "zoom")
width (gpo/width-points bounds)
height (gpo/height-points bounds)
dragging-ref (mf/use-ref false)
start-ref (mf/use-ref nil)
on-pointer-down
(mf/use-callback
(fn [event]
(dom/capture-pointer event)
(mf/set-ref-val! dragging-ref true)
(mf/set-ref-val! start-ref (dom/get-client-position event))))
on-lost-pointer-capture
(mf/use-callback
(fn [event]
(dom/release-pointer event)
(mf/set-ref-val! dragging-ref false)
(mf/set-ref-val! start-ref nil)))
on-mouse-move
(mf/use-callback
(fn [event]
(when (mf/ref-val dragging-ref)
(let [start (mf/ref-val start-ref)
pos (dom/get-client-position event)
_delta (-> (gpt/to-vec start pos)
(get (if (= type :column) :x :y)))]
;; TODO Implement resize
#_(prn ">Delta" delta)))))
[x y width height]
(if (= type :column)
[(- (:x start-p) (/ 8 zoom))
(- (:y start-p) (/ 40 zoom))
(/ 16 zoom)
(+ height (/ 40 zoom))]
[(- (:x start-p) (/ 40 zoom))
(- (:y start-p) (/ 8 zoom))
(+ width (/ 40 zoom))
(/ 16 zoom)])]
[:rect.resize-handler
{:x x
:y y
:height height
:width width
:on-pointer-down on-pointer-down
:on-lost-pointer-capture on-lost-pointer-capture
:on-mouse-move on-mouse-move
:style {:fill "transparent"
:cursor (if (= type :column)
(cur/resize-ew 0)
(cur/resize-ns 0))}}]))
(mf/defc editor
{::mf/wrap-props false}
[props]
(let [shape (unchecked-get props "shape")
objects (unchecked-get props "objects")
zoom (unchecked-get props "zoom")
bounds (:points shape)
grid-edition-id-ref (mf/use-memo #(refs/workspace-grid-edition-id (:id shape)))
grid-edition (mf/deref grid-edition-id-ref)
hover-cells (:hover grid-edition)
selected-cells (:selected grid-edition)
children (->> (cph/get-immediate-children objects (:id shape))
(remove :hidden)
(map #(vector (gpo/parent-coords-bounds (:points %) (:points shape)) %)))
hv #(gpo/start-hv bounds %)
vv #(gpo/start-vv bounds %)
width (gpo/width-points bounds)
height (gpo/height-points bounds)
origin (gpo/origin bounds)
{:keys [row-tracks column-tracks] :as layout-data}
(gsg/calc-layout-data shape children bounds)]
(mf/use-effect
(fn []
#(st/emit! (dwge/stop-grid-layout-editing (:id shape)))))
[: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 (vv height)))]
[:& plus-btn {:start-p start-p
:zoom zoom
:type :row}])
(for [[_ {:keys [column row]}] (:layout-grid-cells shape)]
[:& 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])
}])
(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}]
[:& resize-handler {:type :column
:start-p start-p
:zoom zoom
:bounds bounds}]]))
(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}]]
[:& resize-handler {:type :row
:start-p start-p
:zoom zoom
:bounds bounds}]]))]))

View file

@ -268,7 +268,7 @@
frame (mf/deref (refs/object-by-id frame-id))
selrect (gsh/selection-rect selected-shapes)]
(when-not (ctl/layout? frame)
(when-not (ctl/any-layout? frame)
[:g.distance
[:& shape-distance
{:selrect selrect

View file

@ -175,7 +175,7 @@
shapes (if drawing [drawing] shapes)
frame-id (snap/snap-frame-id shapes)]
(when-not (ctl/layout? objects frame-id)
(when-not (ctl/any-layout? objects frame-id)
[:& snap-feedback {:shapes shapes
:page-id page-id
:remove-snap? remove-snap?

View file

@ -9,6 +9,7 @@
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.geom.point :as gpt]
[app.common.pages.helpers :as cph]
[app.common.types.shape-tree :as ctt]
[app.common.uuid :as uuid]
[app.main.data.workspace :as dw]
@ -55,8 +56,11 @@
drawing (mf/deref refs/workspace-drawing)
drawing-obj (:object drawing)
shape (or drawing-obj (-> selected first))]
(when (or (and (= (count selected) 1) (= (:id shape) edition) (not= :text (:type shape)))
(and (some? drawing-obj) (= :path (:type drawing-obj))
(when (or (and (= (count selected) 1)
(= (:id shape) edition)
(cph/path-shape? shape))
(and (some? drawing-obj)
(cph/path-shape? drawing-obj)
(not= :curve (:tool drawing))))
[:div.viewport-actions
[:& path-actions {:shape shape}]])))

View file

@ -82,7 +82,7 @@
grid-y-data (get-grids-snap-points frame :y)]
(cond-> page-data
(not (ctl/layout-descent? objects frame))
(not (ctl/any-layout-descent? objects frame))
(-> ;; Update root frame information
(assoc-in [uuid/zero :objects-data frame-id] frame-data)
@ -106,7 +106,7 @@
:id (:id shape)
:pt %)))]
(cond-> page-data
(not (ctl/layout-descent? objects shape))
(not (ctl/any-layout-descent? objects shape))
(-> (assoc-in [frame-id :objects-data (:id shape)] shape-data)
(update-in [frame-id :x] (make-insert-tree-data shape-data :x))
(update-in [frame-id :y] (make-insert-tree-data shape-data :y))))))

View file

@ -95,6 +95,9 @@
;; Show shape name and id
:shape-titles
;;
:grid-layout
})
;; These events are excluded when we activate the :events flag