mirror of
https://github.com/penpot/penpot.git
synced 2026-07-26 16:08:09 +00:00
Merge pull request #3396 from penpot/alotor-grid-layout
First grid layout version
This commit is contained in:
commit
6539b7da5b
@ -15,9 +15,8 @@
|
|||||||
(def conjv (fnil conj []))
|
(def conjv (fnil conj []))
|
||||||
|
|
||||||
(defn layout-bounds
|
(defn layout-bounds
|
||||||
[{:keys [layout-padding] :as shape} shape-bounds]
|
[parent shape-bounds]
|
||||||
(let [;; Add padding to the bounds
|
(let [[pad-top pad-right pad-bottom pad-left] (ctl/paddings parent)]
|
||||||
{pad-top :p1 pad-right :p2 pad-bottom :p3 pad-left :p4} layout-padding]
|
|
||||||
(gpo/pad-points shape-bounds pad-top pad-right pad-bottom pad-left)))
|
(gpo/pad-points shape-bounds pad-top pad-right pad-bottom pad-left)))
|
||||||
|
|
||||||
(defn init-layout-lines
|
(defn init-layout-lines
|
||||||
|
|||||||
@ -63,8 +63,7 @@
|
|||||||
{:height target-height
|
{:height target-height
|
||||||
:modifiers (ctm/resize-modifiers (gpt/point 1 fill-scale) child-origin transform transform-inverse)})))
|
:modifiers (ctm/resize-modifiers (gpt/point 1 fill-scale) child-origin transform transform-inverse)})))
|
||||||
|
|
||||||
(defn layout-child-modifiers
|
(defn fill-modifiers
|
||||||
"Calculates the modifiers for the layout"
|
|
||||||
[parent parent-bounds child child-bounds layout-line]
|
[parent parent-bounds child child-bounds layout-line]
|
||||||
(let [child-origin (gpo/origin child-bounds)
|
(let [child-origin (gpo/origin child-bounds)
|
||||||
child-width (gpo/width-points child-bounds)
|
child-width (gpo/width-points child-bounds)
|
||||||
@ -83,15 +82,27 @@
|
|||||||
(calc-fill-height-data parent transform transform-inverse child child-origin child-height layout-line))
|
(calc-fill-height-data parent transform transform-inverse child child-origin child-height layout-line))
|
||||||
|
|
||||||
child-width (or (:width fill-width) child-width)
|
child-width (or (:width fill-width) child-width)
|
||||||
child-height (or (:height fill-height) child-height)
|
child-height (or (:height fill-height) child-height)]
|
||||||
|
|
||||||
|
[child-width
|
||||||
|
child-height
|
||||||
|
(-> (ctm/empty)
|
||||||
|
(cond-> fill-width (ctm/add-modifiers (:modifiers fill-width)))
|
||||||
|
(cond-> fill-height (ctm/add-modifiers (:modifiers fill-height))))]))
|
||||||
|
|
||||||
|
(defn layout-child-modifiers
|
||||||
|
"Calculates the modifiers for the layout"
|
||||||
|
[parent parent-bounds child child-bounds layout-line]
|
||||||
|
(let [child-origin (gpo/origin child-bounds)
|
||||||
|
|
||||||
|
[child-width child-height fill-modifiers]
|
||||||
|
(fill-modifiers parent parent-bounds child child-bounds layout-line)
|
||||||
|
|
||||||
[corner-p layout-line] (fpo/get-child-position parent child child-width child-height layout-line)
|
[corner-p layout-line] (fpo/get-child-position parent child child-width child-height layout-line)
|
||||||
|
|
||||||
move-vec (gpt/to-vec child-origin corner-p)
|
move-vec (gpt/to-vec child-origin corner-p)
|
||||||
|
|
||||||
modifiers
|
modifiers
|
||||||
(-> (ctm/empty)
|
(-> (ctm/empty)
|
||||||
(cond-> fill-width (ctm/add-modifiers (:modifiers fill-width)))
|
(ctm/add-modifiers fill-modifiers)
|
||||||
(cond-> fill-height (ctm/add-modifiers (:modifiers fill-height)))
|
|
||||||
(ctm/move move-vec))]
|
(ctm/move move-vec))]
|
||||||
[modifiers layout-line]))
|
[modifiers layout-line]))
|
||||||
|
|||||||
@ -7,13 +7,15 @@
|
|||||||
(ns app.common.geom.shapes.grid-layout
|
(ns app.common.geom.shapes.grid-layout
|
||||||
(:require
|
(:require
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
|
[app.common.geom.shapes.grid-layout.bounds :as glpb]
|
||||||
[app.common.geom.shapes.grid-layout.layout-data :as glld]
|
[app.common.geom.shapes.grid-layout.layout-data :as glld]
|
||||||
[app.common.geom.shapes.grid-layout.positions :as glp]))
|
[app.common.geom.shapes.grid-layout.positions :as glp]))
|
||||||
|
|
||||||
(dm/export glld/calc-layout-data)
|
(dm/export glld/calc-layout-data)
|
||||||
(dm/export glld/get-cell-data)
|
(dm/export glld/get-cell-data)
|
||||||
(dm/export glp/child-modifiers)
|
(dm/export glp/child-modifiers)
|
||||||
|
(dm/export glp/get-position-grid-coord)
|
||||||
(defn get-drop-index
|
(dm/export glp/get-drop-cell)
|
||||||
[frame objects _position]
|
(dm/export glp/cell-bounds)
|
||||||
(dec (count (get-in objects [frame :shapes]))))
|
(dm/export glpb/layout-content-points)
|
||||||
|
(dm/export glpb/layout-content-bounds)
|
||||||
|
|||||||
95
common/src/app/common/geom/shapes/grid_layout/areas.cljc
Normal file
95
common/src/app/common/geom/shapes/grid_layout/areas.cljc
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
;; 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
|
||||||
|
|
||||||
|
;; Based on the code in:
|
||||||
|
;; https://en.wikibooks.org/wiki/Algorithm_Implementation/Geometry/Rectangle_difference
|
||||||
|
(ns app.common.geom.shapes.grid-layout.areas
|
||||||
|
(:refer-clojure :exclude [contains?]))
|
||||||
|
|
||||||
|
(defn area->cell-props [[column row column-span row-span]]
|
||||||
|
{:row row
|
||||||
|
:column column
|
||||||
|
:row-span row-span
|
||||||
|
:column-span column-span})
|
||||||
|
|
||||||
|
(defn make-area
|
||||||
|
([{:keys [column row column-span row-span]}]
|
||||||
|
(make-area column row column-span row-span))
|
||||||
|
([x y width height]
|
||||||
|
[x y width height]))
|
||||||
|
|
||||||
|
(defn contains?
|
||||||
|
[[a-x a-y a-width a-height :as a]
|
||||||
|
[b-x b-y b-width b-height :as b]]
|
||||||
|
(and (>= b-x a-x)
|
||||||
|
(>= b-y a-y)
|
||||||
|
(<= (+ b-x b-width) (+ a-x a-width))
|
||||||
|
(<= (+ b-y b-height) (+ a-y a-height))))
|
||||||
|
|
||||||
|
(defn intersects?
|
||||||
|
[[a-x a-y a-width a-height ]
|
||||||
|
[b-x b-y b-width b-height]]
|
||||||
|
(not (or (<= (+ b-x b-width) a-x)
|
||||||
|
(<= (+ b-y b-height) a-y)
|
||||||
|
(>= b-x (+ a-x a-width))
|
||||||
|
(>= b-y (+ a-y a-height)))))
|
||||||
|
|
||||||
|
(defn top-rect
|
||||||
|
[[a-x a-y a-width _]
|
||||||
|
[_ b-y _ _]]
|
||||||
|
(let [height (- b-y a-y)]
|
||||||
|
(when (> height 0)
|
||||||
|
(make-area a-x a-y a-width height))))
|
||||||
|
|
||||||
|
(defn bottom-rect
|
||||||
|
[[a-x a-y a-width a-height]
|
||||||
|
[_ b-y _ b-height]]
|
||||||
|
|
||||||
|
(let [y (+ b-y b-height)
|
||||||
|
height (- a-height (- y a-y))]
|
||||||
|
(when (and (> height 0) (< y (+ a-y a-height)))
|
||||||
|
(make-area a-x y a-width height))))
|
||||||
|
|
||||||
|
(defn left-rect
|
||||||
|
[[a-x a-y _ a-height]
|
||||||
|
[b-x b-y _ b-height]]
|
||||||
|
|
||||||
|
(let [rb-y (+ b-y b-height)
|
||||||
|
ra-y (+ a-y a-height)
|
||||||
|
y1 (max a-y b-y)
|
||||||
|
y2 (min ra-y rb-y)
|
||||||
|
height (- y2 y1)
|
||||||
|
width (- b-x a-x)]
|
||||||
|
(when (and (> width 0) (> height 0))
|
||||||
|
(make-area a-x y1 width height))))
|
||||||
|
|
||||||
|
(defn right-rect
|
||||||
|
[[a-x a-y a-width a-height]
|
||||||
|
[b-x b-y b-width b-height]]
|
||||||
|
|
||||||
|
(let [rb-y (+ b-y b-height)
|
||||||
|
ra-y (+ a-y a-height)
|
||||||
|
y1 (max a-y b-y)
|
||||||
|
y2 (min ra-y rb-y)
|
||||||
|
height (- y2 y1)
|
||||||
|
rb-x (+ b-x b-width)
|
||||||
|
width (- a-width (- rb-x a-x))
|
||||||
|
]
|
||||||
|
(when (and (> width 0) (> height 0))
|
||||||
|
(make-area rb-x y1 width height)))
|
||||||
|
)
|
||||||
|
|
||||||
|
(defn difference
|
||||||
|
[area-a area-b]
|
||||||
|
(if (or (nil? area-b)
|
||||||
|
(not (intersects? area-a area-b))
|
||||||
|
(contains? area-b area-a))
|
||||||
|
[]
|
||||||
|
|
||||||
|
(into []
|
||||||
|
(keep #(% area-a area-b))
|
||||||
|
[top-rect left-rect right-rect bottom-rect])))
|
||||||
|
|
||||||
53
common/src/app/common/geom/shapes/grid_layout/bounds.cljc
Normal file
53
common/src/app/common/geom/shapes/grid_layout/bounds.cljc
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
;; 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.common.geom.shapes.grid-layout.bounds
|
||||||
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.common.geom.point :as gpt]
|
||||||
|
[app.common.geom.shapes.grid-layout.layout-data :as ld]
|
||||||
|
[app.common.geom.shapes.points :as gpo]))
|
||||||
|
|
||||||
|
(defn layout-content-points
|
||||||
|
[bounds parent children]
|
||||||
|
(let [parent-id (:id parent)
|
||||||
|
parent-bounds @(get bounds parent-id)
|
||||||
|
|
||||||
|
hv #(gpo/start-hv parent-bounds %)
|
||||||
|
vv #(gpo/start-vv parent-bounds %)
|
||||||
|
|
||||||
|
children (->> children
|
||||||
|
(map #(vector @(get bounds (:id %)) %)))
|
||||||
|
|
||||||
|
{:keys [row-tracks column-tracks]} (ld/calc-layout-data parent children parent-bounds)]
|
||||||
|
(d/concat-vec
|
||||||
|
(->> row-tracks
|
||||||
|
(mapcat #(vector (:start-p %)
|
||||||
|
(gpt/add (:start-p %) (vv (:size %))))))
|
||||||
|
(->> column-tracks
|
||||||
|
(mapcat #(vector (:start-p %)
|
||||||
|
(gpt/add (:start-p %) (hv (:size %)))))))))
|
||||||
|
|
||||||
|
(defn layout-content-bounds
|
||||||
|
[bounds {:keys [layout-padding] :as parent} children]
|
||||||
|
|
||||||
|
(let [parent-id (:id parent)
|
||||||
|
parent-bounds @(get bounds parent-id)
|
||||||
|
|
||||||
|
{pad-top :p1 pad-right :p2 pad-bottom :p3 pad-left :p4} layout-padding
|
||||||
|
pad-top (or pad-top 0)
|
||||||
|
pad-right (or pad-right 0)
|
||||||
|
pad-bottom (or pad-bottom 0)
|
||||||
|
pad-left (or pad-left 0)
|
||||||
|
|
||||||
|
layout-points (layout-content-points bounds parent children)]
|
||||||
|
|
||||||
|
(if (d/not-empty? layout-points)
|
||||||
|
(-> layout-points
|
||||||
|
(gpo/merge-parent-coords-bounds parent-bounds)
|
||||||
|
(gpo/pad-points (- pad-top) (- pad-right) (- pad-bottom) (- pad-left)))
|
||||||
|
;; Cannot create some bounds from the children so we return the parent's
|
||||||
|
parent-bounds)))
|
||||||
@ -4,137 +4,581 @@
|
|||||||
;;
|
;;
|
||||||
;; Copyright (c) KALEIDOS INC
|
;; Copyright (c) KALEIDOS INC
|
||||||
|
|
||||||
|
;; Each track has specified minimum and maximum sizing functions (which may be the same)
|
||||||
|
;; - Fixed
|
||||||
|
;; - Percent
|
||||||
|
;; - Auto
|
||||||
|
;; - Flex
|
||||||
|
;;
|
||||||
|
;; Min functions:
|
||||||
|
;; - Fixed: value
|
||||||
|
;; - Percent: value to pixels
|
||||||
|
;; - Auto: auto
|
||||||
|
;; - Flex: auto
|
||||||
|
;;
|
||||||
|
;; Max functions:
|
||||||
|
;; - Fixed: value
|
||||||
|
;; - Percent: value to pixels
|
||||||
|
;; - Auto: max-content
|
||||||
|
;; - Flex: flex
|
||||||
|
|
||||||
|
;; Algorithm
|
||||||
|
;; - Initialize tracks:
|
||||||
|
;; * base = size or 0
|
||||||
|
;; * max = size or INF
|
||||||
|
;;
|
||||||
|
;; - Resolve intrinsic sizing
|
||||||
|
;; 1. Shim baseline-aligned items so their intrinsic size contributions reflect their baseline alignment
|
||||||
|
;;
|
||||||
|
;; 2. Size tracks to fit non-spanning items
|
||||||
|
;; base-size = max (children min contribution) floored 0
|
||||||
|
;;
|
||||||
|
;; 3. Increase sizes to accommodate spanning items crossing content-sized tracks
|
||||||
|
;;
|
||||||
|
;; 4. Increase sizes to accommodate spanning items crossing flexible tracks:
|
||||||
|
;;
|
||||||
|
;; 5. If any track still has an infinite growth limit set its growth limit to its base size.
|
||||||
|
|
||||||
|
;; - Distribute extra space accross spaned tracks
|
||||||
|
;; - Maximize tracks
|
||||||
|
;;
|
||||||
|
;; - Expand flexible tracks
|
||||||
|
;; - Find `fr` size
|
||||||
|
;;
|
||||||
|
;; - Stretch auto tracks
|
||||||
|
|
||||||
(ns app.common.geom.shapes.grid-layout.layout-data
|
(ns app.common.geom.shapes.grid-layout.layout-data
|
||||||
(:require
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.geom.shapes.points :as gpo]))
|
[app.common.geom.shapes.points :as gpo]
|
||||||
|
[app.common.math :as mth]
|
||||||
|
[app.common.types.shape.layout :as ctl]))
|
||||||
|
|
||||||
#_(defn set-sample-data
|
(defn layout-bounds
|
||||||
[parent children]
|
[parent shape-bounds]
|
||||||
|
(let [[pad-top pad-right pad-bottom pad-left] (ctl/paddings parent)]
|
||||||
|
(gpo/pad-points shape-bounds pad-top pad-right pad-bottom pad-left)))
|
||||||
|
|
||||||
(let [parent (assoc parent
|
(defn child-min-width
|
||||||
:layout-grid-columns
|
[child bounds]
|
||||||
[{:type :percent :value 25}
|
(if (ctl/fill-width? child)
|
||||||
{:type :percent :value 25}
|
(ctl/child-min-width child)
|
||||||
{:type :fixed :value 100}
|
(gpo/width-points bounds)))
|
||||||
;;{:type :auto}
|
|
||||||
;;{:type :flex :value 1}
|
|
||||||
]
|
|
||||||
|
|
||||||
:layout-grid-rows
|
(defn child-min-height
|
||||||
[{:type :percent :value 50}
|
[child bounds]
|
||||||
{:type :percent :value 50}
|
(if (ctl/fill-height? child)
|
||||||
;;{:type :fixed :value 100}
|
(ctl/child-min-height child)
|
||||||
;;{:type :auto}
|
(gpo/height-points bounds)))
|
||||||
;;{:type :flex :value 1}
|
|
||||||
])
|
|
||||||
|
|
||||||
num-rows (count (:layout-grid-rows parent))
|
(defn calculate-initial-track-size
|
||||||
num-columns (count (:layout-grid-columns parent))
|
[total-value {:keys [type value] :as track}]
|
||||||
|
|
||||||
layout-grid-cells
|
(let [[size max-size]
|
||||||
(into
|
(case type
|
||||||
{}
|
:percent
|
||||||
(for [[row-idx _row] (d/enumerate (:layout-grid-rows parent))
|
(let [value (/ (* total-value value) 100) ]
|
||||||
[col-idx _col] (d/enumerate (:layout-grid-columns parent))]
|
[value value])
|
||||||
(let [[_bounds 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])))
|
|
||||||
|
|
||||||
parent (assoc parent :layout-grid-cells layout-grid-cells)]
|
:fixed
|
||||||
|
[value value]
|
||||||
|
|
||||||
[parent children]))
|
;; flex, auto
|
||||||
|
[0.01 ##Inf])]
|
||||||
|
(assoc track :size size :max-size max-size)))
|
||||||
|
|
||||||
(defn calculate-initial-track-values
|
(defn set-auto-base-size
|
||||||
[{:keys [type value]} total-value]
|
[track-list children shape-cells type]
|
||||||
|
|
||||||
(case type
|
(let [[prop prop-span size-fn]
|
||||||
:percent
|
(if (= type :column)
|
||||||
(let [value (/ (* total-value value) 100) ]
|
[:column :column-span child-min-width]
|
||||||
value)
|
[:row :row-span child-min-height])]
|
||||||
|
|
||||||
:fixed
|
(reduce (fn [tracks [child-bounds child-shape]]
|
||||||
value
|
(let [cell (get shape-cells (:id child-shape))
|
||||||
|
idx (dec (get cell prop))
|
||||||
|
track (get tracks idx)]
|
||||||
|
(cond-> tracks
|
||||||
|
(and (= (get cell prop-span) 1)
|
||||||
|
(contains? #{:flex :auto} (:type track)))
|
||||||
|
(update-in [idx :size] max (size-fn child-shape child-bounds)))))
|
||||||
|
track-list
|
||||||
|
children)))
|
||||||
|
|
||||||
:auto
|
(defn tracks-total-size
|
||||||
0
|
[track-list]
|
||||||
))
|
(let [calc-tracks-total-size
|
||||||
|
(fn [acc {:keys [size]}]
|
||||||
|
(+ acc size))]
|
||||||
|
(->> track-list (reduce calc-tracks-total-size 0))))
|
||||||
|
|
||||||
|
(defn tracks-total-frs
|
||||||
|
[track-list]
|
||||||
|
(let [calc-tracks-total-frs
|
||||||
|
(fn [acc {:keys [type value]}]
|
||||||
|
(let [value (max 1 value)]
|
||||||
|
(cond-> acc
|
||||||
|
(= type :flex)
|
||||||
|
(+ value))))]
|
||||||
|
(->> track-list (reduce calc-tracks-total-frs 0))))
|
||||||
|
|
||||||
|
(defn tracks-total-autos
|
||||||
|
[track-list]
|
||||||
|
(let [calc-tracks-total-autos
|
||||||
|
(fn [acc {:keys [type]}]
|
||||||
|
(cond-> acc (= type :auto) (inc)))]
|
||||||
|
(->> track-list (reduce calc-tracks-total-autos 0))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn set-fr-value
|
||||||
|
"Tries to assign the fr value distributing the excess between the free spaces"
|
||||||
|
[track-list fr-value auto?]
|
||||||
|
|
||||||
|
(let [flex? #(= :flex (:type (second %)))
|
||||||
|
|
||||||
|
;; Fixes the assignments so they respect the min size constraint
|
||||||
|
;; returns pending with the necessary space to allocate and free-frs
|
||||||
|
;; are the addition of the fr tracks with free space
|
||||||
|
assign-fn
|
||||||
|
(fn [[assign-fr pending free-frs] [idx t]]
|
||||||
|
(let [fr (:value t)
|
||||||
|
current (get assign-fr idx (* fr-value fr))
|
||||||
|
full? (<= current (:size t))
|
||||||
|
cur-pending (if full? (- (:size t) current) 0)]
|
||||||
|
[(assoc assign-fr idx (if full? (:size t) current))
|
||||||
|
(+ pending cur-pending)
|
||||||
|
(cond-> free-frs (not full?) (+ fr))]))
|
||||||
|
|
||||||
|
;; Sets the assigned-fr map removing the pending/free-frs
|
||||||
|
change-fn
|
||||||
|
(fn [delta]
|
||||||
|
(fn [assign-fr [idx t]]
|
||||||
|
(let [fr (:value t)
|
||||||
|
current (get assign-fr idx)
|
||||||
|
full? (<= current (:size t))]
|
||||||
|
(cond-> assign-fr
|
||||||
|
(not full?)
|
||||||
|
(update idx - (* delta fr))))))
|
||||||
|
|
||||||
|
assign-fr
|
||||||
|
(loop [assign-fr {}]
|
||||||
|
(let [[assign-fr pending free-frs]
|
||||||
|
(->> (d/enumerate track-list)
|
||||||
|
(filter flex?)
|
||||||
|
(reduce assign-fn [assign-fr 0 0]))]
|
||||||
|
|
||||||
|
;; When auto, we don't need to remove the excess
|
||||||
|
(if (or auto?
|
||||||
|
(= free-frs 0)
|
||||||
|
(mth/almost-zero? pending))
|
||||||
|
assign-fr
|
||||||
|
|
||||||
|
(let [delta (/ pending free-frs)
|
||||||
|
assign-fr
|
||||||
|
(->> (d/enumerate track-list)
|
||||||
|
(filter flex?)
|
||||||
|
(reduce (change-fn delta) assign-fr))]
|
||||||
|
|
||||||
|
(recur assign-fr)))))
|
||||||
|
|
||||||
|
;; Apply assign-fr to the track-list
|
||||||
|
track-list
|
||||||
|
(reduce
|
||||||
|
(fn [track-list [idx assignment] ]
|
||||||
|
(-> track-list
|
||||||
|
(update-in [idx :size] max assignment)))
|
||||||
|
track-list
|
||||||
|
assign-fr)]
|
||||||
|
|
||||||
|
track-list))
|
||||||
|
|
||||||
|
(defn add-auto-size
|
||||||
|
[track-list add-size]
|
||||||
|
(->> track-list
|
||||||
|
(mapv (fn [{:keys [type size max-size] :as track}]
|
||||||
|
(cond-> track
|
||||||
|
(= :auto type)
|
||||||
|
(assoc :size (min (+ size add-size) max-size)))))))
|
||||||
|
|
||||||
|
(defn has-flex-track?
|
||||||
|
[type track-list cell]
|
||||||
|
(let [[prop prop-span]
|
||||||
|
(if (= type :column)
|
||||||
|
[:column :column-span]
|
||||||
|
[:row :row-span])
|
||||||
|
from-idx (dec (get cell prop))
|
||||||
|
to-idx (+ (dec (get cell prop)) (get cell prop-span))
|
||||||
|
tracks (subvec track-list from-idx to-idx)]
|
||||||
|
(some? (->> tracks (d/seek #(= :flex (:type %)))))))
|
||||||
|
|
||||||
|
(defn size-to-allocate
|
||||||
|
[type parent [child-bounds child] cell]
|
||||||
|
(let [[row-gap column-gap] (ctl/gaps parent)
|
||||||
|
[sfn gap prop-span]
|
||||||
|
(if (= type :column)
|
||||||
|
[child-min-width column-gap :column-span]
|
||||||
|
[child-min-height row-gap :row-span])
|
||||||
|
span (get cell prop-span)]
|
||||||
|
(- (sfn child child-bounds) (* gap (dec span)))))
|
||||||
|
|
||||||
|
(defn allocate-auto-tracks
|
||||||
|
[allocations indexed-tracks to-allocate]
|
||||||
|
(if (empty? indexed-tracks)
|
||||||
|
allocations
|
||||||
|
(let [[idx track] (first indexed-tracks)
|
||||||
|
old-allocated (get allocations idx 0.01)
|
||||||
|
auto-track? (= :auto (:type track))
|
||||||
|
|
||||||
|
allocated (if auto-track?
|
||||||
|
(max old-allocated
|
||||||
|
(/ to-allocate (count indexed-tracks))
|
||||||
|
(:size track))
|
||||||
|
(:size track))]
|
||||||
|
(recur (cond-> allocations
|
||||||
|
auto-track?
|
||||||
|
(assoc idx allocated))
|
||||||
|
(rest indexed-tracks)
|
||||||
|
(- to-allocate allocated)))))
|
||||||
|
|
||||||
|
(defn allocate-flex-tracks
|
||||||
|
[allocations indexed-tracks to-allocate fr-value]
|
||||||
|
(if (empty? indexed-tracks)
|
||||||
|
allocations
|
||||||
|
(let [[idx track] (first indexed-tracks)
|
||||||
|
old-allocated (get allocations idx 0.01)
|
||||||
|
|
||||||
|
auto-track? (= :auto (:type track))
|
||||||
|
flex-track? (= :flex (:type track))
|
||||||
|
|
||||||
|
fr (if flex-track? (:value track) 0)
|
||||||
|
|
||||||
|
target-allocation (* fr-value fr)
|
||||||
|
|
||||||
|
allocated (if (or auto-track? flex-track?)
|
||||||
|
(max target-allocation
|
||||||
|
old-allocated
|
||||||
|
(:size track))
|
||||||
|
(:size track))]
|
||||||
|
(recur (cond-> allocations (or flex-track? auto-track?)
|
||||||
|
(assoc idx allocated))
|
||||||
|
(rest indexed-tracks)
|
||||||
|
(- to-allocate allocated)
|
||||||
|
fr-value))))
|
||||||
|
|
||||||
|
(defn set-auto-multi-span
|
||||||
|
[parent track-list children-map shape-cells type]
|
||||||
|
|
||||||
|
(let [[prop prop-span]
|
||||||
|
(if (= type :column)
|
||||||
|
[:column :column-span]
|
||||||
|
[:row :row-span])
|
||||||
|
|
||||||
|
;; First calculate allocation without applying so we can modify them on the following tracks
|
||||||
|
allocated
|
||||||
|
(->> shape-cells
|
||||||
|
(vals)
|
||||||
|
(filter #(> (get % prop-span) 1))
|
||||||
|
(remove #(has-flex-track? type track-list %))
|
||||||
|
(sort-by prop-span -)
|
||||||
|
(reduce
|
||||||
|
(fn [allocated cell]
|
||||||
|
(let [shape-id (first (:shapes cell))
|
||||||
|
|
||||||
|
from-idx (dec (get cell prop))
|
||||||
|
to-idx (+ (dec (get cell prop)) (get cell prop-span))
|
||||||
|
|
||||||
|
indexed-tracks (subvec (d/enumerate track-list) from-idx to-idx)
|
||||||
|
to-allocate (size-to-allocate type parent (get children-map shape-id) cell)
|
||||||
|
|
||||||
|
;; Remove the size and the tracks that are not allocated
|
||||||
|
[to-allocate indexed-tracks]
|
||||||
|
(->> indexed-tracks
|
||||||
|
(reduce (fn find-auto-allocations
|
||||||
|
[[to-allocate result] [_ track :as idx-track]]
|
||||||
|
(if (= :auto (:type track))
|
||||||
|
;; If auto, we don't change allocate and add the track
|
||||||
|
[to-allocate (conj result idx-track)]
|
||||||
|
;; If fixed, we remove from allocate and don't add the track
|
||||||
|
[(- to-allocate (:size track)) result]))
|
||||||
|
[to-allocate []]))]
|
||||||
|
(allocate-auto-tracks allocated indexed-tracks (max to-allocate 0))))
|
||||||
|
{}))
|
||||||
|
|
||||||
|
;; Apply the allocations to the tracks
|
||||||
|
track-list
|
||||||
|
(into []
|
||||||
|
(map-indexed #(update %2 :size max (get allocated %1)))
|
||||||
|
track-list)]
|
||||||
|
track-list))
|
||||||
|
|
||||||
|
(defn set-flex-multi-span
|
||||||
|
[parent track-list children-map shape-cells type]
|
||||||
|
|
||||||
|
(let [[prop prop-span]
|
||||||
|
(if (= type :column)
|
||||||
|
[:column :column-span]
|
||||||
|
[:row :row-span])
|
||||||
|
|
||||||
|
;; First calculate allocation without applying so we can modify them on the following tracks
|
||||||
|
allocate-fr-tracks
|
||||||
|
(->> shape-cells
|
||||||
|
(vals)
|
||||||
|
(filter #(> (get % prop-span) 1))
|
||||||
|
(filter #(has-flex-track? type track-list %))
|
||||||
|
(sort-by prop-span -)
|
||||||
|
(reduce
|
||||||
|
(fn [alloc cell]
|
||||||
|
(let [shape-id (first (:shapes cell))
|
||||||
|
from-idx (dec (get cell prop))
|
||||||
|
to-idx (+ (dec (get cell prop)) (get cell prop-span))
|
||||||
|
indexed-tracks (subvec (d/enumerate track-list) from-idx to-idx)
|
||||||
|
|
||||||
|
to-allocate (size-to-allocate type parent (get children-map shape-id) cell)
|
||||||
|
|
||||||
|
;; Remove the size and the tracks that are not allocated
|
||||||
|
[to-allocate total-frs indexed-tracks]
|
||||||
|
(->> indexed-tracks
|
||||||
|
(reduce (fn find-lex-allocations
|
||||||
|
[[to-allocate total-fr result] [_ track :as idx-track]]
|
||||||
|
(if (= :flex (:type track))
|
||||||
|
;; If flex, we don't change allocate and add the track
|
||||||
|
[to-allocate (+ total-fr (:value track)) (conj result idx-track)]
|
||||||
|
|
||||||
|
;; If fixed or auto, we remove from allocate and don't add the track
|
||||||
|
[(- to-allocate (:size track)) total-fr result]))
|
||||||
|
[to-allocate 0 []]))
|
||||||
|
|
||||||
|
to-allocate (max to-allocate 0)
|
||||||
|
fr-value (/ to-allocate total-frs)]
|
||||||
|
(allocate-flex-tracks alloc indexed-tracks to-allocate fr-value)))
|
||||||
|
{}))
|
||||||
|
|
||||||
|
;; Apply the allocations to the tracks
|
||||||
|
track-list
|
||||||
|
(into []
|
||||||
|
(map-indexed #(update %2 :size max (get allocate-fr-tracks %1)))
|
||||||
|
track-list)]
|
||||||
|
track-list))
|
||||||
|
|
||||||
|
(defn min-fr-value
|
||||||
|
[tracks]
|
||||||
|
(loop [tracks (seq tracks)
|
||||||
|
min-fr 0.01]
|
||||||
|
(if (empty? tracks)
|
||||||
|
min-fr
|
||||||
|
(let [{:keys [size type value]} (first tracks)
|
||||||
|
min-fr (if (= type :flex) (max min-fr (/ size value)) min-fr)]
|
||||||
|
(recur (rest tracks) min-fr)))))
|
||||||
|
|
||||||
(defn calc-layout-data
|
(defn calc-layout-data
|
||||||
[parent _children transformed-parent-bounds]
|
[parent children transformed-parent-bounds]
|
||||||
|
|
||||||
(let [height (gpo/height-points transformed-parent-bounds)
|
(let [hv #(gpo/start-hv transformed-parent-bounds %)
|
||||||
width (gpo/width-points transformed-parent-bounds)
|
vv #(gpo/start-vv transformed-parent-bounds %)
|
||||||
|
|
||||||
;; Initialize tracks
|
layout-bounds (layout-bounds parent transformed-parent-bounds)
|
||||||
column-tracks
|
|
||||||
(->> (:layout-grid-columns parent)
|
|
||||||
(map (fn [track]
|
|
||||||
(let [initial (calculate-initial-track-values track width)]
|
|
||||||
(assoc track :value initial)))))
|
|
||||||
|
|
||||||
row-tracks
|
bound-height (gpo/height-points layout-bounds)
|
||||||
(->> (:layout-grid-rows parent)
|
bound-width (gpo/width-points layout-bounds)
|
||||||
(map (fn [track]
|
bound-corner (gpo/origin layout-bounds)
|
||||||
(let [initial (calculate-initial-track-values track height)]
|
|
||||||
(assoc track :value initial)))))
|
|
||||||
|
|
||||||
;; Go through cells to adjust auto sizes
|
[row-gap column-gap] (ctl/gaps parent)
|
||||||
|
auto-height? (ctl/auto-height? parent)
|
||||||
|
auto-width? (ctl/auto-width? parent)
|
||||||
|
|
||||||
|
{:keys [layout-grid-columns layout-grid-rows layout-grid-cells]} parent
|
||||||
|
num-columns (count layout-grid-columns)
|
||||||
|
num-rows (count layout-grid-rows)
|
||||||
|
|
||||||
;; Once auto sizes have been calculated we get calculate the `fr` with the remainining size and adjust the size
|
column-total-gap (* column-gap (dec num-columns))
|
||||||
|
row-total-gap (* row-gap (dec num-rows))
|
||||||
|
|
||||||
;; Adjust final distances
|
|
||||||
|
|
||||||
acc-track-distance
|
|
||||||
(fn [[result next-distance] data]
|
|
||||||
(let [result (conj result (assoc data :distance next-distance))
|
|
||||||
next-distance (+ next-distance (:value data))]
|
|
||||||
[result next-distance]))
|
|
||||||
|
|
||||||
column-tracks
|
|
||||||
(->> column-tracks
|
|
||||||
(reduce acc-track-distance [[] 0])
|
|
||||||
first)
|
|
||||||
|
|
||||||
row-tracks
|
|
||||||
(->> row-tracks
|
|
||||||
(reduce acc-track-distance [[] 0])
|
|
||||||
first)
|
|
||||||
|
|
||||||
|
;; Map shape->cell
|
||||||
shape-cells
|
shape-cells
|
||||||
(into {}
|
(into {}
|
||||||
(mapcat (fn [[_ cell]]
|
(mapcat (fn [[_ cell]]
|
||||||
(->> (:shapes cell)
|
(->> (:shapes cell) (map #(vector % cell)))))
|
||||||
(map #(vector % cell)))))
|
layout-grid-cells)
|
||||||
(:layout-grid-cells parent))
|
|
||||||
]
|
|
||||||
|
|
||||||
{:row-tracks row-tracks
|
children (->> children (remove #(ctl/layout-absolute? (second %))))
|
||||||
|
children-map
|
||||||
|
(into {}
|
||||||
|
(map #(vector (:id (second %)) %))
|
||||||
|
children)
|
||||||
|
|
||||||
|
;; Initialize tracks
|
||||||
|
column-tracks
|
||||||
|
(->> layout-grid-columns
|
||||||
|
(mapv (partial calculate-initial-track-size bound-width)))
|
||||||
|
|
||||||
|
row-tracks
|
||||||
|
(->> layout-grid-rows
|
||||||
|
(mapv (partial calculate-initial-track-size bound-height)))
|
||||||
|
|
||||||
|
;; Go through cells to adjust auto sizes for span=1. Base is the max of its children
|
||||||
|
column-tracks (set-auto-base-size column-tracks children shape-cells :column)
|
||||||
|
row-tracks (set-auto-base-size row-tracks children shape-cells :row)
|
||||||
|
|
||||||
|
;; Adjust multi-spaned cells with no flex columns
|
||||||
|
column-tracks (set-auto-multi-span parent column-tracks children-map shape-cells :column)
|
||||||
|
row-tracks (set-auto-multi-span parent row-tracks children-map shape-cells :row)
|
||||||
|
|
||||||
|
;; Calculate the `fr` unit and adjust the size
|
||||||
|
column-total-size-nofr (tracks-total-size (->> column-tracks (remove #(= :flex (:type %)))))
|
||||||
|
row-total-size-nofr (tracks-total-size (->> row-tracks (remove #(= :flex (:type %)))))
|
||||||
|
|
||||||
|
column-frs (tracks-total-frs column-tracks)
|
||||||
|
row-frs (tracks-total-frs row-tracks)
|
||||||
|
|
||||||
|
;; Assign minimum size to the multi-span flex tracks. We do this after calculating
|
||||||
|
;; the fr size because will affect only the minimum. The maximum will be set by the
|
||||||
|
;; fracion
|
||||||
|
column-tracks (set-flex-multi-span parent column-tracks children-map shape-cells :column)
|
||||||
|
row-tracks (set-flex-multi-span parent row-tracks children-map shape-cells :row)
|
||||||
|
|
||||||
|
;; Once auto sizes have been calculated we get calculate the `fr` unit with the remainining size and adjust the size
|
||||||
|
free-column-space (max 0 (- bound-width (+ column-total-size-nofr column-total-gap)))
|
||||||
|
free-row-space (max 0 (- bound-height (+ row-total-size-nofr row-total-gap)))
|
||||||
|
|
||||||
|
;; Get the minimum values for fr's
|
||||||
|
min-column-fr (min-fr-value column-tracks)
|
||||||
|
min-row-fr (min-fr-value row-tracks)
|
||||||
|
|
||||||
|
column-fr (if auto-width? min-column-fr (mth/finite (/ free-column-space column-frs) 0))
|
||||||
|
row-fr (if auto-height? min-row-fr (mth/finite (/ free-row-space row-frs) 0))
|
||||||
|
|
||||||
|
column-tracks (set-fr-value column-tracks column-fr auto-width?)
|
||||||
|
row-tracks (set-fr-value row-tracks row-fr auto-height?)
|
||||||
|
|
||||||
|
;; Distribute free space between `auto` tracks
|
||||||
|
column-total-size (tracks-total-size column-tracks)
|
||||||
|
row-total-size (tracks-total-size row-tracks)
|
||||||
|
|
||||||
|
free-column-space (max 0 (if auto-width? 0 (- bound-width (+ column-total-size column-total-gap))))
|
||||||
|
free-row-space (max 0 (if auto-height? 0 (- bound-height (+ row-total-size row-total-gap))))
|
||||||
|
column-autos (tracks-total-autos column-tracks)
|
||||||
|
row-autos (tracks-total-autos row-tracks)
|
||||||
|
|
||||||
|
column-add-auto (/ free-column-space column-autos)
|
||||||
|
row-add-auto (/ free-row-space row-autos)
|
||||||
|
|
||||||
|
column-tracks (cond-> column-tracks
|
||||||
|
(= :stretch (:layout-justify-content parent))
|
||||||
|
(add-auto-size column-add-auto))
|
||||||
|
|
||||||
|
row-tracks (cond-> row-tracks
|
||||||
|
(= :stretch (:layout-align-content parent))
|
||||||
|
(add-auto-size row-add-auto))
|
||||||
|
|
||||||
|
column-total-size (tracks-total-size column-tracks)
|
||||||
|
row-total-size (tracks-total-size row-tracks)
|
||||||
|
|
||||||
|
num-columns (count column-tracks)
|
||||||
|
column-gap
|
||||||
|
(case (:layout-justify-content parent)
|
||||||
|
auto-width?
|
||||||
|
column-gap
|
||||||
|
|
||||||
|
:space-evenly
|
||||||
|
(max column-gap (/ (- bound-width column-total-size) (inc num-columns)))
|
||||||
|
|
||||||
|
:space-around
|
||||||
|
(max column-gap (/ (- bound-width column-total-size) num-columns))
|
||||||
|
|
||||||
|
:space-between
|
||||||
|
(max column-gap (if (= num-columns 1) column-gap (/ (- bound-width column-total-size) (dec num-columns))))
|
||||||
|
|
||||||
|
column-gap)
|
||||||
|
|
||||||
|
num-rows (count row-tracks)
|
||||||
|
row-gap
|
||||||
|
(case (:layout-align-content parent)
|
||||||
|
auto-height?
|
||||||
|
row-gap
|
||||||
|
|
||||||
|
:space-evenly
|
||||||
|
(max row-gap (/ (- bound-height row-total-size) (inc num-rows)))
|
||||||
|
|
||||||
|
:space-around
|
||||||
|
(max row-gap (/ (- bound-height row-total-size) num-rows))
|
||||||
|
|
||||||
|
:space-between
|
||||||
|
(max row-gap (if (= num-rows 1) row-gap (/ (- bound-height row-total-size) (dec num-rows))))
|
||||||
|
|
||||||
|
row-gap)
|
||||||
|
|
||||||
|
start-p
|
||||||
|
(cond-> bound-corner
|
||||||
|
(and (not auto-width?) (= :end (:layout-justify-content parent)))
|
||||||
|
(gpt/add (hv (- bound-width (+ column-total-size column-total-gap))))
|
||||||
|
|
||||||
|
(and (not auto-width?) (= :center (:layout-justify-content parent)))
|
||||||
|
(gpt/add (hv (/ (- bound-width (+ column-total-size column-total-gap)) 2)))
|
||||||
|
|
||||||
|
(and (not auto-height?) (= :end (:layout-align-content parent)))
|
||||||
|
(gpt/add (vv (- bound-height (+ row-total-size row-total-gap))))
|
||||||
|
|
||||||
|
(and (not auto-height?) (= :center (:layout-align-content parent)))
|
||||||
|
(gpt/add (vv (/ (- bound-height (+ row-total-size row-total-gap)) 2)))
|
||||||
|
|
||||||
|
(and (not auto-width?) (= :space-around (:layout-justify-content parent)))
|
||||||
|
(gpt/add (hv (/ column-gap 2)))
|
||||||
|
|
||||||
|
(and (not auto-width?) (= :space-evenly (:layout-justify-content parent)))
|
||||||
|
(gpt/add (hv column-gap))
|
||||||
|
|
||||||
|
(and (not auto-height?) (= :space-around (:layout-align-content parent)))
|
||||||
|
(gpt/add (vv (/ row-gap 2)))
|
||||||
|
|
||||||
|
(and (not auto-height?) (= :space-evenly (:layout-align-content parent)))
|
||||||
|
(gpt/add (vv row-gap)))
|
||||||
|
|
||||||
|
column-tracks
|
||||||
|
(->> column-tracks
|
||||||
|
(reduce (fn [[tracks start-p] {:keys [size] :as track}]
|
||||||
|
[(conj tracks (assoc track :start-p start-p))
|
||||||
|
(gpt/add start-p (hv (+ size column-gap)))])
|
||||||
|
[[] start-p])
|
||||||
|
(first))
|
||||||
|
|
||||||
|
row-tracks
|
||||||
|
(->> row-tracks
|
||||||
|
(reduce (fn [[tracks start-p] {:keys [size] :as track}]
|
||||||
|
[(conj tracks (assoc track :start-p start-p))
|
||||||
|
(gpt/add start-p (vv (+ size row-gap)))])
|
||||||
|
[[] start-p])
|
||||||
|
(first))]
|
||||||
|
|
||||||
|
{:origin start-p
|
||||||
|
:layout-bounds layout-bounds
|
||||||
|
:row-tracks row-tracks
|
||||||
:column-tracks column-tracks
|
:column-tracks column-tracks
|
||||||
:shape-cells shape-cells}))
|
:shape-cells shape-cells
|
||||||
|
:column-gap column-gap
|
||||||
|
:row-gap row-gap
|
||||||
|
|
||||||
|
;; Convenient informaton for visualization
|
||||||
|
:column-total-size column-total-size
|
||||||
|
:column-total-gap column-total-gap
|
||||||
|
:row-total-size row-total-size
|
||||||
|
:row-total-gap row-total-gap}))
|
||||||
|
|
||||||
(defn get-cell-data
|
(defn get-cell-data
|
||||||
[{:keys [row-tracks column-tracks shape-cells]} transformed-parent-bounds [_child-bounds child]]
|
[{:keys [origin row-tracks column-tracks shape-cells]} _transformed-parent-bounds [_ child]]
|
||||||
|
|
||||||
(let [origin (gpo/origin transformed-parent-bounds)
|
|
||||||
hv #(gpo/start-hv transformed-parent-bounds %)
|
|
||||||
vv #(gpo/start-vv transformed-parent-bounds %)
|
|
||||||
|
|
||||||
grid-cell (get shape-cells (:id child))]
|
|
||||||
|
|
||||||
|
(let [grid-cell (get shape-cells (:id child))]
|
||||||
(when (some? grid-cell)
|
(when (some? grid-cell)
|
||||||
(let [column (nth column-tracks (dec (:column grid-cell)) nil)
|
(let [column (nth column-tracks (dec (:column grid-cell)) nil)
|
||||||
row (nth row-tracks (dec (:row grid-cell)) nil)
|
row (nth row-tracks (dec (:row grid-cell)) nil)
|
||||||
|
|
||||||
start-p (-> origin
|
column-start-p (:start-p column)
|
||||||
(gpt/add (hv (:distance column)))
|
row-start-p (:start-p row)
|
||||||
(gpt/add (vv (:distance row))))]
|
|
||||||
|
start-p (gpt/add origin
|
||||||
|
(gpt/add
|
||||||
|
(gpt/to-vec origin column-start-p)
|
||||||
|
(gpt/to-vec origin row-start-p)))]
|
||||||
|
|
||||||
(assoc grid-cell :start-p start-p)))))
|
(assoc grid-cell :start-p start-p)))))
|
||||||
|
|||||||
@ -6,11 +6,240 @@
|
|||||||
|
|
||||||
(ns app.common.geom.shapes.grid-layout.positions
|
(ns app.common.geom.shapes.grid-layout.positions
|
||||||
(:require
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
|
[app.common.geom.shapes.grid-layout.layout-data :as ld]
|
||||||
[app.common.geom.shapes.points :as gpo]
|
[app.common.geom.shapes.points :as gpo]
|
||||||
[app.common.types.modifiers :as ctm]))
|
[app.common.geom.shapes.transforms :as gtr]
|
||||||
|
[app.common.math :as mth]
|
||||||
|
[app.common.pages.helpers :as cph]
|
||||||
|
[app.common.types.modifiers :as ctm]
|
||||||
|
[app.common.types.shape.layout :as ctl]))
|
||||||
|
|
||||||
|
(defn cell-bounds
|
||||||
|
[{: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 %)
|
||||||
|
vv #(gpo/start-vv layout-bounds %)
|
||||||
|
|
||||||
|
span-column-tracks (subvec column-tracks (dec column) (+ (dec column) column-span))
|
||||||
|
span-row-tracks (subvec row-tracks (dec row) (+ (dec row) row-span))
|
||||||
|
|
||||||
|
p1
|
||||||
|
(gpt/add
|
||||||
|
origin
|
||||||
|
(gpt/add
|
||||||
|
(gpt/to-vec origin (dm/get-in span-column-tracks [0 :start-p]))
|
||||||
|
(gpt/to-vec origin (dm/get-in span-row-tracks [0 :start-p]))))
|
||||||
|
|
||||||
|
p2
|
||||||
|
(as-> p1 $
|
||||||
|
(reduce (fn [p track] (gpt/add p (hv (:size track)))) $ span-column-tracks)
|
||||||
|
(gpt/add $ (hv (* column-gap (dec (count span-column-tracks))))))
|
||||||
|
|
||||||
|
p3
|
||||||
|
(as-> p2 $
|
||||||
|
(reduce (fn [p track] (gpt/add p (vv (:size track)))) $ span-row-tracks)
|
||||||
|
(gpt/add $ (vv (* row-gap (dec (count span-row-tracks))))))
|
||||||
|
|
||||||
|
p4
|
||||||
|
(as-> p1 $
|
||||||
|
(reduce (fn [p track] (gpt/add p (vv (:size track)))) $ span-row-tracks)
|
||||||
|
(gpt/add $ (vv (* row-gap (dec (count span-row-tracks))))))]
|
||||||
|
|
||||||
|
[p1 p2 p3 p4]))
|
||||||
|
|
||||||
|
(defn calc-fill-width-data
|
||||||
|
"Calculates the size and modifiers for the width of an auto-fill child"
|
||||||
|
[_parent
|
||||||
|
transform
|
||||||
|
transform-inverse
|
||||||
|
_child
|
||||||
|
child-origin child-width
|
||||||
|
cell-bounds]
|
||||||
|
|
||||||
|
(let [target-width (max (gpo/width-points cell-bounds) 0.01)
|
||||||
|
fill-scale (/ target-width child-width)]
|
||||||
|
{:width target-width
|
||||||
|
:modifiers (ctm/resize-modifiers (gpt/point fill-scale 1) child-origin transform transform-inverse)}))
|
||||||
|
|
||||||
|
(defn calc-fill-height-data
|
||||||
|
"Calculates the size and modifiers for the height of an auto-fill child"
|
||||||
|
[_parent
|
||||||
|
transform transform-inverse
|
||||||
|
_child
|
||||||
|
child-origin child-height
|
||||||
|
cell-bounds]
|
||||||
|
(let [target-height (max (gpo/height-points cell-bounds) 0.01)
|
||||||
|
fill-scale (/ target-height child-height)]
|
||||||
|
{:height target-height
|
||||||
|
:modifiers (ctm/resize-modifiers (gpt/point 1 fill-scale) child-origin transform transform-inverse)}))
|
||||||
|
|
||||||
|
(defn fill-modifiers
|
||||||
|
[parent parent-bounds child child-bounds layout-data cell-data]
|
||||||
|
(let [child-origin (gpo/origin child-bounds)
|
||||||
|
child-width (gpo/width-points child-bounds)
|
||||||
|
child-height (gpo/height-points child-bounds)
|
||||||
|
|
||||||
|
cell-bounds (cell-bounds layout-data cell-data)
|
||||||
|
|
||||||
|
[_ transform transform-inverse]
|
||||||
|
(when (or (ctl/fill-width? child) (ctl/fill-height? child))
|
||||||
|
(gtr/calculate-geometry @parent-bounds))
|
||||||
|
|
||||||
|
fill-width
|
||||||
|
(when (ctl/fill-width? child)
|
||||||
|
(calc-fill-width-data parent transform transform-inverse child child-origin child-width cell-bounds))
|
||||||
|
|
||||||
|
fill-height
|
||||||
|
(when (ctl/fill-height? child)
|
||||||
|
(calc-fill-height-data parent transform transform-inverse child child-origin child-height cell-bounds))
|
||||||
|
|
||||||
|
child-width (or (:width fill-width) child-width)
|
||||||
|
child-height (or (:height fill-height) child-height)]
|
||||||
|
|
||||||
|
[child-width
|
||||||
|
child-height
|
||||||
|
(-> (ctm/empty)
|
||||||
|
(cond-> fill-width (ctm/add-modifiers (:modifiers fill-width)))
|
||||||
|
(cond-> fill-height (ctm/add-modifiers (:modifiers fill-height))))]))
|
||||||
|
|
||||||
|
(defn child-position-delta
|
||||||
|
[parent 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)
|
||||||
|
|
||||||
|
align (:layout-align-items parent)
|
||||||
|
justify (:layout-justify-items parent)
|
||||||
|
align-self (:align-self cell-data)
|
||||||
|
justify-self (:justify-self cell-data)
|
||||||
|
|
||||||
|
align-self (when (and align-self (not= align-self :auto)) align-self)
|
||||||
|
justify-self (when (and justify-self (not= justify-self :auto)) justify-self)
|
||||||
|
|
||||||
|
align (or align-self align)
|
||||||
|
justify (or justify-self justify)
|
||||||
|
|
||||||
|
origin-h (gpo/project-point cell-bounds :h child-origin)
|
||||||
|
origin-v (gpo/project-point cell-bounds :v child-origin)
|
||||||
|
hv (partial gpo/start-hv cell-bounds)
|
||||||
|
vv (partial gpo/start-vv cell-bounds)
|
||||||
|
|
||||||
|
;; Adjust alignment/justify
|
||||||
|
[from-h to-h]
|
||||||
|
(case justify
|
||||||
|
:end
|
||||||
|
[(gpt/add origin-h (hv child-width))
|
||||||
|
(nth cell-bounds 1)]
|
||||||
|
|
||||||
|
:center
|
||||||
|
[(gpt/add origin-h (hv (/ child-width 2)))
|
||||||
|
(gpo/project-point cell-bounds :h (gpo/center cell-bounds))]
|
||||||
|
|
||||||
|
[origin-h (first cell-bounds)])
|
||||||
|
|
||||||
|
[from-v to-v]
|
||||||
|
(case align
|
||||||
|
:end
|
||||||
|
[(gpt/add origin-v (vv child-height))
|
||||||
|
(nth cell-bounds 3)]
|
||||||
|
|
||||||
|
:center
|
||||||
|
[(gpt/add origin-v (vv (/ child-height 2)))
|
||||||
|
(gpo/project-point cell-bounds :v (gpo/center cell-bounds))]
|
||||||
|
|
||||||
|
[origin-v (first cell-bounds)])]
|
||||||
|
(-> (gpt/point)
|
||||||
|
(gpt/add (gpt/to-vec from-h to-h))
|
||||||
|
(gpt/add (gpt/to-vec from-v to-v)))))
|
||||||
|
|
||||||
(defn child-modifiers
|
(defn child-modifiers
|
||||||
[_parent _transformed-parent-bounds _child child-bounds cell-data]
|
[parent parent-bounds child child-bounds layout-data cell-data]
|
||||||
(ctm/move-modifiers
|
|
||||||
(gpt/subtract (:start-p cell-data) (gpo/origin child-bounds))))
|
(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)]
|
||||||
|
|
||||||
|
(cond-> (ctm/empty)
|
||||||
|
(not (ctl/layout-absolute? child))
|
||||||
|
(-> (ctm/add-modifiers fill-modifiers)
|
||||||
|
(ctm/move position-delta)))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn line-value
|
||||||
|
[[{px :x py :y} {vx :x vy :y}] {:keys [x y]}]
|
||||||
|
(let [a vy
|
||||||
|
b (- vx)
|
||||||
|
c (+ (* (- vy) px) (* vx py))]
|
||||||
|
(+ (* a x) (* b y) c)))
|
||||||
|
|
||||||
|
(defn is-inside-lines?
|
||||||
|
[line-1 line-2 pos]
|
||||||
|
(< (* (line-value line-1 pos) (line-value line-2 pos)) 0))
|
||||||
|
|
||||||
|
(defn get-position-grid-coord
|
||||||
|
[{:keys [layout-bounds row-tracks column-tracks]} position]
|
||||||
|
|
||||||
|
(let [hv #(gpo/start-hv layout-bounds %)
|
||||||
|
vv #(gpo/start-vv layout-bounds %)
|
||||||
|
|
||||||
|
make-is-inside-track
|
||||||
|
(fn [type]
|
||||||
|
(let [[vfn ofn] (if (= type :column) [vv hv] [hv vv])]
|
||||||
|
(fn is-inside-track? [{:keys [start-p size] :as track}]
|
||||||
|
(let [unit-v (vfn 1)
|
||||||
|
end-p (gpt/add start-p (ofn size))]
|
||||||
|
(is-inside-lines? [start-p unit-v] [end-p unit-v] position)))))
|
||||||
|
|
||||||
|
make-min-distance-track
|
||||||
|
(fn [type]
|
||||||
|
(let [[vfn ofn] (if (= type :column) [vv hv] [hv vv])]
|
||||||
|
(fn [[selected selected-dist] [cur-idx {:keys [start-p size] :as track}]]
|
||||||
|
(let [unit-v (vfn 1)
|
||||||
|
end-p (gpt/add start-p (ofn size))
|
||||||
|
dist-1 (mth/abs (line-value [start-p unit-v] position))
|
||||||
|
dist-2 (mth/abs (line-value [end-p unit-v] position))]
|
||||||
|
|
||||||
|
(if (or (< dist-1 selected-dist) (< dist-2 selected-dist))
|
||||||
|
[[cur-idx track] (min dist-1 dist-2)]
|
||||||
|
[selected selected-dist])))))
|
||||||
|
|
||||||
|
;; Check if it's inside a track
|
||||||
|
[col-idx column]
|
||||||
|
(->> (d/enumerate column-tracks)
|
||||||
|
(d/seek (comp (make-is-inside-track :column) second)))
|
||||||
|
|
||||||
|
[row-idx row]
|
||||||
|
(->> (d/enumerate row-tracks)
|
||||||
|
(d/seek (comp (make-is-inside-track :row) second)))
|
||||||
|
|
||||||
|
;; If not inside we find the closest start/end line
|
||||||
|
[col-idx column]
|
||||||
|
(if (some? column)
|
||||||
|
[col-idx column]
|
||||||
|
(->> (d/enumerate column-tracks)
|
||||||
|
(reduce (make-min-distance-track :column) [[nil nil] ##Inf])
|
||||||
|
(first)))
|
||||||
|
|
||||||
|
[row-idx row]
|
||||||
|
(if (some? row)
|
||||||
|
[row-idx row]
|
||||||
|
(->> (d/enumerate row-tracks)
|
||||||
|
(reduce (make-min-distance-track :row) [[nil nil] ##Inf])
|
||||||
|
(first)))]
|
||||||
|
|
||||||
|
(when (and (some? column) (some? row))
|
||||||
|
[(inc row-idx) (inc col-idx)])))
|
||||||
|
|
||||||
|
(defn get-drop-cell
|
||||||
|
[frame-id objects position]
|
||||||
|
|
||||||
|
(let [frame (get objects frame-id)
|
||||||
|
children (->> (cph/get-immediate-children objects (:id frame))
|
||||||
|
(remove :hidden)
|
||||||
|
(map #(vector (gpo/parent-coords-bounds (:points %) (:points frame)) %)))
|
||||||
|
layout-data (ld/calc-layout-data frame children (:points frame))]
|
||||||
|
|
||||||
|
(get-position-grid-coord layout-data position)))
|
||||||
|
|||||||
@ -204,8 +204,11 @@
|
|||||||
[(-> (get-group-bounds objects bounds modif-tree child)
|
[(-> (get-group-bounds objects bounds modif-tree child)
|
||||||
(gpo/parent-coords-bounds @transformed-parent-bounds))
|
(gpo/parent-coords-bounds @transformed-parent-bounds))
|
||||||
child])
|
child])
|
||||||
(set-child-modifiers [modif-tree cell-data [child-bounds child]]
|
|
||||||
(let [modifiers (gcgl/child-modifiers parent transformed-parent-bounds child child-bounds cell-data)
|
(set-child-modifiers [modif-tree grid-data cell-data [child-bounds child]]
|
||||||
|
(let [modifiers
|
||||||
|
(gcgl/child-modifiers parent transformed-parent-bounds child child-bounds grid-data cell-data)
|
||||||
|
|
||||||
modif-tree
|
modif-tree
|
||||||
(cond-> modif-tree
|
(cond-> modif-tree
|
||||||
(d/not-empty? modifiers)
|
(d/not-empty? modifiers)
|
||||||
@ -217,13 +220,13 @@
|
|||||||
(map apply-modifiers))
|
(map apply-modifiers))
|
||||||
grid-data (gcgl/calc-layout-data parent children @transformed-parent-bounds)]
|
grid-data (gcgl/calc-layout-data parent children @transformed-parent-bounds)]
|
||||||
(loop [modif-tree modif-tree
|
(loop [modif-tree modif-tree
|
||||||
child (first children)
|
bound+child (first children)
|
||||||
pending (rest children)]
|
pending (rest children)]
|
||||||
(if (some? child)
|
(if (some? bound+child)
|
||||||
(let [cell-data (gcgl/get-cell-data grid-data @transformed-parent-bounds child)
|
(let [cell-data (gcgl/get-cell-data grid-data @transformed-parent-bounds bound+child)
|
||||||
modif-tree (cond-> modif-tree
|
modif-tree (cond-> modif-tree
|
||||||
(some? cell-data)
|
(some? cell-data)
|
||||||
(set-child-modifiers cell-data child))]
|
(set-child-modifiers grid-data cell-data bound+child))]
|
||||||
(recur modif-tree (first pending) (rest pending)))
|
(recur modif-tree (first pending) (rest pending)))
|
||||||
modif-tree)))))
|
modif-tree)))))
|
||||||
|
|
||||||
@ -253,7 +256,12 @@
|
|||||||
|
|
||||||
content-bounds
|
content-bounds
|
||||||
(when (and (d/not-empty? children) (or (ctl/auto-height? parent) (ctl/auto-width? parent)))
|
(when (and (d/not-empty? children) (or (ctl/auto-height? parent) (ctl/auto-width? parent)))
|
||||||
(gcfl/layout-content-bounds bounds parent children))
|
(cond
|
||||||
|
(ctl/flex-layout? parent)
|
||||||
|
(gcfl/layout-content-bounds bounds parent children)
|
||||||
|
|
||||||
|
(ctl/grid-layout? parent)
|
||||||
|
(gcgl/layout-content-bounds bounds parent children)))
|
||||||
|
|
||||||
auto-width (when content-bounds (gpo/width-points content-bounds))
|
auto-width (when content-bounds (gpo/width-points content-bounds))
|
||||||
auto-height (when content-bounds (gpo/height-points content-bounds))]
|
auto-height (when content-bounds (gpo/height-points content-bounds))]
|
||||||
@ -297,13 +305,13 @@
|
|||||||
transformed-parent-bounds (delay (gtr/transform-bounds @(get bounds parent-id) modifiers))
|
transformed-parent-bounds (delay (gtr/transform-bounds @(get bounds parent-id) modifiers))
|
||||||
|
|
||||||
children-modifiers
|
children-modifiers
|
||||||
(if flex-layout?
|
(if (or flex-layout? grid-layout?)
|
||||||
(->> (:shapes parent)
|
(->> (:shapes parent)
|
||||||
(filter #(ctl/layout-absolute? objects %)))
|
(filter #(ctl/layout-absolute? objects %)))
|
||||||
(:shapes parent))
|
(:shapes parent))
|
||||||
|
|
||||||
children-layout
|
children-layout
|
||||||
(when flex-layout?
|
(when (or flex-layout? grid-layout?)
|
||||||
(->> (:shapes parent)
|
(->> (:shapes parent)
|
||||||
(remove #(ctl/layout-absolute? objects %))))]
|
(remove #(ctl/layout-absolute? objects %))))]
|
||||||
|
|
||||||
@ -421,7 +429,7 @@
|
|||||||
|
|
||||||
to-reflow
|
to-reflow
|
||||||
(cond-> to-reflow
|
(cond-> to-reflow
|
||||||
(and (ctl/flex-layout-descent? objects parent-base)
|
(and (ctl/any-layout-descent? objects parent-base)
|
||||||
(not= uuid/zero (:frame-id parent-base)))
|
(not= uuid/zero (:frame-id parent-base)))
|
||||||
(conj (:frame-id parent-base)))]
|
(conj (:frame-id parent-base)))]
|
||||||
(recur modif-tree
|
(recur modif-tree
|
||||||
|
|||||||
@ -55,11 +55,13 @@
|
|||||||
|
|
||||||
(defn width-points
|
(defn width-points
|
||||||
[[p0 p1 _ _]]
|
[[p0 p1 _ _]]
|
||||||
(max 0.01 (gpt/length (gpt/to-vec p0 p1))))
|
(when (and (some? p0) (some? p1))
|
||||||
|
(max 0.01 (gpt/length (gpt/to-vec p0 p1)))))
|
||||||
|
|
||||||
(defn height-points
|
(defn height-points
|
||||||
[[p0 _ _ p3]]
|
[[p0 _ _ p3]]
|
||||||
(max 0.01 (gpt/length (gpt/to-vec p0 p3))))
|
(when (and (some? p0) (some? p3))
|
||||||
|
(max 0.01 (gpt/length (gpt/to-vec p0 p3)))))
|
||||||
|
|
||||||
(defn pad-points
|
(defn pad-points
|
||||||
[[p0 p1 p2 p3 :as points] pad-top pad-right pad-bottom pad-left]
|
[[p0 p1 p2 p3 :as points] pad-top pad-right pad-bottom pad-left]
|
||||||
@ -78,7 +80,7 @@
|
|||||||
"Given a point and a line returns the parametric t the cross point with the line going through the other axis projected"
|
"Given a point and a line returns the parametric t the cross point with the line going through the other axis projected"
|
||||||
[point [start end] other-axis-vec]
|
[point [start end] other-axis-vec]
|
||||||
|
|
||||||
(let [line-vec (gpt/to-vec start end)
|
(let [line-vec (gpt/to-vec start end)
|
||||||
pr-point (gsi/line-line-intersect point (gpt/add point other-axis-vec) start end)]
|
pr-point (gsi/line-line-intersect point (gpt/add point other-axis-vec) start end)]
|
||||||
(cond
|
(cond
|
||||||
(not (mth/almost-zero? (:x line-vec)))
|
(not (mth/almost-zero? (:x line-vec)))
|
||||||
@ -91,6 +93,15 @@
|
|||||||
:else
|
:else
|
||||||
0)))
|
0)))
|
||||||
|
|
||||||
|
(defn project-point
|
||||||
|
"Project the point into the given axis: `:h` or `:v` means horizontal or vertical axis"
|
||||||
|
[[p0 p1 _ p3 :as bounds] axis point]
|
||||||
|
(let [[other-vec start end]
|
||||||
|
(if (= axis :h)
|
||||||
|
[(gpt/to-vec p0 p3) p0 p1]
|
||||||
|
[(gpt/to-vec p0 p1) p0 p3])]
|
||||||
|
(gsi/line-line-intersect point (gpt/add point other-vec) start end)))
|
||||||
|
|
||||||
(defn parent-coords-bounds
|
(defn parent-coords-bounds
|
||||||
[child-bounds [p1 p2 _ p4 :as parent-bounds]]
|
[child-bounds [p1 p2 _ p4 :as parent-bounds]]
|
||||||
|
|
||||||
@ -152,3 +163,13 @@
|
|||||||
[bounds vector]
|
[bounds vector]
|
||||||
(->> bounds
|
(->> bounds
|
||||||
(map #(gpt/add % vector))))
|
(map #(gpt/add % vector))))
|
||||||
|
|
||||||
|
(defn center
|
||||||
|
[bounds]
|
||||||
|
(let [width (width-points bounds)
|
||||||
|
height (height-points bounds)
|
||||||
|
half-h (start-hv bounds (/ width 2))
|
||||||
|
half-v (start-vv bounds (/ height 2))]
|
||||||
|
(-> (origin bounds)
|
||||||
|
(gpt/add half-h)
|
||||||
|
(gpt/add half-v))))
|
||||||
|
|||||||
@ -101,9 +101,18 @@
|
|||||||
[:ignore-touched {:optional true} :boolean]
|
[:ignore-touched {:optional true} :boolean]
|
||||||
[:parent-id ::sm/uuid]
|
[:parent-id ::sm/uuid]
|
||||||
[:shapes :any]
|
[:shapes :any]
|
||||||
[:index {:optional true} :int]
|
[:index {:optional true} [:maybe :int]]
|
||||||
[:after-shape {:optional true} :any]]]
|
[:after-shape {:optional true} :any]]]
|
||||||
|
|
||||||
|
[:reorder-children
|
||||||
|
[:map {:title "ReorderChildrenChange"}
|
||||||
|
[:type [:= :reorder-children]]
|
||||||
|
[:page-id {:optional true} ::sm/uuid]
|
||||||
|
[:component-id {:optional true} ::sm/uuid]
|
||||||
|
[:ignore-touched {:optional true} :boolean]
|
||||||
|
[:parent-id ::sm/uuid]
|
||||||
|
[:shapes :any]]]
|
||||||
|
|
||||||
[:add-page
|
[:add-page
|
||||||
[:map {:title "AddPageChange"}
|
[:map {:title "AddPageChange"}
|
||||||
[:type [:= :add-page]]
|
[:type [:= :add-page]]
|
||||||
@ -331,6 +340,51 @@
|
|||||||
(d/update-in-when $ [:components component-id :objects] update-fn))
|
(d/update-in-when $ [:components component-id :objects] update-fn))
|
||||||
(check-modify-component $))))
|
(check-modify-component $))))
|
||||||
|
|
||||||
|
(defmethod process-change :reorder-children
|
||||||
|
[data {:keys [parent-id shapes page-id component-id]}]
|
||||||
|
(let [changed? (atom false)
|
||||||
|
|
||||||
|
update-fn
|
||||||
|
(fn [objects]
|
||||||
|
(let [old-shapes (dm/get-in objects [parent-id :shapes])
|
||||||
|
|
||||||
|
id->idx
|
||||||
|
(update-vals
|
||||||
|
(->> shapes
|
||||||
|
d/enumerate
|
||||||
|
(group-by second))
|
||||||
|
(comp first first))
|
||||||
|
|
||||||
|
new-shapes
|
||||||
|
(into [] (sort-by id->idx < old-shapes))]
|
||||||
|
|
||||||
|
(reset! changed? (not= old-shapes new-shapes))
|
||||||
|
|
||||||
|
(cond-> objects
|
||||||
|
@changed?
|
||||||
|
(assoc-in [parent-id :shapes] new-shapes))))
|
||||||
|
|
||||||
|
check-modify-component
|
||||||
|
(fn [data]
|
||||||
|
(if @changed?
|
||||||
|
;; When a shape is modified, if it belongs to a main component instance,
|
||||||
|
;; the component needs to be marked as modified.
|
||||||
|
(let [objects (if page-id
|
||||||
|
(-> data :pages-index (get page-id) :objects)
|
||||||
|
(-> data :components (get component-id) :objects))
|
||||||
|
shape (get objects parent-id)
|
||||||
|
component-root (ctn/get-component-shape objects shape {:allow-main? true})]
|
||||||
|
(if (and (some? component-root) (ctk/main-instance? component-root))
|
||||||
|
(ctkl/set-component-modified data (:component-id component-root))
|
||||||
|
data))
|
||||||
|
data))]
|
||||||
|
|
||||||
|
(as-> data $
|
||||||
|
(if page-id
|
||||||
|
(d/update-in-when $ [:pages-index page-id :objects] update-fn)
|
||||||
|
(d/update-in-when $ [:components component-id :objects] update-fn))
|
||||||
|
(check-modify-component $))))
|
||||||
|
|
||||||
(defmethod process-change :del-obj
|
(defmethod process-change :del-obj
|
||||||
[data {:keys [page-id component-id id ignore-touched]}]
|
[data {:keys [page-id component-id id ignore-touched]}]
|
||||||
(if page-id
|
(if page-id
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
[app.common.pages.helpers :as cph]
|
[app.common.pages.helpers :as cph]
|
||||||
[app.common.types.component :as ctk]
|
[app.common.types.component :as ctk]
|
||||||
[app.common.types.file :as ctf]
|
[app.common.types.file :as ctf]
|
||||||
|
[app.common.types.shape.layout :as ctl]
|
||||||
[app.common.uuid :as uuid]))
|
[app.common.uuid :as uuid]))
|
||||||
|
|
||||||
;; Auxiliary functions to help create a set of changes (undo + redo)
|
;; Auxiliary functions to help create a set of changes (undo + redo)
|
||||||
@ -712,3 +713,42 @@
|
|||||||
(-> changes
|
(-> changes
|
||||||
(update :redo-changes add-ignore-remote)
|
(update :redo-changes add-ignore-remote)
|
||||||
(update :undo-changes add-ignore-remote))))
|
(update :undo-changes add-ignore-remote))))
|
||||||
|
|
||||||
|
(defn reorder-grid-children
|
||||||
|
[changes ids]
|
||||||
|
(assert-page-id changes)
|
||||||
|
(assert-objects changes)
|
||||||
|
|
||||||
|
(let [page-id (::page-id (meta changes))
|
||||||
|
objects (lookup-objects changes)
|
||||||
|
|
||||||
|
reorder-grid
|
||||||
|
(fn [changes grid]
|
||||||
|
(let [old-shapes (:shapes grid)
|
||||||
|
grid (ctl/reorder-grid-children grid)
|
||||||
|
new-shapes (->> (:shapes grid)
|
||||||
|
(filterv #(contains? objects %)))
|
||||||
|
|
||||||
|
redo-change
|
||||||
|
{:type :reorder-children
|
||||||
|
:parent-id (:id grid)
|
||||||
|
:page-id page-id
|
||||||
|
:shapes new-shapes}
|
||||||
|
|
||||||
|
undo-change
|
||||||
|
{:type :reorder-children
|
||||||
|
:parent-id (:id grid)
|
||||||
|
:page-id page-id
|
||||||
|
:shapes old-shapes}]
|
||||||
|
(-> changes
|
||||||
|
(update :redo-changes conj redo-change)
|
||||||
|
(update :undo-changes d/preconj undo-change)
|
||||||
|
(apply-changes-local))))
|
||||||
|
|
||||||
|
changes
|
||||||
|
(->> ids
|
||||||
|
(map (d/getf objects))
|
||||||
|
(filter ctl/grid-layout?)
|
||||||
|
(reduce reorder-grid changes))]
|
||||||
|
|
||||||
|
changes))
|
||||||
|
|||||||
@ -89,11 +89,16 @@
|
|||||||
:layout-gap :layout-container
|
:layout-gap :layout-container
|
||||||
:layout-gap-type :layout-container
|
:layout-gap-type :layout-container
|
||||||
:layout-justify-content :layout-container
|
:layout-justify-content :layout-container
|
||||||
|
:layout-justify-items :layout-container
|
||||||
:layout-wrap-type :layout-container
|
:layout-wrap-type :layout-container
|
||||||
:layout-padding-type :layout-container
|
:layout-padding-type :layout-container
|
||||||
:layout-padding :layout-container
|
:layout-padding :layout-container
|
||||||
:layout-h-orientation :layout-container
|
:layout-h-orientation :layout-container
|
||||||
:layout-v-orientation :layout-container
|
:layout-v-orientation :layout-container
|
||||||
|
:layout-grid-dir :layout-container
|
||||||
|
:layout-grid-rows :layout-container
|
||||||
|
:layout-grid-columns :layout-container
|
||||||
|
:layout-grid-cells :layout-container
|
||||||
|
|
||||||
:layout-item-margin :layout-item
|
:layout-item-margin :layout-item
|
||||||
:layout-item-margin-type :layout-item
|
:layout-item-margin-type :layout-item
|
||||||
|
|||||||
@ -45,8 +45,10 @@
|
|||||||
(= type :group)))
|
(= type :group)))
|
||||||
|
|
||||||
(defn mask-shape?
|
(defn mask-shape?
|
||||||
[{:keys [type masked-group?]}]
|
([objects id]
|
||||||
(and (= type :group) masked-group?))
|
(mask-shape? (get objects id)))
|
||||||
|
([{:keys [type masked-group?]}]
|
||||||
|
(and (= type :group) masked-group?)))
|
||||||
|
|
||||||
(defn bool-shape?
|
(defn bool-shape?
|
||||||
[{:keys [type]}]
|
[{:keys [type]}]
|
||||||
@ -64,6 +66,10 @@
|
|||||||
[{:keys [type]}]
|
[{:keys [type]}]
|
||||||
(= type :rect))
|
(= type :rect))
|
||||||
|
|
||||||
|
(defn circle-shape?
|
||||||
|
[{:keys [type]}]
|
||||||
|
(= type :circle))
|
||||||
|
|
||||||
(defn image-shape?
|
(defn image-shape?
|
||||||
[{:keys [type]}]
|
[{:keys [type]}]
|
||||||
(= type :image))
|
(= type :image))
|
||||||
@ -131,6 +137,15 @@
|
|||||||
(recur (conj result parent-id) parent-id)
|
(recur (conj result parent-id) parent-id)
|
||||||
result))))
|
result))))
|
||||||
|
|
||||||
|
(defn get-parents
|
||||||
|
"Returns a vector of parents of the specified shape."
|
||||||
|
[objects shape-id]
|
||||||
|
(loop [result [] id shape-id]
|
||||||
|
(let [parent-id (dm/get-in objects [id :parent-id])]
|
||||||
|
(if (and (some? parent-id) (not= parent-id id))
|
||||||
|
(recur (conj result (get objects parent-id)) parent-id)
|
||||||
|
result))))
|
||||||
|
|
||||||
(defn get-parents-with-self
|
(defn get-parents-with-self
|
||||||
[objects id]
|
[objects id]
|
||||||
(let [lookup (d/getf objects)]
|
(let [lookup (d/getf objects)]
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
(:require
|
(:require
|
||||||
[app.common.colors :as clr]
|
[app.common.colors :as clr]
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
[app.common.transit :as t]
|
[app.common.transit :as t]
|
||||||
[clojure.walk :as walk]
|
[clojure.walk :as walk]
|
||||||
[cuerdas.core :as str]))
|
[cuerdas.core :as str]))
|
||||||
@ -29,6 +30,8 @@
|
|||||||
:fills [{:fill-color clr/black
|
:fills [{:fill-color clr/black
|
||||||
:fill-opacity 1}]})
|
:fill-opacity 1}]})
|
||||||
|
|
||||||
|
(def text-attrs (keys default-text-attrs))
|
||||||
|
|
||||||
(def typography-fields
|
(def typography-fields
|
||||||
[:font-id
|
[:font-id
|
||||||
:font-family
|
:font-family
|
||||||
@ -252,3 +255,50 @@
|
|||||||
|
|
||||||
{:blocks (reduce #(conj %1 (build-block %2)) [] (node-seq #(= (:type %) "paragraph") root))
|
{:blocks (reduce #(conj %1 (build-block %2)) [] (node-seq #(= (:type %) "paragraph") root))
|
||||||
:entityMap {}}))
|
:entityMap {}}))
|
||||||
|
|
||||||
|
(defn content->text+styles
|
||||||
|
"Given a root node of a text content extracts the texts with its associated styles"
|
||||||
|
[node]
|
||||||
|
(letfn
|
||||||
|
[(rec-style-text-map [acc node style]
|
||||||
|
(let [node-style (merge style (select-keys node text-attrs))
|
||||||
|
head (or (-> acc first) [{} ""])
|
||||||
|
[head-style head-text] head
|
||||||
|
|
||||||
|
new-acc
|
||||||
|
(cond
|
||||||
|
(:children node)
|
||||||
|
(reduce #(rec-style-text-map %1 %2 node-style) acc (:children node))
|
||||||
|
|
||||||
|
(not= head-style node-style)
|
||||||
|
(cons [node-style (:text node "")] acc)
|
||||||
|
|
||||||
|
:else
|
||||||
|
(cons [node-style (dm/str head-text "" (:text node))] (rest acc)))
|
||||||
|
|
||||||
|
;; We add an end-of-line when finish a paragraph
|
||||||
|
new-acc
|
||||||
|
(if (= (:type node) "paragraph")
|
||||||
|
(let [[hs ht] (first new-acc)]
|
||||||
|
(cons [hs (dm/str ht "\n")] (rest new-acc)))
|
||||||
|
new-acc)]
|
||||||
|
new-acc))]
|
||||||
|
|
||||||
|
(-> (rec-style-text-map [] node {})
|
||||||
|
reverse)))
|
||||||
|
|
||||||
|
(defn index-content
|
||||||
|
"Adds a property `$id` that identifies the current node inside"
|
||||||
|
([content]
|
||||||
|
(index-content content nil 0))
|
||||||
|
([node path index]
|
||||||
|
(let [cur-path (if path (dm/str path "-") (dm/str ""))
|
||||||
|
cur-path (dm/str cur-path (d/name (:type node :text)) "-" index)]
|
||||||
|
(-> node
|
||||||
|
(assoc :$id cur-path)
|
||||||
|
(update :children
|
||||||
|
(fn [children]
|
||||||
|
(->> children
|
||||||
|
(d/enumerate)
|
||||||
|
(mapv (fn [[idx node]]
|
||||||
|
(index-content node cur-path idx))))))))))
|
||||||
|
|||||||
@ -19,8 +19,7 @@
|
|||||||
[app.common.types.shape.blur :as ctsb]
|
[app.common.types.shape.blur :as ctsb]
|
||||||
[app.common.types.shape.export :as ctse]
|
[app.common.types.shape.export :as ctse]
|
||||||
[app.common.types.shape.interactions :as ctsi]
|
[app.common.types.shape.interactions :as ctsi]
|
||||||
;; FIXME: missing spec -> schema
|
[app.common.types.shape.layout :as ctsl]
|
||||||
#_[app.common.types.shape.layout :as ctsl]
|
|
||||||
[app.common.types.shape.shadow :as ctss]
|
[app.common.types.shape.shadow :as ctss]
|
||||||
[app.common.types.shape.text :as ctsx]
|
[app.common.types.shape.text :as ctsx]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
@ -232,48 +231,57 @@
|
|||||||
[:group
|
[:group
|
||||||
[:merge {:title "GroupShape"}
|
[:merge {:title "GroupShape"}
|
||||||
::shape-attrs
|
::shape-attrs
|
||||||
::group-attrs]]
|
::group-attrs
|
||||||
|
::ctsl/layout-child-attrs]]
|
||||||
|
|
||||||
[:frame
|
[:frame
|
||||||
[:merge {:title "FrameShape"}
|
[:merge {:title "FrameShape"}
|
||||||
::shape-attrs
|
::shape-attrs
|
||||||
::frame-attrs]]
|
::frame-attrs
|
||||||
|
::ctsl/layout-attrs
|
||||||
|
::ctsl/layout-child-attrs]]
|
||||||
|
|
||||||
[:bool
|
[:bool
|
||||||
[:merge {:title "BoolShape"}
|
[:merge {:title "BoolShape"}
|
||||||
::shape-attrs
|
::shape-attrs
|
||||||
::bool-attrs]]
|
::bool-attrs
|
||||||
|
::ctsl/layout-child-attrs]]
|
||||||
|
|
||||||
[:rect
|
[:rect
|
||||||
[:merge {:title "RectShape"}
|
[:merge {:title "RectShape"}
|
||||||
::shape-attrs
|
::shape-attrs
|
||||||
::rect-attrs]]
|
::rect-attrs
|
||||||
|
::ctsl/layout-child-attrs]]
|
||||||
|
|
||||||
[:circle
|
[:circle
|
||||||
[:merge {:title "CircleShape"}
|
[:merge {:title "CircleShape"}
|
||||||
::shape-attrs
|
::shape-attrs
|
||||||
::circle-attrs]]
|
::circle-attrs
|
||||||
|
::ctsl/layout-child-attrs]]
|
||||||
|
|
||||||
[:image
|
[:image
|
||||||
[:merge {:title "ImageShape"}
|
[:merge {:title "ImageShape"}
|
||||||
::shape-attrs
|
::shape-attrs
|
||||||
::image-attrs]]
|
::image-attrs
|
||||||
|
::ctsl/layout-child-attrs]]
|
||||||
|
|
||||||
[:svg-raw
|
[:svg-raw
|
||||||
[:merge {:title "SvgRawShape"}
|
[:merge {:title "SvgRawShape"}
|
||||||
::shape-attrs
|
::shape-attrs
|
||||||
::svg-raw-attrs]]
|
::svg-raw-attrs
|
||||||
|
::ctsl/layout-child-attrs]]
|
||||||
|
|
||||||
[:path
|
[:path
|
||||||
[:merge {:title "PathShape"}
|
[:merge {:title "PathShape"}
|
||||||
::shape-attrs
|
::shape-attrs
|
||||||
::path-attrs]]
|
::path-attrs
|
||||||
|
::ctsl/layout-child-attrs]]
|
||||||
|
|
||||||
[:text
|
[:text
|
||||||
[:merge {:title "TextShape"}
|
[:merge {:title "TextShape"}
|
||||||
::shape-attrs
|
::shape-attrs
|
||||||
::text-attrs]]
|
::text-attrs
|
||||||
])
|
::ctsl/layout-child-attrs]]])
|
||||||
|
|
||||||
(def shape?
|
(def shape?
|
||||||
(sm/pred-fn ::shape))
|
(sm/pred-fn ::shape))
|
||||||
@ -429,4 +437,3 @@
|
|||||||
(make-minimal-group uuid/zero geom-props (:name attrs)))
|
(make-minimal-group uuid/zero geom-props (:name attrs)))
|
||||||
(setup-shape geom-props)
|
(setup-shape geom-props)
|
||||||
(merge attrs)))
|
(merge attrs)))
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
|
[app.common.geom.shapes.grid-layout.areas :as sga]
|
||||||
[app.common.math :as mth]
|
[app.common.math :as mth]
|
||||||
[app.common.schema :as sm]
|
[app.common.schema :as sm]
|
||||||
[app.common.uuid :as uuid]))
|
[app.common.uuid :as uuid]))
|
||||||
@ -20,15 +21,16 @@
|
|||||||
;; :layout-gap ;; {:row-gap number , :column-gap number}
|
;; :layout-gap ;; {:row-gap number , :column-gap number}
|
||||||
|
|
||||||
;; :layout-align-items ;; :start :end :center :stretch
|
;; :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-align-content ;; :start :center :end :space-between :space-around :space-evenly :stretch (by default)
|
||||||
|
;; :layout-justify-items ;; :start :center :end :space-between :space-around :space-evenly
|
||||||
|
;; :layout-justify-content ;; :start :center :end :space-between :space-around :space-evenly
|
||||||
;; :layout-wrap-type ;; :wrap, :nowrap
|
;; :layout-wrap-type ;; :wrap, :nowrap
|
||||||
;; :layout-padding-type ;; :simple, :multiple
|
;; :layout-padding-type ;; :simple, :multiple
|
||||||
;; :layout-padding ;; {:p1 num :p2 num :p3 num :p4 num} number could be negative
|
;; :layout-padding ;; {:p1 num :p2 num :p3 num :p4 num} number could be negative
|
||||||
|
|
||||||
;; layout-grid-rows
|
;; layout-grid-rows ;; vector of grid-track
|
||||||
;; layout-grid-columns
|
;; layout-grid-columns ;; vector of grid-track
|
||||||
;; layout-justify-items
|
;; layout-grid-cells ;; map of id->grid-cell
|
||||||
|
|
||||||
;; ITEMS
|
;; ITEMS
|
||||||
;; :layout-item-margin ;; {:m1 0 :m2 0 :m3 0 :m4 0}
|
;; :layout-item-margin ;; {:m1 0 :m2 0 :m3 0 :m4 0}
|
||||||
@ -39,8 +41,9 @@
|
|||||||
;; :layout-item-min-h ;; num
|
;; :layout-item-min-h ;; num
|
||||||
;; :layout-item-max-w ;; num
|
;; :layout-item-max-w ;; num
|
||||||
;; :layout-item-min-w ;; num
|
;; :layout-item-min-w ;; num
|
||||||
;; :layout-item-absolute
|
;; :layout-item-absolute ;; boolean
|
||||||
;; :layout-item-z-index
|
;; :layout-item-z-index ;; int
|
||||||
|
|
||||||
|
|
||||||
(def layout-types
|
(def layout-types
|
||||||
#{:flex :grid})
|
#{:flex :grid})
|
||||||
@ -48,6 +51,9 @@
|
|||||||
(def flex-direction-types
|
(def flex-direction-types
|
||||||
#{:row :reverse-row :row-reverse :column :reverse-column :column-reverse}) ;;TODO remove reverse-column and reverse-row after script
|
#{:row :reverse-row :row-reverse :column :reverse-column :column-reverse}) ;;TODO remove reverse-column and reverse-row after script
|
||||||
|
|
||||||
|
(def grid-direction-types
|
||||||
|
#{:row :column})
|
||||||
|
|
||||||
(def gap-types
|
(def gap-types
|
||||||
#{:simple :multiple})
|
#{:simple :multiple})
|
||||||
|
|
||||||
@ -58,7 +64,7 @@
|
|||||||
#{:simple :multiple})
|
#{:simple :multiple})
|
||||||
|
|
||||||
(def justify-content-types
|
(def justify-content-types
|
||||||
#{:start :center :end :space-between :space-around :space-evenly})
|
#{:start :center :end :space-between :space-around :space-evenly :stretch})
|
||||||
|
|
||||||
(def align-content-types
|
(def align-content-types
|
||||||
#{:start :end :center :space-between :space-around :space-evenly :stretch})
|
#{:start :end :center :space-between :space-around :space-evenly :stretch})
|
||||||
@ -89,48 +95,49 @@
|
|||||||
[:layout-justify-content {:optional true} [::sm/one-of justify-content-types]]
|
[:layout-justify-content {:optional true} [::sm/one-of justify-content-types]]
|
||||||
[:layout-justify-items {:optional true} [::sm/one-of justify-items-types]]
|
[:layout-justify-items {:optional true} [::sm/one-of justify-items-types]]
|
||||||
[:layout-align-content {:optional true} [::sm/one-of align-content-types]]
|
[:layout-align-content {:optional true} [::sm/one-of align-content-types]]
|
||||||
[:layout-align-items {:optional true} [::sm/one-of align-items-types]]])
|
[:layout-align-items {:optional true} [::sm/one-of align-items-types]]
|
||||||
|
|
||||||
;; (s/def :grid/type #{:percent :flex :auto :fixed})
|
[:layout-grid-dir {:optional true} [::sm/one-of grid-direction-types]]
|
||||||
;; (s/def :grid/value (s/nilable ::us/safe-number))
|
[:layout-grid-rows {:optional true}
|
||||||
;; (s/def ::grid-definition (s/keys :req-un [:grid/type]
|
[:vector {:gen/max 2} ::grid-track]]
|
||||||
;; :opt-un [:grid/value]))
|
[:layout-grid-columns {:optional true}
|
||||||
;; (s/def ::layout-grid-rows (s/coll-of ::grid-definition :kind vector?))
|
[:vector {:gen/max 2} ::grid-track]]
|
||||||
;; (s/def ::layout-grid-columns (s/coll-of ::grid-definition :kind vector?))
|
[:layout-grid-cells {:optional true}
|
||||||
|
[:map-of {:gen/max 5} ::sm/uuid ::grid-cell]]])
|
||||||
|
|
||||||
;; (s/def :grid-cell/id uuid?)
|
;; Grid types
|
||||||
;; (s/def :grid-cell/area-name ::us/string)
|
(def grid-track-types
|
||||||
;; (s/def :grid-cell/row-start ::us/safe-integer)
|
#{:percent :flex :auto :fixed})
|
||||||
;; (s/def :grid-cell/row-span ::us/safe-integer)
|
|
||||||
;; (s/def :grid-cell/column-start ::us/safe-integer)
|
|
||||||
;; (s/def :grid-cell/column-span ::us/safe-integer)
|
|
||||||
;; (s/def :grid-cell/position #{:auto :manual :area})
|
|
||||||
;; (s/def :grid-cell/align-self #{:auto :start :end :center :stretch})
|
|
||||||
;; (s/def :grid-cell/justify-self #{:auto :start :end :center :stretch})
|
|
||||||
;; (s/def :grid-cell/shapes (s/coll-of uuid?))
|
|
||||||
|
|
||||||
;; (s/def ::grid-cell (s/keys :opt-un [:grid-cell/id
|
(def grid-position-types
|
||||||
;; :grid-cell/area-name
|
#{:auto :manual :area})
|
||||||
;; :grid-cell/row-start
|
|
||||||
;; :grid-cell/row-span
|
|
||||||
;; :grid-cell/column-start
|
|
||||||
;; :grid-cell/column-span
|
|
||||||
;; :grid-cell/position ;; auto, manual, area
|
|
||||||
;; :grid-cell/align-self
|
|
||||||
;; :grid-cell/justify-self
|
|
||||||
;; :grid-cell/shapes]))
|
|
||||||
;; (s/def ::layout-grid-cells (s/map-of uuid? ::grid-cell))
|
|
||||||
|
|
||||||
;; (s/def ::layout-container-props
|
(def grid-cell-align-self-types
|
||||||
;; (s/keys :opt-un [
|
#{:auto :start :center :end :stretch})
|
||||||
;; ;; grid
|
|
||||||
;; ::layout-grid-dir
|
|
||||||
;; ::layout-justify-items
|
|
||||||
;; ::layout-grid-rows
|
|
||||||
;; ::layout-grid-columns
|
|
||||||
;; ::layout-grid-cells
|
|
||||||
;; ]))
|
|
||||||
|
|
||||||
|
(def grid-cell-justify-self-types
|
||||||
|
#{:auto :start :center :end :stretch})
|
||||||
|
|
||||||
|
(sm/def! ::grid-cell
|
||||||
|
[:map {:title "GridCell"}
|
||||||
|
[:id ::sm/uuid]
|
||||||
|
[:area-name {:optional true} :string]
|
||||||
|
[:row ::sm/safe-int]
|
||||||
|
[:row-span ::sm/safe-int]
|
||||||
|
[:column ::sm/safe-int]
|
||||||
|
[:column-span ::sm/safe-int]
|
||||||
|
[:position {:optional true} [::sm/one-of grid-position-types]]
|
||||||
|
[:align-self {:optional true} [::sm/one-of grid-cell-align-self-types]]
|
||||||
|
[:justify-self {:optional true} [::sm/one-of grid-cell-justify-self-types]]
|
||||||
|
[:shapes
|
||||||
|
[:vector {:gen/max 1} ::sm/uuid]]])
|
||||||
|
|
||||||
|
(sm/def! ::grid-track
|
||||||
|
[:map {:title "GridTrack"}
|
||||||
|
[:type [::sm/one-of grid-track-types]]
|
||||||
|
[:value {:optional true} [:maybe ::sm/safe-number]]])
|
||||||
|
|
||||||
|
;; LAYOUT CHILDREN
|
||||||
|
|
||||||
(def item-margin-types
|
(def item-margin-types
|
||||||
#{:simple :multiple})
|
#{:simple :multiple})
|
||||||
@ -163,13 +170,7 @@
|
|||||||
[:layout-item-absolute {:optional true} :boolean]
|
[:layout-item-absolute {:optional true} :boolean]
|
||||||
[:layout-item-z-index {:optional true} ::sm/safe-number]])
|
[:layout-item-z-index {:optional true} ::sm/safe-number]])
|
||||||
|
|
||||||
(def schema:grid-definition
|
(def grid-track? (sm/pred-fn ::grid-track))
|
||||||
[:map {:title "LayoutGridDefinition"}
|
|
||||||
[:type [::sm/one-of #{:percent :flex :auto :fixed}]]
|
|
||||||
[:value {:optional true} [:maybe ::sm/safe-int]]])
|
|
||||||
|
|
||||||
(def grid-definition?
|
|
||||||
(sm/pred-fn schema:grid-definition))
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; SCHEMAS
|
;; SCHEMAS
|
||||||
@ -207,6 +208,11 @@
|
|||||||
parent (get objects parent-id)]
|
parent (get objects parent-id)]
|
||||||
(flex-layout? parent)))
|
(flex-layout? parent)))
|
||||||
|
|
||||||
|
(defn grid-layout-immediate-child? [objects shape]
|
||||||
|
(let [parent-id (:parent-id shape)
|
||||||
|
parent (get objects parent-id)]
|
||||||
|
(grid-layout? parent)))
|
||||||
|
|
||||||
(defn any-layout-immediate-child? [objects shape]
|
(defn any-layout-immediate-child? [objects shape]
|
||||||
(let [parent-id (:parent-id shape)
|
(let [parent-id (:parent-id shape)
|
||||||
parent (get objects parent-id)]
|
parent (get objects parent-id)]
|
||||||
@ -217,6 +223,11 @@
|
|||||||
parent (get objects parent-id)]
|
parent (get objects parent-id)]
|
||||||
(flex-layout? parent)))
|
(flex-layout? parent)))
|
||||||
|
|
||||||
|
(defn grid-layout-immediate-child-id? [objects id]
|
||||||
|
(let [parent-id (dm/get-in objects [id :parent-id])
|
||||||
|
parent (get objects parent-id)]
|
||||||
|
(grid-layout? parent)))
|
||||||
|
|
||||||
(defn any-layout-immediate-child-id? [objects id]
|
(defn any-layout-immediate-child-id? [objects id]
|
||||||
(let [parent-id (dm/get-in objects [id :parent-id])
|
(let [parent-id (dm/get-in objects [id :parent-id])
|
||||||
parent (get objects parent-id)]
|
parent (get objects parent-id)]
|
||||||
@ -298,32 +309,39 @@
|
|||||||
layout-gap-col (or (-> layout-gap :column-gap (mth/finite 0)) 0)]
|
layout-gap-col (or (-> layout-gap :column-gap (mth/finite 0)) 0)]
|
||||||
[layout-gap-row layout-gap-col]))
|
[layout-gap-row layout-gap-col]))
|
||||||
|
|
||||||
|
(defn paddings
|
||||||
|
[{:keys [layout-padding-type layout-padding]}]
|
||||||
|
(let [{pad-top :p1 pad-right :p2 pad-bottom :p3 pad-left :p4} layout-padding]
|
||||||
|
(if (= :simple layout-padding-type)
|
||||||
|
[pad-top pad-right pad-top pad-right]
|
||||||
|
[pad-top pad-right pad-bottom pad-left])))
|
||||||
|
|
||||||
(defn child-min-width
|
(defn child-min-width
|
||||||
[child]
|
[child]
|
||||||
(if (and (fill-width? child)
|
(if (and (fill-width? child)
|
||||||
(some? (:layout-item-min-w child)))
|
(some? (:layout-item-min-w child)))
|
||||||
(max 0 (:layout-item-min-w child))
|
(max 0.01 (:layout-item-min-w child))
|
||||||
0))
|
0.01))
|
||||||
|
|
||||||
(defn child-max-width
|
(defn child-max-width
|
||||||
[child]
|
[child]
|
||||||
(if (and (fill-width? child)
|
(if (and (fill-width? child)
|
||||||
(some? (:layout-item-max-w child)))
|
(some? (:layout-item-max-w child)))
|
||||||
(max 0 (:layout-item-max-w child))
|
(max 0.01 (:layout-item-max-w child))
|
||||||
##Inf))
|
##Inf))
|
||||||
|
|
||||||
(defn child-min-height
|
(defn child-min-height
|
||||||
[child]
|
[child]
|
||||||
(if (and (fill-height? child)
|
(if (and (fill-height? child)
|
||||||
(some? (:layout-item-min-h child)))
|
(some? (:layout-item-min-h child)))
|
||||||
(max 0 (:layout-item-min-h child))
|
(max 0.01 (:layout-item-min-h child))
|
||||||
0))
|
0.01))
|
||||||
|
|
||||||
(defn child-max-height
|
(defn child-max-height
|
||||||
[child]
|
[child]
|
||||||
(if (and (fill-height? child)
|
(if (and (fill-height? child)
|
||||||
(some? (:layout-item-max-h child)))
|
(some? (:layout-item-max-h child)))
|
||||||
(max 0 (:layout-item-max-h child))
|
(max 0.01 (:layout-item-max-h child))
|
||||||
##Inf))
|
##Inf))
|
||||||
|
|
||||||
(defn child-margins
|
(defn child-margins
|
||||||
@ -506,11 +524,11 @@
|
|||||||
:layout-wrap-type
|
:layout-wrap-type
|
||||||
:layout-padding-type
|
:layout-padding-type
|
||||||
:layout-padding
|
:layout-padding
|
||||||
|
:layout-align-content
|
||||||
:layout-justify-content
|
:layout-justify-content
|
||||||
:layout-align-items
|
:layout-align-items
|
||||||
:layout-align-content
|
|
||||||
:layout-grid-dir
|
|
||||||
:layout-justify-items
|
:layout-justify-items
|
||||||
|
:layout-grid-dir
|
||||||
:layout-grid-columns
|
:layout-grid-columns
|
||||||
:layout-grid-rows
|
:layout-grid-rows
|
||||||
))
|
))
|
||||||
@ -552,9 +570,11 @@
|
|||||||
(d/update-in-when [:layout-item-margin :m3] * scale)
|
(d/update-in-when [:layout-item-margin :m3] * scale)
|
||||||
(d/update-in-when [:layout-item-margin :m4] * scale)))
|
(d/update-in-when [:layout-item-margin :m4] * scale)))
|
||||||
|
|
||||||
|
|
||||||
(declare assign-cells)
|
(declare assign-cells)
|
||||||
|
|
||||||
|
(def default-track-value
|
||||||
|
{:type :auto})
|
||||||
|
|
||||||
(def grid-cell-defaults
|
(def grid-cell-defaults
|
||||||
{:row-span 1
|
{:row-span 1
|
||||||
:column-span 1
|
:column-span 1
|
||||||
@ -563,27 +583,24 @@
|
|||||||
:justify-self :auto
|
:justify-self :auto
|
||||||
:shapes []})
|
:shapes []})
|
||||||
|
|
||||||
;; TODO: GRID ASSIGNMENTS
|
|
||||||
|
|
||||||
;; Adding a track creates the cells. We should check the shapes that are not tracked (with default values) and assign to the correct tracked values
|
;; Adding a track creates the cells. We should check the shapes that are not tracked (with default values) and assign to the correct tracked values
|
||||||
(defn add-grid-column
|
(defn add-grid-column
|
||||||
[parent value]
|
[parent value]
|
||||||
(dm/assert!
|
(dm/assert!
|
||||||
"expected a valid grid definition for `value`"
|
"expected a valid grid definition for `value`"
|
||||||
(grid-definition? value))
|
(grid-track? value))
|
||||||
|
|
||||||
(let [rows (:layout-grid-rows parent)
|
(let [rows (:layout-grid-rows parent)
|
||||||
new-col-num (count (:layout-grid-columns parent))
|
new-col-num (inc (count (:layout-grid-columns parent)))
|
||||||
|
|
||||||
layout-grid-cells
|
layout-grid-cells
|
||||||
(->> (d/enumerate rows)
|
(->> (d/enumerate rows)
|
||||||
(reduce (fn [result [row-idx _row]]
|
(reduce (fn [result [row-idx _]]
|
||||||
(let [id (uuid/next)]
|
(let [id (uuid/next)]
|
||||||
(assoc result id
|
(assoc result id
|
||||||
(merge {:id id
|
(merge {:id id
|
||||||
:row (inc row-idx)
|
:row (inc row-idx)
|
||||||
:column new-col-num
|
:column new-col-num}
|
||||||
:track? true}
|
|
||||||
grid-cell-defaults))))
|
grid-cell-defaults))))
|
||||||
(:layout-grid-cells parent)))]
|
(:layout-grid-cells parent)))]
|
||||||
(-> parent
|
(-> parent
|
||||||
@ -594,45 +611,170 @@
|
|||||||
[parent value]
|
[parent value]
|
||||||
(dm/assert!
|
(dm/assert!
|
||||||
"expected a valid grid definition for `value`"
|
"expected a valid grid definition for `value`"
|
||||||
(grid-definition? value))
|
(grid-track? value))
|
||||||
|
|
||||||
(let [cols (:layout-grid-columns parent)
|
(let [cols (:layout-grid-columns parent)
|
||||||
new-row-num (inc (count (:layout-grid-rows parent)))
|
new-row-num (inc (count (:layout-grid-rows parent)))
|
||||||
|
|
||||||
layout-grid-cells
|
layout-grid-cells
|
||||||
(->> (d/enumerate cols)
|
(->> (d/enumerate cols)
|
||||||
(reduce (fn [result [col-idx _col]]
|
(reduce (fn [result [col-idx _]]
|
||||||
(let [id (uuid/next)]
|
(let [id (uuid/next)]
|
||||||
(assoc result id
|
(assoc result id
|
||||||
(merge {:id id
|
(merge {:id id
|
||||||
:column (inc col-idx)
|
:column (inc col-idx)
|
||||||
:row new-row-num
|
:row new-row-num}
|
||||||
:track? true}
|
|
||||||
grid-cell-defaults))))
|
grid-cell-defaults))))
|
||||||
(:layout-grid-cells parent)))]
|
(:layout-grid-cells parent)))]
|
||||||
(-> parent
|
(-> parent
|
||||||
(update :layout-grid-rows (fnil conj []) value)
|
(update :layout-grid-rows (fnil conj []) value)
|
||||||
(assoc :layout-grid-cells layout-grid-cells))))
|
(assoc :layout-grid-cells layout-grid-cells))))
|
||||||
|
|
||||||
;; TODO: Remove a track and its corresponding cells. We need to reassign the orphaned shapes into not-tracked cells
|
|
||||||
|
(defn make-remove-cell
|
||||||
|
[attr span-attr track-num]
|
||||||
|
(fn [[_ cell]]
|
||||||
|
;; Only remove cells with span=1 otherwise the cell will be fixed
|
||||||
|
(and (= track-num (get cell attr))
|
||||||
|
(= (get cell span-attr) 1))))
|
||||||
|
|
||||||
|
(defn make-decrease-track-num
|
||||||
|
[attr span-attr track-num]
|
||||||
|
(fn [[id cell]]
|
||||||
|
(let [inner-track?
|
||||||
|
(or (= track-num (get cell attr))
|
||||||
|
(< (get cell attr) track-num (+ (get cell attr) (get cell span-attr))))
|
||||||
|
|
||||||
|
displace-cell?
|
||||||
|
(and (not inner-track?) (< track-num (get cell attr)))
|
||||||
|
|
||||||
|
cell
|
||||||
|
(cond-> cell
|
||||||
|
inner-track?
|
||||||
|
(update span-attr dec)
|
||||||
|
|
||||||
|
displace-cell?
|
||||||
|
(update attr dec))]
|
||||||
|
|
||||||
|
[id cell])))
|
||||||
|
|
||||||
(defn remove-grid-column
|
(defn remove-grid-column
|
||||||
[parent _index]
|
[parent index]
|
||||||
parent)
|
|
||||||
|
(let [track-num (inc index)
|
||||||
|
|
||||||
|
decrease-track-num (make-decrease-track-num :column :column-span track-num)
|
||||||
|
remove-track? (make-remove-cell :column :column-span track-num)
|
||||||
|
|
||||||
|
update-cells
|
||||||
|
(fn [cells]
|
||||||
|
(into {}
|
||||||
|
(comp (remove remove-track?)
|
||||||
|
(map decrease-track-num))
|
||||||
|
cells))]
|
||||||
|
(-> parent
|
||||||
|
(update :layout-grid-columns d/remove-at-index index)
|
||||||
|
(update :layout-grid-cells update-cells)
|
||||||
|
(assign-cells))))
|
||||||
|
|
||||||
(defn remove-grid-row
|
(defn remove-grid-row
|
||||||
[parent _index]
|
[parent index]
|
||||||
parent)
|
(let [track-num (inc index)
|
||||||
|
|
||||||
;; TODO: Mix the cells given as arguments leaving only one. It should move all the shapes in those cells in the direction for the grid
|
decrease-track-num (make-decrease-track-num :row :row-span track-num)
|
||||||
;; and lastly use assign-cells to reassing the orphaned shapes
|
remove-track? (make-remove-cell :row :row-span track-num)
|
||||||
(defn merge-cells
|
|
||||||
[parent _cells]
|
|
||||||
parent)
|
|
||||||
|
|
||||||
|
update-cells
|
||||||
|
(fn [cells]
|
||||||
|
(into {}
|
||||||
|
(comp (remove remove-track?)
|
||||||
|
(map decrease-track-num))
|
||||||
|
cells))]
|
||||||
|
(-> parent
|
||||||
|
(update :layout-grid-rows d/remove-at-index index)
|
||||||
|
(update :layout-grid-cells update-cells)
|
||||||
|
(assign-cells))))
|
||||||
|
|
||||||
|
(defn get-cells
|
||||||
|
([parent]
|
||||||
|
(get-cells parent nil))
|
||||||
|
|
||||||
|
([{:keys [layout-grid-cells layout-grid-dir]} {:keys [sort? remove-empty?] :or {sort? false remove-empty? false}}]
|
||||||
|
(let [comp-fn (if (= layout-grid-dir :row)
|
||||||
|
(juxt :row :column)
|
||||||
|
(juxt :column :row))
|
||||||
|
|
||||||
|
maybe-sort?
|
||||||
|
(if sort? (partial sort-by (comp comp-fn second)) identity)
|
||||||
|
|
||||||
|
maybe-remove?
|
||||||
|
(if remove-empty? (partial remove #(empty? (:shapes (second %)))) identity)]
|
||||||
|
|
||||||
|
(->> layout-grid-cells
|
||||||
|
(maybe-sort?)
|
||||||
|
(maybe-remove?)
|
||||||
|
(map (fn [[id cell]] (assoc cell :id id)))))))
|
||||||
|
|
||||||
|
(defn get-free-cells
|
||||||
|
([parent]
|
||||||
|
(get-free-cells parent nil))
|
||||||
|
|
||||||
|
([{:keys [layout-grid-cells layout-grid-dir]} {:keys [sort?] :or {sort? false}}]
|
||||||
|
(let [comp-fn (if (= layout-grid-dir :row)
|
||||||
|
(juxt :row :column)
|
||||||
|
(juxt :column :row))
|
||||||
|
|
||||||
|
maybe-sort?
|
||||||
|
(if sort? (partial sort-by (comp comp-fn second)) identity)]
|
||||||
|
|
||||||
|
(->> layout-grid-cells
|
||||||
|
(filter (comp empty? :shapes second))
|
||||||
|
(maybe-sort?)
|
||||||
|
(map first)))))
|
||||||
|
|
||||||
|
(defn check-deassigned-cells
|
||||||
|
"Clean the cells whith shapes that are no longer in the layout"
|
||||||
|
[parent]
|
||||||
|
|
||||||
|
(let [child? (set (:shapes parent))
|
||||||
|
cells (update-vals
|
||||||
|
(:layout-grid-cells parent)
|
||||||
|
(fn [cell] (update cell :shapes #(filterv child? %))))]
|
||||||
|
|
||||||
|
(assoc parent :layout-grid-cells cells)))
|
||||||
|
|
||||||
|
(defn overlapping-cells
|
||||||
|
"Find overlapping cells"
|
||||||
|
[parent]
|
||||||
|
(let [cells (->> parent
|
||||||
|
:layout-grid-cells
|
||||||
|
(map (fn [[id cell]]
|
||||||
|
[id (sga/make-area cell)])))
|
||||||
|
find-overlaps
|
||||||
|
(fn [result [id area]]
|
||||||
|
(let [[fid _]
|
||||||
|
(d/seek #(and (not= (first %) id)
|
||||||
|
(sga/intersects? (second %) area))
|
||||||
|
cells)]
|
||||||
|
(cond-> result
|
||||||
|
(some? fid)
|
||||||
|
(conj #{id fid}))))]
|
||||||
|
(reduce find-overlaps #{} cells)))
|
||||||
|
|
||||||
|
;; FIXME: This is only for development
|
||||||
|
#_(defn fix-overlaps
|
||||||
|
[parent overlaps]
|
||||||
|
(reduce (fn [parent ids]
|
||||||
|
(let [id (if (empty? (get-in parent [:layout-grid-cells (first ids)]))
|
||||||
|
(first ids)
|
||||||
|
(second ids))]
|
||||||
|
(update parent :layout-grid-cells dissoc id)))
|
||||||
|
parent
|
||||||
|
overlaps))
|
||||||
|
|
||||||
;; TODO
|
|
||||||
;; Assign cells takes the children and move them into the allotted cells. If there are not enough cells it creates
|
;; Assign cells takes the children and move them into the allotted cells. If there are not enough cells it creates
|
||||||
;; not-tracked rows/columns and put the shapes there
|
;; not-tracked rows/columns and put the shapes there
|
||||||
|
;; Non-tracked tracks need to be deleted when they are empty and there are no more shapes unallocated
|
||||||
;; Should be caled each time a child can be added like:
|
;; Should be caled each time a child can be added like:
|
||||||
;; - On shape creation
|
;; - On shape creation
|
||||||
;; - When moving a child from layers
|
;; - When moving a child from layers
|
||||||
@ -641,9 +783,289 @@
|
|||||||
;; - (maybe) create group/frames. This case will assigna a cell that had one of its children
|
;; - (maybe) create group/frames. This case will assigna a cell that had one of its children
|
||||||
(defn assign-cells
|
(defn assign-cells
|
||||||
[parent]
|
[parent]
|
||||||
#_(let [allocated-shapes
|
(let [parent (-> parent check-deassigned-cells)
|
||||||
(into #{} (mapcat :shapes) (:layout-grid-cells parent))
|
|
||||||
|
shape-has-cell?
|
||||||
|
(into #{} (mapcat (comp :shapes second)) (:layout-grid-cells parent))
|
||||||
|
|
||||||
no-cell-shapes
|
no-cell-shapes
|
||||||
(->> (:shapes parent) (remove allocated-shapes))])
|
(->> (:shapes parent) (remove shape-has-cell?))]
|
||||||
parent)
|
|
||||||
|
(if (empty? no-cell-shapes)
|
||||||
|
;; All shapes are within a cell. No need to assign
|
||||||
|
parent
|
||||||
|
|
||||||
|
(let [;; We need to have at least 1 col and 1 row otherwise we can't assign
|
||||||
|
parent
|
||||||
|
(cond-> parent
|
||||||
|
(empty? (:layout-grid-columns parent))
|
||||||
|
(add-grid-column default-track-value)
|
||||||
|
|
||||||
|
(empty? (:layout-grid-rows parent))
|
||||||
|
(add-grid-row default-track-value))
|
||||||
|
|
||||||
|
;; Free cells should be ordered columns/rows depending on the parameter
|
||||||
|
;; in the parent
|
||||||
|
free-cells (get-free-cells parent)
|
||||||
|
|
||||||
|
to-add-tracks
|
||||||
|
(if (= (:layout-grid-dir parent) :row)
|
||||||
|
(mth/ceil (/ (- (count no-cell-shapes) (count free-cells)) (count (:layout-grid-rows parent))))
|
||||||
|
(mth/ceil (/ (- (count no-cell-shapes) (count free-cells)) (count (:layout-grid-columns parent)))))
|
||||||
|
|
||||||
|
add-track (if (= (:layout-grid-dir parent) :row) add-grid-column add-grid-row)
|
||||||
|
|
||||||
|
parent
|
||||||
|
(->> (range to-add-tracks)
|
||||||
|
(reduce (fn [parent _] (add-track parent default-track-value)) parent))
|
||||||
|
|
||||||
|
cells
|
||||||
|
(loop [cells (:layout-grid-cells parent)
|
||||||
|
free-cells (get-free-cells parent {:sort? true})
|
||||||
|
pending no-cell-shapes]
|
||||||
|
(if (or (empty? free-cells) (empty? pending))
|
||||||
|
cells
|
||||||
|
(let [next-free (first free-cells)
|
||||||
|
current (first pending)
|
||||||
|
cells (update-in cells [next-free :shapes] conj current)]
|
||||||
|
(recur cells (rest free-cells) (rest pending)))))]
|
||||||
|
|
||||||
|
;; TODO: Remove after testing
|
||||||
|
(assert (empty? (overlapping-cells parent)) (dm/str (overlapping-cells parent)))
|
||||||
|
(assoc parent :layout-grid-cells cells)))))
|
||||||
|
|
||||||
|
(defn free-cell-push
|
||||||
|
"Frees the cell at index and push the shapes in the order given by the `cells` attribute"
|
||||||
|
[parent cells index]
|
||||||
|
|
||||||
|
(let [start-cell (get cells index)]
|
||||||
|
(if (empty? (:shapes start-cell))
|
||||||
|
[parent cells]
|
||||||
|
(let [[parent result-cells]
|
||||||
|
(loop [parent parent
|
||||||
|
result-cells cells
|
||||||
|
idx index]
|
||||||
|
|
||||||
|
(if (> idx (- (count cells) 2))
|
||||||
|
[parent result-cells]
|
||||||
|
|
||||||
|
(let [cell-from (get cells idx)
|
||||||
|
cell-to (get cells (inc idx))
|
||||||
|
cell (assoc cell-to :shapes (:shapes cell-from))
|
||||||
|
parent (assoc-in parent [:layout-grid-cells (:id cell)] cell)
|
||||||
|
result-cells (assoc result-cells (inc idx) cell)]
|
||||||
|
|
||||||
|
(if (empty? (:shapes cell-to))
|
||||||
|
;; to-cell was empty, so we've finished and every cell allocated
|
||||||
|
[parent result-cells]
|
||||||
|
|
||||||
|
;; otherwise keep pushing cells
|
||||||
|
(recur parent result-cells (inc idx))))))]
|
||||||
|
|
||||||
|
[(assoc-in parent [:layout-grid-cells (get-in cells [index :id]) :shapes] [])
|
||||||
|
(assoc-in result-cells [index :shapes] [])]))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn in-cell?
|
||||||
|
"Given a cell check if the row+column is inside this cell"
|
||||||
|
[{cell-row :row cell-column :column :keys [row-span column-span]} row column]
|
||||||
|
(and (>= row cell-row)
|
||||||
|
(>= column cell-column)
|
||||||
|
(<= row (+ cell-row row-span -1))
|
||||||
|
(<= column (+ cell-column column-span -1))))
|
||||||
|
|
||||||
|
(defn cell-by-row-column
|
||||||
|
[parent row column]
|
||||||
|
(->> (:layout-grid-cells parent)
|
||||||
|
(vals)
|
||||||
|
(d/seek #(in-cell? % row column))))
|
||||||
|
|
||||||
|
(defn seek-indexed-cell
|
||||||
|
[cells row column]
|
||||||
|
(let [cells+index (d/enumerate cells)]
|
||||||
|
(d/seek #(in-cell? (second %) row column) cells+index)))
|
||||||
|
|
||||||
|
(defn push-into-cell
|
||||||
|
"Push the shapes into the row/column cell and moves the rest"
|
||||||
|
[parent shape-ids row column]
|
||||||
|
|
||||||
|
(let [cells (vec (get-cells parent {:sort? true}))
|
||||||
|
[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
|
||||||
|
to-index (min (+ start-index (count shape-ids)) (dec (count cells)))]
|
||||||
|
|
||||||
|
;; Move shift the `shapes` attribute between cells
|
||||||
|
(->> (range start-index (inc to-index))
|
||||||
|
(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])
|
||||||
|
cells]))
|
||||||
|
[parent cells])
|
||||||
|
(first)))
|
||||||
|
parent)))
|
||||||
|
|
||||||
|
(defn create-cells
|
||||||
|
"Create cells in an area. One cell per row/column "
|
||||||
|
[parent [column row column-span row-span]]
|
||||||
|
|
||||||
|
(->> (for [row (range row (+ row row-span))
|
||||||
|
column (range column (+ column column-span))]
|
||||||
|
(merge grid-cell-defaults
|
||||||
|
{:id (uuid/next)
|
||||||
|
:row row
|
||||||
|
:column column
|
||||||
|
:row-span 1
|
||||||
|
:column-span 1}))
|
||||||
|
(reduce #(assoc-in %1 [:layout-grid-cells (:id %2)] %2) parent)))
|
||||||
|
|
||||||
|
(defn resize-cell-area
|
||||||
|
"Increases/decreases the cell size"
|
||||||
|
[parent row column new-row new-column new-row-span new-column-span]
|
||||||
|
|
||||||
|
(if (and (>= new-row 0)
|
||||||
|
(>= new-column 0)
|
||||||
|
(>= new-row-span 1)
|
||||||
|
(>= new-column-span 1))
|
||||||
|
(let [prev-cell (cell-by-row-column parent row column)
|
||||||
|
prev-area (sga/make-area prev-cell)
|
||||||
|
|
||||||
|
target-cell
|
||||||
|
(-> prev-cell
|
||||||
|
(assoc
|
||||||
|
:row new-row
|
||||||
|
:column new-column
|
||||||
|
:row-span new-row-span
|
||||||
|
:column-span new-column-span))
|
||||||
|
|
||||||
|
target-area (sga/make-area target-cell)
|
||||||
|
|
||||||
|
;; Create columns/rows if necessary
|
||||||
|
parent
|
||||||
|
(->> (range (count (:layout-grid-columns parent))
|
||||||
|
(+ new-column new-column-span -1))
|
||||||
|
(reduce (fn [parent _] (add-grid-column parent default-track-value)) parent))
|
||||||
|
|
||||||
|
parent
|
||||||
|
(->> (range (count (:layout-grid-rows parent))
|
||||||
|
(+ new-row new-row-span -1))
|
||||||
|
(reduce (fn [parent _] (add-grid-row parent default-track-value)) parent))
|
||||||
|
|
||||||
|
parent (create-cells parent prev-area)
|
||||||
|
|
||||||
|
cells (vec (get-cells parent {:sort? true}))
|
||||||
|
remove-cells
|
||||||
|
(->> cells
|
||||||
|
(filter #(and (not= (:id target-cell) (:id %))
|
||||||
|
(sga/contains? target-area (sga/make-area %))))
|
||||||
|
(into #{}))
|
||||||
|
|
||||||
|
split-cells
|
||||||
|
(->> cells
|
||||||
|
(filter #(and (not= (:id target-cell) (:id %))
|
||||||
|
(not (contains? remove-cells %))
|
||||||
|
(sga/intersects? target-area (sga/make-area %)))))
|
||||||
|
|
||||||
|
[parent _]
|
||||||
|
(->> (d/enumerate cells)
|
||||||
|
(reduce (fn [[parent cells] [index cur-cell]]
|
||||||
|
(if (contains? remove-cells cur-cell)
|
||||||
|
(let [[parent cells] (free-cell-push parent cells index)]
|
||||||
|
[parent (conj cells cur-cell)])
|
||||||
|
[parent cells]))
|
||||||
|
[parent cells]))
|
||||||
|
|
||||||
|
parent
|
||||||
|
(-> parent
|
||||||
|
(assoc-in [:layout-grid-cells (:id target-cell)] target-cell))
|
||||||
|
|
||||||
|
parent
|
||||||
|
(->> remove-cells
|
||||||
|
(reduce (fn [parent cell]
|
||||||
|
(update parent :layout-grid-cells dissoc (:id cell)))
|
||||||
|
parent))
|
||||||
|
|
||||||
|
parent
|
||||||
|
(->> split-cells
|
||||||
|
(reduce (fn [parent cell]
|
||||||
|
(let [new-areas (sga/difference (sga/make-area cell) target-area)]
|
||||||
|
(as-> parent $
|
||||||
|
(update-in $ [:layout-grid-cells (:id cell)] merge (sga/area->cell-props (first new-areas)))
|
||||||
|
(reduce (fn [parent area]
|
||||||
|
(let [cell (merge (assoc grid-cell-defaults :id (uuid/next)) (sga/area->cell-props area))]
|
||||||
|
(assoc-in parent [:layout-grid-cells (:id cell)] cell))) $ new-areas))))
|
||||||
|
parent))]
|
||||||
|
parent)
|
||||||
|
|
||||||
|
;; Not valid resize: we don't alter the layout
|
||||||
|
parent))
|
||||||
|
|
||||||
|
|
||||||
|
(defn get-cell-by-position
|
||||||
|
[parent target-row target-column]
|
||||||
|
(->> (:layout-grid-cells parent)
|
||||||
|
(d/seek
|
||||||
|
(fn [[_ {:keys [column row column-span row-span]}]]
|
||||||
|
(and (>= target-row row)
|
||||||
|
(>= target-column column)
|
||||||
|
(< target-column (+ column column-span))
|
||||||
|
(< target-row (+ row row-span)))))
|
||||||
|
(second)))
|
||||||
|
|
||||||
|
(defn get-cell-by-shape-id
|
||||||
|
[parent shape-id]
|
||||||
|
(->> (:layout-grid-cells parent)
|
||||||
|
(d/seek
|
||||||
|
(fn [[_ {:keys [shapes]}]]
|
||||||
|
(contains? (set shapes) shape-id)))
|
||||||
|
(second)))
|
||||||
|
|
||||||
|
(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]))))
|
||||||
|
|
||||||
|
(defn add-children-to-cell
|
||||||
|
[frame children objects [row column :as cell]]
|
||||||
|
(let [;; Temporary remove the children when moving them
|
||||||
|
frame (-> frame
|
||||||
|
(update :shapes #(d/removev children %))
|
||||||
|
(assign-cells))
|
||||||
|
|
||||||
|
children (->> children (remove #(layout-absolute? objects %)))]
|
||||||
|
|
||||||
|
(-> frame
|
||||||
|
(update :shapes d/concat-vec children)
|
||||||
|
(cond-> (some? cell)
|
||||||
|
(push-into-cell children row column))
|
||||||
|
(assign-cells))))
|
||||||
|
|
||||||
|
(defn add-children-to-index
|
||||||
|
[parent ids objects to-index]
|
||||||
|
(let [ids (into (d/ordered-set) ids)
|
||||||
|
cells (get-cells parent {:sort? true :remove-empty? true})
|
||||||
|
to-index (- (count cells) to-index)
|
||||||
|
target-cell (nth cells to-index nil)]
|
||||||
|
|
||||||
|
(cond-> parent
|
||||||
|
(some? target-cell)
|
||||||
|
(add-children-to-cell ids objects [(:row target-cell) (:column target-cell)]))))
|
||||||
|
|
||||||
|
(defn reorder-grid-children
|
||||||
|
[parent]
|
||||||
|
(let [cells (get-cells parent {:sort? true})
|
||||||
|
child? (set (:shapes parent))
|
||||||
|
new-shapes
|
||||||
|
(into (d/ordered-set)
|
||||||
|
(comp (keep (comp first :shapes))
|
||||||
|
(filter child?))
|
||||||
|
cells)
|
||||||
|
|
||||||
|
;; Add the children that are not in cells (absolute positioned for example)
|
||||||
|
new-shapes (into new-shapes (:shapes parent))]
|
||||||
|
|
||||||
|
(assoc parent :shapes (into [] (reverse new-shapes)))))
|
||||||
|
|||||||
@ -123,6 +123,10 @@
|
|||||||
padding: $size-1;
|
padding: $size-1;
|
||||||
svg {
|
svg {
|
||||||
fill: $color-gray-20;
|
fill: $color-gray-20;
|
||||||
|
|
||||||
|
&.icon-close {
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
&:hover {
|
&:hover {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
|||||||
@ -104,11 +104,7 @@ $width-settings-bar: 256px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.settings-bar {
|
.settings-bar {
|
||||||
transition: width 0.2s;
|
|
||||||
width: $width-settings-bar;
|
width: $width-settings-bar;
|
||||||
&.expanded {
|
|
||||||
width: $width-settings-bar * 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.settings-bar-right,
|
&.settings-bar-right,
|
||||||
&.settings-bar-left {
|
&.settings-bar-left {
|
||||||
@ -122,6 +118,10 @@ $width-settings-bar: 256px;
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.settings-bar-right {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.inspect-svg-wrapper {
|
.inspect-svg-wrapper {
|
||||||
@ -142,3 +142,19 @@ $width-settings-bar: 256px;
|
|||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: var(--width, $width-settings-bar);
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
& > .resize-area {
|
||||||
|
position: absolute;
|
||||||
|
width: 8px;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 10;
|
||||||
|
cursor: ew-resize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -328,9 +328,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.code-block {
|
.code-block {
|
||||||
|
position: relative;
|
||||||
margin-top: 0.5rem;
|
margin-top: 0.5rem;
|
||||||
border-top: 1px solid $color-gray-60;
|
border-top: 1px solid $color-gray-60;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
.code-row-lang {
|
.code-row-lang {
|
||||||
.expand-button,
|
.expand-button,
|
||||||
@ -353,17 +358,85 @@
|
|||||||
.copy-button {
|
.copy-button {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.custom-select {
|
||||||
|
border: 1px solid $color-gray-40;
|
||||||
|
border-radius: 3px;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0.25rem 1.5rem 0.25rem 0.25rem;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.dropdown-button {
|
||||||
|
position: absolute;
|
||||||
|
right: 0.25rem;
|
||||||
|
top: 7px;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
fill: $color-gray-40;
|
||||||
|
height: 10px;
|
||||||
|
width: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-select-dropdown {
|
||||||
|
background-color: $color-white;
|
||||||
|
border-radius: 3px;
|
||||||
|
box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.25);
|
||||||
|
left: 0;
|
||||||
|
max-height: 30rem;
|
||||||
|
min-width: 7rem;
|
||||||
|
position: absolute;
|
||||||
|
overflow-y: auto;
|
||||||
|
top: 30px;
|
||||||
|
z-index: 12;
|
||||||
|
|
||||||
|
li {
|
||||||
|
color: $color-gray-60;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
display: flex;
|
||||||
|
gap: 0 10px;
|
||||||
|
justify-content: flex-start;
|
||||||
|
padding: 0.5rem;
|
||||||
|
|
||||||
|
.checked-element {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
visibility: hidden;
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
background: none;
|
||||||
|
margin: 0.25rem;
|
||||||
|
fill: $color-black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-selected svg {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.code-row-display {
|
.code-row-display {
|
||||||
|
line-height: 1;
|
||||||
margin: 0.5rem;
|
margin: 0.5rem;
|
||||||
font-size: $fs14;
|
font-size: $fs14;
|
||||||
|
max-height: var(--code-height, 400px);
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
.code-display {
|
.code-display {
|
||||||
|
font-family: monospace;
|
||||||
border-radius: $br4;
|
border-radius: $br4;
|
||||||
padding: 1rem;
|
padding: 0.5rem 1rem;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: pre-wrap;
|
white-space: pre;
|
||||||
|
min-width: fit-content;
|
||||||
background: $color-gray-60;
|
background: $color-gray-60;
|
||||||
user-select: text;
|
user-select: text;
|
||||||
|
|
||||||
@ -378,6 +451,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.resize-area {
|
||||||
|
width: 100%;
|
||||||
|
position: absolute;
|
||||||
|
bottom: -15px;
|
||||||
|
left: 0;
|
||||||
|
height: 18px;
|
||||||
|
z-index: 1;
|
||||||
|
cursor: ns-resize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.element-options > :first-child {
|
.element-options > :first-child {
|
||||||
|
|||||||
@ -1781,20 +1781,31 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.edit-mode {
|
.edit-mode {
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid $color-gray-60;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 10px;
|
||||||
|
justify-content: center;
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
|
padding: 0 8px;
|
||||||
|
text-align: left;
|
||||||
|
width: 120px;
|
||||||
|
|
||||||
button {
|
button {
|
||||||
|
color: $color-gray-30;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
&.active,
|
&.active,
|
||||||
&:hover {
|
&:hover {
|
||||||
|
color: $color-primary;
|
||||||
svg {
|
svg {
|
||||||
fill: $color-primary;
|
fill: $color-primary;
|
||||||
}
|
}
|
||||||
@ -1802,7 +1813,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.align-grid {
|
&.align-grid-items {
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 0px;
|
||||||
|
margin: 7px 0;
|
||||||
|
|
||||||
|
.align-items-style {
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.align-grid-content {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 7px;
|
gap: 7px;
|
||||||
margin: 7px 0;
|
margin: 7px 0;
|
||||||
|
|||||||
@ -35,6 +35,10 @@
|
|||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.inspect .tab-container-content {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.tab-element,
|
.tab-element,
|
||||||
.tab-element-content {
|
.tab-element-content {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|||||||
@ -48,14 +48,12 @@ $height-palette-max: 80px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
.settings-bar.settings-bar-right {
|
.settings-bar.settings-bar-right {
|
||||||
transition: width 0.2s;
|
height: 100%;
|
||||||
min-width: $width-settings-bar;
|
width: var(--width, $width-settings-bar);
|
||||||
max-width: $width-settings-bar * 3;
|
|
||||||
width: $width-settings-bar;
|
|
||||||
grid-area: right-sidebar;
|
grid-area: right-sidebar;
|
||||||
|
|
||||||
&.expanded {
|
&.not-expand {
|
||||||
width: $width-settings-bar * 3;
|
max-width: $width-settings-bar;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -367,7 +365,8 @@ $height-palette-max: 80px;
|
|||||||
z-index: 12;
|
z-index: 12;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|
||||||
.path-actions {
|
.path-actions,
|
||||||
|
.grid-actions {
|
||||||
pointer-events: initial;
|
pointer-events: initial;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@ -378,6 +377,27 @@ $height-palette-max: 80px;
|
|||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.grid-actions {
|
||||||
|
padding-left: 1rem;
|
||||||
|
gap: 12px;
|
||||||
|
color: var(--color-gray-60);
|
||||||
|
align-items: center;
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
.btn-primary,
|
||||||
|
.btn-secondary {
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-edit-title {
|
||||||
|
margin-right: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-edit-board-name {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.viewport-actions-group {
|
.viewport-actions-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|||||||
@ -88,9 +88,9 @@
|
|||||||
|
|
||||||
(defn ^:export reinit
|
(defn ^:export reinit
|
||||||
[]
|
[]
|
||||||
(mf/unmount (dom/get-element "app"))
|
#_(mf/unmount (dom/get-element "app"))
|
||||||
(mf/unmount (dom/get-element "modal"))
|
#_(mf/unmount (dom/get-element "modal"))
|
||||||
(st/emit! (ev/initialize))
|
#_(st/emit! (ev/initialize))
|
||||||
(init-ui))
|
(init-ui))
|
||||||
|
|
||||||
(defn ^:dev/after-load after-load
|
(defn ^:dev/after-load after-load
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.geom.proportions :as gpp]
|
[app.common.geom.proportions :as gpp]
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
|
[app.common.geom.shapes.grid-layout :as gslg]
|
||||||
[app.common.logging :as log]
|
[app.common.logging :as log]
|
||||||
[app.common.pages :as cp]
|
[app.common.pages :as cp]
|
||||||
[app.common.pages.changes-builder :as pcb]
|
[app.common.pages.changes-builder :as pcb]
|
||||||
@ -783,6 +784,18 @@
|
|||||||
(assoc :layout-item-v-sizing :fix))
|
(assoc :layout-item-v-sizing :fix))
|
||||||
parent)))
|
parent)))
|
||||||
|
|
||||||
|
;; Update grid layout
|
||||||
|
(cond-> (ctl/grid-layout? objects parent-id)
|
||||||
|
(pcb/update-shapes [parent-id] #(ctl/add-children-to-index % ids objects to-index)))
|
||||||
|
|
||||||
|
(pcb/update-shapes parents
|
||||||
|
(fn [parent]
|
||||||
|
(cond-> parent
|
||||||
|
(ctl/grid-layout? parent)
|
||||||
|
(ctl/assign-cells))))
|
||||||
|
|
||||||
|
(pcb/reorder-grid-children parents)
|
||||||
|
|
||||||
;; Resize parent containers that need to
|
;; Resize parent containers that need to
|
||||||
(pcb/resize-parents parents))))
|
(pcb/resize-parents parents))))
|
||||||
|
|
||||||
@ -1804,6 +1817,11 @@
|
|||||||
|
|
||||||
;; Adds a resize-parents operation so the groups are updated. We add all the new objects
|
;; Adds a resize-parents operation so the groups are updated. We add all the new objects
|
||||||
new-objects-ids (->> changes :redo-changes (filter #(= (:type %) :add-obj)) (mapv :id))
|
new-objects-ids (->> changes :redo-changes (filter #(= (:type %) :add-obj)) (mapv :id))
|
||||||
|
|
||||||
|
drop-cell
|
||||||
|
(when (ctl/grid-layout? all-objects parent-id)
|
||||||
|
(gslg/get-drop-cell frame-id all-objects mouse-pos))
|
||||||
|
|
||||||
changes (pcb/resize-parents changes new-objects-ids)
|
changes (pcb/resize-parents changes new-objects-ids)
|
||||||
|
|
||||||
selected (->> changes
|
selected (->> changes
|
||||||
@ -1812,6 +1830,13 @@
|
|||||||
(filter #(selected (:old-id %)))
|
(filter #(selected (:old-id %)))
|
||||||
(map #(get-in % [:obj :id]))
|
(map #(get-in % [:obj :id]))
|
||||||
(into (d/ordered-set)))
|
(into (d/ordered-set)))
|
||||||
|
|
||||||
|
changes
|
||||||
|
(cond-> changes
|
||||||
|
(some? drop-cell)
|
||||||
|
(pcb/update-shapes [parent-id]
|
||||||
|
#(ctl/add-children-to-cell % selected all-objects drop-cell)))
|
||||||
|
|
||||||
undo-id (js/Symbol)]
|
undo-id (js/Symbol)]
|
||||||
|
|
||||||
(rx/of (dwu/start-undo-transaction undo-id)
|
(rx/of (dwu/start-undo-transaction undo-id)
|
||||||
@ -2135,20 +2160,6 @@
|
|||||||
(let [orphans (set (into [] (keys (wsh/find-orphan-shapes state))))]
|
(let [orphans (set (into [] (keys (wsh/find-orphan-shapes state))))]
|
||||||
(rx/of (relocate-shapes orphans uuid/zero 0 true))))))
|
(rx/of (relocate-shapes orphans uuid/zero 0 true))))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
;; Inspect
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
||||||
|
|
||||||
|
|
||||||
(defn set-inspect-expanded
|
|
||||||
[expanded?]
|
|
||||||
(ptk/reify ::set-inspect-expanded
|
|
||||||
ptk/UpdateEvent
|
|
||||||
(update [_ state]
|
|
||||||
(assoc-in state [:workspace-local :inspect-expanded] expanded?))))
|
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; Sitemap
|
;; Sitemap
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|||||||
@ -80,6 +80,7 @@
|
|||||||
(pcb/set-stack-undo? stack-undo?)
|
(pcb/set-stack-undo? stack-undo?)
|
||||||
(pcb/with-objects objects))
|
(pcb/with-objects objects))
|
||||||
ids)
|
ids)
|
||||||
|
changes (pcb/reorder-grid-children changes ids)
|
||||||
changes (add-undo-group changes state)]
|
changes (add-undo-group changes state)]
|
||||||
(rx/concat
|
(rx/concat
|
||||||
(if (seq (:redo-changes changes))
|
(if (seq (:redo-changes changes))
|
||||||
|
|||||||
@ -6,8 +6,11 @@
|
|||||||
|
|
||||||
(ns app.main.data.workspace.common
|
(ns app.main.data.workspace.common
|
||||||
(:require
|
(:require
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
[app.common.logging :as log]
|
[app.common.logging :as log]
|
||||||
|
[app.common.types.shape.layout :as ctl]
|
||||||
[app.main.data.workspace.changes :as dch]
|
[app.main.data.workspace.changes :as dch]
|
||||||
|
[app.main.data.workspace.state-helpers :as wsh]
|
||||||
[app.main.data.workspace.undo :as dwu]
|
[app.main.data.workspace.undo :as dwu]
|
||||||
[app.util.router :as rt]
|
[app.util.router :as rt]
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
@ -53,10 +56,13 @@
|
|||||||
(ptk/reify ::undo
|
(ptk/reify ::undo
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [it state _]
|
(watch [it state _]
|
||||||
(let [edition (get-in state [:workspace-local :edition])
|
(let [objects (wsh/lookup-page-objects state)
|
||||||
|
edition (get-in state [:workspace-local :edition])
|
||||||
drawing (get state :workspace-drawing)]
|
drawing (get state :workspace-drawing)]
|
||||||
|
|
||||||
;; Editors handle their own undo's
|
;; Editors handle their own undo's
|
||||||
(when (and (nil? edition) (nil? (:object drawing)))
|
(when (or (and (nil? edition) (nil? (:object drawing)))
|
||||||
|
(ctl/grid-layout? objects edition))
|
||||||
(let [undo (:workspace-undo state)
|
(let [undo (:workspace-undo state)
|
||||||
items (:items undo)
|
items (:items undo)
|
||||||
index (or (:index undo) (dec (count items)))]
|
index (or (:index undo) (dec (count items)))]
|
||||||
@ -64,14 +70,17 @@
|
|||||||
(let [item (get items index)
|
(let [item (get items index)
|
||||||
changes (:undo-changes item)
|
changes (:undo-changes item)
|
||||||
undo-group (:undo-group item)
|
undo-group (:undo-group item)
|
||||||
find-first-group-idx (fn ffgidx[index]
|
|
||||||
(let [item (get items index)]
|
|
||||||
(if (= (:undo-group item) undo-group)
|
|
||||||
(ffgidx (dec index))
|
|
||||||
(inc index))))
|
|
||||||
|
|
||||||
undo-group-index (when undo-group
|
find-first-group-idx
|
||||||
(find-first-group-idx index))]
|
(fn [index]
|
||||||
|
(if (= (dm/get-in items [index :undo-group]) undo-group)
|
||||||
|
(recur (dec index))
|
||||||
|
(inc index)))
|
||||||
|
|
||||||
|
undo-group-index
|
||||||
|
(when undo-group
|
||||||
|
(find-first-group-idx index))]
|
||||||
|
|
||||||
(if undo-group
|
(if undo-group
|
||||||
(rx/of (undo-to-index (dec undo-group-index)))
|
(rx/of (undo-to-index (dec undo-group-index)))
|
||||||
(rx/of (dwu/materialize-undo changes (dec index))
|
(rx/of (dwu/materialize-undo changes (dec index))
|
||||||
@ -117,9 +126,11 @@
|
|||||||
(ptk/reify ::undo-to-index
|
(ptk/reify ::undo-to-index
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [it state _]
|
(watch [it state _]
|
||||||
(let [edition (get-in state [:workspace-local :edition])
|
(let [objects (wsh/lookup-page-objects state)
|
||||||
|
edition (get-in state [:workspace-local :edition])
|
||||||
drawing (get state :workspace-drawing)]
|
drawing (get state :workspace-drawing)]
|
||||||
(when-not (or (some? edition) (not-empty drawing))
|
(when-not (and (or (some? edition) (not-empty drawing))
|
||||||
|
(not (ctl/grid-layout? objects edition)))
|
||||||
(let [undo (:workspace-undo state)
|
(let [undo (:workspace-undo state)
|
||||||
items (:items undo)
|
items (:items undo)
|
||||||
index (or (:index undo) (dec (count items)))]
|
index (or (:index undo) (dec (count items)))]
|
||||||
|
|||||||
@ -8,7 +8,8 @@
|
|||||||
(:require
|
(:require
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.geom.shapes :as gsh]
|
[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.math :as mth]
|
||||||
[app.common.pages.helpers :as cph]
|
[app.common.pages.helpers :as cph]
|
||||||
[app.common.types.modifiers :as ctm]
|
[app.common.types.modifiers :as ctm]
|
||||||
@ -84,7 +85,10 @@
|
|||||||
fid (ctst/top-nested-frame objects initial)
|
fid (ctst/top-nested-frame objects initial)
|
||||||
|
|
||||||
flex-layout? (ctl/flex-layout? objects fid)
|
flex-layout? (ctl/flex-layout? objects fid)
|
||||||
drop-index (when flex-layout? (gsl/get-drop-index fid objects initial))
|
grid-layout? (ctl/grid-layout? objects fid)
|
||||||
|
|
||||||
|
drop-index (when flex-layout? (gslf/get-drop-index fid objects initial))
|
||||||
|
drop-cell (when grid-layout? (gslg/get-drop-cell fid objects initial))
|
||||||
|
|
||||||
shape (get-in state [:workspace-drawing :object])
|
shape (get-in state [:workspace-drawing :object])
|
||||||
shape (-> shape
|
shape (-> shape
|
||||||
@ -101,6 +105,9 @@
|
|||||||
(cond-> (some? drop-index)
|
(cond-> (some? drop-index)
|
||||||
(with-meta {:index drop-index}))
|
(with-meta {:index drop-index}))
|
||||||
|
|
||||||
|
(cond-> (some? drop-cell)
|
||||||
|
(with-meta {:cell drop-cell}))
|
||||||
|
|
||||||
(assoc :initialized? true)
|
(assoc :initialized? true)
|
||||||
(assoc :click-draw? true))]
|
(assoc :click-draw? true))]
|
||||||
(rx/concat
|
(rx/concat
|
||||||
|
|||||||
@ -8,7 +8,8 @@
|
|||||||
(:require
|
(:require
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.geom.shapes :as gsh]
|
[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.geom.shapes.path :as gsp]
|
[app.common.geom.shapes.path :as gsp]
|
||||||
[app.common.types.shape-tree :as ctst]
|
[app.common.types.shape-tree :as ctst]
|
||||||
[app.common.types.shape.layout :as ctl]
|
[app.common.types.shape.layout :as ctl]
|
||||||
@ -53,11 +54,15 @@
|
|||||||
position (when start (gpt/point start))
|
position (when start (gpt/point start))
|
||||||
frame-id (ctst/top-nested-frame objects position)
|
frame-id (ctst/top-nested-frame objects position)
|
||||||
flex-layout? (ctl/flex-layout? objects frame-id)
|
flex-layout? (ctl/flex-layout? objects frame-id)
|
||||||
drop-index (when flex-layout? (gsl/get-drop-index frame-id objects position))]
|
grid-layout? (ctl/grid-layout? objects frame-id)
|
||||||
|
drop-index (when flex-layout? (gslf/get-drop-index frame-id objects position))
|
||||||
|
drop-cell (when grid-layout? (gslg/get-drop-cell frame-id objects position))]
|
||||||
(-> state
|
(-> state
|
||||||
(assoc-in [:workspace-drawing :object :frame-id] frame-id)
|
(assoc-in [:workspace-drawing :object :frame-id] frame-id)
|
||||||
(cond-> (some? drop-index)
|
(cond-> (some? drop-index)
|
||||||
(update-in [:workspace-drawing :object] with-meta {:index drop-index})))))))
|
(update-in [:workspace-drawing :object] with-meta {:index drop-index}))
|
||||||
|
(cond-> (some? drop-cell)
|
||||||
|
(update-in [:workspace-drawing :object] with-meta {:cell drop-cell})))))))
|
||||||
|
|
||||||
(defn curve-to-path [{:keys [segments] :as shape}]
|
(defn curve-to-path [{:keys [segments] :as shape}]
|
||||||
(let [content (gsp/segments->content segments)
|
(let [content (gsp/segments->content segments)
|
||||||
|
|||||||
@ -26,7 +26,8 @@
|
|||||||
(if (contains? objects id)
|
(if (contains? objects id)
|
||||||
(-> state
|
(-> state
|
||||||
(assoc-in [:workspace-local :selected] #{id})
|
(assoc-in [:workspace-local :selected] #{id})
|
||||||
(assoc-in [:workspace-local :edition] id))
|
(assoc-in [:workspace-local :edition] id)
|
||||||
|
(dissoc :workspace-grid-edition))
|
||||||
state)))
|
state)))
|
||||||
|
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
@ -44,4 +45,5 @@
|
|||||||
(let [id (get-in state [:workspace-local :edition])]
|
(let [id (get-in state [:workspace-local :edition])]
|
||||||
(-> state
|
(-> state
|
||||||
(update :workspace-local dissoc :edition)
|
(update :workspace-local dissoc :edition)
|
||||||
|
(dissoc :workspace-grid-edition)
|
||||||
(cond-> (some? id) (update-in [:workspace-local :edit-path] dissoc id)))))))
|
(cond-> (some? id) (update-in [:workspace-local :edit-path] dissoc id)))))))
|
||||||
|
|||||||
@ -6,10 +6,12 @@
|
|||||||
|
|
||||||
(ns app.main.data.workspace.grid-layout.editor
|
(ns app.main.data.workspace.grid-layout.editor
|
||||||
(:require
|
(:require
|
||||||
|
[app.common.geom.shapes :as gsh]
|
||||||
|
[app.main.data.workspace.state-helpers :as wsh]
|
||||||
[potok.core :as ptk]))
|
[potok.core :as ptk]))
|
||||||
|
|
||||||
(defn hover-grid-cell
|
(defn hover-grid-cell
|
||||||
[grid-id row column add-to-set]
|
[grid-id cell-id add-to-set]
|
||||||
(ptk/reify ::hover-grid-cell
|
(ptk/reify ::hover-grid-cell
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
@ -19,15 +21,15 @@
|
|||||||
(fn [hover-set]
|
(fn [hover-set]
|
||||||
(let [hover-set (or hover-set #{})]
|
(let [hover-set (or hover-set #{})]
|
||||||
(if add-to-set
|
(if add-to-set
|
||||||
(conj hover-set [row column])
|
(conj hover-set cell-id)
|
||||||
(disj hover-set [row column]))))))))
|
(disj hover-set cell-id))))))))
|
||||||
|
|
||||||
(defn select-grid-cell
|
(defn select-grid-cell
|
||||||
[grid-id row column]
|
[grid-id cell-id]
|
||||||
(ptk/reify ::select-grid-cell
|
(ptk/reify ::select-grid-cell
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(assoc-in state [:workspace-grid-edition grid-id :selected] [row column]))))
|
(assoc-in state [:workspace-grid-edition grid-id :selected] cell-id))))
|
||||||
|
|
||||||
(defn remove-selection
|
(defn remove-selection
|
||||||
[grid-id]
|
[grid-id]
|
||||||
@ -42,3 +44,21 @@
|
|||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(update state :workspace-grid-edition dissoc grid-id))))
|
(update state :workspace-grid-edition dissoc grid-id))))
|
||||||
|
|
||||||
|
(defn locate-board
|
||||||
|
[grid-id]
|
||||||
|
(ptk/reify ::locate-board
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(let [objects (wsh/lookup-page-objects state)
|
||||||
|
srect (get-in objects [grid-id :selrect])]
|
||||||
|
(-> state
|
||||||
|
(update :workspace-local
|
||||||
|
(fn [{:keys [zoom vport] :as local}]
|
||||||
|
(let [{:keys [x y width height]} srect
|
||||||
|
x (+ x (/ width 2) (- (/ (:width vport) 2 zoom)))
|
||||||
|
y (+ y (/ height 2) (- (/ (:height vport) 2 zoom)))
|
||||||
|
srect (gsh/make-selrect x y width height)]
|
||||||
|
(-> local
|
||||||
|
(update :vbox merge (select-keys srect [:x :y :x1 :x2 :y1 :y2])))))))))))
|
||||||
|
|
||||||
|
|||||||
@ -175,8 +175,29 @@
|
|||||||
(update-in modif-tree [parent-id :modifiers] ctm/remove-children [child-id])))
|
(update-in modif-tree [parent-id :modifiers] ctm/remove-children [child-id])))
|
||||||
modif-tree)))
|
modif-tree)))
|
||||||
|
|
||||||
|
(defn add-grid-children-modifiers
|
||||||
|
[modifiers frame-id selected objects [row column :as cell]]
|
||||||
|
(let [frame (get objects frame-id)
|
||||||
|
|
||||||
|
;; Temporary remove the children when moving them
|
||||||
|
frame (-> frame
|
||||||
|
(update :shapes #(d/removev selected %))
|
||||||
|
(ctl/assign-cells))
|
||||||
|
|
||||||
|
selected (->> selected (remove #(ctl/layout-absolute? objects %)))
|
||||||
|
frame (-> frame
|
||||||
|
(update :shapes d/concat-vec selected)
|
||||||
|
(cond-> (some? cell)
|
||||||
|
(ctl/push-into-cell selected row column))
|
||||||
|
(ctl/assign-cells))]
|
||||||
|
|
||||||
|
(-> modifiers
|
||||||
|
(ctm/change-property :layout-grid-rows (:layout-grid-rows frame))
|
||||||
|
(ctm/change-property :layout-grid-columns (:layout-grid-columns frame))
|
||||||
|
(ctm/change-property :layout-grid-cells (:layout-grid-cells frame)))))
|
||||||
|
|
||||||
(defn build-change-frame-modifiers
|
(defn build-change-frame-modifiers
|
||||||
[modif-tree objects selected target-frame-id drop-index]
|
[modif-tree objects selected target-frame-id drop-index cell-data]
|
||||||
|
|
||||||
(let [origin-frame-ids (->> selected (group-by #(get-in objects [% :frame-id])))
|
(let [origin-frame-ids (->> selected (group-by #(get-in objects [% :frame-id])))
|
||||||
child-set (set (get-in objects [target-frame-id :shapes]))
|
child-set (set (get-in objects [target-frame-id :shapes]))
|
||||||
@ -209,8 +230,10 @@
|
|||||||
children-ids (->> (dm/get-in objects [original-frame :shapes])
|
children-ids (->> (dm/get-in objects [original-frame :shapes])
|
||||||
(remove (set selected)))
|
(remove (set selected)))
|
||||||
|
|
||||||
h-sizing? (ctl/change-h-sizing? original-frame objects children-ids)
|
h-sizing? (and (ctl/flex-layout? objects original-frame)
|
||||||
v-sizing? (ctl/change-v-sizing? original-frame objects children-ids)]
|
(ctl/change-h-sizing? original-frame objects children-ids))
|
||||||
|
v-sizing? (and (ctl/flex-layout? objects original-frame)
|
||||||
|
(ctl/change-v-sizing? original-frame objects children-ids))]
|
||||||
(cond-> modif-tree
|
(cond-> modif-tree
|
||||||
(not= original-frame target-frame-id)
|
(not= original-frame target-frame-id)
|
||||||
(-> (modifier-remove-from-parent objects shapes)
|
(-> (modifier-remove-from-parent objects shapes)
|
||||||
@ -227,11 +250,19 @@
|
|||||||
(as-> modif-tree $
|
(as-> modif-tree $
|
||||||
(reduce update-frame-modifiers $ origin-frame-ids)
|
(reduce update-frame-modifiers $ origin-frame-ids)
|
||||||
(cond-> $
|
(cond-> $
|
||||||
(ctl/change-h-sizing? target-frame-id objects children-ids)
|
;; Set fix position to target frame (horizontal)
|
||||||
(update-in [target-frame-id :modifiers] ctm/change-property :layout-item-h-sizing :fix))
|
(and (ctl/flex-layout? objects target-frame-id)
|
||||||
(cond-> $
|
(ctl/change-h-sizing? target-frame-id objects children-ids))
|
||||||
(ctl/change-v-sizing? target-frame-id objects children-ids)
|
(update-in [target-frame-id :modifiers] ctm/change-property :layout-item-h-sizing :fix)
|
||||||
(update-in [target-frame-id :modifiers] ctm/change-property :layout-item-v-sizing :fix)))))
|
|
||||||
|
;; Set fix position to target frame (vertical)
|
||||||
|
(and (ctl/flex-layout? objects target-frame-id)
|
||||||
|
(ctl/change-v-sizing? target-frame-id objects children-ids))
|
||||||
|
(update-in [target-frame-id :modifiers] ctm/change-property :layout-item-v-sizing :fix)
|
||||||
|
|
||||||
|
;; Add the object to the cell
|
||||||
|
(ctl/grid-layout? objects target-frame-id)
|
||||||
|
(update-in [target-frame-id :modifiers] add-grid-children-modifiers target-frame-id selected objects cell-data)))))
|
||||||
|
|
||||||
(defn modif->js
|
(defn modif->js
|
||||||
[modif-tree objects]
|
[modif-tree objects]
|
||||||
@ -454,6 +485,9 @@
|
|||||||
:layout-gap
|
:layout-gap
|
||||||
:layout-item-margin
|
:layout-item-margin
|
||||||
:layout-item-margin-type
|
:layout-item-margin-type
|
||||||
|
:layout-grid-cells
|
||||||
|
:layout-grid-columns
|
||||||
|
:layout-grid-rows
|
||||||
]})
|
]})
|
||||||
;; We've applied the text-modifier so we can dissoc the temporary data
|
;; We've applied the text-modifier so we can dissoc the temporary data
|
||||||
(fn [state]
|
(fn [state]
|
||||||
|
|||||||
@ -18,6 +18,7 @@
|
|||||||
[app.common.types.file :as ctf]
|
[app.common.types.file :as ctf]
|
||||||
[app.common.types.page :as ctp]
|
[app.common.types.page :as ctp]
|
||||||
[app.common.types.shape.interactions :as ctsi]
|
[app.common.types.shape.interactions :as ctsi]
|
||||||
|
[app.common.types.shape.layout :as ctl]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.main.data.modal :as md]
|
[app.main.data.modal :as md]
|
||||||
[app.main.data.workspace.changes :as dch]
|
[app.main.data.workspace.changes :as dch]
|
||||||
@ -324,6 +325,16 @@
|
|||||||
(when selected
|
(when selected
|
||||||
(rx/of (select-shape (:id selected))))))))
|
(rx/of (select-shape (:id selected))))))))
|
||||||
|
|
||||||
|
(defn remap-grid-cells
|
||||||
|
"Remaps the shapes inside the cells"
|
||||||
|
[shape ids-map]
|
||||||
|
|
||||||
|
(let [do-remap-cells
|
||||||
|
(fn [cell]
|
||||||
|
(-> cell
|
||||||
|
(update :shapes #(mapv ids-map %))))]
|
||||||
|
|
||||||
|
(update shape :layout-grid-cells update-vals do-remap-cells)))
|
||||||
|
|
||||||
;; --- Duplicate Shapes
|
;; --- Duplicate Shapes
|
||||||
(declare prepare-duplicate-shape-change)
|
(declare prepare-duplicate-shape-change)
|
||||||
@ -431,10 +442,16 @@
|
|||||||
:shape-ref
|
:shape-ref
|
||||||
:use-for-thumbnail?)
|
:use-for-thumbnail?)
|
||||||
(gsh/move delta)
|
(gsh/move delta)
|
||||||
(d/update-when :interactions #(ctsi/remap-interactions % ids-map objects)))
|
(d/update-when :interactions #(ctsi/remap-interactions % ids-map objects))
|
||||||
|
|
||||||
|
(cond-> (ctl/grid-layout? obj)
|
||||||
|
(remap-grid-cells ids-map)))
|
||||||
|
|
||||||
changes (-> (pcb/add-object changes new-obj)
|
changes (-> (pcb/add-object changes new-obj)
|
||||||
(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
|
changes (cond-> changes
|
||||||
(and is-component-root? is-component-main?)
|
(and is-component-root? is-component-main?)
|
||||||
|
|||||||
@ -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
|
;; 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/.
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
;;
|
;;
|
||||||
@ -8,6 +8,7 @@
|
|||||||
(:require
|
(:require
|
||||||
[app.common.colors :as clr]
|
[app.common.colors :as clr]
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
[app.common.math :as mth]
|
[app.common.math :as mth]
|
||||||
@ -52,17 +53,18 @@
|
|||||||
:layout-padding-type :simple
|
:layout-padding-type :simple
|
||||||
:layout-padding {:p1 0 :p2 0 :p3 0 :p4 0}})
|
:layout-padding {:p1 0 :p2 0 :p3 0 :p4 0}})
|
||||||
|
|
||||||
(def initial-grid-layout ;; TODO
|
(def initial-grid-layout
|
||||||
{:layout :grid
|
{:layout :grid
|
||||||
:layout-grid-dir :row
|
:layout-grid-dir :row
|
||||||
:layout-gap-type :multiple
|
:layout-gap-type :multiple
|
||||||
:layout-gap {:row-gap 0 :column-gap 0}
|
:layout-gap {:row-gap 0 :column-gap 0}
|
||||||
:layout-align-items :start
|
:layout-align-items :start
|
||||||
:layout-align-content :stretch
|
|
||||||
:layout-justify-items :start
|
:layout-justify-items :start
|
||||||
:layout-justify-content :start
|
:layout-align-content :stretch
|
||||||
|
:layout-justify-content :stretch
|
||||||
:layout-padding-type :simple
|
:layout-padding-type :simple
|
||||||
:layout-padding {:p1 0 :p2 0 :p3 0 :p4 0}
|
:layout-padding {:p1 0 :p2 0 :p3 0 :p4 0}
|
||||||
|
:layout-grid-cells {}
|
||||||
:layout-grid-rows []
|
:layout-grid-rows []
|
||||||
:layout-grid-columns []})
|
:layout-grid-columns []})
|
||||||
|
|
||||||
@ -86,84 +88,90 @@
|
|||||||
([objects shapes]
|
([objects shapes]
|
||||||
(shapes->flex-params objects shapes nil))
|
(shapes->flex-params objects shapes nil))
|
||||||
([objects shapes parent]
|
([objects shapes parent]
|
||||||
(let [points
|
(when (d/not-empty? shapes)
|
||||||
(->> shapes
|
(let [points
|
||||||
(map :id)
|
(->> shapes
|
||||||
(ctt/sort-z-index objects)
|
(map :id)
|
||||||
(map (comp gsh/center-shape (d/getf objects))))
|
(ctt/sort-z-index objects)
|
||||||
|
(map (comp gsh/center-shape (d/getf objects))))
|
||||||
|
|
||||||
start (first points)
|
start (first points)
|
||||||
end (reduce (fn [acc p] (gpt/add acc (gpt/to-vec start p))) points)
|
end (reduce (fn [acc p] (gpt/add acc (gpt/to-vec start p))) points)
|
||||||
|
|
||||||
angle (gpt/signed-angle-with-other
|
angle (gpt/signed-angle-with-other
|
||||||
(gpt/to-vec start end)
|
(gpt/to-vec start end)
|
||||||
(gpt/point 1 0))
|
(gpt/point 1 0))
|
||||||
|
|
||||||
angle (mod angle 360)
|
angle (mod angle 360)
|
||||||
|
|
||||||
t1 (min (abs (- angle 0)) (abs (- angle 360)))
|
t1 (min (abs (- angle 0)) (abs (- angle 360)))
|
||||||
t2 (abs (- angle 90))
|
t2 (abs (- angle 90))
|
||||||
t3 (abs (- angle 180))
|
t3 (abs (- angle 180))
|
||||||
t4 (abs (- angle 270))
|
t4 (abs (- angle 270))
|
||||||
|
|
||||||
tmin (min t1 t2 t3 t4)
|
tmin (min t1 t2 t3 t4)
|
||||||
|
|
||||||
direction
|
direction
|
||||||
(cond
|
(cond
|
||||||
(mth/close? tmin t1) :row
|
(mth/close? tmin t1) :row
|
||||||
(mth/close? tmin t2) :column-reverse
|
(mth/close? tmin t2) :column-reverse
|
||||||
(mth/close? tmin t3) :row-reverse
|
(mth/close? tmin t3) :row-reverse
|
||||||
(mth/close? tmin t4) :column)
|
(mth/close? tmin t4) :column)
|
||||||
|
|
||||||
selrects (->> shapes
|
selrects (->> shapes
|
||||||
(mapv :selrect))
|
(mapv :selrect))
|
||||||
min-x (->> selrects
|
min-x (->> selrects
|
||||||
(mapv #(min (:x1 %) (:x2 %)))
|
(mapv #(min (:x1 %) (:x2 %)))
|
||||||
(apply min))
|
(apply min))
|
||||||
max-x (->> selrects
|
max-x (->> selrects
|
||||||
(mapv #(max (:x1 %) (:x2 %)))
|
(mapv #(max (:x1 %) (:x2 %)))
|
||||||
(apply max))
|
(apply max))
|
||||||
all-width (->> selrects
|
all-width (->> selrects
|
||||||
(map :width)
|
(map :width)
|
||||||
(reduce +))
|
(reduce +))
|
||||||
column-gap (if (and (> (count shapes) 1)
|
column-gap (if (and (> (count shapes) 1)
|
||||||
(or (= direction :row) (= direction :row-reverse)))
|
(or (= direction :row) (= direction :row-reverse)))
|
||||||
(/ (- (- max-x min-x) all-width)
|
(/ (- (- max-x min-x) all-width)
|
||||||
(dec (count shapes)))
|
(dec (count shapes)))
|
||||||
0)
|
0)
|
||||||
|
|
||||||
min-y (->> selrects
|
min-y (->> selrects
|
||||||
(mapv #(min (:y1 %) (:y2 %)))
|
(mapv #(min (:y1 %) (:y2 %)))
|
||||||
(apply min))
|
(apply min))
|
||||||
max-y (->> selrects
|
max-y (->> selrects
|
||||||
(mapv #(max (:y1 %) (:y2 %)))
|
(mapv #(max (:y1 %) (:y2 %)))
|
||||||
(apply max))
|
(apply max))
|
||||||
all-height (->> selrects
|
all-height (->> selrects
|
||||||
(map :height)
|
(map :height)
|
||||||
(reduce +))
|
(reduce +))
|
||||||
row-gap (if (and (> (count shapes) 1)
|
row-gap (if (and (> (count shapes) 1)
|
||||||
(or (= direction :column) (= direction :column-reverse)))
|
(or (= direction :column) (= direction :column-reverse)))
|
||||||
(/ (- (- max-y min-y) all-height)
|
(/ (- (- max-y min-y) all-height)
|
||||||
(dec (count shapes)))
|
(dec (count shapes)))
|
||||||
0)
|
0)
|
||||||
|
|
||||||
layout-gap {:row-gap (max row-gap 0) :column-gap (max column-gap 0)}
|
layout-gap {:row-gap (max row-gap 0) :column-gap (max column-gap 0)}
|
||||||
|
|
||||||
parent-selrect (:selrect parent)
|
parent-selrect (:selrect parent)
|
||||||
padding (when (and (not (nil? parent)) (> (count shapes) 0))
|
padding (when (and (not (nil? parent)) (> (count shapes) 0))
|
||||||
{:p1 (min (- min-y (:y1 parent-selrect)) (- (:y2 parent-selrect) max-y))
|
{:p1 (min (- min-y (:y1 parent-selrect)) (- (:y2 parent-selrect) max-y))
|
||||||
:p2 (min (- min-x (:x1 parent-selrect)) (- (:x2 parent-selrect) max-x))})]
|
:p2 (min (- min-x (:x1 parent-selrect)) (- (:x2 parent-selrect) max-x))})]
|
||||||
|
|
||||||
(cond-> {:layout-flex-dir direction :layout-gap layout-gap}
|
(cond-> {:layout-flex-dir direction :layout-gap layout-gap}
|
||||||
(not (nil? padding))
|
(not (nil? padding))
|
||||||
(assoc :layout-padding {:p1 (:p1 padding) :p2 (:p2 padding) :p3 (:p1 padding) :p4 (:p2 padding)})))))
|
(assoc :layout-padding {:p1 (:p1 padding) :p2 (:p2 padding) :p3 (:p1 padding) :p4 (:p2 padding)}))))))
|
||||||
|
|
||||||
(defn shapes->grid-params
|
(defn shapes->grid-params
|
||||||
"Given the shapes calculate its flex parameters (horizontal vs vertical, gaps, etc)"
|
"Given the shapes calculate its flex parameters (horizontal vs vertical, gaps, etc)"
|
||||||
([objects shapes]
|
([objects shapes]
|
||||||
(shapes->flex-params objects shapes nil))
|
(shapes->flex-params objects shapes nil))
|
||||||
([_objects _shapes _parent]
|
([_objects shapes _parent]
|
||||||
{}))
|
(if (empty? shapes)
|
||||||
|
(ctl/create-cells
|
||||||
|
{:layout-grid-columns [{:type :auto} {:type :auto}]
|
||||||
|
:layout-grid-rows [{:type :auto} {:type :auto}]}
|
||||||
|
[1 1 2 2])
|
||||||
|
{})))
|
||||||
|
|
||||||
(defn create-layout-from-id
|
(defn create-layout-from-id
|
||||||
[ids type from-frame?]
|
[ids type from-frame?]
|
||||||
@ -174,10 +182,9 @@
|
|||||||
children-ids (into [] (mapcat #(get-in objects [% :shapes])) ids)
|
children-ids (into [] (mapcat #(get-in objects [% :shapes])) ids)
|
||||||
children-shapes (map (d/getf objects) children-ids)
|
children-shapes (map (d/getf objects) children-ids)
|
||||||
parent (get objects (first ids))
|
parent (get objects (first ids))
|
||||||
layout-params (when (d/not-empty? children-shapes)
|
layout-params (case type
|
||||||
(case type
|
:flex (shapes->flex-params objects children-shapes parent)
|
||||||
:flex (shapes->flex-params objects children-shapes parent)
|
:grid (shapes->grid-params objects children-shapes parent))
|
||||||
:grid (shapes->grid-params objects children-shapes parent)))
|
|
||||||
undo-id (js/Symbol)]
|
undo-id (js/Symbol)]
|
||||||
(rx/of (dwu/start-undo-transaction undo-id)
|
(rx/of (dwu/start-undo-transaction undo-id)
|
||||||
(dwc/update-shapes ids (get-layout-initializer type from-frame?))
|
(dwc/update-shapes ids (get-layout-initializer type from-frame?))
|
||||||
@ -188,7 +195,10 @@
|
|||||||
(cond-> (not from-frame?)
|
(cond-> (not from-frame?)
|
||||||
(assoc :layout-item-h-sizing :auto
|
(assoc :layout-item-h-sizing :auto
|
||||||
:layout-item-v-sizing :auto))
|
:layout-item-v-sizing :auto))
|
||||||
(merge layout-params))))
|
(merge layout-params)
|
||||||
|
(cond-> (= type :grid)
|
||||||
|
(-> (ctl/assign-cells)
|
||||||
|
(ctl/reorder-grid-children))))))
|
||||||
(ptk/data-event :layout/update ids)
|
(ptk/data-event :layout/update ids)
|
||||||
(dwc/update-shapes children-ids #(dissoc % :constraints-h :constraints-v))
|
(dwc/update-shapes children-ids #(dissoc % :constraints-h :constraints-v))
|
||||||
(dwu/commit-undo-transaction undo-id))))))
|
(dwu/commit-undo-transaction undo-id))))))
|
||||||
@ -278,7 +288,7 @@
|
|||||||
|
|
||||||
(let [new-shape-id (uuid/next)
|
(let [new-shape-id (uuid/next)
|
||||||
undo-id (js/Symbol)
|
undo-id (js/Symbol)
|
||||||
flex-params (shapes->flex-params objects selected-shapes)]
|
flex-params (shapes->flex-params objects selected-shapes)]
|
||||||
(rx/of
|
(rx/of
|
||||||
(dwu/start-undo-transaction undo-id)
|
(dwu/start-undo-transaction undo-id)
|
||||||
(dws/create-artboard-from-selection new-shape-id)
|
(dws/create-artboard-from-selection new-shape-id)
|
||||||
@ -335,22 +345,21 @@
|
|||||||
(create-layout-from-selection type))
|
(create-layout-from-selection type))
|
||||||
(dwu/commit-undo-transaction undo-id))))))
|
(dwu/commit-undo-transaction undo-id))))))
|
||||||
|
|
||||||
(defn toggle-layout-flex
|
(defn toggle-layout
|
||||||
[]
|
[type]
|
||||||
(ptk/reify ::toggle-layout-flex
|
(ptk/reify ::toggle-layout-flex
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state _]
|
(watch [_ state _]
|
||||||
(let [page-id (:current-page-id state)
|
(let [objects (wsh/lookup-page-objects state)
|
||||||
objects (wsh/lookup-page-objects state page-id)
|
|
||||||
selected (wsh/lookup-selected state)
|
selected (wsh/lookup-selected state)
|
||||||
selected-shapes (map (d/getf objects) selected)
|
selected-shapes (map (d/getf objects) selected)
|
||||||
single? (= (count selected-shapes) 1)
|
single? (= (count selected-shapes) 1)
|
||||||
has-flex-layout? (and single? (ctl/flex-layout? objects (:id (first selected-shapes))))]
|
has-layout? (and single? (ctl/any-layout? objects (:id (first selected-shapes))))]
|
||||||
|
|
||||||
(when (not= 0 (count selected))
|
(when (not= 0 (count selected))
|
||||||
(if has-flex-layout?
|
(if has-layout?
|
||||||
(rx/of (remove-layout selected))
|
(rx/of (remove-layout selected))
|
||||||
(rx/of (create-layout :flex))))))))
|
(rx/of (create-layout type))))))))
|
||||||
|
|
||||||
(defn update-layout
|
(defn update-layout
|
||||||
[ids changes]
|
[ids changes]
|
||||||
@ -363,48 +372,6 @@
|
|||||||
(ptk/data-event :layout/update ids)
|
(ptk/data-event :layout/update ids)
|
||||||
(dwu/commit-undo-transaction undo-id))))))
|
(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
|
(defn add-layout-track
|
||||||
[ids type value]
|
[ids type value]
|
||||||
(assert (#{:row :column} type))
|
(assert (#{:row :column} type))
|
||||||
@ -443,12 +410,13 @@
|
|||||||
(defn change-layout-track
|
(defn change-layout-track
|
||||||
[ids type index props]
|
[ids type index props]
|
||||||
(assert (#{:row :column} type))
|
(assert (#{:row :column} type))
|
||||||
(ptk/reify ::change-layout-column
|
(ptk/reify ::change-layout-track
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ _ _]
|
(watch [_ _ _]
|
||||||
(let [undo-id (js/Symbol)
|
(let [undo-id (js/Symbol)
|
||||||
property (case :row :layout-grid-rows
|
property (case type
|
||||||
:column :layout-grid-columns)]
|
:row :layout-grid-rows
|
||||||
|
:column :layout-grid-columns)]
|
||||||
(rx/of (dwu/start-undo-transaction undo-id)
|
(rx/of (dwu/start-undo-transaction undo-id)
|
||||||
(dwc/update-shapes
|
(dwc/update-shapes
|
||||||
ids
|
ids
|
||||||
@ -496,7 +464,7 @@
|
|||||||
(assoc :layout-item-v-sizing :fix))))
|
(assoc :layout-item-v-sizing :fix))))
|
||||||
|
|
||||||
(defn fix-parent-sizing
|
(defn fix-parent-sizing
|
||||||
[objects ids-set changes parent]
|
[parent objects ids-set changes]
|
||||||
|
|
||||||
(let [auto-width? (ctl/auto-width? parent)
|
(let [auto-width? (ctl/auto-width? parent)
|
||||||
auto-height? (ctl/auto-height? parent)
|
auto-height? (ctl/auto-height? parent)
|
||||||
@ -544,6 +512,52 @@
|
|||||||
(rx/of (dwu/start-undo-transaction undo-id)
|
(rx/of (dwu/start-undo-transaction undo-id)
|
||||||
(dwc/update-shapes ids #(d/deep-merge (or % {}) changes))
|
(dwc/update-shapes ids #(d/deep-merge (or % {}) changes))
|
||||||
(dwc/update-shapes children-ids (partial fix-child-sizing objects changes))
|
(dwc/update-shapes children-ids (partial fix-child-sizing objects changes))
|
||||||
(dwc/update-shapes parent-ids (partial fix-parent-sizing objects (set ids) changes))
|
(dwc/update-shapes parent-ids
|
||||||
|
(fn [parent]
|
||||||
|
(-> parent
|
||||||
|
(fix-parent-sizing objects (set ids) changes)
|
||||||
|
(cond-> (ctl/grid-layout? parent)
|
||||||
|
(ctl/assign-cells)))))
|
||||||
(ptk/data-event :layout/update ids)
|
(ptk/data-event :layout/update ids)
|
||||||
(dwu/commit-undo-transaction undo-id))))))
|
(dwu/commit-undo-transaction undo-id))))))
|
||||||
|
|
||||||
|
(defn update-grid-cell
|
||||||
|
[layout-id cell-id props]
|
||||||
|
(ptk/reify ::update-grid-cell
|
||||||
|
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))))))
|
||||||
|
(ptk/data-event :layout/update [layout-id])
|
||||||
|
(dwu/commit-undo-transaction undo-id))))))
|
||||||
|
|
||||||
|
(defn update-grid-cell-position
|
||||||
|
[layout-id cell-id props]
|
||||||
|
|
||||||
|
(ptk/reify ::update-grid-cell-position
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ _ _]
|
||||||
|
(let [undo-id (js/Symbol)]
|
||||||
|
(rx/of
|
||||||
|
(dwu/start-undo-transaction undo-id)
|
||||||
|
(dwc/update-shapes
|
||||||
|
[layout-id]
|
||||||
|
(fn [shape]
|
||||||
|
(let [prev-data (-> (dm/get-in shape [:layout-grid-cells cell-id])
|
||||||
|
(select-keys [:row :column :row-span :column-span]))
|
||||||
|
|
||||||
|
new-data (merge prev-data props)]
|
||||||
|
(-> shape
|
||||||
|
(ctl/resize-cell-area (:row prev-data) (:column prev-data)
|
||||||
|
(:row new-data) (:column new-data)
|
||||||
|
(:row-span new-data) (:column-span new-data))
|
||||||
|
(ctl/assign-cells)))))
|
||||||
|
(ptk/data-event :layout/update [layout-id])
|
||||||
|
(dwu/commit-undo-transaction undo-id))))))
|
||||||
|
|||||||
@ -82,6 +82,7 @@
|
|||||||
selected)
|
selected)
|
||||||
|
|
||||||
index (:index (meta attrs))
|
index (:index (meta attrs))
|
||||||
|
[row column :as cell] (:cell (meta attrs))
|
||||||
|
|
||||||
changes (-> changes
|
changes (-> changes
|
||||||
(pcb/with-objects objects)
|
(pcb/with-objects objects)
|
||||||
@ -91,9 +92,11 @@
|
|||||||
(pcb/add-object shape))
|
(pcb/add-object shape))
|
||||||
(cond-> (some? (:parent-id attrs))
|
(cond-> (some? (:parent-id attrs))
|
||||||
(pcb/change-parent (:parent-id attrs) [shape] index))
|
(pcb/change-parent (:parent-id attrs) [shape] index))
|
||||||
|
(cond-> (some? cell)
|
||||||
|
(pcb/update-shapes [(:parent-id shape)] #(ctl/push-into-cell % [id] row column)))
|
||||||
(cond-> (ctl/grid-layout? objects (:parent-id shape))
|
(cond-> (ctl/grid-layout? objects (:parent-id shape))
|
||||||
(pcb/update-shapes [(:parent-id shape)] ctl/assign-cells)))]
|
(-> (pcb/update-shapes [(:parent-id shape)] ctl/assign-cells)
|
||||||
|
(pcb/reorder-grid-children [(:parent-id shape)]))))]
|
||||||
[shape changes]))
|
[shape changes]))
|
||||||
|
|
||||||
(defn add-shape
|
(defn add-shape
|
||||||
@ -141,7 +144,8 @@
|
|||||||
(pcb/update-shapes ordered-indexes #(cond-> % (cph/frame-shape? %) (assoc :hide-in-viewer true)))
|
(pcb/update-shapes ordered-indexes #(cond-> % (cph/frame-shape? %) (assoc :hide-in-viewer true)))
|
||||||
(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)
|
(cond-> (ctl/grid-layout? objects frame-id)
|
||||||
(pcb/update-shapes [frame-id] ctl/assign-cells))))))
|
(pcb/update-shapes [frame-id] ctl/assign-cells))
|
||||||
|
(pcb/reorder-grid-children [frame-id])))))
|
||||||
|
|
||||||
(defn move-shapes-into-frame
|
(defn move-shapes-into-frame
|
||||||
[frame-id shapes]
|
[frame-id shapes]
|
||||||
@ -357,6 +361,7 @@
|
|||||||
(ptk/data-event :layout/update all-parents)
|
(ptk/data-event :layout/update all-parents)
|
||||||
(dwu/commit-undo-transaction undo-id))))
|
(dwu/commit-undo-transaction undo-id))))
|
||||||
|
|
||||||
|
|
||||||
(defn create-and-add-shape
|
(defn create-and-add-shape
|
||||||
[type frame-x frame-y data]
|
[type frame-x frame-y data]
|
||||||
(ptk/reify ::create-and-add-shape
|
(ptk/reify ::create-and-add-shape
|
||||||
|
|||||||
@ -219,7 +219,12 @@
|
|||||||
:toggle-layout-flex {:tooltip (ds/shift "A")
|
:toggle-layout-flex {:tooltip (ds/shift "A")
|
||||||
:command "shift+a"
|
:command "shift+a"
|
||||||
:subsections [:modify-layers]
|
:subsections [:modify-layers]
|
||||||
:fn #(emit-when-no-readonly (dwsl/toggle-layout-flex))}
|
:fn #(emit-when-no-readonly (dwsl/toggle-layout :flex))}
|
||||||
|
|
||||||
|
:toggle-layout-grid {:tooltip (ds/meta-shift "A")
|
||||||
|
:command (ds/c-mod "shift+a")
|
||||||
|
:subsections [:modify-layers]
|
||||||
|
:fn #(emit-when-no-readonly (dwsl/toggle-layout :grid))}
|
||||||
|
|
||||||
;; TOOLS
|
;; TOOLS
|
||||||
|
|
||||||
|
|||||||
@ -490,10 +490,9 @@
|
|||||||
target-frame (ctst/top-nested-frame objects position exclude-frames)
|
target-frame (ctst/top-nested-frame objects position exclude-frames)
|
||||||
flex-layout? (ctl/flex-layout? objects target-frame)
|
flex-layout? (ctl/flex-layout? objects target-frame)
|
||||||
grid-layout? (ctl/grid-layout? objects target-frame)
|
grid-layout? (ctl/grid-layout? objects target-frame)
|
||||||
drop-index (cond
|
drop-index (when flex-layout? (gslf/get-drop-index target-frame objects position))
|
||||||
flex-layout? (gslf/get-drop-index target-frame objects position)
|
cell-data (when grid-layout? (gslg/get-drop-cell target-frame objects position))]
|
||||||
grid-layout? (gslg/get-drop-index target-frame objects position))]
|
[move-vector target-frame drop-index cell-data])))
|
||||||
[move-vector target-frame drop-index])))
|
|
||||||
|
|
||||||
(rx/take-until stopper))]
|
(rx/take-until stopper))]
|
||||||
|
|
||||||
@ -502,7 +501,7 @@
|
|||||||
(->> move-stream
|
(->> move-stream
|
||||||
(rx/with-latest-from ms/mouse-position-shift)
|
(rx/with-latest-from ms/mouse-position-shift)
|
||||||
(rx/map
|
(rx/map
|
||||||
(fn [[[move-vector target-frame drop-index] shift?]]
|
(fn [[[move-vector target-frame drop-index cell-data] shift?]]
|
||||||
(let [x-disp? (> (mth/abs (:x move-vector)) (mth/abs (:y move-vector)))
|
(let [x-disp? (> (mth/abs (:x move-vector)) (mth/abs (:y move-vector)))
|
||||||
[move-vector snap-ignore-axis]
|
[move-vector snap-ignore-axis]
|
||||||
(cond
|
(cond
|
||||||
@ -516,7 +515,7 @@
|
|||||||
[move-vector nil])]
|
[move-vector nil])]
|
||||||
|
|
||||||
(-> (dwm/create-modif-tree ids (ctm/move-modifiers move-vector))
|
(-> (dwm/create-modif-tree ids (ctm/move-modifiers move-vector))
|
||||||
(dwm/build-change-frame-modifiers objects selected target-frame drop-index)
|
(dwm/build-change-frame-modifiers objects selected target-frame drop-index cell-data)
|
||||||
(dwm/set-modifiers false false {:snap-ignore-axis snap-ignore-axis}))))))
|
(dwm/set-modifiers false false {:snap-ignore-axis snap-ignore-axis}))))))
|
||||||
|
|
||||||
(->> move-stream
|
(->> move-stream
|
||||||
@ -540,8 +539,8 @@
|
|||||||
(fn [[_ target-frame drop-index]]
|
(fn [[_ target-frame drop-index]]
|
||||||
(let [undo-id (js/Symbol)]
|
(let [undo-id (js/Symbol)]
|
||||||
(rx/of (dwu/start-undo-transaction undo-id)
|
(rx/of (dwu/start-undo-transaction undo-id)
|
||||||
(move-shapes-to-frame ids target-frame drop-index)
|
|
||||||
(dwm/apply-modifiers {:undo-transation? false})
|
(dwm/apply-modifiers {:undo-transation? false})
|
||||||
|
(move-shapes-to-frame ids target-frame drop-index)
|
||||||
(finish-transform)
|
(finish-transform)
|
||||||
(dwu/commit-undo-transaction undo-id))))))))))))))
|
(dwu/commit-undo-transaction undo-id))))))))))))))
|
||||||
|
|
||||||
@ -557,57 +556,76 @@
|
|||||||
objects (wsh/lookup-page-objects state)
|
objects (wsh/lookup-page-objects state)
|
||||||
page-id (:current-page-id state)
|
page-id (:current-page-id state)
|
||||||
|
|
||||||
get-new-position
|
get-move-to-index
|
||||||
(fn [parent-id position]
|
(fn [parent-id position]
|
||||||
(let [parent (get objects parent-id)]
|
(let [parent (get objects parent-id)]
|
||||||
(cond
|
(if (or (and (ctl/reverse? parent)
|
||||||
(ctl/flex-layout? parent)
|
(or (= direction :left)
|
||||||
(if (or
|
(= direction :up)))
|
||||||
(and (ctl/reverse? parent)
|
(and (not (ctl/reverse? parent))
|
||||||
(or (= direction :left)
|
(or (= direction :right)
|
||||||
(= direction :up)))
|
(= direction :down))))
|
||||||
(and (not (ctl/reverse? parent))
|
(dec position)
|
||||||
(or (= direction :right)
|
(+ position 2))))
|
||||||
(= direction :down))))
|
|
||||||
(dec position)
|
|
||||||
(+ position 2))
|
|
||||||
|
|
||||||
;; TODO: GRID
|
move-flex-children
|
||||||
(ctl/grid-layout? parent)
|
(fn [changes parent-id children]
|
||||||
nil
|
(->> children
|
||||||
)))
|
;; Add the position to move the children
|
||||||
|
(map (fn [id]
|
||||||
|
(let [position (cph/get-position-on-parent objects id)]
|
||||||
|
[id (get-move-to-index parent-id position)])))
|
||||||
|
(sort-by second >)
|
||||||
|
(reduce (fn [changes [child-id index]]
|
||||||
|
(pcb/change-parent changes parent-id [(get objects child-id)] index))
|
||||||
|
changes)))
|
||||||
|
|
||||||
add-children-position
|
move-grid-children
|
||||||
(fn [[parent-id children]]
|
(fn [changes parent-id children]
|
||||||
(let [children+position
|
(let [parent (get objects parent-id)
|
||||||
|
|
||||||
|
key-prop (case direction
|
||||||
|
(:up :down) :row
|
||||||
|
(:right :left) :column)
|
||||||
|
key-comp (case direction
|
||||||
|
(:up :left) <
|
||||||
|
(:down :right) >)
|
||||||
|
|
||||||
|
{:keys [layout-grid-cells]}
|
||||||
(->> children
|
(->> children
|
||||||
(keep #(let [new-position (get-new-position
|
(keep #(ctl/get-cell-by-shape-id parent %))
|
||||||
parent-id
|
(sort-by key-prop key-comp)
|
||||||
(cph/get-position-on-parent objects %))]
|
(reduce (fn [parent {:keys [id row column row-span column-span]}]
|
||||||
(when new-position
|
(let [[next-row next-column]
|
||||||
(vector % new-position))))
|
(case direction
|
||||||
(sort-by second >))]
|
:up [(dec row) column]
|
||||||
[parent-id children+position]))
|
:right [row (+ column column-span)]
|
||||||
|
:down [(+ row row-span) column]
|
||||||
change-parents-and-position
|
:left [row (dec column)])
|
||||||
(->> selected
|
next-cell (ctl/get-cell-by-position parent next-row next-column)]
|
||||||
(group-by #(dm/get-in objects [% :parent-id]))
|
(cond-> parent
|
||||||
(map add-children-position)
|
(some? next-cell)
|
||||||
(into {}))
|
(ctl/swap-shapes id (:id next-cell)))))
|
||||||
|
parent))]
|
||||||
|
(-> changes
|
||||||
|
(pcb/update-shapes [(:id parent)] (fn [shape] (assoc shape :layout-grid-cells layout-grid-cells)))
|
||||||
|
(pcb/reorder-grid-children [(:id parent)]))))
|
||||||
|
|
||||||
changes
|
changes
|
||||||
(->> change-parents-and-position
|
(->> selected
|
||||||
|
(group-by #(dm/get-in objects [% :parent-id]))
|
||||||
(reduce
|
(reduce
|
||||||
(fn [changes [parent-id children]]
|
(fn [changes [parent-id children]]
|
||||||
(->> children
|
(cond-> changes
|
||||||
(reduce
|
(ctl/flex-layout? objects parent-id)
|
||||||
(fn [changes [child-id index]]
|
(move-flex-children parent-id children)
|
||||||
(pcb/change-parent changes parent-id
|
|
||||||
[(get objects child-id)]
|
(ctl/grid-layout? objects parent-id)
|
||||||
index))
|
(move-grid-children parent-id children)))
|
||||||
changes)))
|
|
||||||
(-> (pcb/empty-changes it page-id)
|
(-> (pcb/empty-changes it page-id)
|
||||||
(pcb/with-objects objects))))
|
(pcb/with-objects objects))))
|
||||||
|
|
||||||
undo-id (js/Symbol)]
|
undo-id (js/Symbol)]
|
||||||
|
|
||||||
(rx/of
|
(rx/of
|
||||||
@ -795,6 +813,7 @@
|
|||||||
(pcb/update-shapes moving-shapes-ids #(cond-> % (cph/frame-shape? %) (assoc :hide-in-viewer true)))
|
(pcb/update-shapes moving-shapes-ids #(cond-> % (cph/frame-shape? %) (assoc :hide-in-viewer true)))
|
||||||
(pcb/update-shapes shape-ids-to-detach ctk/detach-shape)
|
(pcb/update-shapes shape-ids-to-detach ctk/detach-shape)
|
||||||
(pcb/change-parent frame-id moving-shapes drop-index)
|
(pcb/change-parent frame-id moving-shapes drop-index)
|
||||||
|
(pcb/reorder-grid-children [frame-id])
|
||||||
(pcb/remove-objects empty-parents))]
|
(pcb/remove-objects empty-parents))]
|
||||||
|
|
||||||
(when (and (some? frame-id) (d/not-empty? changes))
|
(when (and (some? frame-id) (d/not-empty? changes))
|
||||||
|
|||||||
@ -20,7 +20,7 @@
|
|||||||
(log/set-level! :warn)
|
(log/set-level! :warn)
|
||||||
|
|
||||||
(def available-features
|
(def available-features
|
||||||
#{:auto-layout :components-v2 :new-css-system})
|
#{:components-v2 :new-css-system :grid-layout})
|
||||||
|
|
||||||
(defn- toggle-feature
|
(defn- toggle-feature
|
||||||
[feature]
|
[feature]
|
||||||
|
|||||||
@ -485,23 +485,41 @@
|
|||||||
(defn workspace-text-modifier-by-id [id]
|
(defn workspace-text-modifier-by-id [id]
|
||||||
(l/derived #(get % id) workspace-text-modifier =))
|
(l/derived #(get % id) workspace-text-modifier =))
|
||||||
|
|
||||||
(defn is-flex-layout-child?
|
(defn is-layout-child?
|
||||||
[ids]
|
[ids]
|
||||||
(l/derived
|
(l/derived
|
||||||
(fn [objects]
|
(fn [objects]
|
||||||
(->> ids
|
(->> ids
|
||||||
(map (d/getf objects))
|
(map (d/getf objects))
|
||||||
(some (partial ctl/flex-layout-immediate-child? objects))))
|
(some (partial ctl/any-layout-immediate-child? objects))))
|
||||||
workspace-page-objects))
|
workspace-page-objects))
|
||||||
|
|
||||||
(defn all-flex-layout-child?
|
(defn all-layout-child?
|
||||||
|
[ids]
|
||||||
|
(l/derived
|
||||||
|
(fn [objects]
|
||||||
|
(->> ids
|
||||||
|
(map (d/getf objects))
|
||||||
|
(every? (partial ctl/any-layout-immediate-child? objects))))
|
||||||
|
workspace-page-objects =))
|
||||||
|
|
||||||
|
(defn flex-layout-child?
|
||||||
[ids]
|
[ids]
|
||||||
(l/derived
|
(l/derived
|
||||||
(fn [objects]
|
(fn [objects]
|
||||||
(->> ids
|
(->> ids
|
||||||
(map (d/getf objects))
|
(map (d/getf objects))
|
||||||
(every? (partial ctl/flex-layout-immediate-child? objects))))
|
(every? (partial ctl/flex-layout-immediate-child? objects))))
|
||||||
workspace-page-objects))
|
workspace-page-objects =))
|
||||||
|
|
||||||
|
(defn grid-layout-child?
|
||||||
|
[ids]
|
||||||
|
(l/derived
|
||||||
|
(fn [objects]
|
||||||
|
(->> ids
|
||||||
|
(map (d/getf objects))
|
||||||
|
(every? (partial ctl/grid-layout-immediate-child? objects))))
|
||||||
|
workspace-page-objects =))
|
||||||
|
|
||||||
(defn get-flex-child-viewer
|
(defn get-flex-child-viewer
|
||||||
[ids page-id]
|
[ids page-id]
|
||||||
|
|||||||
@ -27,7 +27,7 @@
|
|||||||
[:button.copy-button
|
[:button.copy-button
|
||||||
{:on-click #(when-not @just-copied
|
{:on-click #(when-not @just-copied
|
||||||
(reset! just-copied true)
|
(reset! just-copied true)
|
||||||
(wapi/write-to-clipboard data))}
|
(wapi/write-to-clipboard (if (fn? data) (data) data)))}
|
||||||
(if @just-copied
|
(if @just-copied
|
||||||
i/tick
|
i/tick
|
||||||
i/copy)]))
|
i/copy)]))
|
||||||
|
|||||||
@ -26,7 +26,8 @@
|
|||||||
(and (ctl/flex-layout? shape) (ctl/row? shape))
|
(and (ctl/flex-layout? shape) (ctl/row? shape))
|
||||||
i/layout-rows
|
i/layout-rows
|
||||||
|
|
||||||
;; TODO: GRID ICON
|
(ctl/grid-layout? shape)
|
||||||
|
i/grid-layout-mode
|
||||||
|
|
||||||
:else
|
:else
|
||||||
i/artboard)
|
i/artboard)
|
||||||
|
|||||||
@ -16,32 +16,36 @@
|
|||||||
(format-percent value nil))
|
(format-percent value nil))
|
||||||
|
|
||||||
([value {:keys [precision] :or {precision 2}}]
|
([value {:keys [precision] :or {precision 2}}]
|
||||||
(when (d/num? value)
|
(let [value (if (string? value) (d/parse-double value) value)]
|
||||||
(let [percent-val (mth/precision (* value 100) precision)]
|
(when (d/num? value)
|
||||||
(dm/str percent-val "%")))))
|
(let [percent-val (mth/precision (* value 100) precision)]
|
||||||
|
(dm/str percent-val "%"))))))
|
||||||
|
|
||||||
(defn format-number
|
(defn format-number
|
||||||
([value]
|
([value]
|
||||||
(format-number value nil))
|
(format-number value nil))
|
||||||
([value {:keys [precision] :or {precision 2}}]
|
([value {:keys [precision] :or {precision 2}}]
|
||||||
(when (d/num? value)
|
(let [value (if (string? value) (d/parse-double value) value)]
|
||||||
(let [value (mth/precision value precision)]
|
(when (d/num? value)
|
||||||
(dm/str value)))))
|
(let [value (mth/precision value precision)]
|
||||||
|
(dm/str value))))))
|
||||||
|
|
||||||
(defn format-pixels
|
(defn format-pixels
|
||||||
([value]
|
([value]
|
||||||
(format-pixels value nil))
|
(format-pixels value nil))
|
||||||
|
|
||||||
([value {:keys [precision] :or {precision 2}}]
|
([value {:keys [precision] :or {precision 2}}]
|
||||||
(when (d/num? value)
|
(let [value (if (string? value) (d/parse-double value) value)]
|
||||||
(let [value (mth/precision value precision)]
|
(when (d/num? value)
|
||||||
(dm/str value "px")))))
|
(let [value (mth/precision value precision)]
|
||||||
|
(dm/str value "px"))))))
|
||||||
|
|
||||||
(defn format-int
|
(defn format-int
|
||||||
[value]
|
[value]
|
||||||
(when (d/num? value)
|
(let [value (if (string? value) (d/parse-double value) value)]
|
||||||
(let [value (mth/precision value 0)]
|
(when (d/num? value)
|
||||||
(dm/str value))))
|
(let [value (mth/precision value 0)]
|
||||||
|
(dm/str value)))))
|
||||||
|
|
||||||
(defn format-padding-margin-shorthand
|
(defn format-padding-margin-shorthand
|
||||||
[values]
|
[values]
|
||||||
@ -97,3 +101,15 @@
|
|||||||
(if (= row-gap column-gap)
|
(if (= row-gap column-gap)
|
||||||
(str/fmt "%spx" (format-number row-gap))
|
(str/fmt "%spx" (format-number row-gap))
|
||||||
(str/fmt "%spx %spx" (format-number row-gap) (format-number column-gap)))))
|
(str/fmt "%spx %spx" (format-number row-gap) (format-number column-gap)))))
|
||||||
|
|
||||||
|
(defn format-matrix
|
||||||
|
([mtx]
|
||||||
|
(format-matrix mtx 2))
|
||||||
|
([{:keys [a b c d e f]} precision]
|
||||||
|
(dm/fmt "matrix(%, %, %, %, %, %)"
|
||||||
|
(mth/to-fixed a precision)
|
||||||
|
(mth/to-fixed b precision)
|
||||||
|
(mth/to-fixed c precision)
|
||||||
|
(mth/to-fixed d precision)
|
||||||
|
(mth/to-fixed e precision)
|
||||||
|
(mth/to-fixed f precision))))
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.logging :as log]
|
[app.common.logging :as log]
|
||||||
|
[app.common.math :as mth]
|
||||||
[app.main.ui.context :as ctx]
|
[app.main.ui.context :as ctx]
|
||||||
[app.main.ui.hooks :as hooks]
|
[app.main.ui.hooks :as hooks]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
@ -65,11 +66,19 @@
|
|||||||
start-size (mf/ref-val start-size-ref)
|
start-size (mf/ref-val start-size-ref)
|
||||||
new-size (-> (+ start-size delta) (max min-val) (min max-val))]
|
new-size (-> (+ start-size delta) (max min-val) (min max-val))]
|
||||||
(reset! size-state new-size)
|
(reset! size-state new-size)
|
||||||
(swap! storage assoc-in [::saved-resize current-file-id key] new-size)))))]
|
(swap! storage assoc-in [::saved-resize current-file-id key] new-size)))))
|
||||||
|
|
||||||
|
set-size
|
||||||
|
(mf/use-callback
|
||||||
|
(fn [new-size]
|
||||||
|
(let [new-size (mth/clamp new-size min-val max-val)]
|
||||||
|
(reset! size-state new-size)
|
||||||
|
(swap! storage assoc-in [::saved-resize current-file-id key] new-size))))]
|
||||||
{:on-pointer-down on-pointer-down
|
{:on-pointer-down on-pointer-down
|
||||||
:on-lost-pointer-capture on-lost-pointer-capture
|
:on-lost-pointer-capture on-lost-pointer-capture
|
||||||
:on-pointer-move on-pointer-move
|
:on-pointer-move on-pointer-move
|
||||||
:parent-ref parent-ref
|
:parent-ref parent-ref
|
||||||
|
:set-size set-size
|
||||||
:size @size-state}))
|
:size @size-state}))
|
||||||
|
|
||||||
(defn use-resize-observer
|
(defn use-resize-observer
|
||||||
|
|||||||
@ -340,9 +340,14 @@
|
|||||||
layout-wrap-type
|
layout-wrap-type
|
||||||
layout-padding-type
|
layout-padding-type
|
||||||
layout-padding
|
layout-padding
|
||||||
|
layout-justify-items
|
||||||
layout-justify-content
|
layout-justify-content
|
||||||
layout-align-items
|
layout-align-items
|
||||||
layout-align-content]}]
|
layout-align-content
|
||||||
|
layout-grid-dir
|
||||||
|
layout-grid-rows
|
||||||
|
layout-grid-columns
|
||||||
|
layout-grid-cells]}]
|
||||||
|
|
||||||
(when layout
|
(when layout
|
||||||
(mf/html
|
(mf/html
|
||||||
@ -358,9 +363,48 @@
|
|||||||
:penpot:layout-padding-p2 (:p2 layout-padding)
|
:penpot:layout-padding-p2 (:p2 layout-padding)
|
||||||
:penpot:layout-padding-p3 (:p3 layout-padding)
|
:penpot:layout-padding-p3 (:p3 layout-padding)
|
||||||
:penpot:layout-padding-p4 (:p4 layout-padding)
|
:penpot:layout-padding-p4 (:p4 layout-padding)
|
||||||
|
:penpot:layout-justify-items (d/name layout-justify-items)
|
||||||
:penpot:layout-justify-content (d/name layout-justify-content)
|
:penpot:layout-justify-content (d/name layout-justify-content)
|
||||||
:penpot:layout-align-items (d/name layout-align-items)
|
:penpot:layout-align-items (d/name layout-align-items)
|
||||||
:penpot:layout-align-content (d/name layout-align-content)}])))
|
:penpot:layout-align-content (d/name layout-align-content)
|
||||||
|
:penpot:layout-grid-dir (d/name layout-grid-dir)}
|
||||||
|
|
||||||
|
[:> "penpot:grid-rows" #js {}
|
||||||
|
(for [[idx {:keys [type value]}] (d/enumerate layout-grid-rows)]
|
||||||
|
[:> "penpot:grid-track"
|
||||||
|
#js {:penpot:index idx
|
||||||
|
:penpot:type (d/name type)
|
||||||
|
:penpot:value value}])]
|
||||||
|
|
||||||
|
[:> "penpot:grid-columns" #js {}
|
||||||
|
(for [[idx {:keys [type value]}] (d/enumerate layout-grid-columns)]
|
||||||
|
[:> "penpot:grid-track"
|
||||||
|
#js {:penpot:index idx
|
||||||
|
:penpot:type (d/name type)
|
||||||
|
:penpot:value value}])]
|
||||||
|
|
||||||
|
[:> "penpot:grid-cells" #js {}
|
||||||
|
(for [[_ {:keys [id
|
||||||
|
area-name
|
||||||
|
row
|
||||||
|
row-span
|
||||||
|
column
|
||||||
|
column-span
|
||||||
|
position
|
||||||
|
align-self
|
||||||
|
justify-self
|
||||||
|
shapes]}] layout-grid-cells]
|
||||||
|
[:> "penpot:grid-cell"
|
||||||
|
#js {:penpot:id id
|
||||||
|
:penpot:area-name area-name
|
||||||
|
:penpot:row row
|
||||||
|
:penpot:row-span row-span
|
||||||
|
:penpot:column column
|
||||||
|
:penpot:column-span column-span
|
||||||
|
:penpot:position (d/name position)
|
||||||
|
:penpot:align-self (d/name align-self)
|
||||||
|
:penpot:justify-self (d/name justify-self)
|
||||||
|
:penpot:shapes (str/join " " shapes)}])]])))
|
||||||
|
|
||||||
(defn- export-layout-item-data
|
(defn- export-layout-item-data
|
||||||
[{:keys [layout-item-margin
|
[{:keys [layout-item-margin
|
||||||
@ -371,7 +415,9 @@
|
|||||||
layout-item-min-h
|
layout-item-min-h
|
||||||
layout-item-max-w
|
layout-item-max-w
|
||||||
layout-item-min-w
|
layout-item-min-w
|
||||||
layout-item-align-self]}]
|
layout-item-align-self
|
||||||
|
layout-item-absolute
|
||||||
|
layout-item-z-index]}]
|
||||||
|
|
||||||
(when (or layout-item-margin
|
(when (or layout-item-margin
|
||||||
layout-item-margin-type
|
layout-item-margin-type
|
||||||
@ -381,7 +427,9 @@
|
|||||||
layout-item-min-h
|
layout-item-min-h
|
||||||
layout-item-max-w
|
layout-item-max-w
|
||||||
layout-item-min-w
|
layout-item-min-w
|
||||||
layout-item-align-self)
|
layout-item-align-self
|
||||||
|
layout-item-absolute
|
||||||
|
layout-item-z-index)
|
||||||
(mf/html
|
(mf/html
|
||||||
[:> "penpot:layout-item"
|
[:> "penpot:layout-item"
|
||||||
#js {:penpot:layout-item-margin-m1 (:m1 layout-item-margin)
|
#js {:penpot:layout-item-margin-m1 (:m1 layout-item-margin)
|
||||||
@ -395,7 +443,9 @@
|
|||||||
:penpot:layout-item-min-h layout-item-min-h
|
:penpot:layout-item-min-h layout-item-min-h
|
||||||
:penpot:layout-item-max-w layout-item-max-w
|
:penpot:layout-item-max-w layout-item-max-w
|
||||||
:penpot:layout-item-min-w layout-item-min-w
|
:penpot:layout-item-min-w layout-item-min-w
|
||||||
:penpot:layout-item-align-self (d/name layout-item-align-self)}])))
|
:penpot:layout-item-align-self (d/name layout-item-align-self)
|
||||||
|
:penpot:layout-item-absolute layout-item-absolute
|
||||||
|
:penpot:layout-item-z-index layout-item-z-index}])))
|
||||||
|
|
||||||
|
|
||||||
(mf/defc export-data
|
(mf/defc export-data
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
[app.main.ui.context :as muc]
|
[app.main.ui.context :as muc]
|
||||||
[app.main.ui.shapes.attrs :as attrs]
|
[app.main.ui.shapes.attrs :as attrs]
|
||||||
[app.main.ui.shapes.custom-stroke :refer [shape-fills shape-strokes]]
|
[app.main.ui.shapes.custom-stroke :refer [shape-fills shape-strokes]]
|
||||||
|
[app.main.ui.shapes.grid-layout-viewer :refer [grid-layout-viewer]]
|
||||||
[app.util.object :as obj]
|
[app.util.object :as obj]
|
||||||
[debug :refer [debug?]]
|
[debug :refer [debug?]]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
@ -138,9 +139,12 @@
|
|||||||
childs (unchecked-get props "childs")
|
childs (unchecked-get props "childs")
|
||||||
childs (cond-> childs
|
childs (cond-> childs
|
||||||
(ctl/any-layout? shape)
|
(ctl/any-layout? shape)
|
||||||
(cph/sort-layout-children-z-index))]
|
(cph/sort-layout-children-z-index))
|
||||||
|
is-component? (mf/use-ctx muc/is-component?)]
|
||||||
[:> frame-container props
|
[:> frame-container props
|
||||||
[:g.frame-children {:opacity (:opacity shape)}
|
[:g.frame-children {:opacity (:opacity shape)}
|
||||||
(for [item childs]
|
(for [item childs]
|
||||||
[:& shape-wrapper {:key (dm/str (:id item)) :shape item}])]])))
|
[:& shape-wrapper {:key (dm/str (:id item)) :shape item}])]
|
||||||
|
(when (and is-component? (empty? childs))
|
||||||
|
[:& grid-layout-viewer {:shape shape :childs childs}])])))
|
||||||
|
|
||||||
|
|||||||
99
frontend/src/app/main/ui/shapes/grid_layout_viewer.cljs
Normal file
99
frontend/src/app/main/ui/shapes/grid_layout_viewer.cljs
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
;; 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.shapes.grid-layout-viewer
|
||||||
|
(:require
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
|
[app.common.geom.matrix :as gmt]
|
||||||
|
[app.common.geom.point :as gpt]
|
||||||
|
[app.common.geom.shapes :as gsh]
|
||||||
|
[app.common.geom.shapes.grid-layout :as gsg]
|
||||||
|
[app.common.geom.shapes.points :as gpo]
|
||||||
|
[app.common.types.shape.layout :as ctl]
|
||||||
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
|
(mf/defc grid-cell-area-label
|
||||||
|
{::mf/wrap-props false}
|
||||||
|
[props]
|
||||||
|
|
||||||
|
(let [cell-origin (unchecked-get props "origin")
|
||||||
|
cell-width (unchecked-get props "width")
|
||||||
|
text (unchecked-get props "text")
|
||||||
|
|
||||||
|
area-width (* 10 (count text))
|
||||||
|
area-height 25
|
||||||
|
area-x (- (+ (:x cell-origin) cell-width) area-width)
|
||||||
|
area-y (:y cell-origin)
|
||||||
|
|
||||||
|
area-text-x (+ area-x (/ area-width 2))
|
||||||
|
area-text-y (+ area-y (/ area-height 2))]
|
||||||
|
|
||||||
|
[:g {:pointer-events "none"}
|
||||||
|
[:rect {:x area-x
|
||||||
|
:y area-y
|
||||||
|
:width area-width
|
||||||
|
:height area-height
|
||||||
|
:style {:fill "var(--color-distance)"
|
||||||
|
:fill-opacity 0.3}}]
|
||||||
|
[:text {:x area-text-x
|
||||||
|
:y area-text-y
|
||||||
|
:style {:fill "var(--color-distance)"
|
||||||
|
:font-family "worksans"
|
||||||
|
:font-weight 600
|
||||||
|
:font-size 14
|
||||||
|
:alignment-baseline "central"
|
||||||
|
:text-anchor "middle"}}
|
||||||
|
text]]))
|
||||||
|
|
||||||
|
(mf/defc grid-cell
|
||||||
|
{::mf/wrap-props false}
|
||||||
|
[props]
|
||||||
|
(let [shape (unchecked-get props "shape")
|
||||||
|
cell (unchecked-get props "cell")
|
||||||
|
layout-data (unchecked-get props "layout-data")
|
||||||
|
|
||||||
|
cell-bounds (gsg/cell-bounds layout-data cell)
|
||||||
|
cell-origin (gpo/origin cell-bounds)
|
||||||
|
cell-width (gpo/width-points cell-bounds)
|
||||||
|
cell-height (gpo/height-points cell-bounds)
|
||||||
|
cell-center (gsh/center-points cell-bounds)
|
||||||
|
cell-origin (gpt/transform cell-origin (gmt/transform-in cell-center (:transform-inverse shape)))]
|
||||||
|
|
||||||
|
[:g.cell
|
||||||
|
[:rect
|
||||||
|
{:transform (dm/str (gmt/transform-in cell-center (:transform shape)))
|
||||||
|
:x (:x cell-origin)
|
||||||
|
:y (:y cell-origin)
|
||||||
|
:width cell-width
|
||||||
|
:height cell-height
|
||||||
|
:style {:stroke "var(--color-distance)"
|
||||||
|
:stroke-width 1.5
|
||||||
|
:fill "none"}}]
|
||||||
|
|
||||||
|
(when (:area-name cell)
|
||||||
|
[:& grid-cell-area-label {:origin cell-origin
|
||||||
|
:width cell-width
|
||||||
|
:text (:area-name cell)}])]))
|
||||||
|
|
||||||
|
(mf/defc grid-layout-viewer
|
||||||
|
{::mf/wrap-props false}
|
||||||
|
[props]
|
||||||
|
(let [shape (unchecked-get props "shape")
|
||||||
|
childs (unchecked-get props "childs")
|
||||||
|
|
||||||
|
children
|
||||||
|
(->> childs
|
||||||
|
(remove :hidden)
|
||||||
|
(map #(vector (gpo/parent-coords-bounds (:points %) (:points shape)) %)))
|
||||||
|
|
||||||
|
layout-data (gsg/calc-layout-data shape children (:points shape))]
|
||||||
|
|
||||||
|
[:g.cells
|
||||||
|
(for [cell (ctl/get-cells shape {:sort? true})]
|
||||||
|
[:& grid-cell {:key (dm/str "cell-" (:id cell))
|
||||||
|
:shape shape
|
||||||
|
:layout-data layout-data
|
||||||
|
:cell cell}])]))
|
||||||
@ -197,7 +197,7 @@
|
|||||||
;; We use a class here because react has a bug that won't use the appropriate selector for
|
;; We use a class here because react has a bug that won't use the appropriate selector for
|
||||||
;; `background-clip`
|
;; `background-clip`
|
||||||
[:style ".text-node { background-clip: text;
|
[:style ".text-node { background-clip: text;
|
||||||
-webkit-background-clip: text;" ]
|
-webkit-background-clip: text; }" ]
|
||||||
[:& render-node {:index 0
|
[:& render-node {:index 0
|
||||||
:shape shape
|
:shape shape
|
||||||
:node content}]]))
|
:node content}]]))
|
||||||
|
|||||||
@ -48,6 +48,18 @@
|
|||||||
|
|
||||||
(mf/ref-val fonts-css-ref)))
|
(mf/ref-val fonts-css-ref)))
|
||||||
|
|
||||||
|
(mf/defc fontfaces-style-html
|
||||||
|
{::mf/wrap-props false
|
||||||
|
::mf/wrap [#(mf/memo' % (mf/check-props ["fonts"]))]}
|
||||||
|
[props]
|
||||||
|
|
||||||
|
(let [fonts (obj/get props "fonts")
|
||||||
|
|
||||||
|
;; Fetch its CSS fontfaces
|
||||||
|
fonts-css (use-fonts-css fonts)]
|
||||||
|
|
||||||
|
[:style fonts-css]))
|
||||||
|
|
||||||
(mf/defc fontfaces-style-render
|
(mf/defc fontfaces-style-render
|
||||||
{::mf/wrap-props false
|
{::mf/wrap-props false
|
||||||
::mf/wrap [#(mf/memo' % (mf/check-props ["fonts"]))]}
|
::mf/wrap [#(mf/memo' % (mf/check-props ["fonts"]))]}
|
||||||
@ -63,7 +75,6 @@
|
|||||||
(mf/deps fonts-css)
|
(mf/deps fonts-css)
|
||||||
#(fonts/extract-fontface-urls fonts-css))
|
#(fonts/extract-fontface-urls fonts-css))
|
||||||
|
|
||||||
|
|
||||||
;; Calculate the data-uris for these fonts
|
;; Calculate the data-uris for these fonts
|
||||||
fonts-embed (embed/use-data-uris fonts-urls)
|
fonts-embed (embed/use-data-uris fonts-urls)
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
|
[app.common.text :as txt]
|
||||||
[app.main.ui.shapes.text.styles :as sts]
|
[app.main.ui.shapes.text.styles :as sts]
|
||||||
[app.util.object :as obj]
|
[app.util.object :as obj]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
@ -18,11 +19,14 @@
|
|||||||
(let [node (obj/get props "node")
|
(let [node (obj/get props "node")
|
||||||
parent (obj/get props "parent")
|
parent (obj/get props "parent")
|
||||||
shape (obj/get props "shape")
|
shape (obj/get props "shape")
|
||||||
|
code? (obj/get props "code?")
|
||||||
text (:text node)
|
text (:text node)
|
||||||
style (if (= text "")
|
style (when-not code?
|
||||||
(sts/generate-text-styles shape parent)
|
(if (= text "")
|
||||||
(sts/generate-text-styles shape node))]
|
(sts/generate-text-styles shape parent)
|
||||||
[:span.text-node {:style style}
|
(sts/generate-text-styles shape node)))
|
||||||
|
class (when code? (:$id node))]
|
||||||
|
[:span.text-node {:style style :class class}
|
||||||
(if (= text "") "\u00A0" text)]))
|
(if (= text "") "\u00A0" text)]))
|
||||||
|
|
||||||
(mf/defc render-root
|
(mf/defc render-root
|
||||||
@ -31,19 +35,25 @@
|
|||||||
(let [node (obj/get props "node")
|
(let [node (obj/get props "node")
|
||||||
children (obj/get props "children")
|
children (obj/get props "children")
|
||||||
shape (obj/get props "shape")
|
shape (obj/get props "shape")
|
||||||
style (sts/generate-root-styles shape node)]
|
code? (obj/get props "code?")
|
||||||
|
style (when-not code? (sts/generate-root-styles shape node))
|
||||||
|
class (when code? (:$id node))]
|
||||||
[:div.root.rich-text
|
[:div.root.rich-text
|
||||||
{:style style
|
{:style style
|
||||||
|
:class class
|
||||||
:xmlns "http://www.w3.org/1999/xhtml"}
|
:xmlns "http://www.w3.org/1999/xhtml"}
|
||||||
children]))
|
children]))
|
||||||
|
|
||||||
(mf/defc render-paragraph-set
|
(mf/defc render-paragraph-set
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
[props]
|
[props]
|
||||||
(let [children (obj/get props "children")
|
(let [node (obj/get props "node")
|
||||||
|
children (obj/get props "children")
|
||||||
shape (obj/get props "shape")
|
shape (obj/get props "shape")
|
||||||
style (sts/generate-paragraph-set-styles shape)]
|
code? (obj/get props "code?")
|
||||||
[:div.paragraph-set {:style style} children]))
|
style (when-not code? (sts/generate-paragraph-set-styles shape))
|
||||||
|
class (when code? (:$id node))]
|
||||||
|
[:div.paragraph-set {:style style :class class} children]))
|
||||||
|
|
||||||
(mf/defc render-paragraph
|
(mf/defc render-paragraph
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
@ -51,15 +61,18 @@
|
|||||||
(let [node (obj/get props "node")
|
(let [node (obj/get props "node")
|
||||||
shape (obj/get props "shape")
|
shape (obj/get props "shape")
|
||||||
children (obj/get props "children")
|
children (obj/get props "children")
|
||||||
style (sts/generate-paragraph-styles shape node)
|
code? (obj/get props "code?")
|
||||||
|
style (when-not code? (sts/generate-paragraph-styles shape node))
|
||||||
|
class (when code? (:$id node))
|
||||||
dir (:text-direction node "auto")]
|
dir (:text-direction node "auto")]
|
||||||
[:p.paragraph {:style style :dir dir} children]))
|
[:p.paragraph {:style style :dir dir :class class} children]))
|
||||||
|
|
||||||
;; -- Text nodes
|
;; -- Text nodes
|
||||||
(mf/defc render-node
|
(mf/defc render-node
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
[props]
|
[props]
|
||||||
(let [{:keys [type text children] :as parent} (obj/get props "node")]
|
(let [{:keys [type text children] :as parent} (obj/get props "node")
|
||||||
|
code? (obj/get props "code?")]
|
||||||
(if (string? text)
|
(if (string? text)
|
||||||
[:> render-text props]
|
[:> render-text props]
|
||||||
(let [component (case type
|
(let [component (case type
|
||||||
@ -74,7 +87,8 @@
|
|||||||
(obj/set! "node" node)
|
(obj/set! "node" node)
|
||||||
(obj/set! "parent" parent)
|
(obj/set! "parent" parent)
|
||||||
(obj/set! "index" index)
|
(obj/set! "index" index)
|
||||||
(obj/set! "key" index))]
|
(obj/set! "key" index)
|
||||||
|
(obj/set! "code?" code?))]
|
||||||
[:> render-node props]))])))))
|
[:> render-node props]))])))))
|
||||||
|
|
||||||
(mf/defc text-shape
|
(mf/defc text-shape
|
||||||
@ -83,23 +97,32 @@
|
|||||||
[props ref]
|
[props ref]
|
||||||
(let [shape (obj/get props "shape")
|
(let [shape (obj/get props "shape")
|
||||||
grow-type (obj/get props "grow-type")
|
grow-type (obj/get props "grow-type")
|
||||||
{:keys [id x y width height content]} shape]
|
code? (obj/get props "code?")
|
||||||
|
{:keys [id x y width height content]} shape
|
||||||
|
|
||||||
|
content (if code? (txt/index-content content) content)
|
||||||
|
|
||||||
|
style
|
||||||
|
(when-not code?
|
||||||
|
#js {:position "fixed"
|
||||||
|
:left 0
|
||||||
|
:top 0
|
||||||
|
:background "white"
|
||||||
|
:width (if (#{:auto-width} grow-type) 100000 width)
|
||||||
|
:height (if (#{:auto-height :auto-width} grow-type) 100000 height)})]
|
||||||
|
|
||||||
[:div.text-node-html
|
[:div.text-node-html
|
||||||
{:id (dm/str "html-text-node-" id)
|
{:id (dm/str "html-text-node-" id)
|
||||||
:ref ref
|
:ref ref
|
||||||
:data-x x
|
:data-x x
|
||||||
:data-y y
|
:data-y y
|
||||||
:style {:position "fixed"
|
:style style}
|
||||||
:left 0
|
|
||||||
:top 0
|
|
||||||
:background "white"
|
|
||||||
:width (if (#{:auto-width} grow-type) 100000 width)
|
|
||||||
:height (if (#{:auto-height :auto-width} grow-type) 100000 height)}}
|
|
||||||
;; We use a class here because react has a bug that won't use the appropriate selector for
|
;; We use a class here because react has a bug that won't use the appropriate selector for
|
||||||
;; `background-clip`
|
;; `background-clip`
|
||||||
[:style ".text-node { background-clip: text;
|
(when (not code?)
|
||||||
-webkit-background-clip: text;" ]
|
[:style ".text-node { background-clip: text;
|
||||||
|
-webkit-background-clip: text; }" ])
|
||||||
[:& render-node {:index 0
|
[:& render-node {:index 0
|
||||||
:shape shape
|
:shape shape
|
||||||
:node content}]]))
|
:node content
|
||||||
|
:code? code?}]]))
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
[app.common.text :as txt]
|
[app.common.text :as txt]
|
||||||
[app.common.transit :as transit]
|
[app.common.transit :as transit]
|
||||||
[app.main.fonts :as fonts]
|
[app.main.fonts :as fonts]
|
||||||
|
[app.main.ui.formats :as fmt]
|
||||||
[app.util.color :as uc]
|
[app.util.color :as uc]
|
||||||
[app.util.object :as obj]
|
[app.util.object :as obj]
|
||||||
[cuerdas.core :as str]))
|
[cuerdas.core :as str]))
|
||||||
@ -17,8 +18,8 @@
|
|||||||
(defn generate-root-styles
|
(defn generate-root-styles
|
||||||
[{:keys [width height]} node]
|
[{:keys [width height]} node]
|
||||||
(let [valign (:vertical-align node "top")
|
(let [valign (:vertical-align node "top")
|
||||||
base #js {:height height
|
base #js {:height (fmt/format-pixels height)
|
||||||
:width width
|
:width (fmt/format-pixels width)
|
||||||
:fontFamily "sourcesanspro"
|
:fontFamily "sourcesanspro"
|
||||||
:display "flex"
|
:display "flex"
|
||||||
:whiteSpace "break-spaces"}]
|
:whiteSpace "break-spaces"}]
|
||||||
@ -48,7 +49,8 @@
|
|||||||
[_shape data]
|
[_shape data]
|
||||||
(let [line-height (:line-height data 1.2)
|
(let [line-height (:line-height data 1.2)
|
||||||
text-align (:text-align data "start")
|
text-align (:text-align data "start")
|
||||||
base #js {:fontSize (str (:font-size data (:font-size txt/default-text-attrs)) "px")
|
base #js {;; Fix a problem when exporting HTML
|
||||||
|
:fontSize 0 ;;(str (:font-size data (:font-size txt/default-text-attrs)) "px")
|
||||||
:lineHeight (:line-height data (:line-height txt/default-text-attrs))
|
:lineHeight (:line-height data (:line-height txt/default-text-attrs))
|
||||||
:margin 0}]
|
:margin 0}]
|
||||||
(cond-> base
|
(cond-> base
|
||||||
@ -72,16 +74,24 @@
|
|||||||
font-size (:font-size data)
|
font-size (:font-size data)
|
||||||
fill-color (or (-> data :fills first :fill-color) (:fill-color data))
|
fill-color (or (-> data :fills first :fill-color) (:fill-color data))
|
||||||
fill-opacity (or (-> data :fills first :fill-opacity) (:fill-opacity data))
|
fill-opacity (or (-> data :fills first :fill-opacity) (:fill-opacity data))
|
||||||
|
fill-gradient (or (-> data :fills first :fill-color-gradient) (:fill-color-gradient data))
|
||||||
|
|
||||||
[r g b a] (uc/hex->rgba fill-color fill-opacity)
|
[r g b a] (uc/hex->rgba fill-color fill-opacity)
|
||||||
text-color (when (and (some? fill-color) (some? fill-opacity))
|
text-color (when (and (some? fill-color) (some? fill-opacity))
|
||||||
(str/format "rgba(%s, %s, %s, %s)" r g b a))
|
(str/format "rgba(%s, %s, %s, %s)" r g b a))
|
||||||
|
|
||||||
|
gradient? (some? fill-gradient)
|
||||||
|
|
||||||
|
text-color (if gradient?
|
||||||
|
(uc/color->background {:gradient fill-gradient})
|
||||||
|
text-color)
|
||||||
|
|
||||||
fontsdb (deref fonts/fontsdb)
|
fontsdb (deref fonts/fontsdb)
|
||||||
|
|
||||||
base #js {:textDecoration text-decoration
|
base #js {:textDecoration text-decoration
|
||||||
:textTransform text-transform
|
:textTransform text-transform
|
||||||
:color (if show-text? text-color "transparent")
|
:color (if (and show-text? (not gradient?)) text-color "transparent")
|
||||||
|
:background (when (and show-text? gradient?) text-color)
|
||||||
:caretColor (or text-color "black")
|
:caretColor (or text-color "black")
|
||||||
:overflowWrap "initial"
|
:overflowWrap "initial"
|
||||||
:lineBreak "auto"
|
:lineBreak "auto"
|
||||||
|
|||||||
@ -6,8 +6,10 @@
|
|||||||
|
|
||||||
(ns app.main.ui.viewer.inspect
|
(ns app.main.ui.viewer.inspect
|
||||||
(:require
|
(:require
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
[app.main.data.viewer :as dv]
|
[app.main.data.viewer :as dv]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
|
[app.main.ui.hooks.resize :refer [use-resize-hook]]
|
||||||
[app.main.ui.viewer.inspect.left-sidebar :refer [left-sidebar]]
|
[app.main.ui.viewer.inspect.left-sidebar :refer [left-sidebar]]
|
||||||
[app.main.ui.viewer.inspect.render :refer [render-frame-svg]]
|
[app.main.ui.viewer.inspect.render :refer [render-frame-svg]]
|
||||||
[app.main.ui.viewer.inspect.right-sidebar :refer [right-sidebar]]
|
[app.main.ui.viewer.inspect.right-sidebar :refer [right-sidebar]]
|
||||||
@ -37,6 +39,11 @@
|
|||||||
(mf/defc viewport
|
(mf/defc viewport
|
||||||
[{:keys [local file page frame index viewer-pagination size share-id]}]
|
[{:keys [local file page frame index viewer-pagination size share-id]}]
|
||||||
(let [inspect-svg-container-ref (mf/use-ref nil)
|
(let [inspect-svg-container-ref (mf/use-ref nil)
|
||||||
|
current-section* (mf/use-state :info)
|
||||||
|
current-section (deref current-section*)
|
||||||
|
|
||||||
|
can-be-expanded? (= current-section :code)
|
||||||
|
|
||||||
on-mouse-wheel
|
on-mouse-wheel
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(when (kbd/mod? event)
|
(when (kbd/mod? event)
|
||||||
@ -55,7 +62,22 @@
|
|||||||
(let [key1 (events/listen goog/global EventType.WHEEL
|
(let [key1 (events/listen goog/global EventType.WHEEL
|
||||||
on-mouse-wheel #js {"passive" false})]
|
on-mouse-wheel #js {"passive" false})]
|
||||||
(fn []
|
(fn []
|
||||||
(events/unlistenByKey key1))))]
|
(events/unlistenByKey key1))))
|
||||||
|
|
||||||
|
{:keys [on-pointer-down on-lost-pointer-capture on-pointer-move]
|
||||||
|
set-right-size :set-size
|
||||||
|
right-size :size}
|
||||||
|
(use-resize-hook :code 256 256 768 :x true :right)
|
||||||
|
|
||||||
|
handle-change-section
|
||||||
|
(mf/use-callback
|
||||||
|
(fn [section]
|
||||||
|
(reset! current-section* section)))
|
||||||
|
|
||||||
|
handle-expand
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps right-size)
|
||||||
|
#(set-right-size (if (> right-size 256) 256 768)))]
|
||||||
|
|
||||||
(mf/use-effect on-mount)
|
(mf/use-effect on-mount)
|
||||||
|
|
||||||
@ -73,8 +95,18 @@
|
|||||||
[:div.inspect-svg-container {:ref inspect-svg-container-ref}
|
[:div.inspect-svg-container {:ref inspect-svg-container-ref}
|
||||||
[:& render-frame-svg {:frame frame :page page :local local :size size}]]]
|
[:& render-frame-svg {:frame frame :page page :local local :size size}]]]
|
||||||
|
|
||||||
[:& right-sidebar {:frame frame
|
[:div.sidebar-container
|
||||||
:selected (:selected local)
|
{:class (when (not can-be-expanded?) "not-expand")
|
||||||
:page page
|
:style #js {"--width" (when can-be-expanded? (dm/str right-size "px"))}}
|
||||||
:file file
|
[:div.resize-area
|
||||||
:share-id share-id}]]))
|
{:on-pointer-down on-pointer-down
|
||||||
|
:on-lost-pointer-capture on-lost-pointer-capture
|
||||||
|
:on-pointer-move on-pointer-move}]
|
||||||
|
[:& right-sidebar {:frame frame
|
||||||
|
:selected (:selected local)
|
||||||
|
:page page
|
||||||
|
:file file
|
||||||
|
:on-change-section handle-change-section
|
||||||
|
:on-expand handle-expand
|
||||||
|
:share-id share-id
|
||||||
|
}]]]))
|
||||||
|
|||||||
@ -6,16 +6,15 @@
|
|||||||
|
|
||||||
(ns app.main.ui.viewer.inspect.attributes
|
(ns app.main.ui.viewer.inspect.attributes
|
||||||
(:require
|
(:require
|
||||||
[app.common.geom.shapes :as gsh]
|
|
||||||
[app.common.types.components-list :as ctkl]
|
[app.common.types.components-list :as ctkl]
|
||||||
[app.main.ui.hooks :as hooks]
|
[app.main.ui.hooks :as hooks]
|
||||||
[app.main.ui.viewer.inspect.annotation :refer [annotation]]
|
[app.main.ui.viewer.inspect.annotation :refer [annotation]]
|
||||||
[app.main.ui.viewer.inspect.attributes.blur :refer [blur-panel]]
|
[app.main.ui.viewer.inspect.attributes.blur :refer [blur-panel]]
|
||||||
[app.main.ui.viewer.inspect.attributes.fill :refer [fill-panel]]
|
[app.main.ui.viewer.inspect.attributes.fill :refer [fill-panel]]
|
||||||
|
[app.main.ui.viewer.inspect.attributes.geometry :refer [geometry-panel]]
|
||||||
[app.main.ui.viewer.inspect.attributes.image :refer [image-panel]]
|
[app.main.ui.viewer.inspect.attributes.image :refer [image-panel]]
|
||||||
[app.main.ui.viewer.inspect.attributes.layout :refer [layout-panel]]
|
[app.main.ui.viewer.inspect.attributes.layout :refer [layout-panel]]
|
||||||
[app.main.ui.viewer.inspect.attributes.layout-flex :refer [layout-flex-panel]]
|
[app.main.ui.viewer.inspect.attributes.layout-element :refer [layout-element-panel]]
|
||||||
[app.main.ui.viewer.inspect.attributes.layout-flex-element :refer [layout-flex-element-panel]]
|
|
||||||
[app.main.ui.viewer.inspect.attributes.shadow :refer [shadow-panel]]
|
[app.main.ui.viewer.inspect.attributes.shadow :refer [shadow-panel]]
|
||||||
[app.main.ui.viewer.inspect.attributes.stroke :refer [stroke-panel]]
|
[app.main.ui.viewer.inspect.attributes.stroke :refer [stroke-panel]]
|
||||||
[app.main.ui.viewer.inspect.attributes.svg :refer [svg-panel]]
|
[app.main.ui.viewer.inspect.attributes.svg :refer [svg-panel]]
|
||||||
@ -24,20 +23,18 @@
|
|||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
(def type->options
|
(def type->options
|
||||||
{:multiple [:fill :stroke :image :text :shadow :blur :layout-flex-item]
|
{:multiple [:fill :stroke :image :text :shadow :blur :layout-element]
|
||||||
:frame [:layout :fill :stroke :shadow :blur :layout-flex :layout-flex-item]
|
:frame [:geometry :fill :stroke :shadow :blur :layout :layout-element]
|
||||||
:group [:layout :svg :layout-flex-item]
|
:group [:geometry :svg :layout-element]
|
||||||
:rect [:layout :fill :stroke :shadow :blur :svg :layout-flex-item]
|
:rect [:geometry :fill :stroke :shadow :blur :svg :layout-element]
|
||||||
:circle [:layout :fill :stroke :shadow :blur :svg :layout-flex-item]
|
:circle [:geometry :fill :stroke :shadow :blur :svg :layout-element]
|
||||||
:path [:layout :fill :stroke :shadow :blur :svg :layout-flex-item]
|
:path [:geometry :fill :stroke :shadow :blur :svg :layout-element]
|
||||||
:image [:image :layout :fill :stroke :shadow :blur :svg :layout-flex-item]
|
:image [:image :geometry :fill :stroke :shadow :blur :svg :layout-element]
|
||||||
:text [:layout :text :shadow :blur :stroke :layout-flex-item]})
|
:text [:geometry :text :shadow :blur :stroke :layout-element]})
|
||||||
|
|
||||||
(mf/defc attributes
|
(mf/defc attributes
|
||||||
[{:keys [page-id file-id shapes frame from libraries share-id]}]
|
[{:keys [page-id file-id shapes frame from libraries share-id objects]}]
|
||||||
(let [shapes (hooks/use-equal-memo shapes)
|
(let [shapes (hooks/use-equal-memo shapes)
|
||||||
shapes (mf/with-memo [shapes]
|
|
||||||
(mapv #(gsh/translate-to-frame % frame) shapes))
|
|
||||||
type (if (= (count shapes) 1) (-> shapes first :type) :multiple)
|
type (if (= (count shapes) 1) (-> shapes first :type) :multiple)
|
||||||
options (type->options type)
|
options (type->options type)
|
||||||
content (when (= (count shapes) 1)
|
content (when (= (count shapes) 1)
|
||||||
@ -46,9 +43,9 @@
|
|||||||
[:div.element-options
|
[:div.element-options
|
||||||
(for [[idx option] (map-indexed vector options)]
|
(for [[idx option] (map-indexed vector options)]
|
||||||
[:> (case option
|
[:> (case option
|
||||||
|
:geometry geometry-panel
|
||||||
:layout layout-panel
|
:layout layout-panel
|
||||||
:layout-flex layout-flex-panel
|
:layout-element layout-element-panel
|
||||||
:layout-flex-item layout-flex-element-panel
|
|
||||||
:fill fill-panel
|
:fill fill-panel
|
||||||
:stroke stroke-panel
|
:stroke stroke-panel
|
||||||
:shadow shadow-panel
|
:shadow shadow-panel
|
||||||
@ -58,6 +55,7 @@
|
|||||||
:svg svg-panel)
|
:svg svg-panel)
|
||||||
{:key idx
|
{:key idx
|
||||||
:shapes shapes
|
:shapes shapes
|
||||||
|
:objects objects
|
||||||
:frame frame
|
:frame frame
|
||||||
:from from}])
|
:from from}])
|
||||||
(when content
|
(when content
|
||||||
|
|||||||
@ -7,32 +7,25 @@
|
|||||||
(ns app.main.ui.viewer.inspect.attributes.blur
|
(ns app.main.ui.viewer.inspect.attributes.blur
|
||||||
(:require
|
(:require
|
||||||
[app.main.ui.components.copy-button :refer [copy-button]]
|
[app.main.ui.components.copy-button :refer [copy-button]]
|
||||||
[app.util.code-gen :as cg]
|
[app.util.code-gen.style-css :as css]
|
||||||
[app.util.i18n :refer [tr]]
|
[app.util.i18n :refer [tr]]
|
||||||
[cuerdas.core :as str]
|
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
(defn has-blur? [shape]
|
(defn has-blur? [shape]
|
||||||
(:blur shape))
|
(:blur shape))
|
||||||
|
|
||||||
(defn copy-data [shape]
|
(mf/defc blur-panel
|
||||||
(cg/generate-css-props
|
[{:keys [objects shapes]}]
|
||||||
shape
|
|
||||||
:blur
|
|
||||||
{:to-prop "filter"
|
|
||||||
:format #(str/fmt "blur(%spx)" (:value %))}))
|
|
||||||
|
|
||||||
(mf/defc blur-panel [{:keys [shapes]}]
|
|
||||||
(let [shapes (->> shapes (filter has-blur?))]
|
(let [shapes (->> shapes (filter has-blur?))]
|
||||||
(when (seq shapes)
|
(when (seq shapes)
|
||||||
[:div.attributes-block
|
[:div.attributes-block
|
||||||
[:div.attributes-block-title
|
[:div.attributes-block-title
|
||||||
[:div.attributes-block-title-text (tr "inspect.attributes.blur")]
|
[:div.attributes-block-title-text (tr "inspect.attributes.blur")]
|
||||||
(when (= (count shapes) 1)
|
(when (= (count shapes) 1)
|
||||||
[:& copy-button {:data (copy-data (first shapes))}])]
|
[:& copy-button {:data (css/get-css-property objects (first shapes) :filter)}])]
|
||||||
|
|
||||||
(for [shape shapes]
|
(for [shape shapes]
|
||||||
[:div.attributes-unit-row
|
[:div.attributes-unit-row
|
||||||
[:div.attributes-label (tr "inspect.attributes.blur.value")]
|
[:div.attributes-label (tr "inspect.attributes.blur.value")]
|
||||||
[:div.attributes-value (-> shape :blur :value) "px"]
|
[:div.attributes-value (css/get-css-value objects shape :filter)]
|
||||||
[:& copy-button {:data (copy-data shape)}]])])))
|
[:& copy-button {:data (css/get-css-property objects shape :filter)}]])])))
|
||||||
|
|||||||
@ -17,7 +17,6 @@
|
|||||||
[okulary.core :as l]
|
[okulary.core :as l]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
|
|
||||||
(def file-colors-ref
|
(def file-colors-ref
|
||||||
(l/derived (l/in [:viewer :file :data :colors]) st/state))
|
(l/derived (l/in [:viewer :file :data :colors]) st/state))
|
||||||
|
|
||||||
|
|||||||
@ -7,12 +7,11 @@
|
|||||||
(ns app.main.ui.viewer.inspect.attributes.fill
|
(ns app.main.ui.viewer.inspect.attributes.fill
|
||||||
(:require
|
(:require
|
||||||
[app.main.ui.viewer.inspect.attributes.common :refer [color-row]]
|
[app.main.ui.viewer.inspect.attributes.common :refer [color-row]]
|
||||||
[app.util.code-gen :as cg]
|
[app.util.code-gen.style-css :as css]
|
||||||
[app.util.color :as uc]
|
|
||||||
[app.util.i18n :refer [tr]]
|
[app.util.i18n :refer [tr]]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
(def fill-attributes [:fill-color :fill-color-gradient])
|
(def properties [:background :background-color :background-image])
|
||||||
|
|
||||||
(defn shape->color [shape]
|
(defn shape->color [shape]
|
||||||
{:color (:fill-color shape)
|
{:color (:fill-color shape)
|
||||||
@ -21,21 +20,15 @@
|
|||||||
:id (:fill-color-ref-id shape)
|
:id (:fill-color-ref-id shape)
|
||||||
:file-id (:fill-color-ref-file shape)})
|
:file-id (:fill-color-ref-file shape)})
|
||||||
|
|
||||||
(defn has-color? [shape]
|
(defn has-fill? [shape]
|
||||||
(and
|
(and
|
||||||
(not (contains? #{:text :group} (:type shape)))
|
(not (contains? #{:text :group} (:type shape)))
|
||||||
(or (:fill-color shape)
|
(or (:fill-color shape)
|
||||||
(:fill-color-gradient shape)
|
(:fill-color-gradient shape)
|
||||||
(seq (:fills shape)))))
|
(seq (:fills shape)))))
|
||||||
|
|
||||||
(defn copy-data [shape]
|
(mf/defc fill-block
|
||||||
(cg/generate-css-props
|
[{:keys [objects shape]}]
|
||||||
shape
|
|
||||||
fill-attributes
|
|
||||||
{:to-prop "background"
|
|
||||||
:format #(uc/color->background (shape->color shape))}))
|
|
||||||
|
|
||||||
(mf/defc fill-block [{:keys [shape]}]
|
|
||||||
(let [color-format (mf/use-state :hex)
|
(let [color-format (mf/use-state :hex)
|
||||||
color (shape->color shape)]
|
color (shape->color shape)]
|
||||||
|
|
||||||
@ -43,11 +36,11 @@
|
|||||||
[:& color-row {:color color
|
[:& color-row {:color color
|
||||||
:format @color-format
|
:format @color-format
|
||||||
:on-change-format #(reset! color-format %)
|
:on-change-format #(reset! color-format %)
|
||||||
:copy-data (copy-data shape)}]]))
|
:copy-data (css/get-shape-properties-css objects {:fills [shape]} properties)}]]))
|
||||||
|
|
||||||
(mf/defc fill-panel
|
(mf/defc fill-panel
|
||||||
[{:keys [shapes]}]
|
[{:keys [shapes]}]
|
||||||
(let [shapes (->> shapes (filter has-color?))]
|
(let [shapes (->> shapes (filter has-fill?))]
|
||||||
(when (seq shapes)
|
(when (seq shapes)
|
||||||
[:div.attributes-block
|
[:div.attributes-block
|
||||||
[:div.attributes-block-title
|
[:div.attributes-block-title
|
||||||
|
|||||||
@ -0,0 +1,38 @@
|
|||||||
|
;; 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.viewer.inspect.attributes.geometry
|
||||||
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.main.ui.components.copy-button :refer [copy-button]]
|
||||||
|
[app.util.code-gen.style-css :as css]
|
||||||
|
[app.util.i18n :refer [tr]]
|
||||||
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
|
(def properties [:width :height :left :top :border-radius :transform])
|
||||||
|
|
||||||
|
(mf/defc geometry-block
|
||||||
|
[{:keys [objects shape]}]
|
||||||
|
[:*
|
||||||
|
(for [property properties]
|
||||||
|
(when-let [value (css/get-css-value objects shape property)]
|
||||||
|
[:div.attributes-unit-row
|
||||||
|
[:div.attributes-label (d/name property)]
|
||||||
|
[:div.attributes-value value]
|
||||||
|
[:& copy-button {:data (css/get-css-property objects shape property)}]]))])
|
||||||
|
|
||||||
|
(mf/defc geometry-panel
|
||||||
|
[{:keys [objects shapes]}]
|
||||||
|
[:div.attributes-block
|
||||||
|
[:div.attributes-block-title
|
||||||
|
[:div.attributes-block-title-text (tr "inspect.attributes.size")]
|
||||||
|
(when (= (count shapes) 1)
|
||||||
|
[:& copy-button {:data (css/get-shape-properties-css objects (first shapes) properties)}])]
|
||||||
|
|
||||||
|
(for [shape shapes]
|
||||||
|
[:& geometry-block {:shape shape
|
||||||
|
:objects objects
|
||||||
|
:key (:id shape)}])])
|
||||||
@ -10,7 +10,7 @@
|
|||||||
[app.common.pages.helpers :as cph]
|
[app.common.pages.helpers :as cph]
|
||||||
[app.config :as cf]
|
[app.config :as cf]
|
||||||
[app.main.ui.components.copy-button :refer [copy-button]]
|
[app.main.ui.components.copy-button :refer [copy-button]]
|
||||||
[app.util.code-gen :as cg]
|
[app.util.code-gen.style-css :as css]
|
||||||
[app.util.i18n :refer [tr]]
|
[app.util.i18n :refer [tr]]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
@ -19,7 +19,7 @@
|
|||||||
(= (:type shape) :image))
|
(= (:type shape) :image))
|
||||||
|
|
||||||
(mf/defc image-panel
|
(mf/defc image-panel
|
||||||
[{:keys [shapes]}]
|
[{:keys [objects shapes]}]
|
||||||
(for [shape (filter cph/image-shape? shapes)]
|
(for [shape (filter cph/image-shape? shapes)]
|
||||||
[:div.attributes-block {:key (str "image-" (:id shape))}
|
[:div.attributes-block {:key (str "image-" (:id shape))}
|
||||||
[:div.attributes-image-row
|
[:div.attributes-image-row
|
||||||
@ -28,13 +28,13 @@
|
|||||||
|
|
||||||
[:div.attributes-unit-row
|
[:div.attributes-unit-row
|
||||||
[:div.attributes-label (tr "inspect.attributes.image.width")]
|
[:div.attributes-label (tr "inspect.attributes.image.width")]
|
||||||
[:div.attributes-value (-> shape :metadata :width) "px"]
|
[:div.attributes-value (css/get-css-value objects (:metadata shape) :width)]
|
||||||
[:& copy-button {:data (cg/generate-css-props shape :width)}]]
|
[:& copy-button {:data (css/get-css-property objects (:metadata shape) :width)}]]
|
||||||
|
|
||||||
[:div.attributes-unit-row
|
[:div.attributes-unit-row
|
||||||
[:div.attributes-label (tr "inspect.attributes.image.height")]
|
[:div.attributes-label (tr "inspect.attributes.image.height")]
|
||||||
[:div.attributes-value (-> shape :metadata :height) "px"]
|
[:div.attributes-value (css/get-css-value objects (:metadata shape) :height)]
|
||||||
[:& copy-button {:data (cg/generate-css-props shape :height)}]]
|
[:& copy-button {:data (css/get-css-property objects (:metadata shape) :height)}]]
|
||||||
|
|
||||||
(let [mtype (-> shape :metadata :mtype)
|
(let [mtype (-> shape :metadata :mtype)
|
||||||
name (:name shape)
|
name (:name shape)
|
||||||
|
|||||||
@ -6,92 +6,46 @@
|
|||||||
|
|
||||||
(ns app.main.ui.viewer.inspect.attributes.layout
|
(ns app.main.ui.viewer.inspect.attributes.layout
|
||||||
(:require
|
(:require
|
||||||
[app.common.types.shape.radius :as ctsr]
|
[app.common.data :as d]
|
||||||
|
[app.common.types.shape.layout :as ctl]
|
||||||
[app.main.ui.components.copy-button :refer [copy-button]]
|
[app.main.ui.components.copy-button :refer [copy-button]]
|
||||||
[app.main.ui.formats :as fmt]
|
[app.util.code-gen.style-css :as css]
|
||||||
[app.util.code-gen :as cg]
|
|
||||||
[app.util.i18n :refer [tr]]
|
|
||||||
[cuerdas.core :as str]
|
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
(def properties [:width :height :x :y :radius :rx :r1])
|
(def properties
|
||||||
|
[:display
|
||||||
(def params
|
:flex-direction
|
||||||
{:to-prop {:x "left"
|
:flex-wrap
|
||||||
:y "top"
|
:grid-template-rows
|
||||||
:rotation "transform"
|
:grid-template-columns
|
||||||
:rx "border-radius"
|
:align-items
|
||||||
:r1 "border-radius"}
|
:align-content
|
||||||
:format {:rotation #(str/fmt "rotate(%sdeg)" %)
|
:justify-items
|
||||||
:r1 #(apply str/fmt "%spx, %spx, %spx, %spx" %)
|
:justify-content
|
||||||
:width #(cg/get-size :width %)
|
:gap
|
||||||
:height #(cg/get-size :height %)}
|
:padding])
|
||||||
:multi {:r1 [:r1 :r2 :r3 :r4]}})
|
|
||||||
|
|
||||||
(defn copy-data
|
|
||||||
([shape]
|
|
||||||
(apply copy-data shape properties))
|
|
||||||
([shape & properties]
|
|
||||||
(cg/generate-css-props shape properties params)))
|
|
||||||
|
|
||||||
(mf/defc layout-block
|
(mf/defc layout-block
|
||||||
[{:keys [shape]}]
|
[{:keys [objects shape]}]
|
||||||
(let [selrect (:selrect shape)
|
[:*
|
||||||
{:keys [x y width height]} selrect]
|
(for [property properties]
|
||||||
[:*
|
(when-let [value (css/get-css-value objects shape property)]
|
||||||
[:div.attributes-unit-row
|
|
||||||
[:div.attributes-label (tr "inspect.attributes.layout.width")]
|
|
||||||
[:div.attributes-value (fmt/format-size :width width shape)]
|
|
||||||
[:& copy-button {:data (copy-data selrect :width)}]]
|
|
||||||
|
|
||||||
[:div.attributes-unit-row
|
|
||||||
[:div.attributes-label (tr "inspect.attributes.layout.height")]
|
|
||||||
[:div.attributes-value (fmt/format-size :height height shape)]
|
|
||||||
[:& copy-button {:data (copy-data selrect :height)}]]
|
|
||||||
|
|
||||||
(when (not= (:x shape) 0)
|
|
||||||
[:div.attributes-unit-row
|
[:div.attributes-unit-row
|
||||||
[:div.attributes-label (tr "inspect.attributes.layout.left")]
|
[:div.attributes-label (d/name property)]
|
||||||
[:div.attributes-value (fmt/format-pixels x)]
|
[:div.attributes-value value]
|
||||||
[:& copy-button {:data (copy-data selrect :x)}]])
|
[:& copy-button {:data (css/get-css-property objects shape property)}]]))])
|
||||||
|
|
||||||
(when (not= (:y shape) 0)
|
|
||||||
[:div.attributes-unit-row
|
|
||||||
[:div.attributes-label (tr "inspect.attributes.layout.top")]
|
|
||||||
[:div.attributes-value (fmt/format-pixels y)]
|
|
||||||
[:& copy-button {:data (copy-data selrect :y)}]])
|
|
||||||
|
|
||||||
(when (ctsr/radius-1? shape)
|
|
||||||
[:div.attributes-unit-row
|
|
||||||
[:div.attributes-label (tr "inspect.attributes.layout.radius")]
|
|
||||||
[:div.attributes-value (fmt/format-pixels (:rx shape 0))]
|
|
||||||
[:& copy-button {:data (copy-data shape :rx)}]])
|
|
||||||
|
|
||||||
(when (ctsr/radius-4? shape)
|
|
||||||
[:div.attributes-unit-row
|
|
||||||
[:div.attributes-label (tr "inspect.attributes.layout.radius")]
|
|
||||||
[:div.attributes-value
|
|
||||||
(fmt/format-number (:r1 shape)) ", "
|
|
||||||
(fmt/format-number (:r2 shape)) ", "
|
|
||||||
(fmt/format-number (:r3 shape)) ", "
|
|
||||||
(fmt/format-pixels (:r4 shape))]
|
|
||||||
[:& copy-button {:data (copy-data shape :r1)}]])
|
|
||||||
|
|
||||||
(when (not= (:rotation shape 0) 0)
|
|
||||||
[:div.attributes-unit-row
|
|
||||||
[:div.attributes-label (tr "inspect.attributes.layout.rotation")]
|
|
||||||
[:div.attributes-value (fmt/format-number (:rotation shape)) "deg"]
|
|
||||||
[:& copy-button {:data (copy-data shape :rotation)}]])]))
|
|
||||||
|
|
||||||
|
|
||||||
(mf/defc layout-panel
|
(mf/defc layout-panel
|
||||||
[{:keys [shapes]}]
|
[{:keys [objects shapes]}]
|
||||||
[:div.attributes-block
|
(let [shapes (->> shapes (filter ctl/any-layout?))]
|
||||||
[:div.attributes-block-title
|
(when (seq shapes)
|
||||||
[:div.attributes-block-title-text (tr "inspect.attributes.size")]
|
[:div.attributes-block
|
||||||
(when (= (count shapes) 1)
|
[:div.attributes-block-title
|
||||||
[:& copy-button {:data (copy-data (first shapes))}])]
|
[:div.attributes-block-title-text "Layout"]
|
||||||
|
(when (= (count shapes) 1)
|
||||||
|
[:& copy-button {:data (css/get-shape-properties-css objects (first shapes) properties)}])]
|
||||||
|
|
||||||
(for [shape shapes]
|
(for [shape shapes]
|
||||||
[:& layout-block {:shape shape
|
[:& layout-block {:shape shape
|
||||||
:key (:id shape)}])])
|
:objects objects
|
||||||
|
:key (:id shape)}])])))
|
||||||
|
|||||||
@ -0,0 +1,60 @@
|
|||||||
|
;; 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.viewer.inspect.attributes.layout-element
|
||||||
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.common.types.shape.layout :as ctl]
|
||||||
|
[app.main.ui.components.copy-button :refer [copy-button]]
|
||||||
|
[app.util.code-gen.style-css :as css]
|
||||||
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
|
(def properties
|
||||||
|
[:margin
|
||||||
|
:max-height
|
||||||
|
:min-height
|
||||||
|
:max-width
|
||||||
|
:min-width
|
||||||
|
:align-self
|
||||||
|
:justify-self
|
||||||
|
|
||||||
|
;; Grid cell properties
|
||||||
|
:grid-column
|
||||||
|
:grid-row])
|
||||||
|
|
||||||
|
(mf/defc layout-element-block
|
||||||
|
[{:keys [objects shape]}]
|
||||||
|
[:*
|
||||||
|
(for [property properties]
|
||||||
|
(when-let [value (css/get-css-value objects shape property)]
|
||||||
|
[:div.attributes-unit-row
|
||||||
|
[:div.attributes-label (d/name property)]
|
||||||
|
[:div.attributes-value value]
|
||||||
|
[:& copy-button {:data (css/get-css-property objects shape property)}]]))])
|
||||||
|
|
||||||
|
(mf/defc layout-element-panel
|
||||||
|
[{:keys [objects shapes]}]
|
||||||
|
(let [shapes (->> shapes (filter #(ctl/any-layout-immediate-child? objects %)))
|
||||||
|
only-flex? (every? #(ctl/flex-layout-immediate-child? objects %) shapes)
|
||||||
|
only-grid? (every? #(ctl/grid-layout-immediate-child? objects %) shapes)]
|
||||||
|
(when (seq shapes)
|
||||||
|
[:div.attributes-block
|
||||||
|
[:div.attributes-block-title
|
||||||
|
[:div.attributes-block-title-text (cond
|
||||||
|
only-flex?
|
||||||
|
"Flex element"
|
||||||
|
only-grid?
|
||||||
|
"Flex element"
|
||||||
|
:else
|
||||||
|
"Layout element"
|
||||||
|
)]
|
||||||
|
(when (= (count shapes) 1)
|
||||||
|
[:& copy-button {:data (css/get-shape-properties-css objects (first shapes) properties)}])]
|
||||||
|
|
||||||
|
(for [shape shapes]
|
||||||
|
[:& layout-element-block {:shape shape
|
||||||
|
:objects objects
|
||||||
|
:key (:id shape)}])])))
|
||||||
@ -1,139 +0,0 @@
|
|||||||
;; 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.viewer.inspect.attributes.layout-flex
|
|
||||||
(:require
|
|
||||||
[app.common.data :as d]
|
|
||||||
[app.main.ui.components.copy-button :refer [copy-button]]
|
|
||||||
[app.main.ui.formats :as fm]
|
|
||||||
[app.util.code-gen :as cg]
|
|
||||||
[cuerdas.core :as str]
|
|
||||||
[rumext.v2 :as mf]))
|
|
||||||
|
|
||||||
(def properties [:layout
|
|
||||||
:layout-flex-dir
|
|
||||||
:layout-align-items
|
|
||||||
:layout-justify-content
|
|
||||||
:layout-gap
|
|
||||||
:layout-padding
|
|
||||||
:layout-wrap-type])
|
|
||||||
|
|
||||||
(def align-contet-prop [:layout-align-content])
|
|
||||||
|
|
||||||
(def layout-flex-params
|
|
||||||
{:props [:layout
|
|
||||||
:layout-align-items
|
|
||||||
:layout-flex-dir
|
|
||||||
:layout-justify-content
|
|
||||||
:layout-gap
|
|
||||||
:layout-padding
|
|
||||||
:layout-wrap-type]
|
|
||||||
:to-prop {:layout "display"
|
|
||||||
:layout-flex-dir "flex-direction"
|
|
||||||
:layout-align-items "align-items"
|
|
||||||
:layout-justify-content "justify-content"
|
|
||||||
:layout-wrap-type "flex-wrap"
|
|
||||||
:layout-gap "gap"
|
|
||||||
:layout-padding "padding"}
|
|
||||||
:format {:layout d/name
|
|
||||||
:layout-flex-dir d/name
|
|
||||||
:layout-align-items d/name
|
|
||||||
:layout-justify-content d/name
|
|
||||||
:layout-wrap-type d/name
|
|
||||||
:layout-gap fm/format-gap
|
|
||||||
:layout-padding fm/format-padding}})
|
|
||||||
|
|
||||||
(def layout-align-content-params
|
|
||||||
{:props [:layout-align-content]
|
|
||||||
:to-prop {:layout-align-content "align-content"}
|
|
||||||
:format {:layout-align-content d/name}})
|
|
||||||
|
|
||||||
(defn copy-data
|
|
||||||
([shape]
|
|
||||||
(let [properties-for-copy (if (:layout-align-content shape)
|
|
||||||
(into [] (concat properties align-contet-prop))
|
|
||||||
properties)]
|
|
||||||
(apply copy-data shape properties-for-copy)))
|
|
||||||
|
|
||||||
([shape & properties]
|
|
||||||
(let [params (if (:layout-align-content shape)
|
|
||||||
(d/deep-merge layout-align-content-params layout-flex-params )
|
|
||||||
layout-flex-params)]
|
|
||||||
(cg/generate-css-props shape properties params))))
|
|
||||||
|
|
||||||
(mf/defc manage-padding
|
|
||||||
[{:keys [padding type]}]
|
|
||||||
(let [values (fm/format-padding-margin-shorthand (vals padding))]
|
|
||||||
[:div.attributes-value
|
|
||||||
{:title (str (str/join "px " (vals values)) "px")}
|
|
||||||
(for [[k v] values]
|
|
||||||
[:span.items {:key (str type "-" k "-" v)} v "px"])]))
|
|
||||||
|
|
||||||
(mf/defc layout-flex-block
|
|
||||||
[{:keys [shape]}]
|
|
||||||
[:*
|
|
||||||
[:div.attributes-unit-row
|
|
||||||
[:div.attributes-label "Display"]
|
|
||||||
[:div.attributes-value "Flex"]
|
|
||||||
[:& copy-button {:data (copy-data shape)}]]
|
|
||||||
|
|
||||||
[:div.attributes-unit-row
|
|
||||||
[:div.attributes-label "Direction"]
|
|
||||||
[:div.attributes-value (str/capital (d/name (:layout-flex-dir shape)))]
|
|
||||||
[:& copy-button {:data (copy-data shape :layout-flex-dir)}]]
|
|
||||||
|
|
||||||
[:div.attributes-unit-row
|
|
||||||
[:div.attributes-label "Align-items"]
|
|
||||||
[:div.attributes-value (str/capital (d/name (:layout-align-items shape)))]
|
|
||||||
[:& copy-button {:data (copy-data shape :layout-align-items)}]]
|
|
||||||
|
|
||||||
[:div.attributes-unit-row
|
|
||||||
[:div.attributes-label "Justify-content"]
|
|
||||||
[:div.attributes-value (str/capital (d/name (:layout-justify-content shape)))]
|
|
||||||
[:& copy-button {:data (copy-data shape :layout-justify-content)}]]
|
|
||||||
|
|
||||||
[:div.attributes-unit-row
|
|
||||||
[:div.attributes-label "Flex wrap"]
|
|
||||||
[:div.attributes-value (str/capital (d/name (:layout-wrap-type shape)))]
|
|
||||||
[:& copy-button {:data (copy-data shape :layout-wrap-type)}]]
|
|
||||||
|
|
||||||
(when (= :wrap (:layout-wrap-type shape))
|
|
||||||
[:div.attributes-unit-row
|
|
||||||
[:div.attributes-label "Align-content"]
|
|
||||||
[:div.attributes-value (str/capital (d/name (:layout-align-content shape)))]
|
|
||||||
[:& copy-button {:data (copy-data shape :layout-align-content)}]])
|
|
||||||
|
|
||||||
[:div.attributes-unit-row
|
|
||||||
[:div.attributes-label "Gaps"]
|
|
||||||
(if (= (:row-gap (:layout-gap shape)) (:column-gap (:layout-gap shape)))
|
|
||||||
[:div.attributes-value
|
|
||||||
[:span (-> shape :layout-gap :row-gap fm/format-pixels)]]
|
|
||||||
[:div.attributes-value
|
|
||||||
[:span.items (-> shape :layout-gap :row-gap fm/format-pixels)]
|
|
||||||
[:span (-> shape :layout-gap :column-gap fm/format-pixels)]])
|
|
||||||
[:& copy-button {:data (copy-data shape :layout-gap)}]]
|
|
||||||
|
|
||||||
[:div.attributes-unit-row
|
|
||||||
[:div.attributes-label "Padding"]
|
|
||||||
[:& manage-padding {:padding (:layout-padding shape) :type "padding"}]
|
|
||||||
[:& copy-button {:data (copy-data shape :layout-padding)}]]])
|
|
||||||
|
|
||||||
(defn has-flex? [shape]
|
|
||||||
(= :flex (:layout shape)))
|
|
||||||
|
|
||||||
(mf/defc layout-flex-panel
|
|
||||||
[{:keys [shapes]}]
|
|
||||||
(let [shapes (->> shapes (filter has-flex?))]
|
|
||||||
(when (seq shapes)
|
|
||||||
[:div.attributes-block
|
|
||||||
[:div.attributes-block-title
|
|
||||||
[:div.attributes-block-title-text "Layout"]
|
|
||||||
(when (= (count shapes) 1)
|
|
||||||
[:& copy-button {:data (copy-data (first shapes))}])]
|
|
||||||
|
|
||||||
(for [shape shapes]
|
|
||||||
[:& layout-flex-block {:shape shape
|
|
||||||
:key (:id shape)}])])))
|
|
||||||
@ -1,155 +0,0 @@
|
|||||||
;; 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.viewer.inspect.attributes.layout-flex-element
|
|
||||||
(:require
|
|
||||||
[app.common.data :as d]
|
|
||||||
[app.main.refs :as refs]
|
|
||||||
[app.main.ui.components.copy-button :refer [copy-button]]
|
|
||||||
[app.main.ui.formats :as fmt]
|
|
||||||
[app.main.ui.viewer.inspect.code :as cd]
|
|
||||||
[app.util.code-gen :as cg]
|
|
||||||
[cuerdas.core :as str]
|
|
||||||
[rumext.v2 :as mf]))
|
|
||||||
|
|
||||||
|
|
||||||
(defn format-margin
|
|
||||||
[margin-values]
|
|
||||||
(let [short-hand (fmt/format-padding-margin-shorthand (vals margin-values))
|
|
||||||
parsed-values (map #(str/fmt "%spx" %) (vals short-hand))]
|
|
||||||
(str/join " " parsed-values)))
|
|
||||||
|
|
||||||
(def properties [:layout-item-margin ;; {:m1 0 :m2 0 :m3 0 :m4 0}
|
|
||||||
:layout-item-max-h ;; num
|
|
||||||
:layout-item-min-h ;; num
|
|
||||||
:layout-item-max-w ;; num
|
|
||||||
:layout-item-min-w ;; num
|
|
||||||
:layout-item-align-self]) ;; :start :end :center
|
|
||||||
|
|
||||||
(def layout-flex-item-params
|
|
||||||
{:props [:layout-item-margin
|
|
||||||
:layout-item-max-h
|
|
||||||
:layout-item-min-h
|
|
||||||
:layout-item-max-w
|
|
||||||
:layout-item-min-w
|
|
||||||
:layout-item-align-self]
|
|
||||||
:to-prop {:layout-item-margin "margin"
|
|
||||||
:layout-item-align-self "align-self"
|
|
||||||
:layout-item-max-h "max-height"
|
|
||||||
:layout-item-min-h "min-height"
|
|
||||||
:layout-item-max-w "max-width"
|
|
||||||
:layout-item-min-w "min-width"}
|
|
||||||
:format {:layout-item-margin format-margin
|
|
||||||
:layout-item-align-self d/name}})
|
|
||||||
|
|
||||||
(defn copy-data
|
|
||||||
([shape]
|
|
||||||
(apply copy-data shape properties))
|
|
||||||
|
|
||||||
([shape & properties]
|
|
||||||
(cg/generate-css-props shape properties layout-flex-item-params)))
|
|
||||||
|
|
||||||
(mf/defc manage-margin
|
|
||||||
[{:keys [margin type]}]
|
|
||||||
(let [values (fmt/format-padding-margin-shorthand (vals margin))]
|
|
||||||
[:div.attributes-value
|
|
||||||
(for [[k v] values]
|
|
||||||
[:span.items {:key (str type "-" k "-" v)} v "px"])]))
|
|
||||||
|
|
||||||
(defn manage-sizing
|
|
||||||
[value type]
|
|
||||||
(let [ref-value-h {:fill "Width 100%"
|
|
||||||
:fix "Fixed width"
|
|
||||||
:auto "Fit content"}
|
|
||||||
ref-value-v {:fill "Height 100%"
|
|
||||||
:fix "Fixed height"
|
|
||||||
:auto "Fit content"}]
|
|
||||||
(if (= :h type)
|
|
||||||
(ref-value-h value)
|
|
||||||
(ref-value-v value))))
|
|
||||||
|
|
||||||
(mf/defc layout-element-block
|
|
||||||
[{:keys [shape]}]
|
|
||||||
(let [old-margin (:layout-item-margin shape)
|
|
||||||
new-margin {:m1 0 :m2 0 :m3 0 :m4 0}
|
|
||||||
merged-margin (merge new-margin old-margin)
|
|
||||||
shape (assoc shape :layout-item-margin merged-margin)]
|
|
||||||
|
|
||||||
[:*
|
|
||||||
(when (:layout-item-align-self shape)
|
|
||||||
[:div.attributes-unit-row
|
|
||||||
[:div.attributes-label "Align self"]
|
|
||||||
[:div.attributes-value (str/capital (d/name (:layout-item-align-self shape)))]
|
|
||||||
[:& copy-button {:data (copy-data shape :layout-item-align-self)}]])
|
|
||||||
|
|
||||||
(when (:layout-item-margin shape)
|
|
||||||
[:div.attributes-unit-row
|
|
||||||
[:div.attributes-label "Margin"]
|
|
||||||
[:& manage-margin {:margin merged-margin :type "margin"}]
|
|
||||||
[:& copy-button {:data (copy-data shape :layout-item-margin)}]])
|
|
||||||
|
|
||||||
(when (:layout-item-h-sizing shape)
|
|
||||||
[:div.attributes-unit-row
|
|
||||||
[:div.attributes-label "Horizontal sizing"]
|
|
||||||
[:div.attributes-value (manage-sizing (:layout-item-h-sizing shape) :h)]
|
|
||||||
[:& copy-button {:data (copy-data shape :layout-item-h-sizing)}]])
|
|
||||||
|
|
||||||
(when (:layout-item-v-sizing shape)
|
|
||||||
[:div.attributes-unit-row
|
|
||||||
[:div.attributes-label "Vertical sizing"]
|
|
||||||
[:div.attributes-value (manage-sizing (:layout-item-v-sizing shape) :v)]
|
|
||||||
[:& copy-button {:data (copy-data shape :layout-item-v-sizing)}]])
|
|
||||||
|
|
||||||
(when (= :fill (:layout-item-h-sizing shape))
|
|
||||||
[:*
|
|
||||||
(when (some? (:layout-item-max-w shape))
|
|
||||||
[:div.attributes-unit-row
|
|
||||||
[:div.attributes-label "Max. width"]
|
|
||||||
[:div.attributes-value (fmt/format-pixels (:layout-item-max-w shape))]
|
|
||||||
[:& copy-button {:data (copy-data shape :layout-item-max-w)}]])
|
|
||||||
|
|
||||||
(when (some? (:layout-item-min-w shape))
|
|
||||||
[:div.attributes-unit-row
|
|
||||||
[:div.attributes-label "Min. width"]
|
|
||||||
[:div.attributes-value (fmt/format-pixels (:layout-item-min-w shape))]
|
|
||||||
[:& copy-button {:data (copy-data shape :layout-item-min-w)}]])])
|
|
||||||
|
|
||||||
(when (= :fill (:layout-item-v-sizing shape))
|
|
||||||
[:*
|
|
||||||
(when (:layout-item-max-h shape)
|
|
||||||
[:div.attributes-unit-row
|
|
||||||
[:div.attributes-label "Max. height"]
|
|
||||||
[:div.attributes-value (fmt/format-pixels (:layout-item-max-h shape))]
|
|
||||||
[:& copy-button {:data (copy-data shape :layout-item-max-h)}]])
|
|
||||||
|
|
||||||
(when (:layout-item-min-h shape)
|
|
||||||
[:div.attributes-unit-row
|
|
||||||
[:div.attributes-label "Min. height"]
|
|
||||||
[:div.attributes-value (fmt/format-pixels (:layout-item-min-h shape))]
|
|
||||||
[:& copy-button {:data (copy-data shape :layout-item-min-h)}]])])]))
|
|
||||||
|
|
||||||
(mf/defc layout-flex-element-panel
|
|
||||||
[{:keys [shapes from]}]
|
|
||||||
(let [route (mf/deref refs/route)
|
|
||||||
page-id (:page-id (:query-params route))
|
|
||||||
mod-shapes (cd/get-flex-elements page-id shapes from)
|
|
||||||
shape (first mod-shapes)
|
|
||||||
has-margin? (some? (:layout-item-margin shape))
|
|
||||||
has-values? (or (some? (:layout-item-max-w shape))
|
|
||||||
(some? (:layout-item-max-h shape))
|
|
||||||
(some? (:layout-item-min-w shape))
|
|
||||||
(some? (:layout-item-min-h shape)))
|
|
||||||
has-align? (some? (:layout-item-align-self shape))
|
|
||||||
has-sizing? (or (some? (:layout-item-h-sizing shape))
|
|
||||||
(some? (:layout-item-w-sizing shape)))
|
|
||||||
must-show (or has-margin? has-values? has-align? has-sizing?)]
|
|
||||||
(when (and (= (count mod-shapes) 1) must-show)
|
|
||||||
[:div.attributes-block
|
|
||||||
[:div.attributes-block-title
|
|
||||||
[:div.attributes-block-title-text "Flex element"]
|
|
||||||
[:& copy-button {:data (copy-data shape)}]]
|
|
||||||
|
|
||||||
[:& layout-element-block {:shape shape}]])))
|
|
||||||
@ -7,30 +7,13 @@
|
|||||||
(ns app.main.ui.viewer.inspect.attributes.shadow
|
(ns app.main.ui.viewer.inspect.attributes.shadow
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.main.ui.components.copy-button :refer [copy-button]]
|
|
||||||
[app.main.ui.viewer.inspect.attributes.common :refer [color-row]]
|
[app.main.ui.viewer.inspect.attributes.common :refer [color-row]]
|
||||||
[app.util.code-gen :as cg]
|
|
||||||
[app.util.i18n :refer [tr]]
|
[app.util.i18n :refer [tr]]
|
||||||
[cuerdas.core :as str]
|
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
(defn has-shadow? [shape]
|
(defn has-shadow? [shape]
|
||||||
(:shadow shape))
|
(:shadow shape))
|
||||||
|
|
||||||
(defn shape-copy-data [shape]
|
|
||||||
(cg/generate-css-props
|
|
||||||
shape
|
|
||||||
:shadow
|
|
||||||
{:to-prop "box-shadow"
|
|
||||||
:format #(str/join ", " (map cg/shadow->css (:shadow shape)))}))
|
|
||||||
|
|
||||||
(defn shadow-copy-data [shadow]
|
|
||||||
(cg/generate-css-props
|
|
||||||
shadow
|
|
||||||
:style
|
|
||||||
{:to-prop "box-shadow"
|
|
||||||
:format #(cg/shadow->css shadow)}))
|
|
||||||
|
|
||||||
(mf/defc shadow-block [{:keys [shadow]}]
|
(mf/defc shadow-block [{:keys [shadow]}]
|
||||||
(let [color-format (mf/use-state :hex)]
|
(let [color-format (mf/use-state :hex)]
|
||||||
[:div.attributes-shadow-block
|
[:div.attributes-shadow-block
|
||||||
@ -48,7 +31,7 @@
|
|||||||
[:div.attributes-shadow {:title (tr "workspace.options.shadow-options.spread")}
|
[:div.attributes-shadow {:title (tr "workspace.options.shadow-options.spread")}
|
||||||
[:div.attributes-value (str (:spread shadow) "px")]]
|
[:div.attributes-value (str (:spread shadow) "px")]]
|
||||||
|
|
||||||
[:& copy-button {:data (shadow-copy-data shadow)}]]
|
#_[:& copy-button {:data (shadow-copy-data shadow)}]]
|
||||||
|
|
||||||
[:& color-row {:color (:color shadow)
|
[:& color-row {:color (:color shadow)
|
||||||
:format @color-format
|
:format @color-format
|
||||||
|
|||||||
@ -7,76 +7,52 @@
|
|||||||
(ns app.main.ui.viewer.inspect.attributes.stroke
|
(ns app.main.ui.viewer.inspect.attributes.stroke
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
[app.main.ui.components.copy-button :refer [copy-button]]
|
[app.main.ui.components.copy-button :refer [copy-button]]
|
||||||
|
[app.main.ui.formats :as fmt]
|
||||||
[app.main.ui.viewer.inspect.attributes.common :refer [color-row]]
|
[app.main.ui.viewer.inspect.attributes.common :refer [color-row]]
|
||||||
[app.util.code-gen :as cg]
|
[app.util.code-gen.style-css-formats :as cssf]
|
||||||
|
[app.util.code-gen.style-css-values :as cssv]
|
||||||
[app.util.color :as uc]
|
[app.util.color :as uc]
|
||||||
[app.util.i18n :refer [tr]]
|
[app.util.i18n :refer [tr]]
|
||||||
[cuerdas.core :as str]
|
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
(defn shape->color [shape]
|
(defn stroke->color [shape]
|
||||||
{:color (:stroke-color shape)
|
{:color (:stroke-color shape)
|
||||||
:opacity (:stroke-opacity shape)
|
:opacity (:stroke-opacity shape)
|
||||||
:gradient (:stroke-color-gradient shape)
|
:gradient (:stroke-color-gradient shape)
|
||||||
:id (:stroke-color-ref-id shape)
|
:id (:stroke-color-ref-id shape)
|
||||||
:file-id (:stroke-color-ref-file shape)})
|
:file-id (:stroke-color-ref-file shape)})
|
||||||
|
|
||||||
(defn format-stroke [shape]
|
|
||||||
(let [width (:stroke-width shape)
|
|
||||||
style (d/name (:stroke-style shape))
|
|
||||||
style (if (= style "svg") "solid" style)
|
|
||||||
color (-> shape shape->color uc/color->background)]
|
|
||||||
(str/format "%spx %s %s" width style color)))
|
|
||||||
|
|
||||||
(defn has-stroke? [shape]
|
(defn has-stroke? [shape]
|
||||||
(let [stroke-style (:stroke-style shape)]
|
(seq (:strokes shape)))
|
||||||
(or
|
|
||||||
(and stroke-style
|
|
||||||
(and (not= stroke-style :none)
|
|
||||||
(not= stroke-style :svg)))
|
|
||||||
(seq (:strokes shape)))))
|
|
||||||
|
|
||||||
(defn copy-stroke-data [shape]
|
|
||||||
(cg/generate-css-props
|
|
||||||
shape
|
|
||||||
:stroke-style
|
|
||||||
{:to-prop "border"
|
|
||||||
:format #(format-stroke shape)}))
|
|
||||||
|
|
||||||
(defn copy-color-data [shape]
|
|
||||||
(cg/generate-css-props
|
|
||||||
shape
|
|
||||||
:stroke-color
|
|
||||||
{:to-prop "border-color"
|
|
||||||
:format #(uc/color->background (shape->color shape))}))
|
|
||||||
|
|
||||||
(mf/defc stroke-block
|
(mf/defc stroke-block
|
||||||
[{:keys [shape]}]
|
[{:keys [stroke]}]
|
||||||
(let [color-format (mf/use-state :hex)
|
(let [color-format (mf/use-state :hex)
|
||||||
color (shape->color shape)]
|
color (stroke->color stroke)]
|
||||||
[:div.attributes-stroke-block
|
[:div.attributes-stroke-block
|
||||||
(let [{:keys [stroke-style stroke-alignment]} shape
|
(let [{:keys [stroke-style stroke-alignment]} stroke
|
||||||
stroke-style (if (= stroke-style :svg) :solid stroke-style)
|
stroke-style (if (= stroke-style :svg) :solid stroke-style)
|
||||||
stroke-alignment (or stroke-alignment :center)]
|
stroke-alignment (or stroke-alignment :center)]
|
||||||
[:div.attributes-stroke-row
|
[:div.attributes-stroke-row
|
||||||
[:div.attributes-label (tr "inspect.attributes.stroke.width")]
|
[:div.attributes-label (tr "inspect.attributes.stroke.width")]
|
||||||
[:div.attributes-value (:stroke-width shape) "px"]
|
[:div.attributes-value (fmt/format-pixels (:stroke-width stroke))]
|
||||||
;; Execution time translation strings:
|
;; Execution time translation strings:
|
||||||
;; inspect.attributes.stroke.style.dotted
|
;; inspect.attributes.stroke.style.dotted
|
||||||
;; inspect.attributes.stroke.style.mixed
|
;; inspect.attributes.stroke.style.mixed
|
||||||
;; inspect.attributes.stroke.style.none
|
;; inspect.attributes.stroke.style.none
|
||||||
;; inspect.attributes.stroke.style.solid
|
;; inspect.attributes.stroke.style.solid
|
||||||
[:div.attributes-value (->> stroke-style d/name (str "inspect.attributes.stroke.style.") (tr))]
|
[:div.attributes-value (tr (dm/str "inspect.attributes.stroke.style." (d/name stroke-style)))]
|
||||||
;; Execution time translation strings:
|
;; Execution time translation strings:
|
||||||
;; inspect.attributes.stroke.alignment.center
|
;; inspect.attributes.stroke.alignment.center
|
||||||
;; inspect.attributes.stroke.alignment.inner
|
;; inspect.attributes.stroke.alignment.inner
|
||||||
;; inspect.attributes.stroke.alignment.outer
|
;; inspect.attributes.stroke.alignment.outer
|
||||||
[:div.attributes-label (->> stroke-alignment d/name (str "inspect.attributes.stroke.alignment.") (tr))]
|
[:div.attributes-label (tr (dm/str "inspect.attributes.stroke.alignment." (d/name stroke-alignment)))]
|
||||||
[:& copy-button {:data (copy-stroke-data shape)}]])
|
[:& copy-button {:data (cssf/format-value :border (cssv/get-stroke-data stroke))}]])
|
||||||
[:& color-row {:color color
|
[:& color-row {:color color
|
||||||
:format @color-format
|
:format @color-format
|
||||||
:copy-data (copy-color-data shape)
|
:copy-data (uc/color->background color)
|
||||||
:on-change-format #(reset! color-format %)}]]))
|
:on-change-format #(reset! color-format %)}]]))
|
||||||
|
|
||||||
(mf/defc stroke-panel
|
(mf/defc stroke-panel
|
||||||
@ -89,9 +65,6 @@
|
|||||||
|
|
||||||
[:div.attributes-stroke-blocks
|
[:div.attributes-stroke-blocks
|
||||||
(for [shape shapes]
|
(for [shape shapes]
|
||||||
(if (seq (:strokes shape))
|
(for [value (:strokes shape)]
|
||||||
(for [value (:strokes shape [])]
|
[:& stroke-block {:key (str "stroke-color-" (:id shape) value)
|
||||||
[:& stroke-block {:key (str "stroke-color-" (:id shape) value)
|
:stroke value}]))]])))
|
||||||
:shape value}])
|
|
||||||
[:& stroke-block {:key (str "stroke-color-only" (:id shape))
|
|
||||||
:shape shape}]))]])))
|
|
||||||
|
|||||||
@ -6,17 +6,14 @@
|
|||||||
|
|
||||||
(ns app.main.ui.viewer.inspect.attributes.text
|
(ns app.main.ui.viewer.inspect.attributes.text
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.text :as txt]
|
[app.common.text :as txt]
|
||||||
[app.main.fonts :as fonts]
|
[app.main.fonts :as fonts]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.components.copy-button :refer [copy-button]]
|
[app.main.ui.components.copy-button :refer [copy-button]]
|
||||||
|
[app.main.ui.formats :as fmt]
|
||||||
[app.main.ui.viewer.inspect.attributes.common :refer [color-row]]
|
[app.main.ui.viewer.inspect.attributes.common :refer [color-row]]
|
||||||
[app.util.code-gen :as cg]
|
|
||||||
[app.util.color :as uc]
|
|
||||||
[app.util.i18n :refer [tr]]
|
[app.util.i18n :refer [tr]]
|
||||||
[app.util.strings :as ust]
|
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[okulary.core :as l]
|
[okulary.core :as l]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
@ -33,58 +30,23 @@
|
|||||||
(get-in state [:viewer-libraries file-id :data :typographies]))]
|
(get-in state [:viewer-libraries file-id :data :typographies]))]
|
||||||
#(l/derived get-library st/state)))
|
#(l/derived get-library st/state)))
|
||||||
|
|
||||||
(defn format-number [number]
|
(defn fill->color [{:keys [fill-color fill-opacity fill-color-gradient fill-color-ref-id fill-color-ref-file]}]
|
||||||
(-> number
|
{:color fill-color
|
||||||
d/parse-double
|
:opacity fill-opacity
|
||||||
(ust/format-precision 2)))
|
:gradient fill-color-gradient
|
||||||
|
:id fill-color-ref-id
|
||||||
|
:file-id fill-color-ref-file})
|
||||||
|
|
||||||
(def properties [:fill-color
|
(mf/defc typography-block
|
||||||
:fill-color-gradient
|
[{:keys [text style]}]
|
||||||
:font-family
|
(let [typography-library-ref
|
||||||
:font-style
|
(mf/use-memo
|
||||||
:font-size
|
(mf/deps (:typography-ref-file style))
|
||||||
:font-weight
|
(make-typographies-library-ref (:typography-ref-file style)))
|
||||||
:line-height
|
|
||||||
:letter-spacing
|
|
||||||
:text-decoration
|
|
||||||
:text-transform])
|
|
||||||
|
|
||||||
(defn shape->color [shape]
|
|
||||||
{:color (:fill-color shape)
|
|
||||||
:opacity (:fill-opacity shape)
|
|
||||||
:gradient (:fill-color-gradient shape)
|
|
||||||
:id (:fill-color-ref-id shape)
|
|
||||||
:file-id (:fill-color-ref-file shape)})
|
|
||||||
|
|
||||||
(def params
|
|
||||||
{:to-prop {:fill-color "color"
|
|
||||||
:fill-color-gradient "color"}
|
|
||||||
:format {:font-family #(dm/str "'" % "'")
|
|
||||||
:font-style #(dm/str % )
|
|
||||||
:font-size #(dm/str (format-number %) "px")
|
|
||||||
:font-weight d/name
|
|
||||||
:line-height #(format-number %)
|
|
||||||
:letter-spacing #(dm/str (format-number %) "px")
|
|
||||||
:text-decoration d/name
|
|
||||||
:text-transform d/name
|
|
||||||
:fill-color #(-> %2 shape->color uc/color->background)
|
|
||||||
:fill-color-gradient #(-> %2 shape->color uc/color->background)}})
|
|
||||||
|
|
||||||
(defn copy-style-data
|
|
||||||
([style]
|
|
||||||
(cg/generate-css-props style properties params))
|
|
||||||
([style & properties]
|
|
||||||
(cg/generate-css-props style properties params)))
|
|
||||||
|
|
||||||
(mf/defc typography-block [{:keys [text style]}]
|
|
||||||
(let [typography-library-ref (mf/use-memo
|
|
||||||
(mf/deps (:typography-ref-file style))
|
|
||||||
(make-typographies-library-ref (:typography-ref-file style)))
|
|
||||||
typography-library (mf/deref typography-library-ref)
|
typography-library (mf/deref typography-library-ref)
|
||||||
|
file-typographies (mf/deref file-typographies-ref)
|
||||||
file-typographies (mf/deref file-typographies-ref)
|
color-format (mf/use-state :hex)
|
||||||
|
|
||||||
color-format (mf/use-state :hex)
|
|
||||||
|
|
||||||
typography (get (or typography-library file-typographies) (:typography-ref-id style))]
|
typography (get (or typography-library file-typographies) (:typography-ref-id style))]
|
||||||
|
|
||||||
@ -98,7 +60,7 @@
|
|||||||
:font-style (:font-style typography)}}
|
:font-style (:font-style typography)}}
|
||||||
(tr "workspace.assets.typography.text-styles")]]
|
(tr "workspace.assets.typography.text-styles")]]
|
||||||
[:div.typography-entry-name (:name typography)]
|
[:div.typography-entry-name (:name typography)]
|
||||||
[:& copy-button {:data (copy-style-data typography)}]]
|
#_[:& copy-button {:data (copy-style-data typography)}]]
|
||||||
|
|
||||||
[:div.attributes-typography-row
|
[:div.attributes-typography-row
|
||||||
[:div.typography-sample
|
[:div.typography-sample
|
||||||
@ -106,51 +68,51 @@
|
|||||||
:font-weight (:font-weight style)
|
:font-weight (:font-weight style)
|
||||||
:font-style (:font-style style)}}
|
:font-style (:font-style style)}}
|
||||||
(tr "workspace.assets.typography.text-styles")]
|
(tr "workspace.assets.typography.text-styles")]
|
||||||
[:& copy-button {:data (copy-style-data style)}]])
|
#_[:& copy-button {:data (copy-style-data style)}]])
|
||||||
|
|
||||||
(when (:fills style)
|
(when (:fills style)
|
||||||
(for [[idx fill] (map-indexed vector (:fills style))]
|
(for [[idx fill] (map-indexed vector (:fills style))]
|
||||||
[:& color-row {:key idx
|
[:& color-row {:key idx
|
||||||
:format @color-format
|
:format @color-format
|
||||||
:color (shape->color fill)
|
:color (fill->color fill)
|
||||||
:copy-data (copy-style-data fill :fill-color :fill-color-gradient)
|
;;:copy-data (copy-style-data fill :fill-color :fill-color-gradient)
|
||||||
:on-change-format #(reset! color-format %)}]))
|
:on-change-format #(reset! color-format %)}]))
|
||||||
|
|
||||||
(when (:font-id style)
|
(when (:font-id style)
|
||||||
[:div.attributes-unit-row
|
[:div.attributes-unit-row
|
||||||
[:div.attributes-label (tr "inspect.attributes.typography.font-family")]
|
[:div.attributes-label (tr "inspect.attributes.typography.font-family")]
|
||||||
[:div.attributes-value (-> style :font-id fonts/get-font-data :name)]
|
[:div.attributes-value (-> style :font-id fonts/get-font-data :name)]
|
||||||
[:& copy-button {:data (copy-style-data style :font-family)}]])
|
#_[:& copy-button {:data (copy-style-data style :font-family)}]])
|
||||||
|
|
||||||
(when (:font-style style)
|
(when (:font-style style)
|
||||||
[:div.attributes-unit-row
|
[:div.attributes-unit-row
|
||||||
[:div.attributes-label (tr "inspect.attributes.typography.font-style")]
|
[:div.attributes-label (tr "inspect.attributes.typography.font-style")]
|
||||||
[:div.attributes-value (str (:font-style style))]
|
[:div.attributes-value (str (:font-style style))]
|
||||||
[:& copy-button {:data (copy-style-data style :font-style)}]])
|
#_[:& copy-button {:data (copy-style-data style :font-style)}]])
|
||||||
|
|
||||||
(when (:font-size style)
|
(when (:font-size style)
|
||||||
[:div.attributes-unit-row
|
[:div.attributes-unit-row
|
||||||
[:div.attributes-label (tr "inspect.attributes.typography.font-size")]
|
[:div.attributes-label (tr "inspect.attributes.typography.font-size")]
|
||||||
[:div.attributes-value (str (format-number (:font-size style))) "px"]
|
[:div.attributes-value (fmt/format-pixels (:font-size style))]
|
||||||
[:& copy-button {:data (copy-style-data style :font-size)}]])
|
#_[:& copy-button {:data (copy-style-data style :font-size)}]])
|
||||||
|
|
||||||
(when (:font-weight style)
|
(when (:font-weight style)
|
||||||
[:div.attributes-unit-row
|
[:div.attributes-unit-row
|
||||||
[:div.attributes-label (tr "inspect.attributes.typography.font-weight")]
|
[:div.attributes-label (tr "inspect.attributes.typography.font-weight")]
|
||||||
[:div.attributes-value (str (:font-weight style))]
|
[:div.attributes-value (str (:font-weight style))]
|
||||||
[:& copy-button {:data (copy-style-data style :font-weight)}]])
|
#_[:& copy-button {:data (copy-style-data style :font-weight)}]])
|
||||||
|
|
||||||
(when (:line-height style)
|
(when (:line-height style)
|
||||||
[:div.attributes-unit-row
|
[:div.attributes-unit-row
|
||||||
[:div.attributes-label (tr "inspect.attributes.typography.line-height")]
|
[:div.attributes-label (tr "inspect.attributes.typography.line-height")]
|
||||||
[:div.attributes-value (format-number (:line-height style))]
|
[:div.attributes-value (fmt/format-number (:line-height style))]
|
||||||
[:& copy-button {:data (copy-style-data style :line-height)}]])
|
#_[:& copy-button {:data (copy-style-data style :line-height)}]])
|
||||||
|
|
||||||
(when (:letter-spacing style)
|
(when (:letter-spacing style)
|
||||||
[:div.attributes-unit-row
|
[:div.attributes-unit-row
|
||||||
[:div.attributes-label (tr "inspect.attributes.typography.letter-spacing")]
|
[:div.attributes-label (tr "inspect.attributes.typography.letter-spacing")]
|
||||||
[:div.attributes-value (str (format-number (:letter-spacing style))) "px"]
|
[:div.attributes-value (fmt/format-pixels (:letter-spacing style))]
|
||||||
[:& copy-button {:data (copy-style-data style :letter-spacing)}]])
|
#_[:& copy-button {:data (copy-style-data style :letter-spacing)}]])
|
||||||
|
|
||||||
(when (:text-decoration style)
|
(when (:text-decoration style)
|
||||||
[:div.attributes-unit-row
|
[:div.attributes-unit-row
|
||||||
@ -159,8 +121,8 @@
|
|||||||
;; inspect.attributes.typography.text-decoration.none
|
;; inspect.attributes.typography.text-decoration.none
|
||||||
;; inspect.attributes.typography.text-decoration.strikethrough
|
;; inspect.attributes.typography.text-decoration.strikethrough
|
||||||
;; inspect.attributes.typography.text-decoration.underline
|
;; inspect.attributes.typography.text-decoration.underline
|
||||||
[:div.attributes-value (->> style :text-decoration (str "inspect.attributes.typography.text-decoration.") (tr))]
|
[:div.attributes-value (tr (dm/str "inspect.attributes.typography.text-decoration." (:text-decoration style)))]
|
||||||
[:& copy-button {:data (copy-style-data style :text-decoration)}]])
|
#_[:& copy-button {:data (copy-style-data style :text-decoration)}]])
|
||||||
|
|
||||||
(when (:text-transform style)
|
(when (:text-transform style)
|
||||||
[:div.attributes-unit-row
|
[:div.attributes-unit-row
|
||||||
@ -170,8 +132,8 @@
|
|||||||
;; inspect.attributes.typography.text-transform.none
|
;; inspect.attributes.typography.text-transform.none
|
||||||
;; inspect.attributes.typography.text-transform.titlecase
|
;; inspect.attributes.typography.text-transform.titlecase
|
||||||
;; inspect.attributes.typography.text-transform.uppercase
|
;; inspect.attributes.typography.text-transform.uppercase
|
||||||
[:div.attributes-value (->> style :text-transform (str "inspect.attributes.typography.text-transform.") (tr))]
|
[:div.attributes-value (tr (dm/str "inspect.attributes.typography.text-transform." (:text-transform style)))]
|
||||||
[:& copy-button {:data (copy-style-data style :text-transform)}]])
|
#_[:& copy-button {:data (copy-style-data style :text-transform)}]])
|
||||||
|
|
||||||
[:div.attributes-content-row
|
[:div.attributes-content-row
|
||||||
[:pre.attributes-content (str/trim text)]
|
[:pre.attributes-content (str/trim text)]
|
||||||
@ -179,8 +141,8 @@
|
|||||||
|
|
||||||
|
|
||||||
(mf/defc text-block [{:keys [shape]}]
|
(mf/defc text-block [{:keys [shape]}]
|
||||||
(let [style-text-blocks (->> (keys txt/default-text-attrs)
|
(let [style-text-blocks (->> (:content shape)
|
||||||
(cg/parse-style-text-blocks (:content shape))
|
(txt/content->text+styles)
|
||||||
(remove (fn [[_ text]] (str/empty? (str/trim text))))
|
(remove (fn [[_ text]] (str/empty? (str/trim text))))
|
||||||
(mapv (fn [[style text]] (vector (merge txt/default-text-attrs style) text))))]
|
(mapv (fn [[style text]] (vector (merge txt/default-text-attrs style) text))))]
|
||||||
|
|
||||||
|
|||||||
@ -7,48 +7,66 @@
|
|||||||
(ns app.main.ui.viewer.inspect.code
|
(ns app.main.ui.viewer.inspect.code
|
||||||
(:require
|
(:require
|
||||||
["js-beautify" :as beautify]
|
["js-beautify" :as beautify]
|
||||||
["react-dom/server" :as rds]
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
|
[app.common.pages.helpers :as cph]
|
||||||
|
[app.common.types.shape-tree :as ctst]
|
||||||
|
[app.config :as cfg]
|
||||||
[app.main.data.events :as ev]
|
[app.main.data.events :as ev]
|
||||||
|
[app.main.fonts :as fonts]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.render :as render]
|
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.components.code-block :refer [code-block]]
|
[app.main.ui.components.code-block :refer [code-block]]
|
||||||
[app.main.ui.components.copy-button :refer [copy-button]]
|
[app.main.ui.components.copy-button :refer [copy-button]]
|
||||||
|
[app.main.ui.components.select :refer [select]]
|
||||||
[app.main.ui.hooks :as hooks]
|
[app.main.ui.hooks :as hooks]
|
||||||
|
[app.main.ui.hooks.resize :refer [use-resize-hook]]
|
||||||
[app.main.ui.icons :as i]
|
[app.main.ui.icons :as i]
|
||||||
|
[app.main.ui.shapes.text.fontfaces :refer [shapes->fonts]]
|
||||||
[app.util.code-gen :as cg]
|
[app.util.code-gen :as cg]
|
||||||
|
[app.util.http :as http]
|
||||||
|
[app.util.webapi :as wapi]
|
||||||
|
[beicon.core :as rx]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[potok.core :as ptk]
|
[potok.core :as ptk]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
(defn generate-markup-code [objects shapes]
|
(def embed-images? true)
|
||||||
;; Here we can render specific HTML code
|
(def remove-localhost? true)
|
||||||
(->> shapes
|
|
||||||
(map (fn [shape]
|
(def page-template
|
||||||
(dm/str
|
"<!DOCTYPE html>
|
||||||
"<!-- Shape: " (:name shape) " -->"
|
<html>
|
||||||
(rds/renderToStaticMarkup
|
<head>
|
||||||
(mf/element
|
<style>
|
||||||
render/object-svg
|
%s
|
||||||
#js {:objects objects
|
</style>
|
||||||
:object-id (-> shape :id)})))))
|
</head>
|
||||||
(str/join "\n\n")))
|
<body>
|
||||||
|
%s
|
||||||
|
</body>
|
||||||
|
</html>")
|
||||||
|
|
||||||
(defn format-code [code type]
|
(defn format-code [code type]
|
||||||
(let [code (-> code
|
(cond-> code
|
||||||
(str/replace "<defs></defs>" "")
|
(= type "svg")
|
||||||
(str/replace "><" ">\n<"))]
|
(-> (str/replace "<defs></defs>" "")
|
||||||
(cond-> code
|
(str/replace "><" ">\n<"))
|
||||||
(= type "svg") (beautify/html #js {"indent_size" 2}))))
|
|
||||||
|
(or (= type "svg") (= type "html"))
|
||||||
|
(beautify/html #js {"indent_size" 2})))
|
||||||
|
|
||||||
(defn get-flex-elements [page-id shapes from]
|
(defn get-flex-elements [page-id shapes from]
|
||||||
(let [ids (mapv :id shapes)
|
(let [ids (mapv :id shapes)
|
||||||
ids (hooks/use-equal-memo ids)
|
ids (hooks/use-equal-memo ids)
|
||||||
get-layout-children-refs (mf/use-memo (mf/deps ids page-id from) #(if (= from :workspace)
|
|
||||||
(refs/workspace-get-flex-child ids)
|
get-layout-children-refs
|
||||||
(refs/get-flex-child-viewer ids page-id)))]
|
(mf/use-memo
|
||||||
|
(mf/deps ids page-id from)
|
||||||
|
#(if (= from :workspace)
|
||||||
|
(refs/workspace-get-flex-child ids)
|
||||||
|
(refs/get-flex-child-viewer ids page-id)))]
|
||||||
|
|
||||||
(mf/deref get-layout-children-refs)))
|
(mf/deref get-layout-children-refs)))
|
||||||
|
|
||||||
@ -62,46 +80,163 @@
|
|||||||
(refs/get-viewer-objects))))]
|
(refs/get-viewer-objects))))]
|
||||||
(mf/deref page-objects-ref)))
|
(mf/deref page-objects-ref)))
|
||||||
|
|
||||||
|
(defn shapes->images
|
||||||
|
[shapes]
|
||||||
|
(->> shapes
|
||||||
|
(keep
|
||||||
|
(fn [shape]
|
||||||
|
(when-let [data (or (:metadata shape) (:fill-image shape))]
|
||||||
|
[(:id shape) (cfg/resolve-file-media data)])))))
|
||||||
|
|
||||||
|
(defn replace-map
|
||||||
|
[value map]
|
||||||
|
(reduce
|
||||||
|
(fn [value [old new]]
|
||||||
|
(str/replace value old new))
|
||||||
|
value map))
|
||||||
|
|
||||||
(mf/defc code
|
(mf/defc code
|
||||||
[{:keys [shapes frame on-expand from]}]
|
[{:keys [shapes frame on-expand from]}]
|
||||||
(let [style-type (mf/use-state "css")
|
(let [style-type* (mf/use-state "css")
|
||||||
markup-type (mf/use-state "svg")
|
markup-type* (mf/use-state "html")
|
||||||
|
fontfaces-css* (mf/use-state nil)
|
||||||
|
images-data* (mf/use-state nil)
|
||||||
|
|
||||||
|
style-type (deref style-type*)
|
||||||
|
markup-type (deref markup-type*)
|
||||||
|
fontfaces-css (deref fontfaces-css*)
|
||||||
|
images-data (deref images-data*)
|
||||||
|
|
||||||
shapes (->> shapes
|
shapes (->> shapes
|
||||||
(map #(gsh/translate-to-frame % frame)))
|
(map #(gsh/translate-to-frame % frame)))
|
||||||
|
|
||||||
route (mf/deref refs/route)
|
route (mf/deref refs/route)
|
||||||
page-id (:page-id (:query-params route))
|
page-id (:page-id (:query-params route))
|
||||||
flex-items (get-flex-elements page-id shapes from)
|
flex-items (get-flex-elements page-id shapes from)
|
||||||
objects (get-objects from)
|
objects (get-objects from)
|
||||||
|
|
||||||
|
;; TODO REMOVE THIS
|
||||||
shapes (->> shapes
|
shapes (->> shapes
|
||||||
(map #(assoc % :parent (get objects (:parent-id %))))
|
(map #(assoc % :parent (get objects (:parent-id %))))
|
||||||
(map #(assoc % :flex-items flex-items)))
|
(map #(assoc % :flex-items flex-items)))
|
||||||
style-code (-> (cg/generate-style-code @style-type shapes)
|
|
||||||
(format-code "css"))
|
all-children (->> shapes
|
||||||
|
(map :id)
|
||||||
|
(cph/selected-with-children objects)
|
||||||
|
(ctst/sort-z-index objects)
|
||||||
|
(map (d/getf objects)))
|
||||||
|
|
||||||
|
|
||||||
|
shapes (hooks/use-equal-memo shapes)
|
||||||
|
all-children (hooks/use-equal-memo all-children)
|
||||||
|
|
||||||
|
fonts (-> (shapes->fonts all-children)
|
||||||
|
(hooks/use-equal-memo))
|
||||||
|
|
||||||
|
images-urls (-> (shapes->images all-children)
|
||||||
|
(hooks/use-equal-memo))
|
||||||
|
|
||||||
|
style-code
|
||||||
|
(mf/use-memo
|
||||||
|
(mf/deps fontfaces-css style-type all-children cg/generate-style-code)
|
||||||
|
(fn []
|
||||||
|
(dm/str
|
||||||
|
fontfaces-css "\n"
|
||||||
|
(-> (cg/generate-style-code objects style-type all-children)
|
||||||
|
(format-code style-type)))))
|
||||||
|
|
||||||
markup-code
|
markup-code
|
||||||
(-> (mf/use-memo (mf/deps shapes) #(generate-markup-code objects shapes))
|
(mf/use-memo
|
||||||
(format-code "svg"))
|
(mf/deps markup-type shapes images-data)
|
||||||
|
(fn []
|
||||||
|
(-> (cg/generate-markup-code objects markup-type shapes)
|
||||||
|
(format-code markup-type))))
|
||||||
|
|
||||||
on-markup-copied
|
on-markup-copied
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
(mf/deps @markup-type)
|
(mf/deps markup-type)
|
||||||
(fn []
|
(fn []
|
||||||
(st/emit! (ptk/event ::ev/event
|
(st/emit! (ptk/event ::ev/event
|
||||||
{::ev/name "copy-inspect-code"
|
{::ev/name "copy-inspect-code"
|
||||||
:type @markup-type}))))
|
:type markup-type}))))
|
||||||
|
|
||||||
on-style-copied
|
on-style-copied
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
(mf/deps @style-type)
|
(mf/deps style-type)
|
||||||
(fn []
|
(fn []
|
||||||
(st/emit! (ptk/event ::ev/event
|
(st/emit! (ptk/event ::ev/event
|
||||||
{::ev/name "copy-inspect-style"
|
{::ev/name "copy-inspect-style"
|
||||||
:type @style-type}))))]
|
:type style-type}))))
|
||||||
|
|
||||||
|
{on-markup-pointer-down :on-pointer-down
|
||||||
|
on-markup-lost-pointer-capture :on-lost-pointer-capture
|
||||||
|
on-markup-pointer-move :on-pointer-move
|
||||||
|
markup-size :size}
|
||||||
|
(use-resize-hook :code 400 100 800 :y false :bottom)
|
||||||
|
|
||||||
|
{on-style-pointer-down :on-pointer-down
|
||||||
|
on-style-lost-pointer-capture :on-lost-pointer-capture
|
||||||
|
on-style-pointer-move :on-pointer-move
|
||||||
|
style-size :size}
|
||||||
|
(use-resize-hook :code 400 100 800 :y false :bottom)
|
||||||
|
|
||||||
|
set-style
|
||||||
|
(mf/use-callback
|
||||||
|
(fn [value]
|
||||||
|
(reset! style-type* value)))
|
||||||
|
|
||||||
|
set-markup
|
||||||
|
(mf/use-callback
|
||||||
|
(fn [value]
|
||||||
|
(reset! markup-type* value)))
|
||||||
|
|
||||||
|
handle-copy-all-code
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps style-code markup-code images-data)
|
||||||
|
(fn []
|
||||||
|
(let [markup-code (cond-> markup-code
|
||||||
|
embed-images? (replace-map images-data))
|
||||||
|
|
||||||
|
style-code (cond-> style-code
|
||||||
|
remove-localhost?
|
||||||
|
(str/replace "http://localhost:3449" ""))
|
||||||
|
|
||||||
|
data (str/format page-template style-code markup-code)]
|
||||||
|
(wapi/write-to-clipboard data))))]
|
||||||
|
|
||||||
|
(mf/use-effect
|
||||||
|
(mf/deps fonts)
|
||||||
|
#(->> (rx/from fonts)
|
||||||
|
(rx/merge-map fonts/fetch-font-css)
|
||||||
|
(rx/reduce conj [])
|
||||||
|
(rx/subs
|
||||||
|
(fn [result]
|
||||||
|
(let [css (str/join "\n" result)]
|
||||||
|
(reset! fontfaces-css* css))))))
|
||||||
|
|
||||||
|
(mf/use-effect
|
||||||
|
(mf/deps images-urls)
|
||||||
|
#(->> (rx/from images-urls)
|
||||||
|
(rx/merge-map
|
||||||
|
(fn [[_ uri]]
|
||||||
|
(->> (http/fetch-data-uri uri true)
|
||||||
|
(rx/catch (fn [_] (rx/of (hash-map uri uri)))))))
|
||||||
|
(rx/reduce conj {})
|
||||||
|
(rx/subs
|
||||||
|
(fn [result]
|
||||||
|
(reset! images-data* result)))))
|
||||||
|
|
||||||
[:div.element-options
|
[:div.element-options
|
||||||
[:div.code-block
|
[:div.attributes-block
|
||||||
[:div.code-row-lang "CSS"
|
[:button.download-button {:on-click handle-copy-all-code}
|
||||||
|
"Copy all code"]]
|
||||||
|
|
||||||
|
[:div.code-block
|
||||||
|
[:div.code-row-lang
|
||||||
|
[:& select {:default-value style-type
|
||||||
|
:class "custom-select"
|
||||||
|
:options [{:label "CSS" :value "css"}]
|
||||||
|
:on-change set-style}]
|
||||||
[:button.expand-button
|
[:button.expand-button
|
||||||
{:on-click on-expand}
|
{:on-click on-expand}
|
||||||
i/full-screen]
|
i/full-screen]
|
||||||
@ -109,19 +244,33 @@
|
|||||||
[:& copy-button {:data style-code
|
[:& copy-button {:data style-code
|
||||||
:on-copied on-style-copied}]]
|
:on-copied on-style-copied}]]
|
||||||
|
|
||||||
[:div.code-row-display
|
[:div.code-row-display {:style #js {"--code-height" (str (or style-size 400) "px")}}
|
||||||
[:& code-block {:type @style-type
|
[:& code-block {:type style-type
|
||||||
:code style-code}]]]
|
:code style-code}]]
|
||||||
|
|
||||||
|
[:div.resize-area {:on-pointer-down on-style-pointer-down
|
||||||
|
:on-lost-pointer-capture on-style-lost-pointer-capture
|
||||||
|
:on-pointer-move on-style-pointer-move}]]
|
||||||
|
|
||||||
[:div.code-block
|
[:div.code-block
|
||||||
[:div.code-row-lang "SVG"
|
[:div.code-row-lang
|
||||||
|
[:& select {:default-value markup-type
|
||||||
|
:class "input-option"
|
||||||
|
:options [{:label "HTML" :value "html"}
|
||||||
|
{:label "SVG" :value "svg"}]
|
||||||
|
:on-change set-markup}]
|
||||||
|
|
||||||
[:button.expand-button
|
[:button.expand-button
|
||||||
{:on-click on-expand}
|
{:on-click on-expand}
|
||||||
i/full-screen]
|
i/full-screen]
|
||||||
|
|
||||||
[:& copy-button {:data markup-code
|
[:& copy-button {:data #(replace-map markup-code images-data)
|
||||||
:on-copied on-markup-copied}]]
|
:on-copied on-markup-copied}]]
|
||||||
[:div.code-row-display
|
|
||||||
[:& code-block {:type @markup-type
|
[:div.code-row-display {:style #js {"--code-height" (str (or markup-size 400) "px")}}
|
||||||
:code markup-code}]]]]))
|
[:& code-block {:type markup-type
|
||||||
|
:code markup-code}]]
|
||||||
|
|
||||||
|
[:div.resize-area {:on-pointer-down on-markup-pointer-down
|
||||||
|
:on-lost-pointer-capture on-markup-lost-pointer-capture
|
||||||
|
:on-pointer-move on-markup-pointer-move}]]]))
|
||||||
|
|||||||
@ -6,9 +6,7 @@
|
|||||||
|
|
||||||
(ns app.main.ui.viewer.inspect.right-sidebar
|
(ns app.main.ui.viewer.inspect.right-sidebar
|
||||||
(:require
|
(:require
|
||||||
[app.main.data.workspace :as dw]
|
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.store :as st]
|
|
||||||
[app.main.ui.components.shape-icon :as si]
|
[app.main.ui.components.shape-icon :as si]
|
||||||
[app.main.ui.components.tabs-container :refer [tabs-container tabs-element]]
|
[app.main.ui.components.tabs-container :refer [tabs-container tabs-element]]
|
||||||
[app.main.ui.icons :as i]
|
[app.main.ui.icons :as i]
|
||||||
@ -38,20 +36,39 @@
|
|||||||
:data local})))))
|
:data local})))))
|
||||||
|
|
||||||
(mf/defc right-sidebar
|
(mf/defc right-sidebar
|
||||||
[{:keys [frame page file selected shapes page-id file-id share-id from]
|
[{:keys [frame page objects file selected shapes page-id file-id share-id from on-change-section on-expand]
|
||||||
:or {from :inspect}}]
|
:or {from :inspect}}]
|
||||||
(let [expanded (mf/use-state false)
|
(let [section (mf/use-state :info #_:code)
|
||||||
section (mf/use-state :info #_:code)
|
objects (or objects (:objects page))
|
||||||
shapes (or shapes
|
shapes (or shapes
|
||||||
(resolve-shapes (:objects page) selected))
|
(resolve-shapes objects selected))
|
||||||
|
|
||||||
first-shape (first shapes)
|
first-shape (first shapes)
|
||||||
page-id (or page-id (:id page))
|
page-id (or page-id (:id page))
|
||||||
file-id (or file-id (:id file))
|
file-id (or file-id (:id file))
|
||||||
|
|
||||||
libraries (get-libraries from)]
|
libraries (get-libraries from)
|
||||||
|
|
||||||
[:aside.settings-bar.settings-bar-right {:class (when @expanded "expanded")}
|
handle-change-tab
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps from on-change-section)
|
||||||
|
(fn [new-section]
|
||||||
|
(reset! section new-section)
|
||||||
|
(when on-change-section
|
||||||
|
(on-change-section new-section))))
|
||||||
|
|
||||||
|
handle-expand
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps on-expand)
|
||||||
|
(fn []
|
||||||
|
(when on-expand (on-expand))))]
|
||||||
|
|
||||||
|
(mf/use-effect
|
||||||
|
(mf/deps shapes handle-change-tab)
|
||||||
|
(fn []
|
||||||
|
(when-not (seq shapes)
|
||||||
|
(handle-change-tab :info))))
|
||||||
|
|
||||||
|
[:aside.settings-bar.settings-bar-right
|
||||||
[:div.settings-bar-inside
|
[:div.settings-bar-inside
|
||||||
(if (seq shapes)
|
(if (seq shapes)
|
||||||
[:div.tool-window
|
[:div.tool-window
|
||||||
@ -77,14 +94,11 @@
|
|||||||
;; inspect.tabs.code.selected.text
|
;; inspect.tabs.code.selected.text
|
||||||
[:span.tool-window-bar-title (:name first-shape)]])]
|
[:span.tool-window-bar-title (:name first-shape)]])]
|
||||||
[:div.tool-window-content.inspect
|
[:div.tool-window-content.inspect
|
||||||
[:& tabs-container {:on-change-tab #(do
|
[:& tabs-container {:on-change-tab handle-change-tab
|
||||||
(reset! expanded false)
|
:selected @section}
|
||||||
(reset! section %)
|
|
||||||
(when (= from :workspace)
|
|
||||||
(st/emit! (dw/set-inspect-expanded false))))
|
|
||||||
:selected @section}
|
|
||||||
[:& tabs-element {:id :info :title (tr "inspect.tabs.info")}
|
[:& tabs-element {:id :info :title (tr "inspect.tabs.info")}
|
||||||
[:& attributes {:page-id page-id
|
[:& attributes {:page-id page-id
|
||||||
|
:objects objects
|
||||||
:file-id file-id
|
:file-id file-id
|
||||||
:frame frame
|
:frame frame
|
||||||
:shapes shapes
|
:shapes shapes
|
||||||
@ -95,10 +109,7 @@
|
|||||||
[:& tabs-element {:id :code :title (tr "inspect.tabs.code")}
|
[:& tabs-element {:id :code :title (tr "inspect.tabs.code")}
|
||||||
[:& code {:frame frame
|
[:& code {:frame frame
|
||||||
:shapes shapes
|
:shapes shapes
|
||||||
:on-expand (fn []
|
:on-expand handle-expand
|
||||||
(when (= from :workspace)
|
|
||||||
(st/emit! (dw/set-inspect-expanded (not @expanded))))
|
|
||||||
(swap! expanded not))
|
|
||||||
:from from}]]]]]
|
:from from}]]]]]
|
||||||
[:div.empty
|
[:div.empty
|
||||||
[:span.tool-window-bar-icon i/code]
|
[:span.tool-window-bar-icon i/code]
|
||||||
|
|||||||
@ -413,9 +413,10 @@
|
|||||||
is-frame? (and single? has-frame?)
|
is-frame? (and single? has-frame?)
|
||||||
is-flex-container? (and is-frame? (= :flex (:layout (first shapes))))
|
is-flex-container? (and is-frame? (= :flex (:layout (first shapes))))
|
||||||
ids (->> shapes (map :id))
|
ids (->> shapes (map :id))
|
||||||
add-flex #(st/emit! (if is-frame?
|
add-layout (fn [type]
|
||||||
(dwsl/create-layout-from-id ids :flex true)
|
(st/emit! (if is-frame?
|
||||||
(dwsl/create-layout-from-selection :flex)))
|
(dwsl/create-layout-from-id ids type true)
|
||||||
|
(dwsl/create-layout-from-selection type))))
|
||||||
remove-flex #(st/emit! (dwsl/remove-layout ids))]
|
remove-flex #(st/emit! (dwsl/remove-layout ids))]
|
||||||
|
|
||||||
[:*
|
[:*
|
||||||
@ -424,7 +425,10 @@
|
|||||||
[:& menu-separator]
|
[:& menu-separator]
|
||||||
[:& menu-entry {:title (tr "workspace.shape.menu.add-flex")
|
[:& menu-entry {:title (tr "workspace.shape.menu.add-flex")
|
||||||
:shortcut (sc/get-tooltip :toggle-layout-flex)
|
:shortcut (sc/get-tooltip :toggle-layout-flex)
|
||||||
:on-click add-flex}]])
|
:on-click #(add-layout :flex)}]
|
||||||
|
[:& menu-entry {:title (tr "workspace.shape.menu.add-grid")
|
||||||
|
:shortcut (sc/get-tooltip :toggle-layout-grid)
|
||||||
|
:on-click #(add-layout :grid)}]])
|
||||||
(when is-flex-container?
|
(when is-flex-container?
|
||||||
[:div
|
[:div
|
||||||
[:& menu-separator]
|
[:& menu-separator]
|
||||||
|
|||||||
@ -151,7 +151,8 @@
|
|||||||
(dom/class? node "frame-title")
|
(dom/class? node "frame-title")
|
||||||
(let [shape (gsh/transform-shape shape modifiers)
|
(let [shape (gsh/transform-shape shape modifiers)
|
||||||
zoom (get-in @st/state [:workspace-local :zoom] 1)
|
zoom (get-in @st/state [:workspace-local :zoom] 1)
|
||||||
mtx (vwu/title-transform shape zoom)]
|
edit-grid? (= (dom/get-data node "edit-grid") "true")
|
||||||
|
mtx (vwu/title-transform shape zoom edit-grid?)]
|
||||||
(override-transform-att! node "transform" mtx))
|
(override-transform-att! node "transform" mtx))
|
||||||
|
|
||||||
(or (= (dom/get-tag-name node) "mask")
|
(or (= (dom/get-tag-name node) "mask")
|
||||||
|
|||||||
@ -26,6 +26,7 @@
|
|||||||
[app.main.ui.workspace.sidebar.sitemap :refer [sitemap]]
|
[app.main.ui.workspace.sidebar.sitemap :refer [sitemap]]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.i18n :refer [tr]]
|
[app.util.i18n :refer [tr]]
|
||||||
|
[app.util.object :as obj]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
;; --- Left Sidebar (Component)
|
;; --- Left Sidebar (Component)
|
||||||
@ -134,16 +135,45 @@
|
|||||||
is-history? (contains? layout :document-history)
|
is-history? (contains? layout :document-history)
|
||||||
is-inspect? (= section :inspect)
|
is-inspect? (= section :inspect)
|
||||||
|
|
||||||
expanded? (mf/deref refs/inspect-expanded)
|
;;expanded? (mf/deref refs/inspect-expanded)
|
||||||
|
;;prev-expanded? (hooks/use-previous expanded?)
|
||||||
|
|
||||||
|
current-section* (mf/use-state :info)
|
||||||
|
current-section (deref current-section*)
|
||||||
|
|
||||||
can-be-expanded? (and (not is-comments?)
|
can-be-expanded? (and (not is-comments?)
|
||||||
(not is-history?)
|
(not is-history?)
|
||||||
is-inspect?)]
|
is-inspect?
|
||||||
|
(= current-section :code))
|
||||||
|
|
||||||
(mf/with-effect [can-be-expanded?]
|
{:keys [on-pointer-down on-lost-pointer-capture on-pointer-move set-size size]}
|
||||||
(when (not can-be-expanded?)
|
(use-resize-hook :code 256 256 768 :x true :right)
|
||||||
(st/emit! (dw/set-inspect-expanded false))))
|
|
||||||
|
|
||||||
[:aside.settings-bar.settings-bar-right {:class (when (and can-be-expanded? expanded?) "expanded")}
|
handle-change-section
|
||||||
|
(mf/use-callback
|
||||||
|
(fn [section]
|
||||||
|
(reset! current-section* section)))
|
||||||
|
|
||||||
|
handle-expand
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps size)
|
||||||
|
(fn []
|
||||||
|
(set-size (if (> size 256) 256 768))))
|
||||||
|
|
||||||
|
props
|
||||||
|
(-> props
|
||||||
|
(obj/clone)
|
||||||
|
(obj/set! "on-change-section" handle-change-section)
|
||||||
|
(obj/set! "on-expand" handle-expand))]
|
||||||
|
|
||||||
|
[:aside.settings-bar.settings-bar-right
|
||||||
|
{:class (when (not can-be-expanded?) "not-expand")
|
||||||
|
:style #js {"--width" (when can-be-expanded? (dm/str size "px"))}}
|
||||||
|
(when can-be-expanded?
|
||||||
|
[:div.resize-area
|
||||||
|
{:on-pointer-down on-pointer-down
|
||||||
|
:on-lost-pointer-capture on-lost-pointer-capture
|
||||||
|
:on-pointer-move on-pointer-move}])
|
||||||
[:div.settings-bar-inside
|
[:div.settings-bar-inside
|
||||||
(cond
|
(cond
|
||||||
(true? is-comments?)
|
(true? is-comments?)
|
||||||
|
|||||||
@ -7,8 +7,10 @@
|
|||||||
(ns app.main.ui.workspace.sidebar.options
|
(ns app.main.ui.workspace.sidebar.options
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
[app.common.pages.helpers :as cph]
|
[app.common.pages.helpers :as cph]
|
||||||
|
[app.common.types.shape.layout :as ctl]
|
||||||
[app.main.data.workspace :as udw]
|
[app.main.data.workspace :as udw]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
@ -18,12 +20,13 @@
|
|||||||
[app.main.ui.workspace.sidebar.options.menus.align :refer [align-options]]
|
[app.main.ui.workspace.sidebar.options.menus.align :refer [align-options]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.bool :refer [bool-options]]
|
[app.main.ui.workspace.sidebar.options.menus.bool :refer [bool-options]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.exports :refer [exports-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.exports :refer [exports-menu]]
|
||||||
|
[app.main.ui.workspace.sidebar.options.menus.grid-cell :as grid-cell]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.interactions :refer [interactions-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.interactions :refer [interactions-menu]]
|
||||||
|
[app.main.ui.workspace.sidebar.options.menus.layout-container :as layout-container]
|
||||||
[app.main.ui.workspace.sidebar.options.page :as page]
|
[app.main.ui.workspace.sidebar.options.page :as page]
|
||||||
[app.main.ui.workspace.sidebar.options.shapes.bool :as bool]
|
[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.circle :as circle]
|
||||||
[app.main.ui.workspace.sidebar.options.shapes.frame :as frame]
|
[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.group :as group]
|
||||||
[app.main.ui.workspace.sidebar.options.shapes.image :as image]
|
[app.main.ui.workspace.sidebar.options.shapes.image :as image]
|
||||||
[app.main.ui.workspace.sidebar.options.shapes.multiple :as multiple]
|
[app.main.ui.workspace.sidebar.options.shapes.multiple :as multiple]
|
||||||
@ -64,70 +67,92 @@
|
|||||||
|
|
||||||
(mf/defc options-content
|
(mf/defc options-content
|
||||||
{::mf/wrap [mf/memo]}
|
{::mf/wrap [mf/memo]}
|
||||||
[{:keys [selected section shapes shapes-with-children page-id file-id]}]
|
[{:keys [selected section shapes shapes-with-children page-id file-id on-change-section on-expand]}]
|
||||||
(let [drawing (mf/deref refs/workspace-drawing)
|
(let [drawing (mf/deref refs/workspace-drawing)
|
||||||
objects (mf/deref refs/workspace-page-objects)
|
objects (mf/deref refs/workspace-page-objects)
|
||||||
shared-libs (mf/deref refs/workspace-libraries)
|
shared-libs (mf/deref refs/workspace-libraries)
|
||||||
|
edition (mf/deref refs/selected-edition)
|
||||||
grid-edition (mf/deref refs/workspace-grid-edition)
|
grid-edition (mf/deref refs/workspace-grid-edition)
|
||||||
|
|
||||||
selected-shapes (into [] (keep (d/getf objects)) selected)
|
selected-shapes (into [] (keep (d/getf objects)) selected)
|
||||||
first-selected-shape (first selected-shapes)
|
first-selected-shape (first selected-shapes)
|
||||||
shape-parent-frame (cph/get-frame objects (:frame-id first-selected-shape))
|
shape-parent-frame (cph/get-frame objects (:frame-id first-selected-shape))
|
||||||
|
|
||||||
[grid-id {[row-selected col-selected] :selected}]
|
edit-grid? (ctl/grid-layout? objects edition)
|
||||||
(d/seek (fn [[_ {:keys [selected]}]] (some? selected)) grid-edition)
|
selected-cell (dm/get-in grid-edition [edition :selected])
|
||||||
|
|
||||||
grid-cell-selected? (and (some? grid-id) (some? row-selected) (some? col-selected))
|
|
||||||
|
|
||||||
on-change-tab
|
on-change-tab
|
||||||
(fn [options-mode]
|
(fn [options-mode]
|
||||||
(st/emit! (udw/set-options-mode options-mode)
|
(st/emit! (udw/set-options-mode options-mode))
|
||||||
(udw/set-inspect-expanded false))
|
(if (= options-mode :inspect)
|
||||||
(if (= options-mode :inspect) ;;TODO maybe move this logic to set-options-mode
|
|
||||||
(st/emit! :interrupt (udw/set-workspace-read-only true))
|
(st/emit! :interrupt (udw/set-workspace-read-only true))
|
||||||
(st/emit! :interrupt (udw/set-workspace-read-only false))))]
|
(st/emit! :interrupt (udw/set-workspace-read-only false))))]
|
||||||
|
|
||||||
[:div.tool-window
|
[:div.tool-window
|
||||||
[:div.tool-window-content
|
[:div.tool-window-content
|
||||||
[:& tabs-container {:on-change-tab on-change-tab
|
[:& tabs-container {:on-change-tab on-change-tab
|
||||||
:selected section}
|
:selected section}
|
||||||
[:& tabs-element {:id :design
|
[:& tabs-element {:id :design
|
||||||
:title (tr "workspace.options.design")}
|
:title (tr "workspace.options.design")}
|
||||||
[:div.element-options
|
[:div.element-options
|
||||||
[:& align-options]
|
[:& align-options]
|
||||||
[:& bool-options]
|
[:& bool-options]
|
||||||
(cond
|
(cond
|
||||||
grid-cell-selected? [:& grid-cell/options {:shape (get objects grid-id)
|
(some? selected-cell)
|
||||||
:row row-selected
|
[:& grid-cell/options
|
||||||
:column col-selected}]
|
{:shape (get objects edition)
|
||||||
|
:cell (dm/get-in objects [edition :layout-grid-cells selected-cell])}]
|
||||||
|
|
||||||
(d/not-empty? drawing) [:& shape-options {:shape (:object drawing)
|
edit-grid?
|
||||||
:page-id page-id
|
[:& layout-container/grid-layout-edition
|
||||||
:file-id file-id
|
{:ids [edition]
|
||||||
:shared-libs shared-libs}]
|
:values (get objects edition)}]
|
||||||
(= 0 (count selected)) [:& page/options]
|
|
||||||
(= 1 (count selected)) [:& shape-options {:shape (first selected-shapes)
|
(d/not-empty? drawing)
|
||||||
:page-id page-id
|
[:& shape-options
|
||||||
:file-id file-id
|
{:shape (:object drawing)
|
||||||
:shared-libs shared-libs
|
:page-id page-id
|
||||||
:shapes-with-children shapes-with-children}]
|
:file-id file-id
|
||||||
:else [:& multiple/options {:shapes-with-children shapes-with-children
|
:shared-libs shared-libs}]
|
||||||
:shapes selected-shapes
|
|
||||||
:page-id page-id
|
(= 0 (count selected))
|
||||||
:file-id file-id
|
[:& page/options]
|
||||||
:shared-libs shared-libs}])]]
|
|
||||||
|
(= 1 (count selected))
|
||||||
|
[:& shape-options
|
||||||
|
{:shape (first selected-shapes)
|
||||||
|
:page-id page-id
|
||||||
|
:file-id file-id
|
||||||
|
:shared-libs shared-libs
|
||||||
|
:shapes-with-children shapes-with-children}]
|
||||||
|
|
||||||
|
:else
|
||||||
|
[:& multiple/options
|
||||||
|
{:shapes-with-children shapes-with-children
|
||||||
|
:shapes selected-shapes
|
||||||
|
:page-id page-id
|
||||||
|
:file-id file-id
|
||||||
|
:shared-libs shared-libs}])]]
|
||||||
|
|
||||||
|
[:& tabs-element
|
||||||
|
{:id :prototype
|
||||||
|
:title (tr "workspace.options.prototype")}
|
||||||
|
|
||||||
[:& tabs-element {:id :prototype
|
|
||||||
:title (tr "workspace.options.prototype")}
|
|
||||||
[:div.element-options
|
[:div.element-options
|
||||||
[:& interactions-menu {:shape (first shapes)}]]]
|
[:& interactions-menu {:shape (first shapes)}]]]
|
||||||
|
|
||||||
[:& tabs-element {:id :inspect
|
[:& tabs-element {:id :inspect
|
||||||
:title (tr "workspace.options.inspect")}
|
:title (tr "workspace.options.inspect")}
|
||||||
[:div.element-options
|
|
||||||
[:& hrs/right-sidebar {:page-id page-id
|
[:div.element-options.element-options-inspect
|
||||||
:file-id file-id
|
[:& hrs/right-sidebar {:page-id page-id
|
||||||
:frame shape-parent-frame
|
:objects objects
|
||||||
:shapes selected-shapes
|
:file-id file-id
|
||||||
:from :workspace}]]]]]]))
|
:frame shape-parent-frame
|
||||||
|
:shapes selected-shapes
|
||||||
|
:on-change-section on-change-section
|
||||||
|
:on-expand on-expand
|
||||||
|
:from :workspace}]]]]]]))
|
||||||
|
|
||||||
;; TODO: this need optimizations, selected-objects and
|
;; TODO: this need optimizations, selected-objects and
|
||||||
;; selected-objects-with-children are derefed always but they only
|
;; selected-objects-with-children are derefed always but they only
|
||||||
@ -139,6 +164,8 @@
|
|||||||
[props]
|
[props]
|
||||||
(let [section (obj/get props "section")
|
(let [section (obj/get props "section")
|
||||||
selected (obj/get props "selected")
|
selected (obj/get props "selected")
|
||||||
|
on-change-section (obj/get props "on-change-section")
|
||||||
|
on-expand (obj/get props "on-expand")
|
||||||
page-id (mf/use-ctx ctx/current-page-id)
|
page-id (mf/use-ctx ctx/current-page-id)
|
||||||
file-id (mf/use-ctx ctx/current-file-id)
|
file-id (mf/use-ctx ctx/current-file-id)
|
||||||
shapes (mf/deref refs/selected-objects)
|
shapes (mf/deref refs/selected-objects)
|
||||||
@ -149,4 +176,6 @@
|
|||||||
:shapes-with-children shapes-with-children
|
:shapes-with-children shapes-with-children
|
||||||
:file-id file-id
|
:file-id file-id
|
||||||
:page-id page-id
|
:page-id page-id
|
||||||
:section section}]))
|
:section section
|
||||||
|
:on-change-section on-change-section
|
||||||
|
:on-expand on-expand}]))
|
||||||
|
|||||||
@ -238,7 +238,8 @@
|
|||||||
[:button.btn-options {:disabled is-default
|
[:button.btn-options {:disabled is-default
|
||||||
:on-click handle-set-as-default} (tr "workspace.options.grid.params.set-default")]]]]))
|
:on-click handle-set-as-default} (tr "workspace.options.grid.params.set-default")]]]]))
|
||||||
|
|
||||||
(mf/defc frame-grid [{:keys [shape]}]
|
(mf/defc frame-grid
|
||||||
|
[{:keys [shape]}]
|
||||||
(let [id (:id shape)
|
(let [id (:id shape)
|
||||||
saved-grids (mf/deref workspace-saved-grids)
|
saved-grids (mf/deref workspace-saved-grids)
|
||||||
default-grid-params (mf/use-memo (mf/deps saved-grids) #(merge dw/default-grid-params saved-grids))
|
default-grid-params (mf/use-memo (mf/deps saved-grids) #(merge dw/default-grid-params saved-grids))
|
||||||
|
|||||||
@ -0,0 +1,194 @@
|
|||||||
|
;; 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.menus.grid-cell
|
||||||
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
|
[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.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]
|
||||||
|
alignment (or alignment :auto)]
|
||||||
|
[: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 cell] :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)
|
||||||
|
|
||||||
|
cell-mode (or mode :auto)
|
||||||
|
cell-mode (if (and (= :auto cell-mode)
|
||||||
|
(or (> (:column-span cell) 1)
|
||||||
|
(> (:row-span cell) 1)))
|
||||||
|
:manual
|
||||||
|
cell-mode)
|
||||||
|
|
||||||
|
set-alignment
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps align-self (:id shape) (:id cell))
|
||||||
|
(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})))))
|
||||||
|
|
||||||
|
set-justify-self
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps justify-self (:id shape) (:id cell))
|
||||||
|
(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})))))
|
||||||
|
|
||||||
|
on-change
|
||||||
|
(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]
|
||||||
|
|
||||||
|
(and (= type :column) (= field :end))
|
||||||
|
[:column-span (max 1 (- value column))]
|
||||||
|
|
||||||
|
(and (= type :row) (or (= field :all) (= field :start)))
|
||||||
|
[:row value]
|
||||||
|
|
||||||
|
(and (= type :row) (= field :end))
|
||||||
|
[:row-span (max 1 (- value row))])]
|
||||||
|
|
||||||
|
(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))
|
||||||
|
(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}))))))
|
||||||
|
|
||||||
|
set-cell-mode
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps (:id shape) (:id cell))
|
||||||
|
(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)))))]
|
||||||
|
|
||||||
|
[: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-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"]
|
||||||
|
[:button.position-btn
|
||||||
|
{:on-click #(set-cell-mode :area)
|
||||||
|
:class (dom/classnames :active (= :area cell-mode))} "Area"]]]
|
||||||
|
|
||||||
|
[:div.manage-grid-columns
|
||||||
|
(when (= :auto cell-mode)
|
||||||
|
[:div.grid-auto
|
||||||
|
[:div.grid-columns-auto
|
||||||
|
[:span.icon i/layout-rows]
|
||||||
|
[:div.input-wrapper
|
||||||
|
[:> numeric-input
|
||||||
|
{:placeholder "--"
|
||||||
|
:on-click #(dom/select-target %)
|
||||||
|
:on-change (partial on-change :all :column)
|
||||||
|
:value column}]]]
|
||||||
|
[:div.grid-rows-auto
|
||||||
|
[:span.icon i/layout-columns]
|
||||||
|
[:div.input-wrapper
|
||||||
|
[:> numeric-input
|
||||||
|
{:placeholder "--"
|
||||||
|
:on-click #(dom/select-target %)
|
||||||
|
:on-change (partial on-change :all :row)
|
||||||
|
:value row}]]]])
|
||||||
|
|
||||||
|
(when (= :area cell-mode)
|
||||||
|
[:div.input-wrapper
|
||||||
|
[:input.input-text
|
||||||
|
{:key (dm/str "name-" (:id cell))
|
||||||
|
: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}]])
|
||||||
|
|
||||||
|
(when (or (= :manual cell-mode) (= :area cell-mode))
|
||||||
|
[:div.grid-manual
|
||||||
|
[:div.grid-columns-auto
|
||||||
|
[:span.icon i/layout-rows]
|
||||||
|
[:div.input-wrapper
|
||||||
|
[:> numeric-input
|
||||||
|
{:placeholder "--"
|
||||||
|
:on-pointer-down #(dom/select-target %)
|
||||||
|
:on-change (partial on-change :start :column)
|
||||||
|
:value column}]
|
||||||
|
[:> numeric-input
|
||||||
|
{:placeholder "--"
|
||||||
|
:on-pointer-down #(dom/select-target %)
|
||||||
|
:on-change (partial on-change :end :column)
|
||||||
|
:value column-end}]]]
|
||||||
|
[:div.grid-rows-auto
|
||||||
|
[:span.icon i/layout-columns]
|
||||||
|
[:div.input-wrapper
|
||||||
|
[:> numeric-input
|
||||||
|
{:placeholder "--"
|
||||||
|
:on-pointer-down #(dom/select-target %)
|
||||||
|
:on-change (partial on-change :start :row)
|
||||||
|
:value row}]
|
||||||
|
[:> numeric-input
|
||||||
|
{:placeholder "--"
|
||||||
|
:on-pointer-down #(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-self
|
||||||
|
:set-alignment set-justify-self}]]]]]))
|
||||||
@ -9,8 +9,10 @@
|
|||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.math :as mth]
|
[app.common.math :as mth]
|
||||||
|
[app.common.types.shape.layout :as ctl]
|
||||||
[app.main.data.workspace :as udw]
|
[app.main.data.workspace :as udw]
|
||||||
[app.main.data.workspace.shape-layout :as dwsl]
|
[app.main.data.workspace.shape-layout :as dwsl]
|
||||||
|
[app.main.features :as features]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.components.numeric-input :refer [numeric-input]]
|
[app.main.ui.components.numeric-input :refer [numeric-input]]
|
||||||
@ -124,17 +126,22 @@
|
|||||||
:justify-items
|
:justify-items
|
||||||
(if is-col?
|
(if is-col?
|
||||||
(case val
|
(case val
|
||||||
|
:stretch i/align-items-row-strech
|
||||||
:start i/grid-justify-content-column-start
|
:start i/grid-justify-content-column-start
|
||||||
:end i/grid-justify-content-column-end
|
:end i/grid-justify-content-column-end
|
||||||
:center i/grid-justify-content-column-center
|
:center i/grid-justify-content-column-center
|
||||||
:space-around i/grid-justify-content-column-around
|
:space-around i/grid-justify-content-column-around
|
||||||
:space-between i/grid-justify-content-column-between)
|
:space-between i/grid-justify-content-column-between
|
||||||
|
:space-evenly i/grid-justify-content-column-between)
|
||||||
|
|
||||||
(case val
|
(case val
|
||||||
|
:stretch i/align-items-column-strech
|
||||||
:start i/grid-justify-content-row-start
|
:start i/grid-justify-content-row-start
|
||||||
:end i/grid-justify-content-row-end
|
:end i/grid-justify-content-row-end
|
||||||
:center i/grid-justify-content-row-center
|
:center i/grid-justify-content-row-center
|
||||||
:space-around i/grid-justify-content-row-around
|
:space-around i/grid-justify-content-row-around
|
||||||
:space-between i/grid-justify-content-row-between))))
|
:space-between i/grid-justify-content-row-between
|
||||||
|
:space-evenly i/grid-justify-content-row-between))))
|
||||||
|
|
||||||
(mf/defc direction-btn
|
(mf/defc direction-btn
|
||||||
[{:keys [dir saved-dir set-direction icon?] :as props}]
|
[{:keys [dir saved-dir set-direction icon?] :as props}]
|
||||||
@ -383,18 +390,21 @@
|
|||||||
:alt "Grid edit mode"
|
:alt "Grid edit mode"
|
||||||
:on-click #(toggle-edit-mode)
|
:on-click #(toggle-edit-mode)
|
||||||
:style {:padding 0}}
|
:style {:padding 0}}
|
||||||
|
"Edit grid"
|
||||||
i/grid-layout-mode]))
|
i/grid-layout-mode]))
|
||||||
|
|
||||||
(mf/defc align-grid-row
|
(mf/defc align-grid-row
|
||||||
[{:keys [is-col? align-items set-align] :as props}]
|
[{:keys [is-col? align-items set-align] :as props}]
|
||||||
(let [type (if is-col? :column :row)]
|
(let [type (if is-col? :column :row)]
|
||||||
[:div.align-items-style
|
[:div.align-items-style
|
||||||
(for [align [:start :center :end :stretch :baseline]]
|
(for [align [:start :center :end]]
|
||||||
[:button.align-start.tooltip
|
[:button.align-start.tooltip
|
||||||
{:class (dom/classnames :active (= align-items align)
|
{:class (dom/classnames :active (= align-items align)
|
||||||
:tooltip-bottom-left (not= align :start)
|
:tooltip-bottom-left (not= align :start)
|
||||||
:tooltip-bottom (= align :start))
|
:tooltip-bottom (= align :start))
|
||||||
:alt (dm/str "Align items " (d/name align))
|
:alt (if is-col?
|
||||||
|
(dm/str "justify-items: " (d/name align))
|
||||||
|
(dm/str "align-items: " (d/name align)))
|
||||||
:on-click #(set-align align type)
|
:on-click #(set-align align type)
|
||||||
:key (dm/str "align-items" (d/name align))}
|
:key (dm/str "align-items" (d/name align))}
|
||||||
(get-layout-flex-icon :align-items align is-col?)])]))
|
(get-layout-flex-icon :align-items align is-col?)])]))
|
||||||
@ -403,12 +413,14 @@
|
|||||||
[{:keys [is-col? justify-items set-justify] :as props}]
|
[{:keys [is-col? justify-items set-justify] :as props}]
|
||||||
(let [type (if is-col? :column :row)]
|
(let [type (if is-col? :column :row)]
|
||||||
[:div.justify-content-style
|
[:div.justify-content-style
|
||||||
(for [align [:start :center :end :space-around :space-between]]
|
(for [align [:start :center :end :space-around :space-between :stretch]]
|
||||||
[:button.align-start.tooltip
|
[:button.align-start.tooltip
|
||||||
{:class (dom/classnames :active (= justify-items align)
|
{:class (dom/classnames :active (= justify-items align)
|
||||||
:tooltip-bottom-left (not= align :start)
|
:tooltip-bottom-left (not= align :start)
|
||||||
:tooltip-bottom (= align :start))
|
:tooltip-bottom (= align :start))
|
||||||
:alt (dm/str "Justify content " (d/name align))
|
:alt (if is-col?
|
||||||
|
(dm/str "align-content: " (d/name align))
|
||||||
|
(dm/str "justify-content: " (d/name align)))
|
||||||
:on-click #(set-justify align type)
|
:on-click #(set-justify align type)
|
||||||
:key (dm/str "justify-content" (d/name align))}
|
:key (dm/str "justify-content" (d/name align))}
|
||||||
(get-layout-grid-icon :justify-items align is-col?)])]))
|
(get-layout-grid-icon :justify-items align is-col?)])]))
|
||||||
@ -441,12 +453,12 @@
|
|||||||
:on-click toggle} generated-name]
|
:on-click toggle} generated-name]
|
||||||
[:button.add-column {:on-click #(do
|
[:button.add-column {:on-click #(do
|
||||||
(when-not expanded? (toggle))
|
(when-not expanded? (toggle))
|
||||||
(add-new-element type {:type :fixed :value 100}))} i/plus]]
|
(add-new-element type ctl/default-track-value))} i/plus]]
|
||||||
|
|
||||||
(when expanded?
|
(when expanded?
|
||||||
[:div.columns-info-wrapper
|
[:div.columns-info-wrapper
|
||||||
(for [[index column] (d/enumerate column-values)]
|
(for [[index column] (d/enumerate column-values)]
|
||||||
[:div.column-info
|
[:div.column-info {:key (dm/str index "-" (name type) "-" column)}
|
||||||
[:div.direction-grid-icon
|
[:div.direction-grid-icon
|
||||||
(if is-col?
|
(if is-col?
|
||||||
i/layout-rows
|
i/layout-rows
|
||||||
@ -456,14 +468,15 @@
|
|||||||
[:> numeric-input {:no-validate true
|
[:> numeric-input {:no-validate true
|
||||||
:value (:value column)
|
:value (:value column)
|
||||||
:on-change #(set-column-value type index %)
|
:on-change #(set-column-value type index %)
|
||||||
:placeholder "--"}]]
|
:placeholder "--"
|
||||||
|
:disabled (= :auto (:type column))}]]
|
||||||
[:div.grid-column-unit
|
[:div.grid-column-unit
|
||||||
[:& select
|
[:& select
|
||||||
{:class "grid-column-unit-selector"
|
{:class "grid-column-unit-selector"
|
||||||
:default-value (:type column)
|
:default-value (:type column)
|
||||||
:options [{:value :flex :label "fr"}
|
:options [{:value :flex :label "FR"}
|
||||||
{:value :auto :label "auto"}
|
{:value :auto :label "AUTO"}
|
||||||
{:value :fixed :label "px"}
|
{:value :fixed :label "PX"}
|
||||||
{:value :percent :label "%"}]
|
{:value :percent :label "%"}]
|
||||||
:on-change #(set-column-type type index %)}]]
|
:on-change #(set-column-type type index %)}]]
|
||||||
[:button.remove-grid-column
|
[:button.remove-grid-column
|
||||||
@ -471,8 +484,8 @@
|
|||||||
i/minus]])])]))
|
i/minus]])])]))
|
||||||
|
|
||||||
(mf/defc layout-container-menu
|
(mf/defc layout-container-menu
|
||||||
{::mf/wrap [#(mf/memo' % (mf/check-props ["ids" "values" "type" "multiple"]))]}
|
{::mf/wrap [#(mf/memo' % (mf/check-props ["ids" "values" "multiple"]))]}
|
||||||
[{:keys [ids _type values multiple] :as props}]
|
[{:keys [ids values multiple] :as props}]
|
||||||
(let [open? (mf/use-state false)
|
(let [open? (mf/use-state false)
|
||||||
|
|
||||||
;; Display
|
;; Display
|
||||||
@ -489,12 +502,12 @@
|
|||||||
(st/emit! (dwsl/remove-layout ids))
|
(st/emit! (dwsl/remove-layout ids))
|
||||||
(reset! open? false))
|
(reset! open? false))
|
||||||
|
|
||||||
_set-flex
|
set-flex
|
||||||
(fn []
|
(fn []
|
||||||
(st/emit! (dwsl/remove-layout ids))
|
(st/emit! (dwsl/remove-layout ids))
|
||||||
(on-add-layout :flex))
|
(on-add-layout :flex))
|
||||||
|
|
||||||
_set-grid
|
set-grid
|
||||||
(fn []
|
(fn []
|
||||||
(st/emit! (dwsl/remove-layout ids))
|
(st/emit! (dwsl/remove-layout ids))
|
||||||
(on-add-layout :grid))
|
(on-add-layout :grid))
|
||||||
@ -580,70 +593,30 @@
|
|||||||
(st/emit! (dwsl/update-layout ids {:layout-justify-items value}))))
|
(st/emit! (dwsl/update-layout ids {:layout-justify-items value}))))
|
||||||
|
|
||||||
;; Justify grid
|
;; Justify grid
|
||||||
grid-justify-content-row (:layout-align-content values)
|
grid-justify-content-row (:layout-justify-content values)
|
||||||
grid-justify-content-column (:layout-justify-content values)
|
grid-justify-content-column (:layout-align-content values)
|
||||||
|
|
||||||
set-justify-grid
|
set-justify-grid
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
(mf/deps ids)
|
(mf/deps ids)
|
||||||
(fn [value type]
|
(fn [value type]
|
||||||
(if (= type :row)
|
(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)
|
|
||||||
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
|
||||||
[:div.element-set-title
|
[:div.element-set-title
|
||||||
[:*
|
[:*
|
||||||
[:span "Layout"]
|
[:span "Layout"]
|
||||||
(if (and (not multiple) (:layout values))
|
(if (and (not multiple) (:layout values))
|
||||||
[:div.title-actions
|
[:div.title-actions
|
||||||
#_[:div.layout-btns
|
(when (features/active-feature? :grid-layout)
|
||||||
|
[:div.layout-btns
|
||||||
[:button {:on-click set-flex
|
[:button {:on-click set-flex
|
||||||
:class (dom/classnames
|
:class (dom/classnames
|
||||||
:active (= :flex layout-type))} "Flex"]
|
:active (= :flex layout-type))} "Flex"]
|
||||||
[:button {:on-click set-grid
|
[:button {:on-click set-grid
|
||||||
:class (dom/classnames
|
:class (dom/classnames
|
||||||
:active (= :grid layout-type))} "Grid"]]
|
:active (= :grid layout-type))} "Grid"]])
|
||||||
[:button.remove-layout {:on-click on-remove-layout} i/minus]]
|
[:button.remove-layout {:on-click on-remove-layout} i/minus]]
|
||||||
|
|
||||||
[:button.add-page {:on-click #(on-add-layout :flex)} i/close])]]
|
[:button.add-page {:on-click #(on-add-layout :flex)} i/close])]]
|
||||||
@ -715,15 +688,15 @@
|
|||||||
:dir dir
|
:dir dir
|
||||||
:saved-dir saved-grid-dir
|
:saved-dir saved-grid-dir
|
||||||
:set-direction #(set-direction dir :grid)
|
:set-direction #(set-direction dir :grid)
|
||||||
:icon? false}])]]
|
:icon? true}])]]
|
||||||
|
|
||||||
(when (= 1 (count ids))
|
(when (= 1 (count ids))
|
||||||
[:div.edit-mode
|
[:div.edit-mode
|
||||||
[:& grid-edit-mode {:id (first ids)}]])]]
|
[:& grid-edit-mode {:id (first ids)}]])]]
|
||||||
|
|
||||||
[:div.layout-row
|
[:div.layout-row
|
||||||
[:div.align-items-grid.row-title "Align"]
|
[:div.align-items-grid.row-title "Items"]
|
||||||
[:div.btn-wrapper.align-grid
|
[:div.btn-wrapper.align-grid-items
|
||||||
[:& align-grid-row {:is-col? false
|
[:& align-grid-row {:is-col? false
|
||||||
:align-items align-items-row
|
:align-items align-items-row
|
||||||
:set-align set-align-grid}]
|
:set-align set-align-grid}]
|
||||||
@ -733,43 +706,181 @@
|
|||||||
:set-align set-align-grid}]]]
|
:set-align set-align-grid}]]]
|
||||||
|
|
||||||
[:div.layout-row
|
[:div.layout-row
|
||||||
[:div.jusfiy-content-grid.row-title "Justify"]
|
[: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-grid-row {:is-col? true
|
||||||
:justify-items grid-justify-content-column
|
:justify-items grid-justify-content-column
|
||||||
:set-justify set-justify-grid}]
|
:set-justify set-justify-grid}]
|
||||||
[:& justify-grid-row {:is-col? false
|
[:& justify-grid-row {:is-col? false
|
||||||
:justify-items grid-justify-content-row
|
:justify-items grid-justify-content-row
|
||||||
:set-justify set-justify-grid}]]]
|
: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
|
;; Default if not grid or flex
|
||||||
nil)))]))
|
nil)))]))
|
||||||
|
|
||||||
|
(mf/defc grid-layout-edition
|
||||||
|
{::mf/wrap [#(mf/memo' % (mf/check-props ["ids" "values"]))]}
|
||||||
|
[{:keys [ids values] :as props}]
|
||||||
|
(let [;; Gap
|
||||||
|
gap-selected? (mf/use-state :none)
|
||||||
|
saved-grid-dir (:layout-grid-dir values)
|
||||||
|
|
||||||
|
set-direction
|
||||||
|
(fn [dir]
|
||||||
|
(st/emit! (dwsl/update-layout ids {:layout-grid-dir dir})))
|
||||||
|
|
||||||
|
set-gap
|
||||||
|
(fn [gap-multiple? type val]
|
||||||
|
(if gap-multiple?
|
||||||
|
(st/emit! (dwsl/update-layout ids {:layout-gap {:row-gap val :column-gap val}}))
|
||||||
|
(st/emit! (dwsl/update-layout ids {:layout-gap {type val}}))))
|
||||||
|
|
||||||
|
;; Padding
|
||||||
|
change-padding-type
|
||||||
|
(fn [type]
|
||||||
|
(st/emit! (dwsl/update-layout ids {:layout-padding-type type})))
|
||||||
|
|
||||||
|
on-padding-change
|
||||||
|
(fn [type prop val]
|
||||||
|
(cond
|
||||||
|
(and (= type :simple) (= prop :p1))
|
||||||
|
(st/emit! (dwsl/update-layout ids {:layout-padding {:p1 val :p3 val}}))
|
||||||
|
|
||||||
|
(and (= type :simple) (= prop :p2))
|
||||||
|
(st/emit! (dwsl/update-layout ids {:layout-padding {:p2 val :p4 val}}))
|
||||||
|
|
||||||
|
:else
|
||||||
|
(st/emit! (dwsl/update-layout ids {:layout-padding {prop val}}))))
|
||||||
|
|
||||||
|
;; Align grid
|
||||||
|
align-items-row (:layout-align-items values)
|
||||||
|
align-items-column (:layout-justify-items values)
|
||||||
|
|
||||||
|
set-items-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-content-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]
|
||||||
|
(let [value (case track-type
|
||||||
|
:auto nil
|
||||||
|
:flex 1
|
||||||
|
:percent 20
|
||||||
|
:fixed 100)]
|
||||||
|
(st/emit! (dwsl/change-layout-track ids type index {:value value
|
||||||
|
:type track-type})))))]
|
||||||
|
|
||||||
|
[:div.element-set
|
||||||
|
[:div.element-set-title
|
||||||
|
[:span "Grid Layout"]]
|
||||||
|
|
||||||
|
[: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)
|
||||||
|
:icon? true}])]
|
||||||
|
|
||||||
|
(when (= 1 (count ids))
|
||||||
|
[:div.edit-mode
|
||||||
|
[:& grid-edit-mode {:id (first ids)}]])]]
|
||||||
|
|
||||||
|
[:div.layout-row
|
||||||
|
[:div.align-items-grid.row-title "Items"]
|
||||||
|
[:div.btn-wrapper.align-grid
|
||||||
|
[:& align-grid-row {:is-col? false
|
||||||
|
:align-items align-items-row
|
||||||
|
:set-align set-items-grid}]
|
||||||
|
|
||||||
|
[:& align-grid-row {:is-col? true
|
||||||
|
:align-items align-items-column
|
||||||
|
:set-align set-items-grid}]]]
|
||||||
|
|
||||||
|
[:div.layout-row
|
||||||
|
[:div.jusfiy-content-grid.row-title "Content"]
|
||||||
|
[:div.btn-wrapper.align-grid
|
||||||
|
[:& justify-grid-row {:is-col? true
|
||||||
|
:justify-items grid-justify-content-column
|
||||||
|
:set-justify set-content-grid}]
|
||||||
|
[:& justify-grid-row {:is-col? false
|
||||||
|
:justify-items grid-justify-content-row
|
||||||
|
:set-justify set-content-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 {: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}]]]))
|
||||||
|
|||||||
@ -177,8 +177,8 @@
|
|||||||
(get-layout-flex-icon :align-self align is-col?)])]))
|
(get-layout-flex-icon :align-self align is-col?)])]))
|
||||||
|
|
||||||
(mf/defc layout-item-menu
|
(mf/defc layout-item-menu
|
||||||
{::mf/wrap [#(mf/memo' % (mf/check-props ["ids" "values" "type" "is-layout-child?"]))]}
|
{::mf/wrap [#(mf/memo' % (mf/check-props ["ids" "values" "type" "is-layout-child?" "is-grid-parent?" "is-flex-parent?"]))]}
|
||||||
[{:keys [ids values is-layout-child? is-layout-container?] :as props}]
|
[{:keys [ids values is-layout-child? is-layout-container? is-grid-parent? is-flex-parent?] :as props}]
|
||||||
|
|
||||||
(let [selection-parents-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))
|
(let [selection-parents-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))
|
||||||
selection-parents (mf/deref selection-parents-ref)
|
selection-parents (mf/deref selection-parents-ref)
|
||||||
@ -188,11 +188,15 @@
|
|||||||
(st/emit! (dwsl/update-layout-child ids {:layout-item-margin-type type})))
|
(st/emit! (dwsl/update-layout-child ids {:layout-item-margin-type type})))
|
||||||
|
|
||||||
align-self (:layout-item-align-self values)
|
align-self (:layout-item-align-self values)
|
||||||
set-align-self (fn [value]
|
|
||||||
(if (= align-self value)
|
set-align-self
|
||||||
(st/emit! (dwsl/update-layout-child ids {:layout-item-align-self nil}))
|
(mf/use-callback
|
||||||
(st/emit! (dwsl/update-layout-child ids {:layout-item-align-self value}))))
|
(mf/deps ids align-self)
|
||||||
|
(fn [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})))))
|
||||||
|
|
||||||
is-absolute? (:layout-item-absolute values)
|
is-absolute? (:layout-item-absolute values)
|
||||||
|
|
||||||
is-col? (every? ctl/col? selection-parents)
|
is-col? (every? ctl/col? selection-parents)
|
||||||
@ -231,9 +235,18 @@
|
|||||||
|
|
||||||
[:div.element-set
|
[:div.element-set
|
||||||
[:div.element-set-title
|
[:div.element-set-title
|
||||||
[:span (if (and is-layout-container? (not is-layout-child?))
|
[:span (cond
|
||||||
|
(and is-layout-container? (not is-layout-child?))
|
||||||
"Flex board"
|
"Flex board"
|
||||||
"Flex element")]]
|
|
||||||
|
is-flex-parent?
|
||||||
|
"Flex element"
|
||||||
|
|
||||||
|
is-grid-parent?
|
||||||
|
"Grid element"
|
||||||
|
|
||||||
|
:else
|
||||||
|
"Layout element")]]
|
||||||
|
|
||||||
[:div.element-set-content.layout-item-menu
|
[:div.element-set-content.layout-item-menu
|
||||||
(when is-layout-child?
|
(when is-layout-child?
|
||||||
@ -272,7 +285,7 @@
|
|||||||
:layout-item-h-sizing (or (:layout-item-h-sizing values) :fix)
|
:layout-item-h-sizing (or (:layout-item-h-sizing values) :fix)
|
||||||
:on-change-behavior on-change-behavior}]]
|
:on-change-behavior on-change-behavior}]]
|
||||||
|
|
||||||
(when is-layout-child?
|
(when (and is-layout-child? is-flex-parent?)
|
||||||
[:div.layout-row
|
[:div.layout-row
|
||||||
[:div.row-title "Align"]
|
[:div.row-title "Align"]
|
||||||
[:div.btn-wrapper
|
[:div.btn-wrapper
|
||||||
|
|||||||
@ -19,6 +19,7 @@
|
|||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.components.dropdown :refer [dropdown]]
|
[app.main.ui.components.dropdown :refer [dropdown]]
|
||||||
[app.main.ui.components.numeric-input :refer [numeric-input]]
|
[app.main.ui.components.numeric-input :refer [numeric-input]]
|
||||||
|
[app.main.ui.hooks :as hooks]
|
||||||
[app.main.ui.icons :as i]
|
[app.main.ui.icons :as i]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
@ -80,6 +81,8 @@
|
|||||||
[shape])
|
[shape])
|
||||||
frames (map #(deref (refs/object-by-id (:frame-id %))) old-shapes)
|
frames (map #(deref (refs/object-by-id (:frame-id %))) old-shapes)
|
||||||
|
|
||||||
|
ids (hooks/use-equal-memo ids)
|
||||||
|
|
||||||
selection-parents-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))
|
selection-parents-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))
|
||||||
selection-parents (mf/deref selection-parents-ref)
|
selection-parents (mf/deref selection-parents-ref)
|
||||||
|
|
||||||
|
|||||||
@ -8,9 +8,11 @@
|
|||||||
(:require
|
(:require
|
||||||
[app.common.types.shape.layout :as ctl]
|
[app.common.types.shape.layout :as ctl]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
|
[app.main.ui.hooks :as hooks]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]]
|
||||||
|
[app.main.ui.workspace.sidebar.options.menus.grid-cell :as grid-cell]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-flex-attrs layout-container-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-flex-attrs layout-container-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-attrs layout-item-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-attrs layout-item-menu]]
|
||||||
@ -30,26 +32,43 @@
|
|||||||
layout-item-values (select-keys shape layout-item-attrs)
|
layout-item-values (select-keys shape layout-item-attrs)
|
||||||
layout-container-values (select-keys shape layout-container-flex-attrs)
|
layout-container-values (select-keys shape layout-container-flex-attrs)
|
||||||
|
|
||||||
is-flex-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-flex-layout-child? ids))
|
is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids))
|
||||||
is-flex-layout-child? (mf/deref is-flex-layout-child-ref)
|
is-layout-child? (mf/deref is-layout-child-ref)
|
||||||
|
|
||||||
is-layout-child-absolute? (ctl/layout-absolute? shape)]
|
is-flex-parent-ref (mf/use-memo (mf/deps ids) #(refs/flex-layout-child? ids))
|
||||||
|
is-flex-parent? (mf/deref is-flex-parent-ref)
|
||||||
|
|
||||||
|
is-grid-parent-ref (mf/use-memo (mf/deps ids) #(refs/grid-layout-child? ids))
|
||||||
|
is-grid-parent? (mf/deref is-grid-parent-ref)
|
||||||
|
|
||||||
|
is-layout-child-absolute? (ctl/layout-absolute? shape)
|
||||||
|
|
||||||
|
ids (hooks/use-equal-memo ids)
|
||||||
|
parents-by-ids-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))
|
||||||
|
parents (mf/deref parents-by-ids-ref)]
|
||||||
[:*
|
[:*
|
||||||
[:& measures-menu {:ids ids
|
[:& measures-menu {:ids ids
|
||||||
:type type
|
:type type
|
||||||
:values measure-values
|
:values measure-values
|
||||||
:shape shape}]
|
:shape shape}]
|
||||||
[:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values :multiple false}]
|
[:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values :multiple false}]
|
||||||
|
|
||||||
|
(when (and (= (count ids) 1) is-layout-child? is-grid-parent?)
|
||||||
|
[:& grid-cell/options
|
||||||
|
{:shape (first parents)
|
||||||
|
:cell (ctl/get-cell-by-shape-id (first parents) (first ids))}])
|
||||||
|
|
||||||
(when is-flex-layout-child?
|
(when is-layout-child?
|
||||||
[:& layout-item-menu
|
[:& layout-item-menu
|
||||||
{:ids ids
|
{:ids ids
|
||||||
:type type
|
:type type
|
||||||
:values layout-item-values
|
:values layout-item-values
|
||||||
:is-layout-child? true
|
:is-layout-child? true
|
||||||
|
:is-flex-parent? is-flex-parent?
|
||||||
|
:is-grid-parent? is-grid-parent?
|
||||||
:shape shape}])
|
:shape shape}])
|
||||||
|
|
||||||
(when (or (not is-flex-layout-child?) is-layout-child-absolute?)
|
(when (or (not is-layout-child?) is-layout-child-absolute?)
|
||||||
[:& constraints-menu {:ids ids
|
[:& constraints-menu {:ids ids
|
||||||
:values constraint-values}])
|
:values constraint-values}])
|
||||||
[:& layer-menu {:ids ids
|
[:& layer-menu {:ids ids
|
||||||
|
|||||||
@ -8,9 +8,11 @@
|
|||||||
(:require
|
(:require
|
||||||
[app.common.types.shape.layout :as ctl]
|
[app.common.types.shape.layout :as ctl]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
|
[app.main.ui.hooks :as hooks]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]]
|
||||||
|
[app.main.ui.workspace.sidebar.options.menus.grid-cell :as grid-cell]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-flex-attrs layout-container-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-flex-attrs layout-container-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-attrs layout-item-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-attrs layout-item-menu]]
|
||||||
@ -32,24 +34,42 @@
|
|||||||
layout-item-values (select-keys shape layout-item-attrs)
|
layout-item-values (select-keys shape layout-item-attrs)
|
||||||
layout-container-values (select-keys shape layout-container-flex-attrs)
|
layout-container-values (select-keys shape layout-container-flex-attrs)
|
||||||
|
|
||||||
is-flex-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-flex-layout-child? ids))
|
is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids))
|
||||||
is-flex-layout-child? (mf/deref is-flex-layout-child-ref)
|
is-layout-child? (mf/deref is-layout-child-ref)
|
||||||
is-layout-child-absolute? (ctl/layout-absolute? shape)]
|
|
||||||
|
is-flex-parent-ref (mf/use-memo (mf/deps ids) #(refs/flex-layout-child? ids))
|
||||||
|
is-flex-parent? (mf/deref is-flex-parent-ref)
|
||||||
|
|
||||||
|
is-grid-parent-ref (mf/use-memo (mf/deps ids) #(refs/grid-layout-child? ids))
|
||||||
|
is-grid-parent? (mf/deref is-grid-parent-ref)
|
||||||
|
|
||||||
|
is-layout-child-absolute? (ctl/layout-absolute? shape)
|
||||||
|
|
||||||
|
ids (hooks/use-equal-memo ids)
|
||||||
|
parents-by-ids-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))
|
||||||
|
parents (mf/deref parents-by-ids-ref)]
|
||||||
[:*
|
[:*
|
||||||
[:& measures-menu {:ids ids
|
[:& measures-menu {:ids ids
|
||||||
:type type
|
:type type
|
||||||
:values measure-values
|
:values measure-values
|
||||||
:shape shape}]
|
:shape shape}]
|
||||||
[:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values :multiple false}]
|
[:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values :multiple false}]
|
||||||
|
|
||||||
(when is-flex-layout-child?
|
(when (and (= (count ids) 1) is-layout-child? is-grid-parent?)
|
||||||
|
[:& grid-cell/options
|
||||||
|
{:shape (first parents)
|
||||||
|
:cell (ctl/get-cell-by-shape-id (first parents) (first ids))}])
|
||||||
|
|
||||||
|
(when is-layout-child?
|
||||||
[:& layout-item-menu {:ids ids
|
[:& layout-item-menu {:ids ids
|
||||||
:type type
|
:type type
|
||||||
:values layout-item-values
|
:values layout-item-values
|
||||||
:is-layout-child? true
|
:is-layout-child? true
|
||||||
:is-layout-container? false
|
:is-layout-container? false
|
||||||
|
:is-flex-parent? is-flex-parent?
|
||||||
|
:is-grid-parent? is-grid-parent?
|
||||||
:shape shape}])
|
:shape shape}])
|
||||||
(when (or (not is-flex-layout-child?) is-layout-child-absolute?)
|
(when (or (not is-layout-child?) is-layout-child-absolute?)
|
||||||
[:& constraints-menu {:ids ids
|
[:& constraints-menu {:ids ids
|
||||||
:values constraint-values}])
|
:values constraint-values}])
|
||||||
[:& layer-menu {:ids ids
|
[:& layer-menu {:ids ids
|
||||||
|
|||||||
@ -9,12 +9,14 @@
|
|||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.types.shape.layout :as ctl]
|
[app.common.types.shape.layout :as ctl]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
|
[app.main.ui.hooks :as hooks]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.color-selection :refer [color-selection-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.color-selection :refer [color-selection-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.component :refer [component-attrs component-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.component :refer [component-attrs component-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs-shape fill-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs-shape fill-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.frame-grid :refer [frame-grid]]
|
[app.main.ui.workspace.sidebar.options.menus.frame-grid :refer [frame-grid]]
|
||||||
|
[app.main.ui.workspace.sidebar.options.menus.grid-cell :as grid-cell]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-flex-attrs layout-container-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-flex-attrs layout-container-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-attrs layout-item-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-attrs layout-item-menu]]
|
||||||
@ -36,10 +38,23 @@
|
|||||||
layout-item-values (select-keys shape layout-item-attrs)
|
layout-item-values (select-keys shape layout-item-attrs)
|
||||||
[comp-ids comp-values] [[(:id shape)] (select-keys shape component-attrs)]
|
[comp-ids comp-values] [[(:id shape)] (select-keys shape component-attrs)]
|
||||||
|
|
||||||
is-flex-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-flex-layout-child? ids))
|
ids (hooks/use-equal-memo ids)
|
||||||
is-flex-layout-child? (mf/deref is-flex-layout-child-ref)
|
|
||||||
is-flex-layout-container? (ctl/flex-layout? shape)
|
is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids))
|
||||||
is-layout-child-absolute? (ctl/layout-absolute? shape)]
|
is-layout-child? (mf/deref is-layout-child-ref)
|
||||||
|
|
||||||
|
is-flex-parent-ref (mf/use-memo (mf/deps ids) #(refs/flex-layout-child? ids))
|
||||||
|
is-flex-parent? (mf/deref is-flex-parent-ref)
|
||||||
|
|
||||||
|
is-grid-parent-ref (mf/use-memo (mf/deps ids) #(refs/grid-layout-child? ids))
|
||||||
|
is-grid-parent? (mf/deref is-grid-parent-ref)
|
||||||
|
|
||||||
|
is-layout-container? (ctl/any-layout? shape)
|
||||||
|
is-layout-child-absolute? (ctl/layout-absolute? shape)
|
||||||
|
|
||||||
|
ids (hooks/use-equal-memo ids)
|
||||||
|
parents-by-ids-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))
|
||||||
|
parents (mf/deref parents-by-ids-ref)]
|
||||||
[:*
|
[:*
|
||||||
[:& measures-menu {:ids [(:id shape)]
|
[:& measures-menu {:ids [(:id shape)]
|
||||||
:values measure-values
|
:values measure-values
|
||||||
@ -48,18 +63,25 @@
|
|||||||
[:& component-menu {:ids comp-ids
|
[:& component-menu {:ids comp-ids
|
||||||
:values comp-values
|
:values comp-values
|
||||||
:shape shape}]
|
:shape shape}]
|
||||||
(when (or (not is-flex-layout-child?) is-layout-child-absolute?)
|
(when (or (not is-layout-child?) is-layout-child-absolute?)
|
||||||
[:& constraints-menu {:ids ids
|
[:& constraints-menu {:ids ids
|
||||||
:values constraint-values}])
|
:values constraint-values}])
|
||||||
[:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values :multiple false}]
|
[:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values :multiple false}]
|
||||||
|
|
||||||
(when (or is-flex-layout-child? is-flex-layout-container?)
|
(when (and (= (count ids) 1) is-layout-child? is-grid-parent?)
|
||||||
|
[:& grid-cell/options
|
||||||
|
{:shape (first parents)
|
||||||
|
:cell (ctl/get-cell-by-shape-id (first parents) (first ids))}])
|
||||||
|
|
||||||
|
(when (or is-layout-child? is-layout-container?)
|
||||||
[:& layout-item-menu
|
[:& layout-item-menu
|
||||||
{:ids ids
|
{:ids ids
|
||||||
:type type
|
:type type
|
||||||
:values layout-item-values
|
:values layout-item-values
|
||||||
:is-layout-child? is-flex-layout-child?
|
:is-flex-parent? is-flex-parent?
|
||||||
:is-layout-container? is-flex-layout-container?
|
:is-grid-parent? is-grid-parent?
|
||||||
|
:is-layout-child? is-layout-child?
|
||||||
|
:is-layout-container? is-layout-container?
|
||||||
:shape shape}])
|
:shape shape}])
|
||||||
|
|
||||||
[:& layer-menu {:ids ids
|
[:& layer-menu {:ids ids
|
||||||
|
|||||||
@ -1,171 +0,0 @@
|
|||||||
;; 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}]]]]]))
|
|
||||||
@ -9,11 +9,13 @@
|
|||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.types.shape.layout :as ctl]
|
[app.common.types.shape.layout :as ctl]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
|
[app.main.ui.hooks :as hooks]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.color-selection :refer [color-selection-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.color-selection :refer [color-selection-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.component :refer [component-attrs component-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.component :refer [component-attrs component-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraints-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraints-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-menu]]
|
||||||
|
[app.main.ui.workspace.sidebar.options.menus.grid-cell :as grid-cell]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-flex-attrs layout-container-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-flex-attrs layout-container-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-menu]]
|
||||||
@ -36,10 +38,21 @@
|
|||||||
file-id (unchecked-get props "file-id")
|
file-id (unchecked-get props "file-id")
|
||||||
layout-container-values (select-keys shape layout-container-flex-attrs)
|
layout-container-values (select-keys shape layout-container-flex-attrs)
|
||||||
ids [(:id shape)]
|
ids [(:id shape)]
|
||||||
is-flex-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-flex-layout-child? ids))
|
is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids))
|
||||||
is-flex-layout-child? (mf/deref is-flex-layout-child-ref)
|
is-layout-child? (mf/deref is-layout-child-ref)
|
||||||
|
|
||||||
|
is-flex-parent-ref (mf/use-memo (mf/deps ids) #(refs/flex-layout-child? ids))
|
||||||
|
is-flex-parent? (mf/deref is-flex-parent-ref)
|
||||||
|
|
||||||
|
is-grid-parent-ref (mf/use-memo (mf/deps ids) #(refs/grid-layout-child? ids))
|
||||||
|
is-grid-parent? (mf/deref is-grid-parent-ref)
|
||||||
|
|
||||||
is-layout-child-absolute? (ctl/layout-absolute? shape)
|
is-layout-child-absolute? (ctl/layout-absolute? shape)
|
||||||
|
|
||||||
|
ids (hooks/use-equal-memo ids)
|
||||||
|
parents-by-ids-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))
|
||||||
|
parents (mf/deref parents-by-ids-ref)
|
||||||
|
|
||||||
type :group
|
type :group
|
||||||
[measure-ids measure-values] (get-attrs [shape] objects :measure)
|
[measure-ids measure-values] (get-attrs [shape] objects :measure)
|
||||||
[layer-ids layer-values] (get-attrs [shape] objects :layer)
|
[layer-ids layer-values] (get-attrs [shape] objects :layer)
|
||||||
@ -58,15 +71,22 @@
|
|||||||
[:& component-menu {:ids comp-ids :values comp-values :shape shape}] ;;remove this in components-v2
|
[:& component-menu {:ids comp-ids :values comp-values :shape shape}] ;;remove this in components-v2
|
||||||
[:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values :multiple false}]
|
[:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values :multiple false}]
|
||||||
|
|
||||||
(when is-flex-layout-child?
|
(when (and (= (count ids) 1) is-layout-child? is-grid-parent?)
|
||||||
|
[:& grid-cell/options
|
||||||
|
{:shape (first parents)
|
||||||
|
:cell (ctl/get-cell-by-shape-id (first parents) (first ids))}])
|
||||||
|
|
||||||
|
(when is-layout-child?
|
||||||
[:& layout-item-menu
|
[:& layout-item-menu
|
||||||
{:type type
|
{:type type
|
||||||
:ids layout-item-ids
|
:ids layout-item-ids
|
||||||
:is-layout-child? true
|
:is-layout-child? true
|
||||||
:is-layout-container? false
|
:is-layout-container? false
|
||||||
|
:is-flex-parent? is-flex-parent?
|
||||||
|
:is-grid-parent? is-grid-parent?
|
||||||
:values layout-item-values}])
|
:values layout-item-values}])
|
||||||
|
|
||||||
(when (or (not is-flex-layout-child?) is-layout-child-absolute?)
|
(when (or (not is-layout-child?) is-layout-child-absolute?)
|
||||||
[:& constraints-menu {:ids constraint-ids :values constraint-values}])
|
[:& constraints-menu {:ids constraint-ids :values constraint-values}])
|
||||||
|
|
||||||
[:& layer-menu {:type type :ids layer-ids :values layer-values}]
|
[:& layer-menu {:type type :ids layer-ids :values layer-values}]
|
||||||
|
|||||||
@ -8,9 +8,11 @@
|
|||||||
(:require
|
(:require
|
||||||
[app.common.types.shape.layout :as ctl]
|
[app.common.types.shape.layout :as ctl]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
|
[app.main.ui.hooks :as hooks]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]]
|
||||||
|
[app.main.ui.workspace.sidebar.options.menus.grid-cell :as grid-cell]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-flex-attrs layout-container-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-flex-attrs layout-container-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-attrs layout-item-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-attrs layout-item-menu]]
|
||||||
@ -32,25 +34,43 @@
|
|||||||
layout-item-values (select-keys shape layout-item-attrs)
|
layout-item-values (select-keys shape layout-item-attrs)
|
||||||
layout-container-values (select-keys shape layout-container-flex-attrs)
|
layout-container-values (select-keys shape layout-container-flex-attrs)
|
||||||
|
|
||||||
is-flex-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-flex-layout-child? ids))
|
is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids))
|
||||||
is-flex-layout-child? (mf/deref is-flex-layout-child-ref)
|
is-layout-child? (mf/deref is-layout-child-ref)
|
||||||
is-layout-child-absolute? (ctl/layout-absolute? shape)]
|
|
||||||
|
is-flex-parent-ref (mf/use-memo (mf/deps ids) #(refs/flex-layout-child? ids))
|
||||||
|
is-flex-parent? (mf/deref is-flex-parent-ref)
|
||||||
|
|
||||||
|
is-grid-parent-ref (mf/use-memo (mf/deps ids) #(refs/grid-layout-child? ids))
|
||||||
|
is-grid-parent? (mf/deref is-grid-parent-ref)
|
||||||
|
|
||||||
|
is-layout-child-absolute? (ctl/layout-absolute? shape)
|
||||||
|
|
||||||
|
ids (hooks/use-equal-memo ids)
|
||||||
|
parents-by-ids-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))
|
||||||
|
parents (mf/deref parents-by-ids-ref)]
|
||||||
[:*
|
[:*
|
||||||
[:& measures-menu {:ids ids
|
[:& measures-menu {:ids ids
|
||||||
:type type
|
:type type
|
||||||
:values measure-values
|
:values measure-values
|
||||||
:shape shape}]
|
:shape shape}]
|
||||||
[:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values :multiple false}]
|
[:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values :multiple false}]
|
||||||
|
|
||||||
|
(when (and (= (count ids) 1) is-layout-child? is-grid-parent?)
|
||||||
|
[:& grid-cell/options
|
||||||
|
{:shape (first parents)
|
||||||
|
:cell (ctl/get-cell-by-shape-id (first parents) (first ids))}])
|
||||||
|
|
||||||
(when is-flex-layout-child?
|
(when is-layout-child?
|
||||||
[:& layout-item-menu
|
[:& layout-item-menu
|
||||||
{:ids ids
|
{:ids ids
|
||||||
:type type
|
:type type
|
||||||
:values layout-item-values
|
:values layout-item-values
|
||||||
:is-layout-child? true
|
:is-layout-child? true
|
||||||
|
:is-flex-parent? is-flex-parent?
|
||||||
|
:is-grid-parent? is-grid-parent?
|
||||||
:shape shape}])
|
:shape shape}])
|
||||||
|
|
||||||
(when (or (not is-flex-layout-child?) is-layout-child-absolute?)
|
(when (or (not is-layout-child?) is-layout-child-absolute?)
|
||||||
[:& constraints-menu {:ids ids
|
[:& constraints-menu {:ids ids
|
||||||
:values constraint-values}])
|
:values constraint-values}])
|
||||||
|
|
||||||
|
|||||||
@ -294,15 +294,21 @@
|
|||||||
all-types (into #{} (map :type shapes))
|
all-types (into #{} (map :type shapes))
|
||||||
|
|
||||||
ids (->> shapes (map :id))
|
ids (->> shapes (map :id))
|
||||||
is-flex-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-flex-layout-child? ids))
|
is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids))
|
||||||
is-flex-layout-child? (mf/deref is-flex-layout-child-ref)
|
is-layout-child? (mf/deref is-layout-child-ref)
|
||||||
|
|
||||||
|
is-flex-parent-ref (mf/use-memo (mf/deps ids) #(refs/flex-layout-child? ids))
|
||||||
|
is-flex-parent? (mf/deref is-flex-parent-ref)
|
||||||
|
|
||||||
|
is-grid-parent-ref (mf/use-memo (mf/deps ids) #(refs/grid-layout-child? ids))
|
||||||
|
is-grid-parent? (mf/deref is-grid-parent-ref)
|
||||||
|
|
||||||
has-text? (contains? all-types :text)
|
has-text? (contains? all-types :text)
|
||||||
|
|
||||||
has-flex-layout-container? (->> shapes (some ctl/flex-layout?))
|
has-flex-layout-container? (->> shapes (some ctl/flex-layout?))
|
||||||
|
|
||||||
all-flex-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/all-flex-layout-child? ids))
|
all-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/all-layout-child? ids))
|
||||||
all-flex-layout-child? (mf/deref all-flex-layout-child-ref)
|
all-layout-child? (mf/deref all-layout-child-ref)
|
||||||
|
|
||||||
all-flex-layout-container? (->> shapes (every? ctl/flex-layout?))
|
all-flex-layout-container? (->> shapes (every? ctl/flex-layout?))
|
||||||
|
|
||||||
@ -342,15 +348,17 @@
|
|||||||
|
|
||||||
[:& layout-container-menu {:type type :ids layout-container-ids :values layout-container-values :multiple true}]
|
[:& layout-container-menu {:type type :ids layout-container-ids :values layout-container-values :multiple true}]
|
||||||
|
|
||||||
(when (or is-flex-layout-child? has-flex-layout-container?)
|
(when (or is-layout-child? has-flex-layout-container?)
|
||||||
[:& layout-item-menu
|
[:& layout-item-menu
|
||||||
{:type type
|
{:type type
|
||||||
:ids layout-item-ids
|
:ids layout-item-ids
|
||||||
:is-layout-child? all-flex-layout-child?
|
:is-layout-child? all-layout-child?
|
||||||
:is-layout-container? all-flex-layout-container?
|
:is-layout-container? all-flex-layout-container?
|
||||||
|
:is-flex-parent? is-flex-parent?
|
||||||
|
:is-grid-parent? is-grid-parent?
|
||||||
:values layout-item-values}])
|
:values layout-item-values}])
|
||||||
|
|
||||||
(when-not (or (empty? constraint-ids) is-flex-layout-child?)
|
(when-not (or (empty? constraint-ids) is-layout-child?)
|
||||||
[:& constraints-menu {:ids constraint-ids :values constraint-values}])
|
[:& constraints-menu {:ids constraint-ids :values constraint-values}])
|
||||||
|
|
||||||
(when-not (empty? layer-ids)
|
(when-not (empty? layer-ids)
|
||||||
|
|||||||
@ -8,9 +8,11 @@
|
|||||||
(:require
|
(:require
|
||||||
[app.common.types.shape.layout :as ctl]
|
[app.common.types.shape.layout :as ctl]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
|
[app.main.ui.hooks :as hooks]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]]
|
||||||
|
[app.main.ui.workspace.sidebar.options.menus.grid-cell :as grid-cell]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-flex-attrs layout-container-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-flex-attrs layout-container-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-attrs layout-item-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-attrs layout-item-menu]]
|
||||||
@ -32,9 +34,20 @@
|
|||||||
layout-item-values (select-keys shape layout-item-attrs)
|
layout-item-values (select-keys shape layout-item-attrs)
|
||||||
layout-container-values (select-keys shape layout-container-flex-attrs)
|
layout-container-values (select-keys shape layout-container-flex-attrs)
|
||||||
|
|
||||||
is-flex-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-flex-layout-child? ids))
|
is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids))
|
||||||
is-flex-layout-child? (mf/deref is-flex-layout-child-ref)
|
is-layout-child? (mf/deref is-layout-child-ref)
|
||||||
is-layout-child-absolute? (ctl/layout-absolute? shape)]
|
|
||||||
|
is-flex-parent-ref (mf/use-memo (mf/deps ids) #(refs/flex-layout-child? ids))
|
||||||
|
is-flex-parent? (mf/deref is-flex-parent-ref)
|
||||||
|
|
||||||
|
is-grid-parent-ref (mf/use-memo (mf/deps ids) #(refs/grid-layout-child? ids))
|
||||||
|
is-grid-parent? (mf/deref is-grid-parent-ref)
|
||||||
|
|
||||||
|
is-layout-child-absolute? (ctl/layout-absolute? shape)
|
||||||
|
|
||||||
|
ids (hooks/use-equal-memo ids)
|
||||||
|
parents-by-ids-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))
|
||||||
|
parents (mf/deref parents-by-ids-ref)]
|
||||||
[:*
|
[:*
|
||||||
[:& measures-menu {:ids ids
|
[:& measures-menu {:ids ids
|
||||||
:type type
|
:type type
|
||||||
@ -42,14 +55,21 @@
|
|||||||
:shape shape}]
|
:shape shape}]
|
||||||
[:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values :multiple false}]
|
[:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values :multiple false}]
|
||||||
|
|
||||||
(when is-flex-layout-child?
|
(when (and (= (count ids) 1) is-layout-child? is-grid-parent?)
|
||||||
|
[:& grid-cell/options
|
||||||
|
{:shape (first parents)
|
||||||
|
:cell (ctl/get-cell-by-shape-id (first parents) (first ids))}])
|
||||||
|
|
||||||
|
(when is-layout-child?
|
||||||
[:& layout-item-menu {:ids ids
|
[:& layout-item-menu {:ids ids
|
||||||
:type type
|
:type type
|
||||||
:values layout-item-values
|
:values layout-item-values
|
||||||
:is-layout-child? true
|
:is-layout-child? true
|
||||||
:is-layout-container? false
|
:is-layout-container? false
|
||||||
|
:is-flex-parent? is-flex-parent?
|
||||||
|
:is-grid-parent? is-grid-parent?
|
||||||
:shape shape}])
|
:shape shape}])
|
||||||
(when (or (not is-flex-layout-child?) is-layout-child-absolute?)
|
(when (or (not is-layout-child?) is-layout-child-absolute?)
|
||||||
[:& constraints-menu {:ids ids
|
[:& constraints-menu {:ids ids
|
||||||
:values constraint-values}])
|
:values constraint-values}])
|
||||||
[:& layer-menu {:ids ids
|
[:& layer-menu {:ids ids
|
||||||
|
|||||||
@ -8,9 +8,11 @@
|
|||||||
(:require
|
(:require
|
||||||
[app.common.types.shape.layout :as ctl]
|
[app.common.types.shape.layout :as ctl]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
|
[app.main.ui.hooks :as hooks]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]]
|
||||||
|
[app.main.ui.workspace.sidebar.options.menus.grid-cell :as grid-cell]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-flex-attrs layout-container-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-flex-attrs layout-container-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-attrs layout-item-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-attrs layout-item-menu]]
|
||||||
@ -32,24 +34,43 @@
|
|||||||
stroke-values (select-keys shape stroke-attrs)
|
stroke-values (select-keys shape stroke-attrs)
|
||||||
layout-item-values (select-keys shape layout-item-attrs)
|
layout-item-values (select-keys shape layout-item-attrs)
|
||||||
layout-container-values (select-keys shape layout-container-flex-attrs)
|
layout-container-values (select-keys shape layout-container-flex-attrs)
|
||||||
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-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids))
|
||||||
is-layout-child-absolute? (ctl/layout-absolute? shape)]
|
is-layout-child? (mf/deref is-layout-child-ref)
|
||||||
|
|
||||||
|
is-flex-parent-ref (mf/use-memo (mf/deps ids) #(refs/flex-layout-child? ids))
|
||||||
|
is-flex-parent? (mf/deref is-flex-parent-ref)
|
||||||
|
|
||||||
|
is-grid-parent-ref (mf/use-memo (mf/deps ids) #(refs/grid-layout-child? ids))
|
||||||
|
is-grid-parent? (mf/deref is-grid-parent-ref)
|
||||||
|
is-layout-child-absolute? (ctl/layout-absolute? shape)
|
||||||
|
|
||||||
|
ids (hooks/use-equal-memo ids)
|
||||||
|
parents-by-ids-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))
|
||||||
|
parents (mf/deref parents-by-ids-ref)]
|
||||||
[:*
|
[:*
|
||||||
[:& measures-menu {:ids ids
|
[:& measures-menu {:ids ids
|
||||||
:type type
|
:type type
|
||||||
:values measure-values
|
:values measure-values
|
||||||
:shape shape}]
|
:shape shape}]
|
||||||
[:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values :multiple false}]
|
[:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values :multiple false}]
|
||||||
(when is-flex-layout-child?
|
|
||||||
|
(when (and (= (count ids) 1) is-layout-child? is-grid-parent?)
|
||||||
|
[:& grid-cell/options
|
||||||
|
{:shape (first parents)
|
||||||
|
:cell (ctl/get-cell-by-shape-id (first parents) (first ids))}])
|
||||||
|
|
||||||
|
(when is-layout-child?
|
||||||
[:& layout-item-menu
|
[:& layout-item-menu
|
||||||
{:ids ids
|
{:ids ids
|
||||||
:type type
|
:type type
|
||||||
:values layout-item-values
|
:values layout-item-values
|
||||||
:is-layout-child? true
|
:is-layout-child? true
|
||||||
|
:is-flex-parent? is-flex-parent?
|
||||||
|
:is-grid-parent? is-grid-parent?
|
||||||
:shape shape}])
|
:shape shape}])
|
||||||
|
|
||||||
(when (or (not is-flex-layout-child?) is-layout-child-absolute?)
|
(when (or (not is-layout-child?) is-layout-child-absolute?)
|
||||||
[:& constraints-menu {:ids ids
|
[:& constraints-menu {:ids ids
|
||||||
:values constraint-values}])
|
:values constraint-values}])
|
||||||
|
|
||||||
|
|||||||
@ -10,9 +10,11 @@
|
|||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.types.shape.layout :as ctl]
|
[app.common.types.shape.layout :as ctl]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
|
[app.main.ui.hooks :as hooks]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]]
|
||||||
|
[app.main.ui.workspace.sidebar.options.menus.grid-cell :as grid-cell]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-flex-attrs layout-container-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-flex-attrs layout-container-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-attrs layout-item-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-attrs layout-item-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.measures :refer [measure-attrs measures-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.measures :refer [measure-attrs measures-menu]]
|
||||||
@ -106,9 +108,20 @@
|
|||||||
layout-item-values (select-keys shape layout-item-attrs)
|
layout-item-values (select-keys shape layout-item-attrs)
|
||||||
layout-container-values (select-keys shape layout-container-flex-attrs)
|
layout-container-values (select-keys shape layout-container-flex-attrs)
|
||||||
|
|
||||||
is-flex-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-flex-layout-child? ids))
|
is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids))
|
||||||
is-flex-layout-child? (mf/deref is-flex-layout-child-ref)
|
is-layout-child? (mf/deref is-layout-child-ref)
|
||||||
is-layout-child-absolute? (ctl/layout-absolute? shape)]
|
|
||||||
|
is-flex-parent-ref (mf/use-memo (mf/deps ids) #(refs/flex-layout-child? ids))
|
||||||
|
is-flex-parent? (mf/deref is-flex-parent-ref)
|
||||||
|
|
||||||
|
is-grid-parent-ref (mf/use-memo (mf/deps ids) #(refs/grid-layout-child? ids))
|
||||||
|
is-grid-parent? (mf/deref is-grid-parent-ref)
|
||||||
|
|
||||||
|
is-layout-child-absolute? (ctl/layout-absolute? shape)
|
||||||
|
|
||||||
|
ids (hooks/use-equal-memo ids)
|
||||||
|
parents-by-ids-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))
|
||||||
|
parents (mf/deref parents-by-ids-ref)]
|
||||||
|
|
||||||
(when (contains? svg-elements tag)
|
(when (contains? svg-elements tag)
|
||||||
[:*
|
[:*
|
||||||
@ -118,15 +131,22 @@
|
|||||||
:shape shape}]
|
:shape shape}]
|
||||||
[:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values :multiple false}]
|
[:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values :multiple false}]
|
||||||
|
|
||||||
(when is-flex-layout-child?
|
(when (and (= (count ids) 1) is-layout-child? is-grid-parent?)
|
||||||
|
[:& grid-cell/options
|
||||||
|
{:shape (first parents)
|
||||||
|
:cell (ctl/get-cell-by-shape-id (first parents) (first ids))}])
|
||||||
|
|
||||||
|
(when is-layout-child?
|
||||||
[:& layout-item-menu
|
[:& layout-item-menu
|
||||||
{:ids ids
|
{:ids ids
|
||||||
:type type
|
:type type
|
||||||
:values layout-item-values
|
:values layout-item-values
|
||||||
:is-layout-child? true
|
:is-layout-child? true
|
||||||
|
:is-flex-parent? is-flex-parent?
|
||||||
|
:is-grid-parent? is-grid-parent?
|
||||||
:shape shape}])
|
:shape shape}])
|
||||||
|
|
||||||
(when (or (not is-flex-layout-child?) is-layout-child-absolute?)
|
(when (or (not is-layout-child?) is-layout-child-absolute?)
|
||||||
[:& constraints-menu {:ids ids
|
[:& constraints-menu {:ids ids
|
||||||
:values constraint-values}])
|
:values constraint-values}])
|
||||||
|
|
||||||
|
|||||||
@ -10,10 +10,12 @@
|
|||||||
[app.common.types.shape.layout :as ctl]
|
[app.common.types.shape.layout :as ctl]
|
||||||
[app.main.data.workspace.texts :as dwt :refer [text-fill-attrs root-attrs paragraph-attrs text-attrs]]
|
[app.main.data.workspace.texts :as dwt :refer [text-fill-attrs root-attrs paragraph-attrs text-attrs]]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
|
[app.main.ui.hooks :as hooks]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.color-selection :refer [color-selection-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.color-selection :refer [color-selection-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-menu fill-attrs]]
|
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-menu fill-attrs]]
|
||||||
|
[app.main.ui.workspace.sidebar.options.menus.grid-cell :as grid-cell]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-flex-attrs layout-container-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.layout-container :refer [layout-container-flex-attrs layout-container-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-attrs layout-item-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.layout-item :refer [layout-item-attrs layout-item-menu]]
|
||||||
@ -28,10 +30,22 @@
|
|||||||
(let [ids [(:id shape)]
|
(let [ids [(:id shape)]
|
||||||
type (:type shape)
|
type (:type shape)
|
||||||
|
|
||||||
is-flex-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-flex-layout-child? ids))
|
is-layout-child-ref (mf/use-memo (mf/deps ids) #(refs/is-layout-child? ids))
|
||||||
is-flex-layout-child? (mf/deref is-flex-layout-child-ref)
|
is-layout-child? (mf/deref is-layout-child-ref)
|
||||||
|
|
||||||
|
is-flex-parent-ref (mf/use-memo (mf/deps ids) #(refs/flex-layout-child? ids))
|
||||||
|
is-flex-parent? (mf/deref is-flex-parent-ref)
|
||||||
|
|
||||||
|
is-grid-parent-ref (mf/use-memo (mf/deps ids) #(refs/grid-layout-child? ids))
|
||||||
|
is-grid-parent? (mf/deref is-grid-parent-ref)
|
||||||
|
|
||||||
layout-container-values (select-keys shape layout-container-flex-attrs)
|
layout-container-values (select-keys shape layout-container-flex-attrs)
|
||||||
is-layout-child-absolute? (ctl/layout-absolute? shape)
|
is-layout-child-absolute? (ctl/layout-absolute? shape)
|
||||||
|
|
||||||
|
ids (hooks/use-equal-memo ids)
|
||||||
|
parents-by-ids-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))
|
||||||
|
parents (mf/deref parents-by-ids-ref)
|
||||||
|
|
||||||
state-map (mf/deref refs/workspace-editor-state)
|
state-map (mf/deref refs/workspace-editor-state)
|
||||||
shared-libs (mf/deref refs/workspace-libraries)
|
shared-libs (mf/deref refs/workspace-libraries)
|
||||||
|
|
||||||
@ -76,15 +90,22 @@
|
|||||||
:shape shape}]
|
:shape shape}]
|
||||||
[:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values :multiple false}]
|
[:& layout-container-menu {:type type :ids [(:id shape)] :values layout-container-values :multiple false}]
|
||||||
|
|
||||||
(when is-flex-layout-child?
|
(when (and (= (count ids) 1) is-layout-child? is-grid-parent?)
|
||||||
|
[:& grid-cell/options
|
||||||
|
{:shape (first parents)
|
||||||
|
:cell (ctl/get-cell-by-shape-id (first parents) (first ids))}])
|
||||||
|
|
||||||
|
(when is-layout-child?
|
||||||
[:& layout-item-menu
|
[:& layout-item-menu
|
||||||
{:ids ids
|
{:ids ids
|
||||||
:type type
|
:type type
|
||||||
:values layout-item-values
|
:values layout-item-values
|
||||||
:is-layout-child? true
|
:is-layout-child? true
|
||||||
|
:is-flex-parent? is-flex-parent?
|
||||||
|
:is-grid-parent? is-grid-parent?
|
||||||
:shape shape}])
|
:shape shape}])
|
||||||
|
|
||||||
(when (or (not is-flex-layout-child?) is-layout-child-absolute?)
|
(when (or (not is-layout-child?) is-layout-child-absolute?)
|
||||||
[:& constraints-menu
|
[:& constraints-menu
|
||||||
{:ids ids
|
{:ids ids
|
||||||
:values (select-keys shape constraint-attrs)}])
|
:values (select-keys shape constraint-attrs)}])
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
[app.common.pages.helpers :as cph]
|
[app.common.pages.helpers :as cph]
|
||||||
|
[app.common.types.shape-tree :as ctt]
|
||||||
[app.common.types.shape.layout :as ctl]
|
[app.common.types.shape.layout :as ctl]
|
||||||
[app.main.data.workspace.modifiers :as dwm]
|
[app.main.data.workspace.modifiers :as dwm]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
@ -159,7 +160,7 @@
|
|||||||
create-comment? (= :comments drawing-tool)
|
create-comment? (= :comments drawing-tool)
|
||||||
drawing-path? (or (and edition (= :draw (get-in edit-path [edition :edit-mode])))
|
drawing-path? (or (and edition (= :draw (get-in edit-path [edition :edit-mode])))
|
||||||
(and (some? drawing-obj) (= :path (:type drawing-obj))))
|
(and (some? drawing-obj) (= :path (:type drawing-obj))))
|
||||||
node-editing? (and edition (not= :text (get-in base-objects [edition :type])))
|
node-editing? (and edition (= :path (get-in base-objects [edition :type])))
|
||||||
text-editing? (and edition (= :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))
|
grid-editing? (and edition (ctl/grid-layout? base-objects edition))
|
||||||
|
|
||||||
@ -168,7 +169,7 @@
|
|||||||
|
|
||||||
on-click (actions/on-click hover selected edition drawing-path? drawing-tool space? selrect z?)
|
on-click (actions/on-click hover selected edition drawing-path? drawing-tool space? selrect z?)
|
||||||
on-context-menu (actions/on-context-menu hover hover-ids workspace-read-only?)
|
on-context-menu (actions/on-context-menu hover hover-ids workspace-read-only?)
|
||||||
on-double-click (actions/on-double-click hover hover-ids drawing-path? base-objects edition drawing-tool z? 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-enter (actions/on-drag-enter)
|
||||||
on-drag-over (actions/on-drag-over)
|
on-drag-over (actions/on-drag-over)
|
||||||
on-drop (actions/on-drop file)
|
on-drop (actions/on-drop file)
|
||||||
@ -203,6 +204,10 @@
|
|||||||
show-pixel-grid? (and (contains? layout :show-pixel-grid)
|
show-pixel-grid? (and (contains? layout :show-pixel-grid)
|
||||||
(>= zoom 8))
|
(>= zoom 8))
|
||||||
show-text-editor? (and editing-shape (= :text (:type editing-shape)))
|
show-text-editor? (and editing-shape (= :text (:type editing-shape)))
|
||||||
|
|
||||||
|
hover-grid? (and (some? @hover-top-frame-id)
|
||||||
|
(ctl/grid-layout? objects @hover-top-frame-id))
|
||||||
|
|
||||||
show-grid-editor? (and editing-shape (ctl/grid-layout? editing-shape))
|
show-grid-editor? (and editing-shape (ctl/grid-layout? editing-shape))
|
||||||
show-presence? page-id
|
show-presence? page-id
|
||||||
show-prototypes? (= options-mode :prototype)
|
show-prototypes? (= options-mode :prototype)
|
||||||
@ -248,7 +253,9 @@
|
|||||||
|
|
||||||
offset-y (if selecting-first-level-frame?
|
offset-y (if selecting-first-level-frame?
|
||||||
(:y (first selected-shapes))
|
(:y (first selected-shapes))
|
||||||
(:y selected-frame))]
|
(:y selected-frame))
|
||||||
|
|
||||||
|
rule-area-size (/ rules/rule-area-size zoom)]
|
||||||
|
|
||||||
(hooks/setup-dom-events zoom disable-paste in-viewport? workspace-read-only?)
|
(hooks/setup-dom-events zoom disable-paste in-viewport? workspace-read-only?)
|
||||||
(hooks/setup-viewport-size vport viewport-ref)
|
(hooks/setup-viewport-size vport viewport-ref)
|
||||||
@ -259,7 +266,7 @@
|
|||||||
(hooks/setup-shortcuts node-editing? drawing-path? text-editing?)
|
(hooks/setup-shortcuts node-editing? drawing-path? text-editing?)
|
||||||
(hooks/setup-active-frames base-objects hover-ids selected active-frames zoom transform vbox)
|
(hooks/setup-active-frames base-objects hover-ids selected active-frames zoom transform vbox)
|
||||||
|
|
||||||
[:div.viewport
|
[:div.viewport {:style #js {"--zoom" zoom}}
|
||||||
[:div.viewport-overlays
|
[:div.viewport-overlays
|
||||||
;; The behaviour inside a foreign object is a bit different that in plain HTML so we wrap
|
;; The behaviour inside a foreign object is a bit different that in plain HTML so we wrap
|
||||||
;; inside a foreign object "dummy" so this awkward behaviour is take into account
|
;; inside a foreign object "dummy" so this awkward behaviour is take into account
|
||||||
@ -346,6 +353,14 @@
|
|||||||
:on-pointer-move on-pointer-move
|
:on-pointer-move on-pointer-move
|
||||||
:on-pointer-up on-pointer-up}
|
:on-pointer-up on-pointer-up}
|
||||||
|
|
||||||
|
[:defs
|
||||||
|
;; This clip is so the handlers are not over the rules
|
||||||
|
[:clipPath {:id "clip-handlers"}
|
||||||
|
[:rect {:x (+ (:x vbox) rule-area-size)
|
||||||
|
:y (+ (:y vbox) rule-area-size)
|
||||||
|
:width (max 0 (- (:width vbox) (* rule-area-size 2)))
|
||||||
|
:height (max 0 (- (:height vbox) (* rule-area-size 2)))}]]]
|
||||||
|
|
||||||
[:g {:style {:pointer-events (if disable-events? "none" "auto")}}
|
[:g {:style {:pointer-events (if disable-events? "none" "auto")}}
|
||||||
(when show-text-editor?
|
(when show-text-editor?
|
||||||
[:& editor/text-editor-svg {:shape editing-shape
|
[:& editor/text-editor-svg {:shape editing-shape
|
||||||
@ -556,15 +571,6 @@
|
|||||||
|
|
||||||
(when show-selection-handlers?
|
(when show-selection-handlers?
|
||||||
[:g.selection-handlers {:clipPath "url(#clip-handlers)"}
|
[:g.selection-handlers {:clipPath "url(#clip-handlers)"}
|
||||||
[:defs
|
|
||||||
(let [rule-area-size (/ rules/rule-area-size zoom)]
|
|
||||||
;; This clip is so the handlers are not over the rules
|
|
||||||
[:clipPath {:id "clip-handlers"}
|
|
||||||
[:rect {:x (+ (:x vbox) rule-area-size)
|
|
||||||
:y (+ (:y vbox) rule-area-size)
|
|
||||||
:width (max 0 (- (:width vbox) (* rule-area-size 2)))
|
|
||||||
:height (max 0 (- (:height vbox) (* rule-area-size 2)))}]])]
|
|
||||||
|
|
||||||
[:& selection/selection-handlers
|
[:& selection/selection-handlers
|
||||||
{:selected selected
|
{:selected selected
|
||||||
:shapes selected-shapes
|
:shapes selected-shapes
|
||||||
@ -586,8 +592,24 @@
|
|||||||
{:id (first selected)
|
{:id (first selected)
|
||||||
:zoom zoom}])
|
:zoom zoom}])
|
||||||
|
|
||||||
(when show-grid-editor?
|
[:g.grid-layout-editor {:clipPath "url(#clip-handlers)"}
|
||||||
[:& grid-layout/editor
|
(when (or show-grid-editor? hover-grid?)
|
||||||
{:zoom zoom
|
[:& grid-layout/editor
|
||||||
:objects base-objects
|
{:zoom zoom
|
||||||
:shape (get base-objects edition)}])]]]))
|
:objects base-objects
|
||||||
|
:modifiers modifiers
|
||||||
|
:shape (or (get base-objects edition)
|
||||||
|
(get base-objects @hover-top-frame-id))
|
||||||
|
:view-only (not show-grid-editor?)}])
|
||||||
|
|
||||||
|
(for [frame (ctt/get-frames objects)]
|
||||||
|
(when (and (ctl/grid-layout? frame)
|
||||||
|
(empty? (:shapes frame))
|
||||||
|
(not= edition (:id frame))
|
||||||
|
(not= @hover-top-frame-id (:id frame)))
|
||||||
|
[:& grid-layout/editor
|
||||||
|
{:zoom zoom
|
||||||
|
:objects base-objects
|
||||||
|
:modifiers modifiers
|
||||||
|
:shape frame
|
||||||
|
:view-only true}]))]]]]))
|
||||||
|
|||||||
@ -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
|
;; 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/.
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
;;
|
;;
|
||||||
@ -6,9 +6,11 @@
|
|||||||
|
|
||||||
(ns app.main.ui.workspace.viewport.actions
|
(ns app.main.ui.workspace.viewport.actions
|
||||||
(:require
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.math :as mth]
|
[app.common.math :as mth]
|
||||||
[app.common.pages.helpers :as cph]
|
[app.common.pages.helpers :as cph]
|
||||||
|
[app.common.types.shape.layout :as ctl]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.config :as cfg]
|
[app.config :as cfg]
|
||||||
[app.main.data.workspace :as dw]
|
[app.main.data.workspace :as dw]
|
||||||
@ -53,9 +55,9 @@
|
|||||||
(.setPointerCapture editor (.-pointerId bevent))
|
(.setPointerCapture editor (.-pointerId bevent))
|
||||||
(.setPointerCapture target (.-pointerId bevent))))
|
(.setPointerCapture target (.-pointerId bevent))))
|
||||||
|
|
||||||
|
|
||||||
(when (or (dom/class? (dom/get-target bevent) "viewport-controls")
|
(when (or (dom/class? (dom/get-target bevent) "viewport-controls")
|
||||||
(dom/class? (dom/get-target bevent) "viewport-selrect"))
|
(dom/child? (dom/get-target bevent) (dom/query ".viewport-controls")))
|
||||||
|
|
||||||
(dom/stop-propagation bevent)
|
(dom/stop-propagation bevent)
|
||||||
|
|
||||||
(when-not @z?
|
(when-not @z?
|
||||||
@ -79,7 +81,6 @@
|
|||||||
(st/emit! (dw/start-zooming pt)))
|
(st/emit! (dw/start-zooming pt)))
|
||||||
(st/emit! (dw/start-panning))))
|
(st/emit! (dw/start-panning))))
|
||||||
|
|
||||||
|
|
||||||
left-click?
|
left-click?
|
||||||
(do
|
(do
|
||||||
(st/emit! (ms/->MouseEvent :down ctrl? shift? alt? meta?))
|
(st/emit! (ms/->MouseEvent :down ctrl? shift? alt? meta?))
|
||||||
@ -160,6 +161,7 @@
|
|||||||
(fn [event]
|
(fn [event]
|
||||||
(when (and (nil? selrect)
|
(when (and (nil? selrect)
|
||||||
(or (dom/class? (dom/get-target event) "viewport-controls")
|
(or (dom/class? (dom/get-target event) "viewport-controls")
|
||||||
|
(dom/child? (dom/get-target event) (dom/query ".viewport-controls"))
|
||||||
(dom/class? (dom/get-target event) "viewport-selrect")))
|
(dom/class? (dom/get-target event) "viewport-selrect")))
|
||||||
(let [ctrl? (kbd/ctrl? event)
|
(let [ctrl? (kbd/ctrl? event)
|
||||||
shift? (kbd/shift? event)
|
shift? (kbd/shift? event)
|
||||||
@ -187,10 +189,10 @@
|
|||||||
(st/emit! (dw/increase-zoom pt)))))))))
|
(st/emit! (dw/increase-zoom pt)))))))))
|
||||||
|
|
||||||
(defn on-double-click
|
(defn on-double-click
|
||||||
[hover hover-ids drawing-path? objects edition drawing-tool z? workspace-read-only?]
|
[hover hover-ids hover-top-frame-id drawing-path? objects edition drawing-tool z? workspace-read-only?]
|
||||||
|
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
(mf/deps @hover @hover-ids drawing-path? edition drawing-tool @z? workspace-read-only?)
|
(mf/deps @hover @hover-ids @hover-top-frame-id drawing-path? edition drawing-tool @z? workspace-read-only?)
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
(when-not @z?
|
(when-not @z?
|
||||||
@ -201,11 +203,16 @@
|
|||||||
|
|
||||||
{:keys [id type] :as shape} (or @hover (get objects (first @hover-ids)))
|
{:keys [id type] :as shape} (or @hover (get objects (first @hover-ids)))
|
||||||
|
|
||||||
editable? (contains? #{:text :rect :path :image :circle} type)]
|
editable? (contains? #{:text :rect :path :image :circle} type)
|
||||||
|
|
||||||
|
hover-shape (->> @hover-ids (filter (partial cph/is-child? objects id)) first)
|
||||||
|
selected-shape (get objects hover-shape)
|
||||||
|
|
||||||
|
grid-layout-id (->> @hover-ids reverse (d/seek (partial ctl/grid-layout? objects)))]
|
||||||
|
|
||||||
(st/emit! (ms/->MouseEvent :double-click ctrl? shift? alt? meta?))
|
(st/emit! (ms/->MouseEvent :double-click ctrl? shift? alt? meta?))
|
||||||
|
|
||||||
;; Emit asynchronously so the double click to exit shapes won't break
|
;; Emit asynchronously so the double click to exit shapes won't break
|
||||||
(timers/schedule
|
(timers/schedule
|
||||||
(fn []
|
(fn []
|
||||||
(when (and (not drawing-path?) shape)
|
(when (and (not drawing-path?) shape)
|
||||||
@ -214,27 +221,26 @@
|
|||||||
(st/emit! (dw/select-shape id)
|
(st/emit! (dw/select-shape id)
|
||||||
(dw/start-editing-selected))
|
(dw/start-editing-selected))
|
||||||
|
|
||||||
:else
|
(some? selected-shape)
|
||||||
(let [;; We only get inside childrens of the hovering shape
|
(do (reset! hover selected-shape)
|
||||||
hover-ids (->> @hover-ids (filter (partial cph/is-child? objects id)))
|
(st/emit! (dw/select-shape (:id selected-shape))))
|
||||||
selected (get objects (first hover-ids))]
|
|
||||||
(when (some? selected)
|
(and (not selected-shape) (some? grid-layout-id))
|
||||||
(reset! hover selected)
|
(st/emit! (dw/start-edition-mode grid-layout-id)))))))))))
|
||||||
(st/emit! (dw/select-shape (:id selected))))))))))))))
|
|
||||||
|
|
||||||
(defn on-context-menu
|
(defn on-context-menu
|
||||||
[hover hover-ids workspace-read-only?]
|
[hover hover-ids workspace-read-only?]
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps @hover @hover-ids workspace-read-only?)
|
(mf/deps @hover @hover-ids workspace-read-only?)
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(if workspace-read-only?
|
(dom/prevent-default event)
|
||||||
(dom/prevent-default event)
|
(when-not workspace-read-only?
|
||||||
(when (or (dom/class? (dom/get-target event) "viewport-controls")
|
(when (or (dom/class? (dom/get-target event) "viewport-controls")
|
||||||
(dom/class? (dom/get-target event) "viewport-selrect"))
|
(dom/child? (dom/get-target event) (dom/query ".viewport-controls"))
|
||||||
(dom/prevent-default event)
|
(dom/class? (dom/get-target event) "viewport-selrect")
|
||||||
|
workspace-read-only?)
|
||||||
(let [position (dom/get-client-position event)]
|
(let [position (dom/get-client-position event)]
|
||||||
;; Delayed callback because we need to wait to the previous context menu to be closed
|
;; Delayed callback because we need to wait to the previous context menu to be closed
|
||||||
(timers/schedule
|
(timers/schedule
|
||||||
#(st/emit!
|
#(st/emit!
|
||||||
(if (some? @hover)
|
(if (some? @hover)
|
||||||
|
|||||||
@ -40,8 +40,19 @@
|
|||||||
(let [children (->> (cph/get-immediate-children objects (:id shape))
|
(let [children (->> (cph/get-immediate-children objects (:id shape))
|
||||||
(remove :hidden))
|
(remove :hidden))
|
||||||
bounds (d/lazy-map (keys objects) #(dm/get-in objects [% :points]))
|
bounds (d/lazy-map (keys objects) #(dm/get-in objects [% :points]))
|
||||||
layout-bounds (gsl/layout-content-bounds bounds shape children)
|
layout-bounds
|
||||||
layout-points (flatten (gsl/layout-content-points bounds shape children))]
|
(cond (ctl/flex-layout? shape)
|
||||||
|
(gsl/layout-content-bounds bounds shape children)
|
||||||
|
|
||||||
|
(ctl/grid-layout? shape)
|
||||||
|
(gsg/layout-content-bounds bounds shape children))
|
||||||
|
layout-points
|
||||||
|
(cond (ctl/flex-layout? shape)
|
||||||
|
(flatten (gsl/layout-content-points bounds shape children))
|
||||||
|
|
||||||
|
(ctl/grid-layout? shape)
|
||||||
|
(flatten (gsg/layout-content-points bounds shape children)))]
|
||||||
|
|
||||||
[:g.debug-layout {:pointer-events "none"}
|
[:g.debug-layout {:pointer-events "none"}
|
||||||
[:polygon {:points (->> layout-bounds (map #(dm/fmt "%, %" (:x %) (:y %))) (str/join " "))
|
[:polygon {:points (->> layout-bounds (map #(dm/fmt "%, %" (:x %) (:y %))) (str/join " "))
|
||||||
:style {:stroke "red" :fill "none"}}]
|
:style {:stroke "red" :fill "none"}}]
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1 @@
|
|||||||
|
{"grid-track-marker":"viewport_grid_layout_editor_grid-track-marker_HABEp","marker-shape":"viewport_grid_layout_editor_marker-shape_FZTUQ","marker-text":"viewport_grid_layout_editor_marker-text_5xM8J","grid-editor-label":"viewport_grid_layout_editor_grid-editor-label_2NbYe","grid-frame":"viewport_grid_layout_editor_grid-frame_CzMnU","grid-plus-button":"viewport_grid_layout_editor_grid-plus-button_brOge","grid-plus-shape":"viewport_grid_layout_editor_grid-plus-shape_jtOU9","grid-plus-icon":"viewport_grid_layout_editor_grid-plus-icon_Zolso","grid-cell-outline":"viewport_grid_layout_editor_grid-cell-outline_1-cRq","hover":"viewport_grid_layout_editor_hover_Rn-tv","selected":"viewport_grid_layout_editor_selected_nhyhL"}
|
||||||
@ -0,0 +1,65 @@
|
|||||||
|
.grid-track-marker {
|
||||||
|
.marker-shape {
|
||||||
|
fill: var(--color-distance);
|
||||||
|
fill-opacity: 0.3;
|
||||||
|
}
|
||||||
|
.marker-text {
|
||||||
|
fill: var(--color-distance);
|
||||||
|
font-size: calc(12px / var(--zoom));
|
||||||
|
font-family: worksans;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-editor-label {
|
||||||
|
position: absolute;
|
||||||
|
background: none;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
text-align: center;
|
||||||
|
font-family: worksans;
|
||||||
|
color: var(--color-distance);
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
font-size: calc(12px / var(--zoom));
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
border-bottom: calc(1px / var(--zoom)) solid var(--color-distance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-frame {
|
||||||
|
fill: #f6f6f6;
|
||||||
|
stroke: var(--color-distance);
|
||||||
|
stroke-width: calc(1 / var(--zoom));
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-plus-button {
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.grid-plus-shape {
|
||||||
|
fill: var(--color-distance);
|
||||||
|
stroke: var(--color-distance);
|
||||||
|
stroke-width: calc(1 / var(--zoom));
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-plus-icon {
|
||||||
|
fill: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-cell-outline {
|
||||||
|
fill: transparent;
|
||||||
|
stroke: var(--color-distance);
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-width: calc(2 / var(--zoom));
|
||||||
|
stroke-dasharray: 0 calc(8 / var(--zoom));
|
||||||
|
|
||||||
|
&.hover,
|
||||||
|
&.selected {
|
||||||
|
stroke-dasharray: initial;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -206,7 +206,9 @@
|
|||||||
(remove #(dm/get-in objects [% :blocked]))
|
(remove #(dm/get-in objects [% :blocked]))
|
||||||
(ctt/sort-z-index objects ids {:bottom-frames? mod?}))
|
(ctt/sort-z-index objects ids {:bottom-frames? mod?}))
|
||||||
|
|
||||||
grouped? (fn [id] (contains? #{:group :bool} (get-in objects [id :type])))
|
grouped? (fn [id]
|
||||||
|
(and (cph/group-shape? objects id)
|
||||||
|
(not (cph/mask-shape? objects id))))
|
||||||
|
|
||||||
selected-with-parents
|
selected-with-parents
|
||||||
(into #{} (mapcat #(cph/get-parent-ids objects %)) selected)
|
(into #{} (mapcat #(cph/get-parent-ids objects %)) selected)
|
||||||
@ -354,5 +356,5 @@
|
|||||||
(do (st/emit! (dsc/push-shortcuts ::path psc/shortcuts))
|
(do (st/emit! (dsc/push-shortcuts ::path psc/shortcuts))
|
||||||
#(st/emit! (dsc/pop-shortcuts ::path)))
|
#(st/emit! (dsc/pop-shortcuts ::path)))
|
||||||
text-editing?
|
text-editing?
|
||||||
(do (st/emit! (dsc/push-shortcuts ::text tsc/shortcuts))
|
(do (st/emit! (dsc/push-shortcuts ::text tsc/shortcuts))
|
||||||
#(st/emit! (dsc/pop-shortcuts ::text)))))))
|
#(st/emit! (dsc/pop-shortcuts ::text)))))))
|
||||||
|
|||||||
@ -69,7 +69,8 @@
|
|||||||
(> (:x cand) (:x cur)) cand
|
(> (:x cand) (:x cur)) cand
|
||||||
:else cur)))
|
:else cur)))
|
||||||
|
|
||||||
(defn title-transform [{:keys [points] :as shape} zoom]
|
(defn title-transform
|
||||||
|
[{:keys [points] :as shape} zoom grid-edition?]
|
||||||
(let [leftmost (->> points (reduce left?))
|
(let [leftmost (->> points (reduce left?))
|
||||||
topmost (->> points (remove #{leftmost}) (reduce top?))
|
topmost (->> points (remove #{leftmost}) (reduce top?))
|
||||||
rightmost (->> points (remove #{leftmost topmost}) (reduce right?))
|
rightmost (->> points (remove #{leftmost topmost}) (reduce right?))
|
||||||
@ -81,14 +82,18 @@
|
|||||||
top-right-angle (gpt/angle top-right)
|
top-right-angle (gpt/angle top-right)
|
||||||
|
|
||||||
;; Choose the position that creates the less angle between left-side and top-side
|
;; Choose the position that creates the less angle between left-side and top-side
|
||||||
[label-pos angle v-pos]
|
[label-pos angle h-pos v-pos]
|
||||||
(if (< (mth/abs left-top-angle) (mth/abs top-right-angle))
|
(if (< (mth/abs left-top-angle) (mth/abs top-right-angle))
|
||||||
[leftmost left-top-angle (gpt/perpendicular left-top)]
|
[leftmost left-top-angle left-top (gpt/perpendicular left-top)]
|
||||||
[topmost top-right-angle (gpt/perpendicular top-right)])
|
[topmost top-right-angle top-right (gpt/perpendicular top-right)])
|
||||||
|
|
||||||
|
delta-x (if grid-edition? 40 0)
|
||||||
|
delta-y (if grid-edition? 50 10)
|
||||||
|
|
||||||
label-pos
|
label-pos
|
||||||
(gpt/subtract label-pos (gpt/scale (gpt/unit v-pos) (/ 10 zoom)))]
|
(-> label-pos
|
||||||
|
(gpt/subtract (gpt/scale (gpt/unit v-pos) (/ delta-y zoom)))
|
||||||
|
(gpt/subtract (gpt/scale (gpt/unit h-pos) (/ delta-x zoom))))]
|
||||||
|
|
||||||
(dm/fmt "rotate(% %,%) scale(%, %) translate(%, %)"
|
(dm/fmt "rotate(% %,%) scale(%, %) translate(%, %)"
|
||||||
;; rotate
|
;; rotate
|
||||||
|
|||||||
@ -12,8 +12,10 @@
|
|||||||
[app.common.pages.helpers :as cph]
|
[app.common.pages.helpers :as cph]
|
||||||
[app.common.types.container :as ctn]
|
[app.common.types.container :as ctn]
|
||||||
[app.common.types.shape-tree :as ctt]
|
[app.common.types.shape-tree :as ctt]
|
||||||
|
[app.common.types.shape.layout :as ctl]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.main.data.workspace :as dw]
|
[app.main.data.workspace :as dw]
|
||||||
|
[app.main.data.workspace.grid-layout.editor :as dwge]
|
||||||
[app.main.data.workspace.interactions :as dwi]
|
[app.main.data.workspace.interactions :as dwi]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
@ -24,6 +26,7 @@
|
|||||||
[app.main.ui.workspace.viewport.path-actions :refer [path-actions]]
|
[app.main.ui.workspace.viewport.path-actions :refer [path-actions]]
|
||||||
[app.main.ui.workspace.viewport.utils :as vwu]
|
[app.main.ui.workspace.viewport.utils :as vwu]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
[app.util.timers :as ts]
|
[app.util.timers :as ts]
|
||||||
[debug :refer [debug?]]
|
[debug :refer [debug?]]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
@ -56,16 +59,34 @@
|
|||||||
selected (mf/deref refs/selected-objects)
|
selected (mf/deref refs/selected-objects)
|
||||||
drawing (mf/deref refs/workspace-drawing)
|
drawing (mf/deref refs/workspace-drawing)
|
||||||
drawing-obj (:object drawing)
|
drawing-obj (:object drawing)
|
||||||
shape (or drawing-obj (-> selected first))]
|
shape (or drawing-obj (-> selected first))
|
||||||
(when (or (and (= (count selected) 1)
|
|
||||||
(= (:id shape) edition)
|
single? (= (count selected) 1)
|
||||||
(and (not (cph/text-shape? shape))
|
editing? (= (:id shape) edition)
|
||||||
(not (cph/frame-shape? shape))))
|
draw-path? (and (some? drawing-obj)
|
||||||
(and (some? drawing-obj)
|
(cph/path-shape? drawing-obj)
|
||||||
(cph/path-shape? drawing-obj)
|
(not= :curve (:tool drawing)))
|
||||||
(not= :curve (:tool drawing))))
|
|
||||||
|
path-edition? (or (and single? editing?
|
||||||
|
(and (not (cph/text-shape? shape))
|
||||||
|
(not (cph/frame-shape? shape))))
|
||||||
|
draw-path?)
|
||||||
|
|
||||||
|
grid-edition? (and single? editing? (ctl/grid-layout? shape))]
|
||||||
|
|
||||||
|
(cond
|
||||||
|
path-edition?
|
||||||
[:div.viewport-actions
|
[:div.viewport-actions
|
||||||
[:& path-actions {:shape shape}]])))
|
[:& path-actions {:shape shape}]]
|
||||||
|
|
||||||
|
grid-edition?
|
||||||
|
[:div.viewport-actions
|
||||||
|
[:div.grid-actions
|
||||||
|
[:div.grid-edit-title
|
||||||
|
(tr "workspace.layout_grid.editor.title") " " [:span.grid-edit-board-name (:name shape)]]
|
||||||
|
[:button.btn-secondary {:on-click #(st/emit! (dwge/locate-board (:id shape)))} "Locate"]
|
||||||
|
[:button.btn-primary {:on-click #(st/emit! dw/clear-edition-mode)} "Done"]
|
||||||
|
[:button.btn-icon-basic {:on-click #(st/emit! dw/clear-edition-mode)} i/close]]])))
|
||||||
|
|
||||||
(mf/defc cursor-tooltip
|
(mf/defc cursor-tooltip
|
||||||
[{:keys [zoom tooltip] :as props}]
|
[{:keys [zoom tooltip] :as props}]
|
||||||
@ -97,7 +118,7 @@
|
|||||||
(mf/defc frame-title
|
(mf/defc frame-title
|
||||||
{::mf/wrap [mf/memo
|
{::mf/wrap [mf/memo
|
||||||
#(mf/deferred % ts/raf)]}
|
#(mf/deferred % ts/raf)]}
|
||||||
[{:keys [frame selected? zoom show-artboard-names? show-id? on-frame-enter on-frame-leave on-frame-select]}]
|
[{:keys [frame selected? zoom show-artboard-names? show-id? on-frame-enter on-frame-leave on-frame-select grid-edition?]}]
|
||||||
(let [workspace-read-only? (mf/use-ctx ctx/workspace-read-only?)
|
(let [workspace-read-only? (mf/use-ctx ctx/workspace-read-only?)
|
||||||
|
|
||||||
;; Note that we don't use mf/deref to avoid a repaint dependency here
|
;; Note that we don't use mf/deref to avoid a repaint dependency here
|
||||||
@ -114,8 +135,8 @@
|
|||||||
(fn [bevent]
|
(fn [bevent]
|
||||||
(let [event (.-nativeEvent bevent)]
|
(let [event (.-nativeEvent bevent)]
|
||||||
(when (= 1 (.-which event))
|
(when (= 1 (.-which event))
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default bevent)
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation bevent)
|
||||||
(on-frame-select event (:id frame))))))
|
(on-frame-select event (:id frame))))))
|
||||||
|
|
||||||
on-double-click
|
on-double-click
|
||||||
@ -146,13 +167,15 @@
|
|||||||
(mf/deps (:id frame) on-frame-leave)
|
(mf/deps (:id frame) on-frame-leave)
|
||||||
(fn [_]
|
(fn [_]
|
||||||
(on-frame-leave (:id frame))))
|
(on-frame-leave (:id frame))))
|
||||||
text-pos-x (if (:use-for-thumbnail? frame) 15 0)]
|
text-pos-x (if (or (:use-for-thumbnail? frame) grid-edition?) 15 0)]
|
||||||
|
|
||||||
(when (not (:hidden frame))
|
(when (not (:hidden frame))
|
||||||
[:g.frame-title {:id (dm/str "frame-title-" (:id frame))
|
[:g.frame-title {:id (dm/str "frame-title-" (:id frame))
|
||||||
:transform (vwu/title-transform frame zoom)
|
:data-edit-grid grid-edition?
|
||||||
|
:transform (vwu/title-transform frame zoom grid-edition?)
|
||||||
:pointer-events (when (:blocked frame) "none")}
|
:pointer-events (when (:blocked frame) "none")}
|
||||||
(when (:use-for-thumbnail? frame)
|
(cond
|
||||||
|
(or (:use-for-thumbnail? frame) grid-edition?)
|
||||||
[:svg {:x 0
|
[:svg {:x 0
|
||||||
:y -9
|
:y -9
|
||||||
:width 12
|
:width 12
|
||||||
@ -160,7 +183,13 @@
|
|||||||
:class "workspace-frame-icon"
|
:class "workspace-frame-icon"
|
||||||
:style {:fill color}
|
:style {:fill color}
|
||||||
:visibility (if show-artboard-names? "visible" "hidden")}
|
:visibility (if show-artboard-names? "visible" "hidden")}
|
||||||
[:use {:href "#icon-set-thumbnail"}]])
|
(cond
|
||||||
|
(:use-for-thumbnail? frame)
|
||||||
|
[:use {:href "#icon-set-thumbnail"}]
|
||||||
|
|
||||||
|
grid-edition?
|
||||||
|
[:use {:href "#icon-grid-layout-mode"}])])
|
||||||
|
|
||||||
[:text {:x text-pos-x
|
[:text {:x text-pos-x
|
||||||
:y 0
|
:y 0
|
||||||
:width (:width frame)
|
:width (:width frame)
|
||||||
@ -195,7 +224,10 @@
|
|||||||
(map (d/getf objects))
|
(map (d/getf objects))
|
||||||
selected)
|
selected)
|
||||||
shapes)
|
shapes)
|
||||||
focus (unchecked-get props "focus")]
|
focus (unchecked-get props "focus")
|
||||||
|
|
||||||
|
edition (mf/deref refs/selected-edition)
|
||||||
|
grid-edition? (ctl/grid-layout? objects edition)]
|
||||||
|
|
||||||
[:g.frame-titles
|
[:g.frame-titles
|
||||||
(for [{:keys [id parent-id] :as shape} shapes]
|
(for [{:keys [id parent-id] :as shape} shapes]
|
||||||
@ -211,7 +243,8 @@
|
|||||||
:show-id? (debug? :shape-titles)
|
:show-id? (debug? :shape-titles)
|
||||||
:on-frame-enter on-frame-enter
|
:on-frame-enter on-frame-enter
|
||||||
:on-frame-leave on-frame-leave
|
:on-frame-leave on-frame-leave
|
||||||
:on-frame-select on-frame-select}]))]))
|
:on-frame-select on-frame-select
|
||||||
|
:grid-edition? (and (= id edition) grid-edition?)}]))]))
|
||||||
|
|
||||||
(mf/defc frame-flow
|
(mf/defc frame-flow
|
||||||
[{:keys [flow frame selected? zoom on-frame-enter on-frame-leave on-frame-select]}]
|
[{:keys [flow frame selected? zoom on-frame-enter on-frame-leave on-frame-select]}]
|
||||||
|
|||||||
@ -6,343 +6,21 @@
|
|||||||
|
|
||||||
(ns app.util.code-gen
|
(ns app.util.code-gen
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.util.code-gen.markup-html :as html]
|
||||||
[app.common.data.macros :as dm]
|
[app.util.code-gen.markup-svg :as svg]
|
||||||
[app.common.pages.helpers :as cph]
|
[app.util.code-gen.style-css :as css]))
|
||||||
[app.common.text :as txt]
|
|
||||||
[app.common.types.shape.layout :as ctl]
|
|
||||||
[app.main.ui.formats :as fmt]
|
|
||||||
[app.util.color :as uc]
|
|
||||||
[cuerdas.core :as str]))
|
|
||||||
|
|
||||||
(defn shadow->css [shadow]
|
(defn generate-markup-code
|
||||||
(let [{:keys [style offset-x offset-y blur spread]} shadow
|
[objects type shapes]
|
||||||
css-color (uc/color->background (:color shadow))]
|
(let [generate-markup
|
||||||
(dm/str
|
(case type
|
||||||
(if (= style :inner-shadow) "inset " "")
|
"html" html/generate-markup
|
||||||
(str/fmt "%spx %spx %spx %spx %s" offset-x offset-y blur spread css-color))))
|
"svg" svg/generate-markup)]
|
||||||
|
(generate-markup objects shapes)))
|
||||||
|
|
||||||
(defn fill-color->background
|
(defn generate-style-code
|
||||||
[fill]
|
[objects type shapes]
|
||||||
(uc/color->background {:color (:fill-color fill)
|
(let [generate-style
|
||||||
:opacity (:fill-opacity fill)
|
(case type
|
||||||
:gradient (:fill-color-gradient fill)}))
|
"css" css/generate-style)]
|
||||||
|
(generate-style objects shapes)))
|
||||||
(defn format-fill-color [_ shape]
|
|
||||||
(let [fills (:fills shape)
|
|
||||||
first-fill (first fills)
|
|
||||||
colors (if (> (count fills) 1)
|
|
||||||
(map (fn [fill]
|
|
||||||
(let [color (fill-color->background fill)]
|
|
||||||
(if (some? (:fill-color-gradient fill))
|
|
||||||
color
|
|
||||||
(str/format "linear-gradient(%s,%s)" color color))))
|
|
||||||
(:fills shape))
|
|
||||||
[(fill-color->background first-fill)])]
|
|
||||||
(str/join ", " colors)))
|
|
||||||
|
|
||||||
(defn format-stroke [_ shape]
|
|
||||||
(let [first-stroke (first (:strokes shape))
|
|
||||||
width (:stroke-width first-stroke)
|
|
||||||
style (let [style (:stroke-style first-stroke)]
|
|
||||||
(when (keyword? style) (d/name style)))
|
|
||||||
color {:color (:stroke-color first-stroke)
|
|
||||||
:opacity (:stroke-opacity first-stroke)
|
|
||||||
:gradient (:stroke-color-gradient first-stroke)}]
|
|
||||||
(when-not (= :none (:stroke-style first-stroke))
|
|
||||||
(str/format "%spx %s %s" width style (uc/color->background color)))))
|
|
||||||
|
|
||||||
(defn format-position [_ shape]
|
|
||||||
(let [relative? (cph/frame-shape? shape)
|
|
||||||
absolute? (or (empty? (:flex-items shape))
|
|
||||||
(and (ctl/any-layout? (:parent shape)) (ctl/layout-absolute? shape)))]
|
|
||||||
(cond
|
|
||||||
absolute? "absolute"
|
|
||||||
relative? "relative"
|
|
||||||
|
|
||||||
;; static is default value in css
|
|
||||||
:else nil)))
|
|
||||||
|
|
||||||
(defn get-size
|
|
||||||
[type values]
|
|
||||||
(let [value (cond
|
|
||||||
(number? values) values
|
|
||||||
(string? values) values
|
|
||||||
(type values) (type values)
|
|
||||||
:else (type (:selrect values)))]
|
|
||||||
|
|
||||||
(if (= :width type)
|
|
||||||
(fmt/format-size :width value values)
|
|
||||||
(fmt/format-size :heigth value values))))
|
|
||||||
|
|
||||||
(defn styles-data
|
|
||||||
[shape]
|
|
||||||
{:position {:props [:type]
|
|
||||||
:to-prop {:type "position"}
|
|
||||||
:format {:type format-position}}
|
|
||||||
:layout {:props (if (or (empty? (:flex-items shape))
|
|
||||||
(ctl/layout-absolute? shape))
|
|
||||||
[:width :height :x :y :radius :rx :r1]
|
|
||||||
[:width :height :radius :rx :r1])
|
|
||||||
:to-prop {:x "left"
|
|
||||||
:y "top"
|
|
||||||
:rotation "transform"
|
|
||||||
:rx "border-radius"
|
|
||||||
:r1 "border-radius"}
|
|
||||||
:format {:rotation #(str/fmt "rotate(%sdeg)" %)
|
|
||||||
:r1 #(apply str/fmt "%spx %spx %spx %spx" %)
|
|
||||||
:width #(get-size :width %)
|
|
||||||
:height #(get-size :height %)}
|
|
||||||
:multi {:r1 [:r1 :r2 :r3 :r4]}}
|
|
||||||
:fill {:props [:fills]
|
|
||||||
:to-prop {:fills (if (> (count (:fills shape)) 1) "background-image" "background-color")}
|
|
||||||
:format {:fills format-fill-color}}
|
|
||||||
:stroke {:props [:strokes]
|
|
||||||
:to-prop {:strokes "border"}
|
|
||||||
:format {:strokes format-stroke}}
|
|
||||||
:shadow {:props [:shadow]
|
|
||||||
:to-prop {:shadow :box-shadow}
|
|
||||||
:format {:shadow #(str/join ", " (map shadow->css %1))}}
|
|
||||||
:blur {:props [:blur]
|
|
||||||
:to-prop {:blur "filter"}
|
|
||||||
:format {:blur #(str/fmt "blur(%spx)" (:value %))}}
|
|
||||||
:layout-flex {:props [:layout
|
|
||||||
:layout-flex-dir
|
|
||||||
:layout-align-items
|
|
||||||
:layout-justify-content
|
|
||||||
:layout-gap
|
|
||||||
:layout-padding
|
|
||||||
:layout-wrap-type]
|
|
||||||
:to-prop {:layout "display"
|
|
||||||
:layout-flex-dir "flex-direction"
|
|
||||||
:layout-align-items "align-items"
|
|
||||||
:layout-justify-content "justify-content"
|
|
||||||
:layout-wrap-type "flex-wrap"
|
|
||||||
:layout-gap "gap"
|
|
||||||
:layout-padding "padding"}
|
|
||||||
:format {:layout d/name
|
|
||||||
:layout-flex-dir d/name
|
|
||||||
:layout-align-items d/name
|
|
||||||
:layout-justify-content d/name
|
|
||||||
:layout-wrap-type d/name
|
|
||||||
:layout-gap fmt/format-gap
|
|
||||||
:layout-padding fmt/format-padding}}})
|
|
||||||
|
|
||||||
(def style-text
|
|
||||||
{:props [:fills
|
|
||||||
:font-family
|
|
||||||
:font-style
|
|
||||||
:font-size
|
|
||||||
:font-weight
|
|
||||||
:line-height
|
|
||||||
:letter-spacing
|
|
||||||
:text-decoration
|
|
||||||
:text-transform]
|
|
||||||
:to-prop {:fills "color"}
|
|
||||||
:format {:font-family #(dm/str "'" % "'")
|
|
||||||
:font-style #(dm/str %)
|
|
||||||
:font-size #(dm/str % "px")
|
|
||||||
:font-weight #(dm/str %)
|
|
||||||
:line-height #(dm/str %)
|
|
||||||
:letter-spacing #(dm/str % "px")
|
|
||||||
:text-decoration d/name
|
|
||||||
:text-transform d/name
|
|
||||||
:fills format-fill-color}})
|
|
||||||
|
|
||||||
(def layout-flex-item-params
|
|
||||||
{:props [:layout-item-margin
|
|
||||||
:layout-item-max-h
|
|
||||||
:layout-item-min-h
|
|
||||||
:layout-item-max-w
|
|
||||||
:layout-item-min-w
|
|
||||||
:layout-item-align-self]
|
|
||||||
:to-prop {:layout-item-margin "margin"
|
|
||||||
:layout-item-max-h "max-height"
|
|
||||||
:layout-item-min-h "min-height"
|
|
||||||
:layout-item-max-w "max-width"
|
|
||||||
:layout-item-min-w "min-width"
|
|
||||||
:layout-item-align-self "align-self"}
|
|
||||||
:format {:layout-item-margin fmt/format-margin
|
|
||||||
:layout-item-max-h #(dm/str % "px")
|
|
||||||
:layout-item-min-h #(dm/str % "px")
|
|
||||||
:layout-item-max-w #(dm/str % "px")
|
|
||||||
:layout-item-min-w #(dm/str % "px")
|
|
||||||
:layout-item-align-self d/name}})
|
|
||||||
|
|
||||||
(def layout-align-content
|
|
||||||
{:props [:layout-align-content]
|
|
||||||
:to-prop {:layout-align-content "align-content"}
|
|
||||||
:format {:layout-align-content d/name}})
|
|
||||||
|
|
||||||
(defn get-specific-value
|
|
||||||
[values prop]
|
|
||||||
(let [result (if (get values prop)
|
|
||||||
(get values prop)
|
|
||||||
(get (:selrect values) prop))
|
|
||||||
result (if (= :width prop)
|
|
||||||
(get-size :width values)
|
|
||||||
result)
|
|
||||||
result (if (= :height prop)
|
|
||||||
(get-size :height values)
|
|
||||||
result)]
|
|
||||||
|
|
||||||
result))
|
|
||||||
|
|
||||||
(defn generate-css-props
|
|
||||||
([values properties]
|
|
||||||
(generate-css-props values properties nil))
|
|
||||||
|
|
||||||
([values properties params]
|
|
||||||
(let [{:keys [to-prop format tab-size multi]
|
|
||||||
:or {to-prop {} tab-size 0 multi {}}} params
|
|
||||||
|
|
||||||
;; We allow the :format and :to-prop to be a map for different properties
|
|
||||||
;; or just a value for a single property. This code transform a single
|
|
||||||
;; property to a uniform one
|
|
||||||
properties (if-not (coll? properties) [properties] properties)
|
|
||||||
|
|
||||||
format (if (not (map? format))
|
|
||||||
(into {} (map #(vector % format) properties))
|
|
||||||
format)
|
|
||||||
|
|
||||||
to-prop (if (not (map? to-prop))
|
|
||||||
(into {} (map #(vector % to-prop) properties))
|
|
||||||
to-prop)
|
|
||||||
|
|
||||||
get-value (fn [prop]
|
|
||||||
(if-let [props (prop multi)]
|
|
||||||
(map #(get values %) props)
|
|
||||||
(get-specific-value values prop)))
|
|
||||||
|
|
||||||
null? (fn [value]
|
|
||||||
(if (coll? value)
|
|
||||||
(every? #(or (nil? %) (= % 0)) value)
|
|
||||||
(or (nil? value) (= value 0))))
|
|
||||||
|
|
||||||
default-format (fn [value] (dm/str (fmt/format-pixels value)))
|
|
||||||
format-property (fn [prop]
|
|
||||||
(let [css-prop (or (prop to-prop) (d/name prop))
|
|
||||||
format-fn (or (prop format) default-format)
|
|
||||||
css-val (format-fn (get-value prop) values)]
|
|
||||||
(when css-val
|
|
||||||
(dm/str
|
|
||||||
(str/repeat " " tab-size)
|
|
||||||
(str/fmt "%s: %s;" css-prop css-val)))))]
|
|
||||||
|
|
||||||
(->> properties
|
|
||||||
(remove #(null? (get-value %)))
|
|
||||||
(map format-property)
|
|
||||||
(filter (comp not nil?))
|
|
||||||
(str/join "\n")))))
|
|
||||||
|
|
||||||
(defn shape->properties [shape]
|
|
||||||
(let [;; This property is added in an earlier step (code.cljs),
|
|
||||||
;; it will come with a vector of flex-items if any.
|
|
||||||
;; If there are none it will continue as usual.
|
|
||||||
flex-items (:flex-items shape)
|
|
||||||
props (->> (styles-data shape) vals (mapcat :props))
|
|
||||||
to-prop (->> (styles-data shape) vals (map :to-prop) (reduce merge))
|
|
||||||
format (->> (styles-data shape) vals (map :format) (reduce merge))
|
|
||||||
multi (->> (styles-data shape) vals (map :multi) (reduce merge))
|
|
||||||
props (cond-> props
|
|
||||||
(seq flex-items) (concat (:props layout-flex-item-params))
|
|
||||||
(= :wrap (:layout-wrap-type shape)) (concat (:props layout-align-content)))
|
|
||||||
to-prop (cond-> to-prop
|
|
||||||
(seq flex-items) (merge (:to-prop layout-flex-item-params))
|
|
||||||
(= :wrap (:layout-wrap-type shape)) (merge (:to-prop layout-align-content)))
|
|
||||||
format (cond-> format
|
|
||||||
(seq flex-items) (merge (:format layout-flex-item-params))
|
|
||||||
(= :wrap (:layout-wrap-type shape)) (merge (:format layout-align-content)))]
|
|
||||||
(generate-css-props shape props {:to-prop to-prop
|
|
||||||
:format format
|
|
||||||
:multi multi
|
|
||||||
:tab-size 2})))
|
|
||||||
|
|
||||||
(defn search-text-attrs
|
|
||||||
[node attrs]
|
|
||||||
(->> (txt/node-seq node)
|
|
||||||
(map #(select-keys % attrs))
|
|
||||||
(reduce d/merge)))
|
|
||||||
|
|
||||||
|
|
||||||
;; TODO: used on inspect
|
|
||||||
(defn parse-style-text-blocks
|
|
||||||
[node attrs]
|
|
||||||
(letfn
|
|
||||||
[(rec-style-text-map [acc node style]
|
|
||||||
(let [node-style (merge style (select-keys node attrs))
|
|
||||||
head (or (-> acc first) [{} ""])
|
|
||||||
[head-style head-text] head
|
|
||||||
|
|
||||||
new-acc
|
|
||||||
(cond
|
|
||||||
(:children node)
|
|
||||||
(reduce #(rec-style-text-map %1 %2 node-style) acc (:children node))
|
|
||||||
|
|
||||||
(not= head-style node-style)
|
|
||||||
(cons [node-style (:text node "")] acc)
|
|
||||||
|
|
||||||
:else
|
|
||||||
(cons [node-style (dm/str head-text "" (:text node))] (rest acc)))
|
|
||||||
|
|
||||||
;; We add an end-of-line when finish a paragraph
|
|
||||||
new-acc
|
|
||||||
(if (= (:type node) "paragraph")
|
|
||||||
(let [[hs ht] (first new-acc)]
|
|
||||||
(cons [hs (dm/str ht "\n")] (rest new-acc)))
|
|
||||||
new-acc)]
|
|
||||||
new-acc))]
|
|
||||||
|
|
||||||
(-> (rec-style-text-map [] node {})
|
|
||||||
reverse)))
|
|
||||||
|
|
||||||
(defn text->properties [shape]
|
|
||||||
(let [flex-items (:flex-items shape)
|
|
||||||
text-shape-style (select-keys (styles-data shape) [:layout :shadow :blur])
|
|
||||||
|
|
||||||
shape-props (->> text-shape-style vals (mapcat :props))
|
|
||||||
shape-to-prop (->> text-shape-style vals (map :to-prop) (reduce merge))
|
|
||||||
shape-format (->> text-shape-style vals (map :format) (reduce merge))
|
|
||||||
|
|
||||||
shape-props (cond-> shape-props
|
|
||||||
(seq flex-items) (concat (:props layout-flex-item-params)))
|
|
||||||
shape-to-prop (cond-> shape-to-prop
|
|
||||||
(seq flex-items) (merge (:to-prop layout-flex-item-params)))
|
|
||||||
shape-format (cond-> shape-format
|
|
||||||
(seq flex-items) (merge (:format layout-flex-item-params)))
|
|
||||||
|
|
||||||
text-values (->> (search-text-attrs
|
|
||||||
(:content shape)
|
|
||||||
(conj (:props style-text) :fill-color-gradient :fill-opacity))
|
|
||||||
(d/merge txt/default-text-attrs))]
|
|
||||||
(str/join
|
|
||||||
"\n"
|
|
||||||
[(generate-css-props shape
|
|
||||||
shape-props
|
|
||||||
{:to-prop shape-to-prop
|
|
||||||
:format shape-format
|
|
||||||
:tab-size 2})
|
|
||||||
(generate-css-props text-values
|
|
||||||
(:props style-text)
|
|
||||||
{:to-prop (:to-prop style-text)
|
|
||||||
:format (:format style-text)
|
|
||||||
:tab-size 2})])))
|
|
||||||
|
|
||||||
(defn generate-css [shape]
|
|
||||||
(let [name (:name shape)
|
|
||||||
properties (if (= :text (:type shape))
|
|
||||||
(text->properties shape)
|
|
||||||
(shape->properties shape))
|
|
||||||
selector (str/css-selector name)
|
|
||||||
selector (if (str/starts-with? selector "-") (subs selector 1) selector)]
|
|
||||||
(str/join "\n" [(str/fmt "/* %s */" name)
|
|
||||||
(str/fmt ".%s {" selector)
|
|
||||||
properties
|
|
||||||
"}"])))
|
|
||||||
|
|
||||||
(defn generate-style-code [type shapes]
|
|
||||||
(let [generate-style-fn (case type
|
|
||||||
"css" generate-css)]
|
|
||||||
(->> shapes
|
|
||||||
(map generate-style-fn)
|
|
||||||
(str/join "\n\n"))))
|
|
||||||
|
|||||||
23
frontend/src/app/util/code_gen/common.cljs
Normal file
23
frontend/src/app/util/code_gen/common.cljs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
;; 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.util.code-gen.common
|
||||||
|
(:require
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
|
[cuerdas.core :as str]))
|
||||||
|
|
||||||
|
(defn shape->selector
|
||||||
|
[shape]
|
||||||
|
(let [name (-> (:name shape)
|
||||||
|
(subs 0 (min 10 (count (:name shape))))
|
||||||
|
(str/replace #"[^a-zA-Z0-9\s\:]+" ""))
|
||||||
|
;; selectors cannot start with numbers
|
||||||
|
name (if (re-matches #"^\d.*" name) (dm/str "c-" name) name)
|
||||||
|
id (-> (dm/str (:id shape))
|
||||||
|
(subs 24 36))
|
||||||
|
selector (str/css-selector (dm/str name " " id))
|
||||||
|
selector (if (str/starts-with? selector "-") (subs selector 1) selector)]
|
||||||
|
selector))
|
||||||
103
frontend/src/app/util/code_gen/markup_html.cljs
Normal file
103
frontend/src/app/util/code_gen/markup_html.cljs
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
;; 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.util.code-gen.markup-html
|
||||||
|
(:require
|
||||||
|
["react-dom/server" :as rds]
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
|
[app.common.pages.helpers :as cph]
|
||||||
|
[app.common.types.shape.layout :as ctl]
|
||||||
|
[app.config :as cfg]
|
||||||
|
[app.main.ui.shapes.text.html-text :as text]
|
||||||
|
[app.util.code-gen.common :as cgc]
|
||||||
|
[app.util.code-gen.markup-svg :refer [generate-svg]]
|
||||||
|
[cuerdas.core :as str]
|
||||||
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
|
(defn svg-markup?
|
||||||
|
"Function to determine whether a shape is rendered in HTML+CSS or is rendered
|
||||||
|
through a SVG"
|
||||||
|
[shape]
|
||||||
|
(or
|
||||||
|
;; path and path-like shapes
|
||||||
|
(cph/path-shape? shape)
|
||||||
|
(cph/bool-shape? shape)
|
||||||
|
|
||||||
|
;; imported SVG images
|
||||||
|
(cph/svg-raw-shape? shape)
|
||||||
|
(some? (:svg-attrs shape))
|
||||||
|
|
||||||
|
;; CSS masks are not enough we need to delegate to SVG
|
||||||
|
(cph/mask-shape? shape)
|
||||||
|
|
||||||
|
;; Texts with shadows or strokes we render in SVG
|
||||||
|
(and (cph/text-shape? shape)
|
||||||
|
(or (d/not-empty? (:shadow shape))
|
||||||
|
(d/not-empty? (:strokes shape))))
|
||||||
|
|
||||||
|
;; When a shape has several strokes or the stroke is not a "border"
|
||||||
|
(or (> (count (:strokes shape)) 1)
|
||||||
|
(and (= (count (:strokes shape)) 1)
|
||||||
|
(not= (-> shape :strokes first :stroke-alignment) :center)))))
|
||||||
|
|
||||||
|
(defn generate-html
|
||||||
|
([objects shape]
|
||||||
|
(generate-html objects shape 0))
|
||||||
|
|
||||||
|
([objects shape level]
|
||||||
|
(let [indent (str/repeat " " level)
|
||||||
|
maybe-reverse (if (ctl/any-layout? shape) reverse identity)
|
||||||
|
|
||||||
|
shape-html
|
||||||
|
(cond
|
||||||
|
(svg-markup? shape)
|
||||||
|
(let [svg-markup (generate-svg objects shape)]
|
||||||
|
(dm/fmt "%<div class=\"%\">\n%\n%</div>"
|
||||||
|
indent
|
||||||
|
(cgc/shape->selector shape)
|
||||||
|
svg-markup
|
||||||
|
indent))
|
||||||
|
|
||||||
|
(cph/text-shape? shape)
|
||||||
|
(let [text-shape-html (rds/renderToStaticMarkup (mf/element text/text-shape #js {:shape shape :code? true}))]
|
||||||
|
(dm/fmt "%<div class=\"%\">\n%\n%</div>"
|
||||||
|
indent
|
||||||
|
(cgc/shape->selector shape)
|
||||||
|
text-shape-html
|
||||||
|
indent))
|
||||||
|
|
||||||
|
(cph/image-shape? shape)
|
||||||
|
(let [data (or (:metadata shape) (:fill-image shape))
|
||||||
|
image-url (cfg/resolve-file-media data)]
|
||||||
|
(dm/fmt "%<img src=\"%\" class=\"%\">\n%</img>"
|
||||||
|
indent
|
||||||
|
image-url
|
||||||
|
(cgc/shape->selector shape)
|
||||||
|
indent))
|
||||||
|
|
||||||
|
(empty? (:shapes shape))
|
||||||
|
(dm/fmt "%<div class=\"%\">\n%</div>"
|
||||||
|
indent
|
||||||
|
(cgc/shape->selector shape)
|
||||||
|
indent)
|
||||||
|
|
||||||
|
:else
|
||||||
|
(dm/fmt "%<div class=\"%\">\n%\n%</div>"
|
||||||
|
indent
|
||||||
|
(cgc/shape->selector shape)
|
||||||
|
(->> (:shapes shape)
|
||||||
|
(maybe-reverse)
|
||||||
|
(map #(generate-html objects (get objects %) (inc level)))
|
||||||
|
(str/join "\n"))
|
||||||
|
indent))]
|
||||||
|
(dm/fmt "%<!-- % -->\n%" indent (dm/str (d/name (:type shape)) ": " (:name shape)) shape-html))))
|
||||||
|
|
||||||
|
(defn generate-markup
|
||||||
|
[objects shapes]
|
||||||
|
(->> shapes
|
||||||
|
(map #(generate-html objects %))
|
||||||
|
(str/join "\n")))
|
||||||
26
frontend/src/app/util/code_gen/markup_svg.cljs
Normal file
26
frontend/src/app/util/code_gen/markup_svg.cljs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
;; 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.util.code-gen.markup-svg
|
||||||
|
(:require
|
||||||
|
["react-dom/server" :as rds]
|
||||||
|
[app.main.render :as render]
|
||||||
|
[cuerdas.core :as str]
|
||||||
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
|
(defn generate-svg
|
||||||
|
[objects shape]
|
||||||
|
(rds/renderToStaticMarkup
|
||||||
|
(mf/element
|
||||||
|
render/object-svg
|
||||||
|
#js {:objects objects
|
||||||
|
:object-id (-> shape :id)})))
|
||||||
|
|
||||||
|
(defn generate-markup
|
||||||
|
[objects shapes]
|
||||||
|
(->> shapes
|
||||||
|
(map #(generate-svg objects %))
|
||||||
|
(str/join "\n")))
|
||||||
235
frontend/src/app/util/code_gen/style_css.cljs
Normal file
235
frontend/src/app/util/code_gen/style_css.cljs
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
;; 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.util.code-gen.style-css
|
||||||
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
|
[app.common.pages.helpers :as cph]
|
||||||
|
[app.common.text :as txt]
|
||||||
|
[app.main.ui.shapes.text.styles :as sts]
|
||||||
|
[app.util.code-gen.common :as cgc]
|
||||||
|
[app.util.code-gen.style-css-formats :refer [format-value]]
|
||||||
|
[app.util.code-gen.style-css-values :refer [get-value]]
|
||||||
|
[cuerdas.core :as str]))
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; Common styles to display always. Will be attached as a prelude to the generated CSS
|
||||||
|
;;
|
||||||
|
(def prelude "
|
||||||
|
html, body {
|
||||||
|
background-color: #E8E9EA;
|
||||||
|
margin: 0;
|
||||||
|
min-height: 100%;
|
||||||
|
min-width: 100%;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 2rem;
|
||||||
|
gap: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-node { background-clip: text !important; -webkit-background-clip: text !important; }
|
||||||
|
|
||||||
|
")
|
||||||
|
|
||||||
|
(def shape-css-properties
|
||||||
|
[:position
|
||||||
|
:left
|
||||||
|
:top
|
||||||
|
:width
|
||||||
|
:height
|
||||||
|
:transform
|
||||||
|
:background
|
||||||
|
:background-color
|
||||||
|
:background-image
|
||||||
|
:border
|
||||||
|
:border-radius
|
||||||
|
:box-shadow
|
||||||
|
:filter
|
||||||
|
:opacity
|
||||||
|
:overflow
|
||||||
|
|
||||||
|
;; Flex/grid related properties
|
||||||
|
:display
|
||||||
|
:align-items
|
||||||
|
:align-content
|
||||||
|
:justify-items
|
||||||
|
:justify-content
|
||||||
|
:gap
|
||||||
|
:column-gap
|
||||||
|
:row-gap
|
||||||
|
:padding
|
||||||
|
|
||||||
|
;; Flex related properties
|
||||||
|
:flex-direction
|
||||||
|
:flex-wrap
|
||||||
|
|
||||||
|
;; Grid related properties
|
||||||
|
:grid-template-rows
|
||||||
|
:grid-template-columns
|
||||||
|
|
||||||
|
;; Flex/grid self properties
|
||||||
|
:flex-shrink
|
||||||
|
:margin
|
||||||
|
:max-height
|
||||||
|
:min-height
|
||||||
|
:max-width
|
||||||
|
:min-width
|
||||||
|
:align-self
|
||||||
|
:justify-self
|
||||||
|
|
||||||
|
;; Grid cell properties
|
||||||
|
:grid-column
|
||||||
|
:grid-row
|
||||||
|
])
|
||||||
|
|
||||||
|
(def text-node-css-properties
|
||||||
|
[:font-family
|
||||||
|
:font-style
|
||||||
|
:font-size
|
||||||
|
:font-weight
|
||||||
|
:line-height
|
||||||
|
:letter-spacing
|
||||||
|
:text-decoration
|
||||||
|
:text-transform
|
||||||
|
:color])
|
||||||
|
|
||||||
|
(defn shape->css-property
|
||||||
|
[shape objects property]
|
||||||
|
(when-let [value (get-value property shape objects)]
|
||||||
|
[property value]))
|
||||||
|
|
||||||
|
(defn shape->css-properties
|
||||||
|
"Given a shape extract the CSS properties in the format of list [property value]"
|
||||||
|
[shape objects properties]
|
||||||
|
(->> properties
|
||||||
|
(keep (fn [property]
|
||||||
|
(when-let [value (get-value property shape objects)]
|
||||||
|
[property value])))))
|
||||||
|
|
||||||
|
(defn format-css-value
|
||||||
|
([[property value] options]
|
||||||
|
(format-css-value property value options))
|
||||||
|
|
||||||
|
([property value options]
|
||||||
|
(when (some? value)
|
||||||
|
(format-value property value options))))
|
||||||
|
|
||||||
|
(defn format-css-property
|
||||||
|
[[property value] options]
|
||||||
|
(when (some? value)
|
||||||
|
(let [formatted-value (format-css-value property value options)]
|
||||||
|
(dm/fmt "%: %;" (d/name property) formatted-value))))
|
||||||
|
|
||||||
|
(defn format-css-properties
|
||||||
|
"Format a list of [property value] into a list of css properties in the format 'property: value;'"
|
||||||
|
[properties options]
|
||||||
|
(->> properties
|
||||||
|
(map #(dm/str " " (format-css-property % options)))
|
||||||
|
(str/join "\n")))
|
||||||
|
|
||||||
|
(defn get-shape-properties-css
|
||||||
|
([objects shape properties]
|
||||||
|
(get-shape-properties-css objects shape properties nil))
|
||||||
|
|
||||||
|
([objects shape properties options]
|
||||||
|
(-> shape
|
||||||
|
(shape->css-properties objects properties)
|
||||||
|
(format-css-properties options))))
|
||||||
|
|
||||||
|
(defn format-js-styles
|
||||||
|
[properties _options]
|
||||||
|
(format-css-properties
|
||||||
|
(->> (.keys js/Object properties)
|
||||||
|
(remove #(str/starts-with? % "--"))
|
||||||
|
(mapv (fn [key]
|
||||||
|
[(str/kebab key) (unchecked-get properties key)])))
|
||||||
|
nil))
|
||||||
|
|
||||||
|
(defn node->css
|
||||||
|
[shape shape-selector node]
|
||||||
|
(let [properties
|
||||||
|
(case (:type node)
|
||||||
|
(:root "root")
|
||||||
|
(sts/generate-root-styles shape node)
|
||||||
|
|
||||||
|
(:paragraph-set "paragraph-set")
|
||||||
|
(sts/generate-paragraph-set-styles shape)
|
||||||
|
|
||||||
|
(:paragraph "paragraph")
|
||||||
|
(sts/generate-paragraph-styles shape node)
|
||||||
|
|
||||||
|
(sts/generate-text-styles shape node))]
|
||||||
|
(dm/fmt
|
||||||
|
".% {\n%\n}"
|
||||||
|
(dm/str shape-selector " ." (:$id node))
|
||||||
|
(format-js-styles properties nil))))
|
||||||
|
|
||||||
|
(defn generate-text-css
|
||||||
|
[shape]
|
||||||
|
(let [selector (cgc/shape->selector shape)]
|
||||||
|
(->> shape
|
||||||
|
:content
|
||||||
|
(txt/index-content)
|
||||||
|
(txt/node-seq)
|
||||||
|
(map #(node->css shape selector %))
|
||||||
|
(str/join "\n"))))
|
||||||
|
|
||||||
|
(defn get-shape-css-selector
|
||||||
|
([objects shape]
|
||||||
|
(get-shape-css-selector shape objects nil))
|
||||||
|
|
||||||
|
([shape objects options]
|
||||||
|
(let [properties (-> shape
|
||||||
|
(shape->css-properties objects shape-css-properties)
|
||||||
|
(format-css-properties options))
|
||||||
|
selector (cgc/shape->selector shape)]
|
||||||
|
(str/join "\n" [(str/fmt "/* %s */" (:name shape))
|
||||||
|
(str/fmt ".%s {\n%s\n}" selector properties)
|
||||||
|
(when (cph/text-shape? shape) (generate-text-css shape))]))))
|
||||||
|
|
||||||
|
(defn get-css-property
|
||||||
|
([objects shape property]
|
||||||
|
(get-css-property objects shape property nil))
|
||||||
|
|
||||||
|
([objects shape property options]
|
||||||
|
(-> shape
|
||||||
|
(shape->css-property objects property)
|
||||||
|
(format-css-property options))))
|
||||||
|
|
||||||
|
(defn get-css-value
|
||||||
|
([objects shape property]
|
||||||
|
(get-css-value objects shape property nil))
|
||||||
|
|
||||||
|
([objects shape property options]
|
||||||
|
(when-let [prop (shape->css-property shape objects property)]
|
||||||
|
(format-css-value prop options))))
|
||||||
|
|
||||||
|
(defn generate-style
|
||||||
|
([objects shapes]
|
||||||
|
(generate-style objects shapes nil))
|
||||||
|
([objects shapes options]
|
||||||
|
(dm/str
|
||||||
|
prelude
|
||||||
|
(->> shapes
|
||||||
|
(map #(get-shape-css-selector % objects options))
|
||||||
|
(str/join "\n\n")))))
|
||||||
144
frontend/src/app/util/code_gen/style_css_formats.cljs
Normal file
144
frontend/src/app/util/code_gen/style_css_formats.cljs
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
;; 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.util.code-gen.style-css-formats
|
||||||
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
|
[app.main.ui.formats :as fmt]
|
||||||
|
[app.util.color :as uc]
|
||||||
|
[cuerdas.core :as str]))
|
||||||
|
|
||||||
|
(def css-formatters
|
||||||
|
{:left :position
|
||||||
|
:top :position
|
||||||
|
:width :size
|
||||||
|
:height :size
|
||||||
|
:background :color
|
||||||
|
:background-color :color
|
||||||
|
:background-image :color-array
|
||||||
|
:border :border
|
||||||
|
:border-radius :string-or-size-array
|
||||||
|
:box-shadow :shadows
|
||||||
|
:filter :blur
|
||||||
|
:gap :size-array
|
||||||
|
:row-gap :size-array
|
||||||
|
:column-gap :size-array
|
||||||
|
:padding :size-array
|
||||||
|
:grid-template-rows :tracks
|
||||||
|
:grid-template-columns :tracks
|
||||||
|
:transform :matrix
|
||||||
|
})
|
||||||
|
|
||||||
|
(defmulti format-value
|
||||||
|
(fn [property _value _options] (css-formatters property)))
|
||||||
|
|
||||||
|
(defmethod format-value :position
|
||||||
|
[_ value _options]
|
||||||
|
(cond
|
||||||
|
(number? value) (fmt/format-pixels value)
|
||||||
|
:else value))
|
||||||
|
|
||||||
|
(defmethod format-value :size
|
||||||
|
[_ value _options]
|
||||||
|
(cond
|
||||||
|
(= value :fill) "100%"
|
||||||
|
(= value :auto) "auto"
|
||||||
|
(number? value) (fmt/format-pixels value)
|
||||||
|
:else value))
|
||||||
|
|
||||||
|
(defn format-color
|
||||||
|
[value _options]
|
||||||
|
(cond
|
||||||
|
(not= (:opacity value) 1)
|
||||||
|
(uc/color->background value)
|
||||||
|
|
||||||
|
:else
|
||||||
|
(str/upper (:color value))))
|
||||||
|
|
||||||
|
(defmethod format-value :color
|
||||||
|
[_ value options]
|
||||||
|
(format-color value options))
|
||||||
|
|
||||||
|
(defmethod format-value :color-array
|
||||||
|
[_ value options]
|
||||||
|
(->> value
|
||||||
|
(map #(format-color % options))
|
||||||
|
(str/join ", ")))
|
||||||
|
|
||||||
|
(defmethod format-value :border
|
||||||
|
[_ {:keys [color style width]} options]
|
||||||
|
(dm/fmt "% % %"
|
||||||
|
(fmt/format-pixels width)
|
||||||
|
(d/name style)
|
||||||
|
(format-color color options)))
|
||||||
|
|
||||||
|
(defmethod format-value :size-array
|
||||||
|
[_ value _options]
|
||||||
|
(cond
|
||||||
|
(and (coll? value) (d/not-empty? value))
|
||||||
|
(->> value
|
||||||
|
(map fmt/format-pixels)
|
||||||
|
(str/join " "))
|
||||||
|
|
||||||
|
(some? value)
|
||||||
|
value))
|
||||||
|
|
||||||
|
(defmethod format-value :string-or-size-array
|
||||||
|
[_ value _]
|
||||||
|
(cond
|
||||||
|
(string? value)
|
||||||
|
value
|
||||||
|
|
||||||
|
(and (coll? value) (d/not-empty? value))
|
||||||
|
(->> value
|
||||||
|
(map fmt/format-pixels)
|
||||||
|
(str/join " "))
|
||||||
|
|
||||||
|
(some? value)
|
||||||
|
value))
|
||||||
|
|
||||||
|
(defmethod format-value :keyword
|
||||||
|
[_ value _options]
|
||||||
|
(d/name value))
|
||||||
|
|
||||||
|
(defmethod format-value :tracks
|
||||||
|
[_ value _options]
|
||||||
|
(->> value
|
||||||
|
(map (fn [{:keys [type value]}]
|
||||||
|
(case type
|
||||||
|
:flex (dm/str (fmt/format-number value) "fr")
|
||||||
|
:percent (fmt/format-percent (/ value 100))
|
||||||
|
:auto "auto"
|
||||||
|
(fmt/format-pixels value))))
|
||||||
|
(str/join " ")))
|
||||||
|
|
||||||
|
(defn format-shadow
|
||||||
|
[{:keys [style offset-x offset-y blur spread color]} options]
|
||||||
|
(let [css-color (format-color color options)]
|
||||||
|
(dm/str
|
||||||
|
(if (= style :inner-shadow) "inset " "")
|
||||||
|
(str/fmt "%spx %spx %spx %spx %s" offset-x offset-y blur spread css-color))))
|
||||||
|
|
||||||
|
(defmethod format-value :shadows
|
||||||
|
[_ value options]
|
||||||
|
(->> value
|
||||||
|
(map #(format-shadow % options))
|
||||||
|
(str/join ", " )))
|
||||||
|
|
||||||
|
(defmethod format-value :blur
|
||||||
|
[_ value _options]
|
||||||
|
(dm/fmt "blur(%)" (fmt/format-pixels value)))
|
||||||
|
|
||||||
|
(defmethod format-value :matrix
|
||||||
|
[_ value _options]
|
||||||
|
(fmt/format-matrix value))
|
||||||
|
|
||||||
|
(defmethod format-value :default
|
||||||
|
[_ value _options]
|
||||||
|
(if (keyword? value)
|
||||||
|
(d/name value)
|
||||||
|
value))
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user