Merge pull request #3635 from penpot/alotor-grid-polishing

Grid polishing
This commit is contained in:
Aitor Moreno
2023-09-18 14:16:50 +02:00
committed by GitHub
28 changed files with 698 additions and 209 deletions

View File

@@ -62,15 +62,17 @@
(defn child-min-width
[child bounds]
(if (ctl/fill-width? child)
(ctl/child-min-width child)
(gpo/width-points bounds)))
(+ (if (ctl/fill-width? child)
(ctl/child-min-width child)
(gpo/width-points bounds))
(ctl/child-width-margin child)))
(defn child-min-height
[child bounds]
(if (ctl/fill-height? child)
(ctl/child-min-height child)
(gpo/height-points bounds)))
(+ (if (ctl/fill-height? child)
(ctl/child-min-height child)
(gpo/height-points bounds))
(ctl/child-height-margin child)))
(defn calculate-initial-track-size
[total-value {:keys [type value] :as track}]

View File

@@ -18,6 +18,7 @@
[app.common.types.shape.layout :as ctl]))
(defn cell-bounds
"Retrieves the points that define the bounds for given cell"
[{:keys [origin row-tracks column-tracks layout-bounds column-gap row-gap] :as layout-data} {:keys [row column row-span column-span] :as cell}]
(let [hv #(gpo/start-hv layout-bounds %)
@@ -55,11 +56,13 @@
[_parent
transform
transform-inverse
_child
child
child-origin child-width
cell-bounds]
(let [target-width (max (gpo/width-points cell-bounds) 0.01)
(let [target-width (max (- (gpo/width-points cell-bounds) (ctl/child-width-margin child)) 0.01)
max-width (max (ctl/child-max-width child) 0.01)
target-width (mth/clamp target-width (ctl/child-min-width child) max-width)
fill-scale (/ target-width child-width)]
{:width target-width
:modifiers (ctm/resize-modifiers (gpt/point fill-scale 1) child-origin transform transform-inverse)}))
@@ -68,10 +71,12 @@
"Calculates the size and modifiers for the height of an auto-fill child"
[_parent
transform transform-inverse
_child
child
child-origin child-height
cell-bounds]
(let [target-height (max (gpo/height-points cell-bounds) 0.01)
(let [target-height (max (- (gpo/height-points cell-bounds) (ctl/child-height-margin child)) 0.01)
max-height (max (ctl/child-max-height child) 0.01)
target-height (mth/clamp target-height (ctl/child-min-height child) max-height)
fill-scale (/ target-height child-height)]
{:height target-height
:modifiers (ctm/resize-modifiers (gpt/point 1 fill-scale) child-origin transform transform-inverse)}))
@@ -106,7 +111,7 @@
(cond-> fill-height (ctm/add-modifiers (:modifiers fill-height))))]))
(defn child-position-delta
[parent child-bounds child-width child-height layout-data cell-data]
[parent child child-bounds child-width child-height layout-data cell-data]
(let [cell-bounds (cell-bounds layout-data cell-data)
child-origin (gpo/origin child-bounds)
@@ -126,30 +131,35 @@
hv (partial gpo/start-hv cell-bounds)
vv (partial gpo/start-vv cell-bounds)
[top-m right-m bottom-m left-m] (ctl/child-margins child)
;; Adjust alignment/justify
[from-h to-h]
(case justify
:end
[(gpt/add origin-h (hv child-width))
(nth cell-bounds 1)]
(gpt/subtract (nth cell-bounds 1) (hv right-m))]
:center
[(gpt/add origin-h (hv (/ child-width 2)))
(gpo/project-point cell-bounds :h (gpo/center cell-bounds))]
[origin-h (first cell-bounds)])
[origin-h
(gpt/add (first cell-bounds) (hv left-m))])
[from-v to-v]
(case align
:end
[(gpt/add origin-v (vv child-height))
(nth cell-bounds 3)]
(gpt/subtract (nth cell-bounds 3) (vv bottom-m))]
:center
[(gpt/add origin-v (vv (/ child-height 2)))
(gpo/project-point cell-bounds :v (gpo/center cell-bounds))]
[origin-v (first cell-bounds)])]
[origin-v
(gpt/add (first cell-bounds) (vv top-m))])]
(-> (gpt/point)
(gpt/add (gpt/to-vec from-h to-h))
(gpt/add (gpt/to-vec from-v to-v)))))
@@ -160,7 +170,7 @@
(let [[child-width child-height fill-modifiers]
(fill-modifiers parent parent-bounds child child-bounds layout-data cell-data)
position-delta (child-position-delta parent child-bounds child-width child-height layout-data cell-data)]
position-delta (child-position-delta parent child child-bounds child-width child-height layout-data cell-data)]
(cond-> (ctm/empty)
(not (ctl/layout-absolute? child))

View File

@@ -104,14 +104,15 @@
(defn absolute-move
"Move the shape to the exactly specified position."
[shape pos]
(let [x (dm/get-prop pos :x)
y (dm/get-prop pos :y)
sr (dm/get-prop shape :selrect)
px (dm/get-prop sr :x)
py (dm/get-prop sr :y)
dx (- (d/check-num x) px)
dy (- (d/check-num y) py)]
(move shape (gpt/point dx dy))))
(when shape
(let [x (dm/get-prop pos :x)
y (dm/get-prop pos :y)
sr (dm/get-prop shape :selrect)
px (dm/get-prop sr :x)
py (dm/get-prop sr :y)
dx (- (d/check-num x) px)
dy (- (d/check-num y) py)]
(move shape (gpt/point dx dy)))))
;; --- Transformation matrix operations

View File

@@ -13,8 +13,6 @@
[app.common.schema :as sm]
[app.common.uuid :as uuid]))
;; FIXME: need proper schemas
;; :layout ;; :flex, :grid in the future
;; :layout-flex-dir ;; :row, :row-reverse, :column, :column-reverse
;; :layout-gap-type ;; :simple, :multiple
@@ -695,6 +693,29 @@
(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)]
(mapv
second
(-> tracks
(d/enumerate) ;; make unique so the insert-at-index won't remove the value
(assoc from-index nil)
(d/insert-at-index (inc to-index) [[nil 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))
@@ -827,7 +848,9 @@
cells
(let [next-free (first free-cells)
current (first pending)
cells (update-in cells [next-free :shapes] conj current)]
cells (-> cells
(update-in [next-free :shapes] conj current)
(assoc-in [next-free :position] :auto))]
(recur cells (rest free-cells) (rest pending)))))]
;; TODO: Remove after testing
@@ -851,7 +874,7 @@
(let [cell-from (get cells idx)
cell-to (get cells (inc idx))
cell (assoc cell-to :shapes (:shapes cell-from))
cell (assoc cell-to :shapes (:shapes cell-from) :position (:position cell-from))
parent (assoc-in parent [:layout-grid-cells (:id cell)] cell)
result-cells (assoc result-cells (inc idx) cell)]
@@ -863,7 +886,7 @@
(recur parent result-cells (inc idx))))))]
[(assoc-in parent [:layout-grid-cells (get-in cells [index :id]) :shapes] [])
(assoc-in result-cells [index :shapes] [])]))))
(update result-cells index assoc :shapes [] :position :auto)]))))
(defn in-cell?
@@ -893,7 +916,7 @@
[start-index start-cell] (seek-indexed-cell cells row column)]
(if (some? start-cell)
(let [ ;; start-index => to-index is the range where the shapes inserted will be added
(let [;; start-index => to-index is the range where the shapes inserted will be added
to-index (min (+ start-index (count shape-ids)) (dec (count cells)))]
;; Move shift the `shapes` attribute between cells
@@ -901,7 +924,8 @@
(map vector shape-ids)
(reduce (fn [[parent cells] [shape-id idx]]
(let [[parent cells] (free-cell-push parent cells idx)]
[(assoc-in parent [:layout-grid-cells (get-in cells [idx :id]) :shapes] [shape-id])
[(update-in parent [:layout-grid-cells (get-in cells [idx :id])]
assoc :position :manual :shapes [shape-id])
cells]))
[parent cells])
(first)))
@@ -1025,9 +1049,17 @@
(defn swap-shapes
[parent id-from id-to]
(-> parent
(assoc-in [:layout-grid-cells id-from :shapes] (dm/get-in parent [:layout-grid-cells id-to :shapes]))
(assoc-in [:layout-grid-cells id-to :shapes] (dm/get-in parent [:layout-grid-cells id-from :shapes]))))
(let [cell-to (dm/get-in parent [:layout-grid-cells id-to])
cell-from (dm/get-in parent [:layout-grid-cells id-from])]
(-> parent
(update-in [:layout-grid-cells id-from]
assoc
:shapes (:shapes cell-to)
:podition (:position cell-to))
(update-in [:layout-grid-cells id-to]
assoc
:shapes (:shapes cell-from)
:position (:position cell-from)))))
(defn add-children-to-cell
[frame children objects [row column :as cell]]
@@ -1069,3 +1101,80 @@
new-shapes (into new-shapes (:shapes parent))]
(assoc parent :shapes (into [] (reverse new-shapes)))))
(defn shapes-by-row
[parent index]
(->> (:layout-grid-cells parent)
(filter (fn [[_ {:keys [row row-span]}]]
(and (>= (inc index) row)
(< (inc index) (+ row row-span)))))
(map second)
(mapcat :shapes)))
(defn shapes-by-column
[parent index]
(->> (:layout-grid-cells parent)
(filter (fn [[_ {:keys [column column-span]}]]
(and (>= (inc index) column)
(< (inc index) (+ column column-span)))))
(map second)
(mapcat :shapes)))
(defn cells-coordinates
"Given a group of cells returns the coordinates that define"
[cells]
(loop [cells (seq cells)
result
{:first-row ##Inf
:first-column ##Inf
:last-row ##-Inf
:last-column ##-Inf
:cell-coords #{}}]
(if (empty? cells)
result
(let [{:keys [first-row last-row first-column last-column cell-coords]} result
current (first cells)
first-row
(if (< (:row current) first-row)
(:row current)
first-row)
last-row
(if (> (+ (:row current) (:row-span current) -1) last-row)
(+ (:row current) (:row-span current) -1)
last-row)
first-column
(if (< (:column current) first-column)
(:column current)
first-column)
last-column
(if (> (+ (:column current) (:column-span current) -1) last-column)
(+ (:column current) (:column-span current) -1)
last-column)
cell-coords
(into cell-coords
(for [r (range (:row current) (+ (:row current) (:row-span current)))
c (range (:column current) (+ (:column current) (:column-span current)))]
[r c]))]
(recur (rest cells)
(assoc result
:first-row first-row
:last-row last-row
:first-column first-column
:last-column last-column
:cell-coords cell-coords))))))
(defn valid-area-cells?
[cells]
(let [{:keys [first-row last-row first-column last-column cell-coords]} (cells-coordinates cells)]
(every?
#(contains? cell-coords %)
(for [r (range first-row (inc last-row))
c (range first-column (inc last-column))]
[r c]))))

View File

@@ -1855,13 +1855,13 @@
}
}
.position-wrapper {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
display: flex;
width: 100%;
max-width: 185px;
height: 26px;
border-radius: 4px;
border: 1px solid $color-gray-60;
.position-btn {
display: flex;
justify-content: center;
@@ -1871,6 +1871,8 @@
cursor: pointer;
color: $color-gray-20;
border-right: 1px solid $color-gray-60;
flex: 1;
&:last-child {
border-right: none;
}
@@ -1878,6 +1880,23 @@
&:hover {
color: $color-primary;
}
&[disabled] {
opacity: 0.5;
&:hover {
color: $color-gray-20;
}
}
}
}
&.single-button {
display: flex;
justify-content: end;
height: 1.5rem;
.btn-wrapper {
width: initial;
}
}
}
@@ -2262,6 +2281,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;
}

View File

@@ -71,7 +71,8 @@
}
}
&:hover {
&:hover,
&.hover {
background-color: $color-primary;
svg {

View File

@@ -41,7 +41,7 @@
(> dy dx)
(assoc :x (- (:x point) (* sx (- dy dx)))))))
(defn resize-shape [{:keys [x y width height] :as shape} initial point lock?]
(defn resize-shape [{:keys [x y width height] :as shape} initial point lock? mod?]
(if (and (some? x) (some? y) (some? width) (some? height))
(let [draw-rect (grc/make-rect initial (cond-> point lock? (adjust-ratio initial)))
shape-rect (grc/make-rect x y width height)
@@ -56,13 +56,14 @@
(-> shape
(assoc :click-draw? false)
(vary-meta merge {:mod? mod?})
(gsh/transform-shape (-> (ctm/empty)
(ctm/resize scalev (gpt/point x y))
(ctm/move movev)))))
shape))
(defn update-drawing [state initial point lock?]
(update-in state [:workspace-drawing :object] resize-shape initial point lock?))
(defn update-drawing [state initial point lock? mod?]
(update-in state [:workspace-drawing :object] resize-shape initial point lock? mod?))
(defn move-drawing
[{:keys [x y]}]
@@ -105,9 +106,7 @@
(cond-> (some? drop-index)
(with-meta {:index drop-index}))
(cond-> (some? drop-cell)
(with-meta {:cell drop-cell})))
]
(with-meta {:cell drop-cell})))]
(rx/concat
;; Add shape to drawing state
@@ -120,14 +119,14 @@
(->> ms/mouse-position
(rx/filter #(> (gpt/distance % initial) (/ 2 zoom)))
(rx/with-latest vector ms/mouse-position-shift)
(rx/with-latest conj ms/mouse-position-mod)
(rx/switch-map
(fn [[point :as current]]
(->> (snap/closest-snap-point page-id [shape] objects layout zoom focus point)
(rx/map #(conj current %)))))
(rx/map
(fn [[_ shift? point]]
#(update-drawing % initial (cond-> point snap-pixel? (gpt/round-step snap-prec)) shift?)))))
(fn [[_ shift? mod? point]]
#(update-drawing % initial (cond-> point snap-pixel? (gpt/round-step snap-prec)) shift? mod?)))))
(rx/take-until stoper))
(->> (rx/of (common/handle-finish-drawing))

View File

@@ -25,19 +25,41 @@
(disj hover-set cell-id))))))))
(defn select-grid-cell
[grid-id cell-id]
[grid-id cell-id add?]
(ptk/reify ::select-grid-cell
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:workspace-grid-edition grid-id :selected] cell-id))))
(if add?
(update-in state [:workspace-grid-edition grid-id :selected] (fnil conj #{}) cell-id)
(assoc-in state [:workspace-grid-edition grid-id :selected] #{cell-id})))))
(defn remove-selection
[grid-id]
[grid-id cell-id]
(ptk/reify ::remove-selection
ptk/UpdateEvent
(update [_ state]
(update-in state [:workspace-grid-edition grid-id :selected] disj cell-id))))
(defn clear-selection
[grid-id]
(ptk/reify ::clear-selection
ptk/UpdateEvent
(update [_ state]
(update-in state [:workspace-grid-edition grid-id] dissoc :selected))))
(defn clean-selection
[grid-id]
(ptk/reify ::clean-selection
ptk/UpdateEvent
(update [_ state]
(let [objects (wsh/lookup-page-objects state)
shape (get objects grid-id)]
(update-in state [:workspace-grid-edition grid-id :selected]
(fn [selected]
(into #{}
(filter #(contains? (:layout-grid-cells shape) %))
selected)))))))
(defn stop-grid-layout-editing
[grid-id]
(ptk/reify ::stop-grid-layout-editing

View File

@@ -490,11 +490,13 @@
(let [page (wsh/lookup-page state)
libraries (wsh/get-libraries state)
objects (:objects page)
changes (-> (pcb/empty-changes it (:id page))
(pcb/with-objects (:objects page)))
(pcb/with-objects objects))
[new-shape changes]
(dwlh/generate-instantiate-component changes
objects
file-id
component-id
position

View File

@@ -10,6 +10,7 @@
[app.common.data.macros :as dm]
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh]
[app.common.geom.shapes.grid-layout :as gslg]
[app.common.logging :as log]
[app.common.pages.changes-builder :as pcb]
[app.common.pages.helpers :as cph]
@@ -21,6 +22,7 @@
[app.common.types.container :as ctn]
[app.common.types.file :as ctf]
[app.common.types.shape-tree :as ctst]
[app.common.types.shape.layout :as ctl]
[app.common.types.typography :as cty]
[app.common.uuid :as uuid]
[app.main.data.workspace.state-helpers :as wsh]
@@ -158,10 +160,10 @@
(defn generate-instantiate-component
"Generate changes to create a new instance from a component."
([changes file-id component-id position page libraries]
(generate-instantiate-component changes file-id component-id position page libraries nil nil))
([changes objects file-id component-id position page libraries]
(generate-instantiate-component changes objects file-id component-id position page libraries nil nil))
([changes file-id component-id position page libraries old-id parent-id]
([changes objects file-id component-id position page libraries old-id parent-id]
(let [component (ctf/get-component libraries file-id component-id)
library (get libraries file-id)
@@ -182,6 +184,19 @@
changes (cond-> (pcb/add-object changes first-shape {:ignore-touched true})
(some? old-id) (pcb/amend-last-change #(assoc % :old-id old-id)))
changes
(if (ctl/grid-layout? objects (:parent-id first-shape))
(let [[row column] (gslg/get-drop-cell (:parent-id first-shape) objects position)]
(-> changes
(pcb/update-shapes
[(:parent-id first-shape)]
(fn [shape]
(-> shape
(ctl/push-into-cell [(:id first-shape)] row column)
(ctl/assign-cells))))
(pcb/reorder-grid-children [(:parent-id first-shape)])))
changes)
changes (reduce #(pcb/add-object %1 %2 {:ignore-touched true})
changes
(rest new-shapes))]

View File

@@ -384,7 +384,7 @@
(prepare-duplicate-guides shapes page ids-map delta)))))
(defn- prepare-duplicate-component-change
[changes page component-root parent-id delta libraries library-data it]
[changes objects page component-root parent-id delta libraries library-data it]
(let [component-id (:component-id component-root)
file-id (:component-file component-root)
main-component (ctf/get-component libraries file-id component-id)
@@ -393,6 +393,7 @@
instantiate-component
#(dwlh/generate-instantiate-component changes
objects
file-id
(:component-id component-root)
pos
@@ -421,7 +422,7 @@
changes
(ctf/is-known-component? obj libraries)
(prepare-duplicate-component-change changes page obj parent-id delta libraries library-data it)
(prepare-duplicate-component-change changes objects page obj parent-id delta libraries library-data it)
:else
(let [frame? (cph/frame-shape? obj)
@@ -469,7 +470,10 @@
; We want the first added object to touch it's parent, but not subsequent children
changes (-> (pcb/add-object changes new-obj {:ignore-touched (and duplicating-component? child?)})
(pcb/amend-last-change #(assoc % :old-id (:id obj))))
(pcb/amend-last-change #(assoc % :old-id (:id obj)))
(cond-> (ctl/grid-layout? objects (:parent-id obj))
(-> (pcb/update-shapes [(:parent-id obj)] ctl/assign-cells)
(pcb/reorder-grid-children [(:parent-id obj)]))))
changes (cond-> changes
(and is-component-root? is-component-main?)

View File

@@ -20,6 +20,7 @@
[app.common.uuid :as uuid]
[app.main.data.workspace.changes :as dwc]
[app.main.data.workspace.colors :as cl]
[app.main.data.workspace.grid-layout.editor :as dwge]
[app.main.data.workspace.modifiers :as dwm]
[app.main.data.workspace.selection :as dwse]
[app.main.data.workspace.shapes :as dws]
@@ -407,6 +408,48 @@
(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]
(let [objects (wsh/lookup-page-objects state)
shape (get objects (first ids))
highlighted (when hover?
(->> (if (= type :row)
(ctl/shapes-by-row shape index)
(ctl/shapes-by-column shape index))
(set)))]
(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])
:always
(assoc-in [:workspace-local :highlighted] highlighted))))))
(defn change-layout-track
[ids type index props]
(assert (#{:row :column} type))
@@ -521,20 +564,73 @@
(ptk/data-event :layout/update ids)
(dwu/commit-undo-transaction undo-id))))))
(defn update-grid-cell
[layout-id cell-id props]
(ptk/reify ::update-grid-cell
(defn update-grid-cells
[layout-id ids props]
(ptk/reify ::update-grid-cells
ptk/WatchEvent
(watch [_ _ _]
(let [undo-id (js/Symbol)]
(rx/of
(dwu/start-undo-transaction undo-id)
(dwc/update-shapes
[layout-id]
(fn [shape]
(-> shape
(d/update-in-when [:layout-grid-cells cell-id]
#(d/without-nils (merge % props))))))
(->> ids
(reduce (fn [shape cell-id]
(-> shape
(d/update-in-when [:layout-grid-cells cell-id]
#(d/without-nils (merge % props)))))
shape))))
(ptk/data-event :layout/update [layout-id])
(dwu/commit-undo-transaction undo-id))))))
(defn change-cells-mode
[layout-id ids mode]
(ptk/reify ::change-cells-mode
ptk/WatchEvent
(watch [_ _ _]
(let [undo-id (js/Symbol)]
(rx/of
(dwu/start-undo-transaction undo-id)
(dwc/update-shapes
[layout-id]
(fn [shape]
(cond
(= mode :area)
;; Create area with the selected cells
(let [{:keys [first-row first-column last-row last-column]}
(ctl/cells-coordinates (->> ids (map #(get-in shape [:layout-grid-cells %]))))
target-cell
(ctl/get-cell-by-position shape first-row first-column)
shape
(-> shape
(ctl/resize-cell-area
(:row target-cell) (:column target-cell)
first-row
first-column
(inc (- last-row first-row))
(inc (- last-column first-column)))
(ctl/assign-cells))]
(-> shape
(d/update-in-when [:layout-grid-cells (:id target-cell)] assoc :position :area)))
(= mode :auto)
;; change the manual cells and move to auto
(->> ids
(reduce
(fn [shape cell-id]
(cond-> shape
(contains? #{:area :manual} (get-in shape [:layout-grid-cells cell-id :position]))
(-> (d/update-in-when [:layout-grid-cells cell-id] assoc :shapes [] :position :auto)
(ctl/assign-cells))))
shape)))))
(dwge/clean-selection layout-id)
(ptk/data-event :layout/update [layout-id])
(dwu/commit-undo-transaction undo-id))))))

View File

@@ -36,10 +36,10 @@
(defn prepare-add-shape
[changes shape objects _selected]
(let [index (:index (meta shape))
;; FIXME: revisit
id (:id shape)
[row column :as cell] (:cell (meta shape))
mod? (:mod? (meta shape))
[row column :as cell] (when-not mod? (:cell (meta shape)))
changes (-> changes
(pcb/with-objects objects)

View File

@@ -490,7 +490,7 @@
flex-layout? (ctl/flex-layout? objects target-frame)
grid-layout? (ctl/grid-layout? objects target-frame)
drop-index (when flex-layout? (gslf/get-drop-index target-frame objects position))
cell-data (when grid-layout? (gslg/get-drop-cell target-frame objects position))]
cell-data (when (and grid-layout? (not mod?)) (gslg/get-drop-cell target-frame objects position))]
[move-vector target-frame drop-index cell-data])))
(rx/take-until stopper))]

View File

@@ -126,6 +126,9 @@
[id]
(l/derived #(contains? % id) selected-shapes))
(def highlighted-shapes
(l/derived :highlighted workspace-local))
(def export-in-progress?
(l/derived :export-in-progress? export))

View File

@@ -32,7 +32,7 @@
(mf/defc layer-item
{::mf/wrap-props false}
[{:keys [index item selected objects sortable? filtered? depth parent-size component-child?]}]
[{:keys [index item selected objects sortable? filtered? depth parent-size component-child? highlighted]}]
(let [id (:id item)
name (:name item)
blocked? (:blocked item)
@@ -50,6 +50,7 @@
expanded? (mf/deref expanded-iref)
selected? (contains? selected id)
highlighted? (contains? highlighted id)
container? (or (cph/frame-shape? item)
(cph/group-shape? item))
absolute? (ctl/layout-absolute? item)
@@ -341,6 +342,7 @@
[:div.element-list-body {:class (stl/css-case*
:selected selected?
:hover highlighted?
:icon-layer (= (:type item) :icon))
:on-click select-shape
:on-pointer-enter on-pointer-enter
@@ -395,6 +397,7 @@
[:& layer-item
{:item item
:selected selected
:highlighted highlighted
:index index
:objects objects
:key (dm/str id)

View File

@@ -43,6 +43,8 @@
[{:keys [objects filtered? parent-size] :as props}]
(let [selected (mf/deref refs/selected-shapes)
selected (hooks/use-equal-memo selected)
highlighted (mf/deref refs/highlighted-shapes)
highlighted (hooks/use-equal-memo highlighted)
root (get objects uuid/zero)
new-css-system (mf/use-ctx ctx/new-css-system)]
[:ul
@@ -54,6 +56,7 @@
[:& frame-wrapper
{:item obj
:selected selected
:highlighted highlighted
:index index
:objects objects
:key id
@@ -64,6 +67,7 @@
[:& layer-item
{:item obj
:selected selected
:highlighted highlighted
:index index
:objects objects
:key id

View File

@@ -82,7 +82,8 @@
shape-parent-frame (cph/get-frame objects (:frame-id first-selected-shape))
edit-grid? (ctl/grid-layout? objects edition)
selected-cell (dm/get-in grid-edition [edition :selected])
selected-cells (->> (dm/get-in grid-edition [edition :selected])
(map #(dm/get-in objects [edition :layout-grid-cells %])))
on-change-tab
(fn [options-mode]
@@ -105,10 +106,10 @@
[:& bool-options]
(cond
(some? selected-cell)
(d/not-empty? selected-cells)
[:& grid-cell/options
{:shape (get objects edition)
:cell (dm/get-in objects [edition :layout-grid-cells selected-cell])}]
:cells selected-cells}]
edit-grid?
[:& layout-container/grid-layout-edition
@@ -166,10 +167,10 @@
[:& align-options]
[:& bool-options]
(cond
(some? selected-cell)
(d/not-empty? selected-cells)
[:& grid-cell/options
{:shape (get objects edition)
:cell (dm/get-in objects [edition :layout-grid-cells selected-cell])}]
:cells selected-cells}]
edit-grid?
[:& layout-container/grid-layout-edition

View File

@@ -6,16 +6,31 @@
(ns app.main.ui.workspace.sidebar.options.menus.grid-cell
(:require
[app.common.attrs :as attrs]
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.types.shape.layout :as ctl]
[app.main.data.workspace :as dw]
[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*]]
[app.main.ui.hooks :as hooks]
[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]))
(def cell-props [:id
:position
:row
:row-span
:column
:column-span
:align-self
:justify-self
:area-name])
(mf/defc set-self-alignment
[{:keys [is-col? alignment set-alignment] :as props}]
(let [dir-v [:auto :start :center :end :stretch #_:baseline]
@@ -34,72 +49,91 @@
(mf/defc options
{::mf/wrap [mf/memo]}
[{:keys [shape cell] :as props}]
[{:keys [shape cell cells] :as props}]
(let [{:keys [mode area-name align-self justify-self column column-span row row-span]} cell
column-end (+ column column-span)
row-end (+ row row-span)
(let [cells (hooks/use-equal-memo cells)
cell (or cell (attrs/get-attrs-multi cells cell-props))
cell-mode (or mode :auto)
multiple? (= :multiple (:id cell))
cell-ids (if (some? cell) [(:id cell)] (->> cells (map :id)))
cell-ids (hooks/use-equal-memo cell-ids)
{:keys [position area-name align-self justify-self column column-span row row-span]} cell
column-end (when (and (d/num? column) (d/num? column-span))
(+ column column-span))
row-end (when (and (d/num? row) (d/num? row-span))
(+ row row-span))
cell-mode (or position :auto)
cell-mode (if (and (= :auto cell-mode)
(or (> (:column-span cell) 1)
(> (:row-span cell) 1)))
:manual
cell-mode)
valid-area-cells? (mf/use-memo
(mf/deps cells)
#(ctl/valid-area-cells? cells))
set-alignment
(mf/use-callback
(mf/deps align-self (:id shape) (:id cell))
(mf/deps align-self (:id shape) cell-ids)
(fn [value]
(if (= align-self value)
(st/emit! (dwsl/update-grid-cell (:id shape) (:id cell) {:align-self nil}))
(st/emit! (dwsl/update-grid-cell (:id shape) (:id cell) {:align-self value})))))
(st/emit! (dwsl/update-grid-cells (:id shape) cell-ids {:align-self nil}))
(st/emit! (dwsl/update-grid-cells (:id shape) cell-ids {:align-self value})))))
set-justify-self
(mf/use-callback
(mf/deps justify-self (:id shape) (:id cell))
(mf/deps justify-self (:id shape) cell-ids)
(fn [value]
(if (= justify-self value)
(st/emit! (dwsl/update-grid-cell (:id shape) (:id cell) {:justify-self nil}))
(st/emit! (dwsl/update-grid-cell (:id shape) (:id cell) {:justify-self value})))))
(st/emit! (dwsl/update-grid-cells (:id shape) cell-ids {:justify-self nil}))
(st/emit! (dwsl/update-grid-cells (:id shape) cell-ids {:justify-self value})))))
on-change
on-grid-coordinates
(mf/use-callback
(mf/deps column row (:id shape) (:id cell))
(fn [field type value]
(let [[property value]
(cond
(and (= type :column) (or (= field :all) (= field :start)))
[:column value]
(when-not multiple?
(let [[property value]
(cond
(and (= type :column) (or (= field :all) (= field :start)))
[:column value]
(and (= type :column) (= field :end))
[:column-span (max 1 (- value column))]
(and (= type :column) (= field :end))
[:column-span (max 1 (- value column))]
(and (= type :row) (or (= field :all) (= field :start)))
[:row value]
(and (= type :row) (or (= field :all) (= field :start)))
[:row value]
(and (= type :row) (= field :end))
[:row-span (max 1 (- value row))])]
(and (= type :row) (= field :end))
[:row-span (max 1 (- value row))])]
(st/emit! (dwsl/update-grid-cell-position (:id shape) (:id cell) {property value})))))
(st/emit! (dwsl/update-grid-cell-position (:id shape) (:id cell) {property value}))))))
on-area-name-change
(mf/use-callback
(mf/deps (:id shape) (:id cell))
(mf/deps (:id shape) cell-ids)
(fn [event]
(let [value (dom/get-value (dom/get-target event))]
(if (= value "")
(st/emit! (dwsl/update-grid-cell (:id shape) (:id cell) {:area-name nil}))
(st/emit! (dwsl/update-grid-cell (:id shape) (:id cell) {:area-name value}))))))
(st/emit! (dwsl/update-grid-cells (:id shape) cell-ids {:area-name nil}))
(st/emit! (dwsl/update-grid-cells (:id shape) cell-ids {:area-name value}))))))
set-cell-mode
(mf/use-callback
(mf/deps (:id shape) (:id cell))
(mf/deps (:id shape) cell-ids)
(fn [mode]
(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/change-cells-mode (:id shape) cell-ids mode))))
toggle-edit-mode
(mf/use-fn
(mf/deps (:id shape))
(fn []
(st/emit! (dw/start-edition-mode (:id shape))
(dwge/clear-selection (:id shape)))))]
[:div.element-set
[:div.element-set-title
@@ -112,15 +146,17 @@
[:button.position-btn
{:on-click #(set-cell-mode :auto)
:class (dom/classnames :active (= :auto cell-mode))} "Auto"]
[:button.position-btn
{:on-click #(set-cell-mode :manual)
:class (dom/classnames :active (= :manual cell-mode))} "Manual"]
(when-not multiple?
[:button.position-btn
{:on-click #(set-cell-mode :manual)
:class (dom/classnames :active (= :manual cell-mode))} "Manual"])
[:button.position-btn
{:on-click #(set-cell-mode :area)
:disabled (not valid-area-cells?)
:class (dom/classnames :active (= :area cell-mode))} "Area"]]]
[:div.manage-grid-columns
(when (= :auto cell-mode)
(when (and (not multiple?) (= :auto cell-mode))
[:div.grid-auto
[:div.grid-columns-auto
[:span.icon i/layout-rows]
@@ -128,7 +164,7 @@
[:> numeric-input*
{:placeholder "--"
:on-click #(dom/select-target %)
:on-change (partial on-change :all :column)
:on-change (partial on-grid-coordinates :all :column)
:value column}]]]
[:div.grid-rows-auto
[:span.icon i/layout-columns]
@@ -136,7 +172,7 @@
[:> numeric-input*
{:placeholder "--"
:on-click #(dom/select-target %)
:on-change (partial on-change :all :row)
:on-change (partial on-grid-coordinates :all :row)
:value row}]]]])
(when (= :area cell-mode)
@@ -151,7 +187,7 @@
:auto-complete "off"
:on-change on-area-name-change}]])
(when (or (= :manual cell-mode) (= :area cell-mode))
(when (and (not multiple?) (or (= :manual cell-mode) (= :area cell-mode)))
[:div.grid-manual
[:div.grid-columns-auto
[:span.icon i/layout-rows]
@@ -159,12 +195,12 @@
[:> numeric-input*
{:placeholder "--"
:on-pointer-down #(dom/select-target %)
:on-change (partial on-change :start :column)
:on-change (partial on-grid-coordinates :start :column)
:value column}]
[:> numeric-input*
{:placeholder "--"
:on-pointer-down #(dom/select-target %)
:on-change (partial on-change :end :column)
:on-change (partial on-grid-coordinates :end :column)
:value column-end}]]]
[:div.grid-rows-auto
[:span.icon i/layout-columns]
@@ -172,12 +208,12 @@
[:> numeric-input*
{:placeholder "--"
:on-pointer-down #(dom/select-target %)
:on-change (partial on-change :start :row)
:on-change (partial on-grid-coordinates :start :row)
:value row}]
[:> numeric-input*
{:placeholder "--"
:on-pointer-down #(dom/select-target %)
:on-change (partial on-change :end :row)
:on-change (partial on-grid-coordinates :end :row)
:value row-end}]]]])]
[:div.layout-row
@@ -191,4 +227,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]]]]]]))

View File

@@ -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/grid-track"
: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
@@ -1401,8 +1449,8 @@
(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})))))
(st/emit! (dwsl/update-layout ids {:layout-justify-content value}))
(st/emit! (dwsl/update-layout ids {:layout-align-content value})))))
;;Grid columns
column-grid-values (:layout-grid-columns values)
@@ -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)
@@ -1482,12 +1542,12 @@
[:div.layout-row
[:div.jusfiy-content-grid.row-title "Content"]
[:div.btn-wrapper.align-grid
[:div.btn-wrapper.align-grid-content
[:& justify-grid-row {:is-col? true
:justify-items grid-justify-content-column
:justify-items grid-justify-content-row
:set-justify set-content-grid}]
[:& justify-grid-row {:is-col? false
:justify-items grid-justify-content-row
:justify-items grid-justify-content-column
:set-justify set-content-grid}]]]
[:& grid-columns-row {:is-col? true
:expanded? @grid-columns-open?
@@ -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

View File

@@ -533,7 +533,7 @@
:nillable true
:value (:layout-item-z-index values)}]])]
(when is-layout-child?
(when (and is-layout-child? is-flex-parent?)
[:div {:class (stl/css :second-row)}
[:& align-self-row {:is-col? is-col?
:align-self align-self
@@ -667,7 +667,7 @@
:on-change-behaviour-v-refactor on-change-behaviour-v
:on-change on-change-behaviour}]]
(when is-layout-child?
(when (and is-layout-child? is-flex-parent?)
[:div.layout-row
[:div.row-title "Align"]
[:div.btn-wrapper

View File

@@ -27,7 +27,8 @@
;; --- Page Item
(mf/defc page-item [{:keys [page index deletable? selected? editing?] :as props}]
(mf/defc page-item
[{:keys [page index deletable? selected? editing? hovering?] :as props}]
(let [input-ref (mf/use-ref)
id (:id page)
new-css-system (mf/use-ctx ctx/new-css-system)
@@ -135,6 +136,7 @@
(css :selected) selected?)
(dom/classnames
:element-list-body true
:hover hovering?
:selected selected?))
:data-test (dm/str "page-" id)
:tab-index "0"

View File

@@ -176,7 +176,8 @@
}
}
}
&:hover {
&:hover,
&.hover {
.element-list-body {
color: var(--layer-row-foreground-color-hover);
background-color: var(--layer-row-background-color-hover);

View File

@@ -173,7 +173,7 @@
on-context-menu (actions/on-context-menu hover hover-ids workspace-read-only?)
on-double-click (actions/on-double-click hover hover-ids hover-top-frame-id drawing-path? base-objects edition drawing-tool z? workspace-read-only?)
on-drag-enter (actions/on-drag-enter)
on-drag-over (actions/on-drag-over)
on-drag-over (actions/on-drag-over move-stream)
on-drop (actions/on-drop file)
on-pointer-down (actions/on-pointer-down @hover selected edition drawing-tool text-editing? node-editing? grid-editing?
drawing-path? create-comment? space? panning z? workspace-read-only?)

View File

@@ -404,15 +404,17 @@
(dnd/has-type? e "text/asset-id"))
(dom/prevent-default e)))))
(defn on-drag-over []
(mf/use-callback
(fn [e]
(when (or (dnd/has-type? e "penpot/shape")
(dnd/has-type? e "penpot/component")
(dnd/has-type? e "Files")
(dnd/has-type? e "text/uri-list")
(dnd/has-type? e "text/asset-id"))
(dom/prevent-default e)))))
(defn on-drag-over [move-stream]
(let [on-pointer-move (on-pointer-move move-stream)]
(mf/use-callback
(fn [e]
(when (or (dnd/has-type? e "penpot/shape")
(dnd/has-type? e "penpot/component")
(dnd/has-type? e "Files")
(dnd/has-type? e "text/uri-list")
(dnd/has-type? e "text/asset-id"))
(on-pointer-move e)
(dom/prevent-default e))))))
(defn on-drop
[file]

View File

@@ -25,6 +25,7 @@
[app.main.ui.css-cursors :as cur]
[app.main.ui.formats :as fmt]
[app.main.ui.hooks :as hooks]
[app.main.ui.icons :as i]
[app.main.ui.workspace.viewport.viewport-ref :as uwvv]
[app.util.dom :as dom]
[app.util.keyboard :as kbd]
@@ -301,10 +302,10 @@
handle-pointer-down
(mf/use-callback
(mf/deps (:id shape) (:id cell) selected?)
(fn []
(if selected?
(st/emit! (dwge/remove-selection (:id shape)))
(st/emit! (dwge/select-grid-cell (:id shape) (:id cell))))))]
(fn [event]
(if (and (kbd/shift? event) selected?)
(st/emit! (dwge/remove-selection (:id shape) (:id cell)))
(st/emit! (dwge/select-grid-cell (:id shape) (:id cell) (kbd/shift? event)) ))))]
[:g.cell-editor
[:rect
@@ -434,19 +435,19 @@
[width height]
(if (= type :column)
[(max layout-gap-col (/ 16 zoom))
[(max 0 (- layout-gap-col (/ 10 zoom)) (/ 16 zoom))
(+ row-total-size row-total-gap)]
[(+ column-total-size column-total-gap)
(max layout-gap-row (/ 16 zoom))])
(max 0 (- layout-gap-row (/ 10 zoom)) (/ 16 zoom))])
start-p
(cond-> start-p
(and (= type :column) (= index 0))
(gpt/subtract (hv width))
(gpt/subtract (hv (/ width 2)))
(and (= type :row) (= index 0))
(gpt/subtract (vv height))
(gpt/subtract (vv (/ height 2)))
(and (= type :column) (not= index 0) (not last?))
(-> (gpt/subtract (hv (/ layout-gap-col 2)))
@@ -531,16 +532,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)
@@ -604,6 +606,24 @@
(when esc?
(dom/blur! (dom/get-target event))))))
handle-pointer-enter
(mf/use-callback
(mf/deps (:id shape) type index)
(fn []
(st/emit! (dwsl/hover-layout-track [(:id shape)] type index true))))
handle-pointer-leave
(mf/use-callback
(mf/deps (:id shape) type index)
(fn []
(st/emit! (dwsl/hover-layout-track [(:id shape)] type index false))))
handle-remove-track
(mf/use-callback
(mf/deps (:id shape) type index)
(fn []
(st/emit! (dwsl/remove-layout-track [(:id shape)] type index))))
track-list-prop (if (= type :column) :column-tracks :row-tracks)
[text-x text-y text-width text-height]
(if (= type :column)
@@ -618,18 +638,34 @@
(dom/set-value! (mf/ref-val track-input-ref) (format-size track-data))))
[:g.track
[:g {:transform (if (= type :column)
[:g {:on-pointer-enter handle-pointer-enter
:on-pointer-leave handle-pointer-leave
: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 "var(--color-distance)"
:opacity 0.2}])
[:foreignObject {:x text-x :y text-y :width text-width :height text-height}
[:input
{:ref track-input-ref
:class (css :grid-editor-label)
:type "text"
:default-value (format-size track-data)
:data-default-value (format-size track-data)
:on-key-down handle-keydown-track-input
:on-blur handle-blur-track-input}]]]
[:div {:class (css :grid-editor-wrapper)}
[:input
{:ref track-input-ref
:style {}
:class (css :grid-editor-label)
:type "text"
:default-value (format-size track-data)
:data-default-value (format-size track-data)
:on-key-down handle-keydown-track-input
:on-blur handle-blur-track-input}]
(when hovering?
[:button {:class (css :grid-editor-button)
:on-click handle-remove-track} i/trash])]]]
[:g {:transform (when (= type :row) (dm/fmt "rotate(-90 % %)" (:x marker-p) (:y marker-p)))}
[:& track-marker
@@ -684,6 +720,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)
@@ -733,6 +781,15 @@
[:g.grid-editor {:pointer-events (when view-only "none")
:on-pointer-down handle-pointer-down}
[:g.cells
(for [cell (ctl/get-cells shape {:sort? true})]
[:& grid-cell {:key (dm/str "cell-" (:id cell))
:shape base-shape
:layout-data layout-data
:cell cell
:zoom zoom
:hover? (contains? hover-cells (:id cell))
:selected? (contains? selected-cells (:id cell))}])]
(when-not view-only
[:*
[:& grid-editor-frame {:zoom zoom
@@ -759,7 +816,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 +854,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)
@@ -823,14 +881,4 @@
:type :row
:track-before (last row-tracks)
:snap-pixel? snap-pixel?
:zoom zoom}]]))])
[:g.cells
(for [cell (ctl/get-cells shape {:sort? true})]
[:& grid-cell {:key (dm/str "cell-" (:id cell))
:shape base-shape
:layout-data layout-data
:cell cell
:zoom zoom
:hover? (contains? hover-cells (:id cell))
:selected? (= selected-cells (:id cell))}])]]))
:zoom zoom}]]))])]))

View File

@@ -11,19 +11,27 @@
}
}
.grid-editor-label {
position: absolute;
background: none;
.grid-editor-wrapper {
width: 100%;
height: 100%;
text-align: center;
font-family: worksans;
height: 80%;
display: flex;
justify-content: center;
align-items: center;
}
.grid-editor-label {
background: none;
border-bottom: calc(1px / var(--zoom)) solid transparent;
border: 0;
color: var(--color-distance);
font-family: worksans;
font-size: calc(12px / var(--zoom));
font-weight: 600;
margin: 0;
max-width: calc(80px / var(--zoom));
padding: 0;
border: 0;
font-size: calc(12px / var(--zoom));
padding: 4px;
text-align: center;
&:focus {
outline: none;
@@ -31,6 +39,24 @@
}
}
.grid-editor-button {
background: none;
border: none;
cursor: pointer;
margin: 0;
padding: 0;
position: absolute;
right: calc(10px / var(--zoom));
width: calc(20px / var(--zoom));
height: calc(20px / var(--zoom));
svg {
width: calc(16px / var(--zoom));
height: auto;
fill: var(--color-distance);
}
}
.grid-frame {
fill: #f6f6f6;
stroke: var(--color-distance);

View File

@@ -153,12 +153,14 @@
([state label component-id file-id]
(let [page (current-page state)
libraries (wsh/get-libraries state)
objects (:objects page)
changes (-> (pcb/empty-changes nil (:id page))
(pcb/with-objects (:objects page)))
(pcb/with-objects objects))
[new-shape changes]
(dwlh/generate-instantiate-component changes
objects
file-id
component-id
(gpt/point 100 100)