0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-24 13:41:39 -05:00

Merge remote-tracking branch 'origin/feature-grid' into develop

This commit is contained in:
alonso.torres 2023-12-11 14:55:58 +01:00
commit 2a9b99e086
41 changed files with 846 additions and 749 deletions

View file

@ -82,7 +82,6 @@
children
(->> children
(keep (d/getf objects))
(remove :hidden)
(remove gco/invalid-geometry?)
(map (partial apply-modifiers bounds)))
@ -169,13 +168,13 @@
children-modifiers
(if (or flex-layout? grid-layout?)
(->> (:shapes parent)
(filter #(ctl/layout-absolute? objects %)))
(filter #(ctl/position-absolute? objects %)))
(:shapes parent))
children-layout
(when (or flex-layout? grid-layout?)
(->> (:shapes parent)
(remove #(ctl/layout-absolute? objects %))))]
(remove #(ctl/position-absolute? objects %))))]
(cond-> modif-tree
(and has-modifiers? parent? (not root?))
@ -222,7 +221,7 @@
(ctm/resize (gpt/point 1 scale-height) origin (:transform parent) (:transform-inverse parent)))))
children (->> (cfh/get-immediate-children objects parent-id)
(remove :hidden)
(remove ctl/position-absolute?)
(remove gco/invalid-geometry?))
content-bounds

View file

@ -299,7 +299,7 @@
ignore-constraints
:scale
(and (ctl/any-layout? parent) (not (ctl/layout-absolute? child)))
(and (ctl/any-layout? parent) (not (ctl/position-absolute? child)))
:left
:else
@ -310,7 +310,7 @@
ignore-constraints
:scale
(and (ctl/any-layout? parent) (not (ctl/layout-absolute? child)))
(and (ctl/any-layout? parent) (not (ctl/position-absolute? child)))
:top
:else
@ -335,13 +335,14 @@
child-bounds (gtr/transform-bounds child-bounds modifiers)
parent-bounds transformed-parent-bounds))
transformed-child-bounds (if reset-modifiers?
child-bounds
(gtr/transform-bounds child-bounds modifiers))]
transformed-child-bounds
(if reset-modifiers?
child-bounds
(gtr/transform-bounds child-bounds modifiers))]
;; If the parent is a layout we don't need to calculate its constraints. Finish
;; after normalize the children (to keep proper proportions)
(if (ctl/any-layout? parent)
(if (and (ctl/any-layout? parent) (not (ctl/position-absolute? child)))
modifiers
(let [child-points-before (gpo/parent-coords-bounds child-bounds parent-bounds)
child-points-after (gpo/parent-coords-bounds transformed-child-bounds transformed-parent-bounds)

View file

@ -421,8 +421,12 @@
reverse? (ctl/reverse? shape)
children (cond->> children (not reverse?) reverse)
ignore-child?
(fn [[_ child]]
(ctl/position-absolute? child))
;; Don't take into account absolute children
children (->> children (remove (comp ctl/layout-absolute? second)))
children (->> children (remove ignore-child?))
;; Creates the layout lines information
layout-lines

View file

@ -424,7 +424,7 @@
children
(->> children
(remove #(ctl/layout-absolute? (second %))))
(remove #(ctl/position-absolute? (second %))))
children-map
(into {}

View file

@ -178,7 +178,7 @@
position-delta (child-position-delta parent child child-bounds child-width child-height layout-data cell-data)]
(cond-> (ctm/empty)
(not (ctl/layout-absolute? child))
(not (ctl/position-absolute? child))
(-> (ctm/add-modifiers fill-modifiers)
(ctm/move position-delta)))))

View file

@ -491,12 +491,19 @@
(defn align-self-stretch? [{:keys [layout-item-align-self]}]
(= :stretch layout-item-align-self))
(defn layout-absolute?
(defn item-absolute?
([objects id]
(layout-absolute? (get objects id)))
(item-absolute? (get objects id)))
([shape]
(true? (:layout-item-absolute shape))))
(defn position-absolute?
([objects id]
(position-absolute? (get objects id)))
([shape]
(or (item-absolute? shape)
(:hidden shape))))
(defn layout-z-index
([objects id]
(layout-z-index (get objects id)))
@ -509,11 +516,11 @@
(auto-width? objects frame-id)
(or (and (col? objects frame-id)
(->> children-ids
(remove (partial layout-absolute? objects))
(remove (partial position-absolute? objects))
(every? (partial fill-width? objects))))
(and (row? objects frame-id)
(->> children-ids
(remove (partial layout-absolute? objects))
(remove (partial position-absolute? objects))
(some (partial fill-width? objects)))))))
(defn change-v-sizing?
@ -705,8 +712,9 @@
(update :layout-grid-cells update-cells)
(assign-cells))))
(defn- reorder-grid-track
[prop parent from-index to-index]
(defn- reorder-grid-tracks
"Swap the positions of the tracks info"
[parent prop from-index to-index]
(-> parent
(update
prop
@ -720,13 +728,70 @@
(d/insert-at-index (inc to-index) [[nil tr]])
(d/vec-without-nils))))))))
(defn- swap-track-content
"Swap the shapes contained in the given tracks moves as necessary the others."
[parent prop from-track to-track]
(let [remap-tracks
(cond
(> from-track to-track)
(into {from-track to-track}
(map #(vector % (inc %)))
(range to-track from-track))
(< from-track to-track)
(into {from-track to-track}
(map #(vector % (dec %)))
(range (inc from-track) (inc to-track))))]
(-> parent
(update
:layout-grid-cells
update-vals
(fn [cell] (update cell prop #(get remap-tracks % %)))))))
(declare resize-cell-area)
(declare cells-by-column)
(declare cells-by-row)
(defn- reorder-grid-track
[parent from-index to-index move-content? cells-by tracks-props prop prop-span]
(let [from-track (inc from-index)
to-track (if (< to-index from-index)
(+ to-index 2)
(inc to-index))
move-content?
(and move-content? (not= from-track to-track))
parent
(if move-content?
(->> (concat (cells-by parent (dec from-track))
(cells-by parent (dec to-track)))
(reduce (fn [parent cell]
(cond-> parent
(and (> (get cell prop-span) 1)
(or (> to-track from-track) (not (= to-track (get cell prop))))
(or (< to-track from-track) (not (= to-track (+ (get cell prop) (dec (get cell prop-span)))))))
(resize-cell-area
(:row cell)
(:column cell)
(:row cell)
(:column cell)
(if (= prop :row) 1 (:row-span cell))
(if (= prop :column) 1 (:column-span cell)))))
parent))
parent)
parent
(reorder-grid-tracks parent tracks-props from-index to-index)]
(cond-> parent
move-content?
(swap-track-content prop from-track to-track))))
(defn reorder-grid-column
[parent from-index to-index]
(reorder-grid-track :layout-grid-columns parent from-index to-index))
[parent from-index to-index move-content?]
(reorder-grid-track parent from-index to-index move-content? cells-by-column :layout-grid-columns :column :column-span))
(defn reorder-grid-row
[parent from-index to-index]
(reorder-grid-track :layout-grid-rows parent from-index to-index))
[parent from-index to-index move-content?]
(reorder-grid-track parent from-index to-index move-content? cells-by-row :layout-grid-rows :row :row-span))
(defn cells-seq
[{:keys [layout-grid-cells layout-grid-dir]} & {:keys [sort?] :or {sort? false}}]
@ -992,9 +1057,8 @@
(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)
(if (and (>= new-row 1)
(>= new-column 1)
(>= new-row-span 1)
(>= new-column-span 1))
(let [prev-cell (cell-by-row-column parent row column)
@ -1112,7 +1176,7 @@
(update :shapes #(d/removev children %))
(assign-cells))
children (->> children (remove #(layout-absolute? objects %)))]
children (->> children (remove #(position-absolute? objects %)))]
(-> frame
(update :shapes d/concat-vec children)
@ -1146,22 +1210,30 @@
(assoc parent :shapes (into [] (reverse new-shapes)))))
(defn shapes-by-row
(defn cells-by-row
[parent index]
(->> (:layout-grid-cells parent)
(filter (fn [[_ {:keys [row row-span]}]]
(and (>= (inc index) row)
(< (inc index) (+ row row-span)))))
(map second)
(mapcat :shapes)))
(map second)))
(defn shapes-by-column
(defn cells-by-column
[parent index]
(->> (:layout-grid-cells parent)
(filter (fn [[_ {:keys [column column-span]}]]
(and (>= (inc index) column)
(< (inc index) (+ column column-span)))))
(map second)
(map second)))
(defn shapes-by-row
[parent index]
(->> (cells-by-row parent index)
(mapcat :shapes)))
(defn shapes-by-column
[parent index]
(->> (cells-by-column parent index)
(mapcat :shapes)))
(defn cells-coordinates

View file

@ -1039,9 +1039,7 @@
(rx/of (dwe/start-edition-mode id))
(:group :bool :frame)
(let [shapes-ids (into (d/ordered-set)
(remove #(dm/get-in objects [% :hidden]))
shapes)]
(let [shapes-ids (into (d/ordered-set) shapes)]
(rx/of (dws/select-shapes shapes-ids)))
:svg-raw

View file

@ -7,6 +7,7 @@
(ns app.main.data.workspace.grid-layout.editor
(:require
[app.common.geom.rect :as grc]
[app.common.types.shape.layout :as ctl]
[app.main.data.workspace.state-helpers :as wsh]
[potok.core :as ptk]))
@ -84,3 +85,18 @@
(-> local
(update :vbox merge (select-keys srect [:x :y :x1 :x2 :y1 :y2])))))))))))
(defn select-track-cells
[grid-id type index]
(ptk/reify ::select-track-cells
ptk/UpdateEvent
(update [_ state]
(let [objects (wsh/lookup-page-objects state)
parent (get objects grid-id)
cells
(if (= type :column)
(ctl/cells-by-column parent index)
(ctl/cells-by-row parent index))
selected (into #{} (map :id) cells)]
(assoc-in state [:workspace-grid-edition grid-id :selected] selected)))))

View file

@ -193,7 +193,7 @@
(update :shapes #(d/removev ids %))
(ctl/assign-cells))
ids (->> ids (remove #(ctl/layout-absolute? objects %)))
ids (->> ids (remove #(ctl/position-absolute? objects %)))
frame (-> frame
(update :shapes d/concat-vec ids)
(cond-> (some? cell)

View file

@ -285,7 +285,7 @@
(dwu/commit-undo-transaction undo-id))))))
(defn reorder-layout-track
[ids type from-index to-index]
[ids type from-index to-index move-content?]
(assert (#{:row :column} type))
(ptk/reify ::reorder-layout-track
@ -297,8 +297,8 @@
ids
(fn [shape]
(case type
:row (ctl/reorder-grid-row shape from-index to-index)
:column (ctl/reorder-grid-column shape from-index to-index))))
:row (ctl/reorder-grid-row shape from-index to-index move-content?)
:column (ctl/reorder-grid-column shape from-index to-index move-content?))))
(ptk/data-event :layout/update ids)
(dwu/commit-undo-transaction undo-id))))))
@ -311,11 +311,13 @@
(update [_ state]
(let [objects (wsh/lookup-page-objects state)
shape (get objects (first ids))
highlighted (when hover?
(->> (if (= type :row)
(ctl/shapes-by-row shape index)
(ctl/shapes-by-column shape index))
(set)))]
highlighted
(when hover?
(->> (if (= type :row)
(ctl/shapes-by-row shape index)
(ctl/shapes-by-column shape index))
(set)))]
(cond-> state
hover?
(update-in [:workspace-grid-edition (first ids) :hover-track] (fnil conj #{}) [type index])
@ -359,7 +361,7 @@
all-children (->> parent
:shapes
(map (d/getf objects))
(remove ctl/layout-absolute?))]
(remove ctl/position-absolute?))]
(cond-> shape
;; If the parent is hug width and the direction column

View file

@ -721,7 +721,7 @@
selected (wsh/lookup-selected state {:omit-blocked? true})
selected-shapes (->> selected (map (d/getf objects)))]
(if (every? #(and (ctl/any-layout-immediate-child? objects %)
(not (ctl/layout-absolute? %)))
(not (ctl/position-absolute? %)))
selected-shapes)
(rx/of (reorder-selected-layout-child direction))
(rx/of (nudge-selected-shapes direction shift?)))))))
@ -829,7 +829,7 @@
moving-shapes
(->> moving-shapes
(remove (fn [shape]
(and (ctl/layout-absolute? shape)
(and (ctl/position-absolute? shape)
(= frame-id (:parent-id shape))))))
frame-component

View file

@ -26,6 +26,7 @@
[app.common.types.file :as ctf]
[app.common.types.modifiers :as ctm]
[app.common.types.shape-tree :as ctst]
[app.common.types.shape.layout :as ctl]
[app.config :as cfg]
[app.main.fonts :as fonts]
[app.main.ui.context :as muc]
@ -34,6 +35,7 @@
[app.main.ui.shapes.embed :as embed]
[app.main.ui.shapes.export :as export]
[app.main.ui.shapes.frame :as frame]
[app.main.ui.shapes.grid-layout-viewer :refer [grid-layout-viewer]]
[app.main.ui.shapes.group :as group]
[app.main.ui.shapes.image :as image]
[app.main.ui.shapes.path :as path]
@ -306,11 +308,22 @@
:fill "none"}
[:& shape-wrapper {:shape frame}]]]))
(mf/defc empty-grids
{::mf/wrap-props false}
[{:keys [root-shape-id objects]}]
(let [empty-grids
(->> (cons root-shape-id (cfh/get-children-ids objects root-shape-id))
(filter #(ctl/grid-layout? objects %))
(map #(get objects %))
(filter #(empty? (:shapes %))))]
(for [grid empty-grids]
[:& grid-layout-viewer {:shape grid :objects objects}])))
;; Component for rendering a thumbnail of a single componenent. Mainly
;; used to render thumbnails on assets panel.
(mf/defc component-svg
{::mf/wrap [mf/memo #(mf/deferred % ts/idle-then-raf)]}
[{:keys [objects root-shape zoom] :or {zoom 1} :as props}]
[{:keys [objects root-shape show-grids? zoom] :or {zoom 1} :as props}]
(when root-shape
(let [root-shape-id (:id root-shape)
include-metadata (mf/use-ctx export/include-metadata-ctx)
@ -352,9 +365,59 @@
:xmlns:penpot (when include-metadata "https://penpot.app/xmlns")
:fill "none"}
[:> shape-container {:shape root-shape'}
[:& (mf/provider muc/is-component?) {:value true}
[:& root-shape-wrapper {:shape root-shape' :view-box vbox}]]]])))
[:*
[:> shape-container {:shape root-shape'}
[:& (mf/provider muc/is-component?) {:value true}
[:& root-shape-wrapper {:shape root-shape' :view-box vbox}]]]
(when show-grids?
[:& empty-grids {:root-shape-id root-shape-id :objects objects}])]])))
(mf/defc component-svg-thumbnail
{::mf/wrap [mf/memo #(mf/deferred % ts/idle-then-raf)]}
[{:keys [thumbnail-uri on-error show-grids?
objects root-shape zoom] :or {zoom 1} :as props}]
(when root-shape
(let [root-shape-id (:id root-shape)
vector
(mf/use-memo
(mf/deps (:x root-shape) (:y root-shape))
(fn []
(-> (gpt/point (:x root-shape) (:y root-shape))
(gpt/negate))))
objects
(mf/use-memo
(mf/deps vector objects root-shape-id)
(fn []
(let [children-ids (cons root-shape-id (cfh/get-children-ids objects root-shape-id))
update-fn #(update %1 %2 gsh/transform-shape (ctm/move-modifiers vector))]
(reduce update-fn objects children-ids))))
root-shape' (get objects root-shape-id)
width (:width root-shape' 0)
height (:height root-shape' 0)
width-zoom (* (:width root-shape') zoom)
height-zoom (* (:height root-shape') zoom)
vbox (format-viewbox {:width width :height height})]
[:svg {:view-box vbox
:width (ust/format-precision width-zoom viewbox-decimal-precision)
:height (ust/format-precision height-zoom viewbox-decimal-precision)
:version "1.1"
:xmlns "http://www.w3.org/2000/svg"
:xmlnsXlink "http://www.w3.org/1999/xlink"
:fill "none"}
[:foreignObject {:x 0 :y 0 :width width :height height }
[:img {:src thumbnail-uri
:on-error on-error
:loading "lazy"
:decoding "async"}]]
(when show-grids?
[:& empty-grids {:root-shape-id root-shape-id :objects objects}])])))
(mf/defc object-svg
{::mf/wrap [mf/memo]}

View file

@ -10,6 +10,7 @@
["highlight.js" :as hljs]
[app.common.data.macros :as dm]
[app.main.ui.context :as ctx]
[app.util.dom :as dom]
[cuerdas.core :as str]
[rumext.v2 :as mf]))
@ -19,8 +20,10 @@
(let [new-css-system (mf/use-ctx ctx/new-css-system)
block-ref (mf/use-ref)
code (str/trim code)]
(mf/with-effect [code type]
(mf/with-effect
[code type]
(when-let [node (mf/ref-val block-ref)]
(dom/set-data! node "highlighted" nil)
(hljs/highlightElement node)))
(if new-css-system

View file

@ -21,6 +21,15 @@
(let [percent-val (mth/precision (* value 100) precision)]
(dm/str percent-val "%"))))))
(defn format-frs
([value]
(format-frs value nil))
([value {:keys [precision] :or {precision 2}}]
(let [value (if (string? value) (d/parse-double value) value)]
(when (d/num? value)
(let [value (mth/precision value precision)]
(dm/str value "fr"))))))
(defn format-number
([value]
(format-number value nil))

View file

@ -171,7 +171,7 @@
(cleanup)
(rx/push! global-drag-end nil)
(when (fn? on-drop)
(on-drop side drop-data))))
(on-drop side drop-data event))))
on-drag-end
(fn [event]

View file

@ -16,6 +16,7 @@
[app.common.geom.shapes.points :as gpo]
[app.common.math :as mth]
[app.common.types.modifiers :as ctm]
[app.common.types.shape.layout :as ctl]
[app.common.uuid :as uuid]
[app.main.data.workspace.modifiers :as dwm]
[app.main.data.workspace.state-helpers :as wsh]
@ -693,8 +694,7 @@
objects (wsh/lookup-page-objects @st/state)
children (->> (cfh/get-immediate-children objects frame-id)
(remove :layout-item-absolute)
(remove :hidden))
(remove ctl/position-absolute?))
children-to-display (if (or (= :row-reverse saved-dir)
(= :column-reverse saved-dir))

View file

@ -15,7 +15,6 @@
[app.main.ui.context :as muc]
[app.main.ui.shapes.attrs :as attrs]
[app.main.ui.shapes.custom-stroke :refer [shape-fills shape-strokes]]
[app.main.ui.shapes.grid-layout-viewer :refer [grid-layout-viewer]]
[app.util.debug :as dbg]
[app.util.object :as obj]
[rumext.v2 :as mf]))
@ -165,7 +164,6 @@
[props]
(let [shape (unchecked-get props "shape")
childs (unchecked-get props "childs")
is-component? (mf/use-ctx muc/is-component?)
childs (cond-> childs
(ctl/any-layout? shape)
(cfh/sort-layout-children-z-index))]
@ -175,9 +173,5 @@
(for [item childs]
(let [id (dm/get-prop item :id)]
(when (some? id)
[:& shape-wrapper {:key (dm/str id) :shape item}])))]
(when (and ^boolean is-component?
^boolean (empty? childs))
[:& grid-layout-viewer {:shape shape :childs childs}])])))
[:& shape-wrapper {:key (dm/str id) :shape item}])))]])))

View file

@ -15,7 +15,6 @@
[app.common.geom.shapes.grid-layout :as gsg]
[app.common.geom.shapes.points :as gpo]
[app.common.types.shape.layout :as ctl]
[app.main.refs :as refs]
[rumext.v2 :as mf]))
(mf/defc grid-cell-area-label
@ -85,9 +84,8 @@
{::mf/wrap-props false}
[props]
(let [shape (unchecked-get props "shape")
objects (mf/deref refs/workspace-page-objects)
objects (unchecked-get props "objects")
bounds (d/lazy-map (keys objects) #(gsh/shape->points (get objects %)))
children
(->> (cfh/get-immediate-children objects (:id shape))
(remove :hidden)

View file

@ -5,6 +5,7 @@
;; Copyright (c) KALEIDOS INC
(ns app.main.ui.viewer.inspect.left-sidebar
(:require-macros [app.main.style :as stl])
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
@ -15,7 +16,9 @@
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.components.shape-icon :as si]
[app.main.ui.context :as ctx]
[app.main.ui.icons :as i]
[app.main.ui.workspace.sidebar.layer-item :refer [layer-item-inner]]
[app.main.ui.workspace.sidebar.layer-name :refer [layer-name]]
[app.util.dom :as dom]
[app.util.keyboard :as kbd]
@ -28,43 +31,55 @@
(l/derived st/state)))
(mf/defc layer-item
[{:keys [item selected objects disable-collapse?] :as props}]
(let [id (:id item)
name (:name item)
hidden? (:hidden item)
touched? (-> item :touched seq boolean)
[{:keys [item selected objects disable-collapse? depth component-child? hide-toggle?] :as props}]
(let [new-css-system (mf/use-ctx ctx/new-css-system)
id (:id item)
name (:name item)
hidden? (:hidden item)
touched? (-> item :touched seq boolean)
selected? (contains? selected id)
item-ref (mf/use-ref nil)
depth (+ depth 1)
file (mf/deref refs/viewer-file)
components-v2 (dm/get-in file [:data :options :components-v2])
main-instance? (if components-v2
(ctk/main-instance? item)
true)
collapsed-iref (mf/use-memo
(mf/deps id)
(make-collapsed-iref id))
file (mf/deref refs/viewer-file)
components-v2 (dm/get-in file [:data :options :components-v2])
main-instance?
(if components-v2
(ctk/main-instance? item)
true)
component-tree? (or component-child? (:component-root item))
collapsed-iref
(mf/use-memo
(mf/deps id)
(make-collapsed-iref id))
expanded? (not (mf/deref collapsed-iref))
absolute? (ctl/layout-absolute? item)
absolute? (ctl/item-absolute? item)
toggle-collapse
(fn [event]
(dom/stop-propagation event)
(st/emit! (dv/toggle-collapse id)))
(mf/use-callback
(mf/deps id)
(fn [event]
(dom/stop-propagation event)
(st/emit! (dv/toggle-collapse id))))
select-shape
(fn [event]
(dom/prevent-default event)
(let [id (:id item)]
(cond
(kbd/mod? event)
(st/emit! (dv/toggle-selection id))
(mf/use-callback
(mf/deps id)
(fn [event]
(dom/prevent-default event)
(cond
(kbd/mod? event)
(st/emit! (dv/toggle-selection id))
(kbd/shift? event)
(st/emit! (dv/shift-select-to id))
(kbd/shift? event)
(st/emit! (dv/shift-select-to id))
:else
(st/emit! (dv/select-shape id)))))]
:else
(st/emit! (dv/select-shape id)))))]
(mf/use-effect
(mf/deps selected)
@ -72,55 +87,102 @@
(when (and (= (count selected) 1) selected?)
(dom/scroll-into-view-if-needed! (mf/ref-val item-ref) true))))
[:li {:ref item-ref
:class (dom/classnames
:component (not (nil? (:component-id item)))
:masked (:masked-group item)
:selected selected?)}
(if new-css-system
[:& layer-item-inner
{:ref item-ref
:item item
:depth depth
:read-only? true
:highlighted? false
:selected? selected?
:component-tree? component-tree?
:hidden? hidden?
:filtered? false
:expanded? expanded?
:hide-toggle? hide-toggle?
:on-select-shape select-shape
:on-toggle-collapse toggle-collapse}
[:div.element-list-body {:class (dom/classnames :selected selected?
:icon-layer (= (:type item) :icon))
:on-click select-shape}
[:div.icon
(when absolute?
[:div.absolute i/position-absolute])
[:& si/element-icon {:shape item :main-instance? main-instance?}]]
[:& layer-name {:shape-id id
:shape-name name
:shape-touched? touched?
:hidden? hidden?
:selected? selected?
:type-frame (cfh/frame-shape? item)
:disabled-double-click true}]
(when (and (:shapes item) expanded?)
[:div {:class (stl/css-case
:element-children true
:parent-selected selected?)}
(for [[index id] (reverse (d/enumerate (:shapes item)))]
(when-let [item (get objects id)]
[:& layer-item
{:item item
:selected selected
:index index
:objects objects
:key (dm/str id)
:depth depth
:component-child? component-tree?}]))])]
(when (and (not disable-collapse?) (:shapes item))
[:span.toggle-content
{:on-click toggle-collapse
:class (when expanded? "inverse")}
i/arrow-slide])]
;; OLD
[:li {:ref item-ref
:class (dom/classnames
:component (not (nil? (:component-id item)))
:masked (:masked-group item)
:selected selected?)}
(when (and (:shapes item) expanded?)
[:ul.element-children
(for [[index id] (reverse (d/enumerate (:shapes item)))]
(when-let [item (get objects id)]
[:& layer-item
{:item item
:selected selected
:index index
:objects objects
:key (:id item)}]))])]))
[:div.element-list-body {:class (dom/classnames :selected selected?
:icon-layer (= (:type item) :icon))
:on-click select-shape}
[:div.icon
(when absolute?
[:div.absolute i/position-absolute])
[:& si/element-icon {:shape item :main-instance? main-instance?}]]
[:& layer-name {:shape-id id
:shape-name name
:shape-touched? touched?
:hidden? hidden?
:selected? selected?
:type-frame (cfh/frame-shape? item)
:disabled-double-click true}]
(when (and (not disable-collapse?) (:shapes item))
[:span.toggle-content
{:on-click toggle-collapse
:class (when expanded? "inverse")}
i/arrow-slide])]
(when (and (:shapes item) expanded?)
[:ul.element-children
(for [[index id] (reverse (d/enumerate (:shapes item)))]
(when-let [item (get objects id)]
[:& layer-item
{:item item
:selected selected
:index index
:objects objects
:key (:id item)}]))])])))
(mf/defc left-sidebar
[{:keys [frame page local]}]
(let [selected (:selected local)
(let [new-css-system (mf/use-ctx ctx/new-css-system)
selected (:selected local)
objects (:objects page)]
[:aside.settings-bar.settings-bar-left
[:div.settings-bar-inside
[:ul.element-list
[:& layer-item
{:item frame
:selected selected
:index 0
:objects objects
:disable-collapse? true}]]]]))
(if new-css-system
[:aside {:class (stl/css :settings-bar-left)}
[:div {:class (stl/css :settings-bar-inside)}
[:div {:class (stl/css :element-list)}
[:& layer-item
{:item frame
:selected selected
:index 0
:objects objects
:sortable? false
:filtered? false
:depth -2
:hide-toggle? true}]]]]
[:aside.settings-bar.settings-bar-left
[:div.settings-bar-inside
[:ul.element-list
[:& layer-item
{:item frame
:selected selected
:index 0
:objects objects
:disable-collapse? true}]]]])))

View file

@ -0,0 +1,22 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) KALEIDOS INC
@use "common/refactor/common-refactor.scss" as *;
.settings-bar-left {
background-color: $db-primary;
height: 100%;
width: $s-256;
}
.settings-bar-inside {
display: grid;
grid-template-columns: 100%;
grid-template-rows: 100%;
height: calc(100% - $s-2);
overflow-y: auto;
padding-top: $s-8;
}

View file

@ -19,7 +19,7 @@
[app.main.data.workspace.libraries :as dwl]
[app.main.data.workspace.undo :as dwu]
[app.main.refs :as refs]
[app.main.render :refer [component-svg]]
[app.main.render :refer [component-svg component-svg-thumbnail]]
[app.main.store :as st]
[app.main.ui.components.context-menu :refer [context-menu]]
[app.main.ui.components.context-menu-a11y :refer [context-menu-a11y]]
@ -283,18 +283,26 @@
{::mf/wrap-props false}
[{:keys [file-id root-shape component container]}]
(let [retry (mf/use-state 0)
thumbnail-uri (get-component-thumbnail-uri file-id component)]
(if (some? thumbnail-uri)
[:img {:src thumbnail-uri
:on-error (fn []
(when (@retry < 3)
(inc retry)))
:loading "lazy"
:decoding "async"
:class (dom/classnames (css :thumbnail) true)}]
[:& component-svg {:root-shape root-shape
:objects (:objects container)}])))
thumbnail-uri (get-component-thumbnail-uri file-id component)
handle-error
(mf/use-fn
(mf/deps @retry)
(fn []
(when (@retry < 3)
(inc retry))))]
(if (some? thumbnail-uri)
[:& component-svg-thumbnail
{:thumbnail-uri thumbnail-uri
:on-error handle-error
:root-shape root-shape
:objects (:objects container)
:show-grids? true}]
[:& component-svg
{:root-shape root-shape
:objects (:objects container)
:show-grids? true}])))
(defn generate-components-menu-entries
[shapes components-v2]

View file

@ -32,6 +32,129 @@
[okulary.core :as l]
[rumext.v2 :as mf]))
(mf/defc layer-item-inner
{::mf/wrap-props false
::mf/forward-ref true}
[{:keys [item depth parent-size name-ref children
;; Flags
read-only? highlighted? selected? component-tree?
filtered? expanded? dnd-over? dnd-over-top? dnd-over-bot? hide-toggle?
;; Callbacks
on-select-shape on-context-menu on-pointer-enter on-pointer-leave on-zoom-to-selected
on-toggle-collapse on-enable-drag on-disable-drag on-toggle-visibility on-toggle-blocking]}
dref]
(let [id (:id item)
name (:name item)
blocked? (:blocked item)
hidden? (:hidden item)
has-shapes? (-> item :shapes seq boolean)
touched? (-> item :touched seq boolean)
parent-board? (and (cfh/frame-shape? item)
(= uuid/zero (:parent-id item)))
absolute? (ctl/item-absolute? item)
components-v2 (mf/use-ctx ctx/components-v2)
main-instance? (or (not components-v2) (:main-instance item))]
[:*
[:div {:id id
:ref dref
:on-click on-select-shape
:on-context-menu on-context-menu
:class (stl/css-case
:layer-row true
:highlight highlighted?
:component (some? (:component-id item))
:masked (:masked-group item)
:selected selected?
:type-frame (cfh/frame-shape? item)
:type-bool (cfh/bool-shape? item)
:type-comp component-tree?
:hidden hidden?
:dnd-over dnd-over?
:dnd-over-top dnd-over-top?
:dnd-over-bot dnd-over-bot?
:root-board parent-board?)}
[:span {:class (stl/css-case
:tab-indentation true
:filtered filtered?)
:style {"--depth" depth}}]
[:div {:class (stl/css-case
:element-list-body true
:filtered filtered?
:selected selected?
:icon-layer (= (:type item) :icon))
:style {"--depth" depth}
:on-pointer-enter on-pointer-enter
:on-pointer-leave on-pointer-leave
:on-double-click dom/stop-propagation}
(if (< 0 (count (:shapes item)))
[:div {:class (stl/css :button-content)}
(when (and (not hide-toggle?) (not filtered?))
[:button {:class (stl/css-case
:toggle-content true
:inverse expanded?)
:on-click on-toggle-collapse}
i/arrow-refactor])
[:div {:class (stl/css :icon-shape)
:on-double-click on-zoom-to-selected}
(when absolute?
[:div {:class (stl/css :absolute)}])
[:& sic/element-icon-refactor
{:shape item
:main-instance? main-instance?}]]]
[:div {:class (stl/css :button-content)}
(when (not ^boolean filtered?)
[:span {:class (stl/css :toggle-content)}])
[:div {:class (stl/css :icon-shape)
:on-double-click on-zoom-to-selected}
(when ^boolean absolute?
[:div {:class (stl/css :absolute)}])
[:& sic/element-icon-refactor
{:shape item
:main-instance? main-instance?}]]])
[:& layer-name {:ref name-ref
:shape-id id
:shape-name name
:shape-touched? touched?
:disabled-double-click read-only?
:on-start-edit on-disable-drag
:on-stop-edit on-enable-drag
:depth depth
:parent-size parent-size
:selected? selected?
:type-comp component-tree?
:type-frame (cfh/frame-shape? item)
:hidden? hidden?}]
(when (not read-only?)
[:div {:class (stl/css-case
:element-actions true
:is-parent has-shapes?
:selected hidden?
:selected blocked?)}
[:button {:class (stl/css-case
:toggle-element true
:selected hidden?)
:title (if hidden?
(tr "workspace.shape.menu.show")
(tr "workspace.shape.menu.hide"))
:on-click on-toggle-visibility}
(if ^boolean hidden? i/hide-refactor i/shown-refactor)]
[:button {:class (stl/css-case
:block-element true
:selected blocked?)
:title (if (:blocked item)
(tr "workspace.shape.menu.unlock")
(tr "workspace.shape.menu.lock"))
:on-click on-toggle-blocking}
(if ^boolean blocked? i/lock-refactor i/unlock-refactor)]])]]
children]))
(mf/defc layer-item
{::mf/wrap-props false}
[{:keys [index item selected objects sortable? filtered? depth parent-size component-child? highlighted]}]
@ -53,9 +176,10 @@
selected? (contains? selected id)
highlighted? (contains? highlighted id)
container? (or (cfh/frame-shape? item)
(cfh/group-shape? item))
absolute? (ctl/layout-absolute? item)
absolute? (ctl/item-absolute? item)
components-v2 (mf/use-ctx ctx/components-v2)
read-only? (mf/use-ctx ctx/workspace-read-only?)
@ -210,113 +334,44 @@
(let [scroll-to @scroll-to-middle?]
(ts/schedule
100
#(let [scroll-distance-ratio (dom/get-scroll-distance-ratio node scroll-node)
scroll-behavior (if (> scroll-distance-ratio 1) "instant" "smooth")]
(if scroll-to
(dom/scroll-into-view! first-child-node #js {:block "center" :behavior scroll-behavior :inline "start"})
(do
(dom/scroll-into-view-if-needed! first-child-node #js {:block "center" :behavior scroll-behavior :inline "start"})
(reset! scroll-to-middle? true)))))))]
#(when (and node scroll-node)
(let [scroll-distance-ratio (dom/get-scroll-distance-ratio node scroll-node)
scroll-behavior (if (> scroll-distance-ratio 1) "instant" "smooth")]
(if scroll-to
(dom/scroll-into-view! first-child-node #js {:block "center" :behavior scroll-behavior :inline "start"})
(do
(dom/scroll-into-view-if-needed! first-child-node #js {:block "center" :behavior scroll-behavior :inline "start"})
(reset! scroll-to-middle? true))))))))]
#(when (some? subid)
(rx/dispose! subid))))
(if new-css-system
[:*
[:div {:on-context-menu on-context-menu
:ref dref
:on-click select-shape
:id id
:class (stl/css-case
:layer-row true
:component (some? (:component-id item))
:masked (:masked-group item)
:selected selected?
:type-frame (cfh/frame-shape? item)
:type-bool (cfh/bool-shape? item)
:type-comp component-tree?
:hidden hidden?
:dnd-over (= (:over dprops) :center)
:dnd-over-top (= (:over dprops) :top)
:dnd-over-bot (= (:over dprops) :bot)
:root-board parent-board?)}
[:span {:class (stl/css-case
:tab-indentation true
:filtered filtered?)
:style {"--depth" depth}}]
[:div {:class (stl/css-case
:element-list-body true
:filtered filtered?
:selected selected?
:icon-layer (= (:type item) :icon))
:style {"--depth" depth}
:on-pointer-enter on-pointer-enter
:on-pointer-leave on-pointer-leave
:on-double-click dom/stop-propagation}
(if (< 0 (count (:shapes item)))
[:div {:class (stl/css :button-content)}
(when (not filtered?)
[:button {:class (stl/css-case
:toggle-content true
:inverse expanded?)
:on-click toggle-collapse}
i/arrow-refactor])
[:div {:class (stl/css :icon-shape)
:on-double-click zoom-to-selected}
(when absolute?
[:div {:class (stl/css :absolute)}])
[:& sic/element-icon-refactor
{:shape item
:main-instance? main-instance?}]]]
[:div {:class (stl/css :button-content)}
(when (not ^boolean filtered?)
[:span {:class (stl/css :toggle-content)}])
[:div {:class (stl/css :icon-shape)
:on-double-click zoom-to-selected}
(when ^boolean absolute?
[:div {:class (stl/css :absolute)}])
[:& sic/element-icon-refactor
{:shape item
:main-instance? main-instance?}]]])
[:& layer-name {:ref ref
:shape-id id
:shape-name name
:shape-touched? touched?
:disabled-double-click read-only?
:on-start-edit disable-drag
:on-stop-edit enable-drag
:depth depth
:parent-size parent-size
:selected? selected?
:type-comp component-tree?
:type-frame (cfh/frame-shape? item)
:hidden? hidden?}]
[:div {:class (stl/css-case
:element-actions true
:is-parent has-shapes?
:selected hidden?
:selected blocked?)}
[:button {:class (stl/css-case
:toggle-element true
:selected hidden?)
:title (if hidden?
(tr "workspace.shape.menu.show")
(tr "workspace.shape.menu.hide"))
:on-click toggle-visibility}
(if ^boolean hidden? i/hide-refactor i/shown-refactor)]
[:button {:class (stl/css-case
:block-element true
:selected blocked?)
:title (if (:blocked item)
(tr "workspace.shape.menu.unlock")
(tr "workspace.shape.menu.lock"))
:on-click toggle-blocking}
(if ^boolean blocked? i/lock-refactor i/unlock-refactor)]]]]
[:& layer-item-inner
{:ref dref
:item item
:depth depth
:parent-size parent-size
:name-ref ref
:read-only? read-only?
:highlighted? highlighted?
:selected? selected?
:component-tree? component-tree?
:filtered? filtered?
:expanded? expanded?
:dnd-over? (= (:over dprops) :center)
:dnd-over-top? (= (:over dprops) :top)
:dnd-over-bot? (= (:over dprops) :bot)
:on-select-shape select-shape
:on-context-menu on-context-menu
:on-pointer-enter on-pointer-enter
:on-pointer-leave on-pointer-leave
:on-zoom-to-selected zoom-to-selected
:on-toggle-collapse toggle-collapse
:on-enable-drag enable-drag
:on-disable-drag disable-drag
:on-toggle-visibility toggle-visibility
:on-toggle-blocking toggle-blocking}
(when (and (:shapes item) expanded?)
[:div {:class (stl/css-case
@ -324,11 +379,11 @@
:parent-selected selected?
:sticky-children parent-board?)
:data-id (when ^boolean parent-board? id)}
(for [[index id] (reverse (d/enumerate (:shapes item)))]
(when-let [item (get objects id)]
[:& layer-item
{:item item
:highlighted highlighted
:selected selected
:index index
:objects objects

View file

@ -12,478 +12,242 @@
align-items: center;
width: 100%;
background-color: var(--layer-row-background-color);
border: 2px solid transparent;
.element-list-body {
display: flex;
align-items: center;
height: $s-32;
width: calc(100% - (var(--depth) * var(--layer-indentation-size)));
padding-right: $s-12;
cursor: pointer;
&.filtered {
width: calc(100% - $s-12);
}
.button-content {
display: flex;
height: 100%;
.toggle-content {
@include buttonStyle;
display: grid;
grid-template-columns: 1fr 1fr;
align-items: center;
height: 100%;
width: $s-24;
padding: 0 4px 0 8px;
svg {
@extend .button-icon-small;
stroke: var(--icon-foreground);
}
&.inverse {
svg {
transform: rotate(90deg);
}
.icon-shape {
transform: rotate(-90deg);
}
}
}
.icon-shape {
@include flexCenter;
@include buttonStyle;
position: relative;
justify-self: flex-end;
width: $s-16;
height: 100%;
width: $s-24;
padding: 0 $s-8 0 $s-4;
svg {
@extend .button-icon-small;
stroke: var(--icon-foreground);
}
.absolute {
position: absolute;
background-color: var(--layer-row-foreground-color);
opacity: $op-4;
width: $s-12;
height: $s-12;
border-radius: $br-2;
}
}
}
.element-actions {
display: none;
height: 100%;
.toggle-element,
.block-element {
@include buttonStyle;
@include flexCenter;
height: 100%;
width: $s-24;
margin: 0;
display: none;
svg {
@extend .button-icon-small;
stroke: var(--icon-foreground);
}
}
&.selected {
display: flex;
.toggle-element,
.block-element {
display: flex;
opacity: $op-0;
&.selected {
opacity: $op-10;
}
}
}
}
}
.element-children {
width: 100%;
ul {
margin-bottom: 0;
}
&.parent-selected {
.layer-row {
background-color: var(--layer-child-row-background-color);
}
}
}
&.hidden {
.element-list-body {
.button-content {
.toggle-content {
svg {
opacity: $op-7;
}
}
.icon-shape {
svg {
opacity: $op-7;
}
.absolute {
opacity: $op-1;
}
}
}
.element-actions {
.toggle-element,
.block-element {
svg {
opacity: $op-7;
}
}
}
}
}
&.highlight,
&:hover {
--context-hover-color: var(--layer-row-foreground-color-hover);
--context-hover-opacity: $op-10;
background-color: var(--layer-row-background-color-hover);
color: var(--layer-row-foreground-color-hover);
box-shadow: $s-16 0px 0px 0px var(--layer-row-background-color-hover);
&.hidden {
opacity: $op-10;
}
.element-list-body {
.button-content {
.toggle-content {
background-color: var(--layer-row-background-color-hover);
svg {
opacity: $op-10;
stroke: var(--layer-row-foreground-color-hover);
}
}
.icon-shape {
opacity: $op-10;
svg {
stroke: var(--layer-row-foreground-color-hover);
}
& .absolute {
opacity: $op-4;
background-color: var(--layer-row-foreground-color-hover);
}
}
}
.element-actions {
display: flex;
.toggle-element,
.block-element {
display: flex;
svg {
opacity: $op-10;
stroke: var(--layer-row-foreground-color-hover);
}
}
&.selected {
.toggle-element,
.block-element {
opacity: $op-10;
}
}
}
}
.element-children {
.layer-row {
background-color: transparent;
color: var(--layer-row-foreground-color-hover);
&:hover {
background-color: var(--layer-row-background-color-hover);
}
}
}
}
&.selected {
background-color: var(--layer-row-background-color-selected);
box-shadow: $s-16 0px 0px 0px var(--layer-row-background-color-selected);
.element-list-body {
.button-content {
.toggle-content {
background-color: var(--layer-row-background-color-selected);
svg {
stroke: var(--layer-row-foreground-color-selected);
}
}
.icon-shape {
svg {
stroke: var(--layer-row-foreground-color-selected);
}
.absolute {
background-color: var(--layer-row-foreground-color-selected);
}
}
}
.element-actions {
.toggle-element,
.block-element {
display: flex;
svg {
stroke: var(--layer-row-foreground-color-selected);
}
}
&.selected {
.toggle-element,
.block-element {
display: flex;
opacity: $op-10;
&.selected {
opacity: $op-10;
}
}
}
}
}
.element-children {
background-color: transparent;
color: var(--layer-row-foreground-color-selected);
&:hover {
background-color: var(--layer-row-background-color-selected);
}
}
&:hover {
background-color: var(--layer-row-background-color-selected);
}
}
&.type-comp {
.button-content {
.toggle-content {
svg {
stroke: var(--layer-row-component-foreground-color);
}
}
.icon-shape {
svg {
stroke: var(--layer-row-component-foreground-color);
}
.absolute {
background-color: var(--layer-row-component-foreground-color);
}
}
}
.element-actions {
.toggle-element,
.block-element {
svg {
stroke: var(--layer-row-component-foreground-color);
}
}
}
.element-children {
color: var(--layer-row-component-foreground-color);
}
&.hidden {
.element-list-body {
.button-content {
.toggle-content {
opacity: $op-7;
}
.icon-shape {
opacity: $op-7;
.absolute {
opacity: $op-1;
}
}
}
.element-actions {
.toggle-element,
.block-element {
svg {
opacity: $op-7;
}
}
}
}
&:hover {
.element-list-body {
.button-content {
.toggle-content {
opacity: $op-10;
svg {
stroke: var(--layer-row-foreground-color-hover);
}
}
.icon-shape {
opacity: $op-10;
svg {
stroke: var(--layer-row-foreground-color-hover);
}
& .absolute {
opacity: $op-4;
}
}
}
.element-actions {
.toggle-element,
.block-element {
svg {
opacity: $op-10;
stroke: var(--layer-row-foreground-color-hover);
}
}
}
}
}
}
&.selected.highlight,
&.selected:hover {
background-color: var(--layer-row-background-color-selected);
}
&.type-comp.selected {
.button-content {
.toggle-content {
svg {
stroke: var(--layer-row-component-foreground-color);
}
}
.icon-shape {
svg {
stroke: var(--layer-row-component-foreground-color);
}
.absolute {
background-color: var(--layer-row-component-foreground-color);
}
}
}
.element-actions {
.toggle-element,
.block-element {
svg {
stroke: var(--layer-row-component-foreground-color);
}
}
}
.element-children {
color: var(--layer-row-component-foreground-color);
}
&.hidden {
.element-list-body {
.button-content {
.toggle-content {
opacity: $op-7;
}
.icon-shape {
opacity: $op-7;
.absolute {
opacity: $op-1;
}
}
}
.element-actions {
.toggle-element,
.block-element {
svg {
opacity: $op-7;
}
}
}
}
&:hover {
.element-list-body {
.button-content {
.toggle-content {
opacity: $op-10;
svg {
stroke: var(--layer-row-foreground-color-hover);
}
}
.icon-shape {
opacity: $op-10;
& .absolute {
opacity: $op-4;
}
}
}
.element-actions {
.toggle-element,
.block-element {
svg {
opacity: $op-10;
stroke: var(--layer-row-foreground-color-hover);
}
}
}
}
}
}
&:hover {
.element-list-body {
.button-content {
.toggle-content {
svg {
stroke: var(--layer-row-foreground-color-hover);
}
}
.icon-shape {
svg {
stroke: var(--layer-row-foreground-color-hover);
}
}
}
.element-actions {
.toggle-element,
.block-element {
svg {
stroke: var(--layer-row-foreground-color-hover);
}
}
}
}
}
.parent-selected & {
background-color: var(--layer-child-row-background-color);
}
&:global(.sticky) {
position: sticky;
top: 0px;
z-index: 3;
.parent-selected &.highlight,
.parent-selected &:hover {
background-color: var(--layer-row-background-color-hover);
}
&.dnd-over-bot {
border-bottom: $s-2 solid var(--layer-row-foreground-color-hover);
}
&.dnd-over-top {
border-bottom: $s-2 solid var(--layer-row-foreground-color-hover);
}
&.dnd-over {
border: $s-2 solid var(--layer-row-foreground-color-hover);
}
}
.parent-selected .layer-row {
background-color: var(--layer-child-row-background-color);
&:hover {
background-color: var(--layer-row-background-color-hover);
&.hidden {
.element-children {
.layer-row.highlight &,
.layer-row:hover & {
background-color: var(--layer-row-background-color-selected);
}
.layer-row.type-comp & {
color: var(--layer-row-component-foreground-color);
}
.layer-row.selected & {
background-color: transparent;
color: var(--layer-row-foreground-color-selected);
}
}
.element-list-body {
display: flex;
align-items: center;
height: $s-32;
width: calc(100% - (var(--depth) * var(--layer-indentation-size)));
padding-right: $s-12;
cursor: pointer;
&.filtered {
width: calc(100% - $s-12);
}
}
.element-actions {
display: none;
height: 100%;
&.selected {
display: flex;
}
.layer-row.highlight &,
.layer-row:hover & {
display: flex;
}
}
.button-content {
display: flex;
height: 100%;
}
.icon-shape {
@include flexCenter;
@include buttonStyle;
position: relative;
justify-self: flex-end;
width: $s-16;
height: 100%;
width: $s-24;
padding: 0 $s-8 0 $s-4;
svg {
@extend .button-icon-small;
stroke: var(--icon-foreground);
.layer-row.selected & {
stroke: var(--layer-row-foreground-color-selected);
}
.layer-row.type-comp & {
stroke: var(--layer-row-component-foreground-color);
}
}
.inverse & {
transform: rotate(-90deg);
}
.layer-row.hidden & {
opacity: $op-7;
}
.layer-row.highlight &,
.layer-row:hover & {
opacity: $op-10;
svg {
stroke: var(--layer-row-foreground-color-hover);
}
}
}
.absolute {
position: absolute;
background-color: var(--layer-row-foreground-color);
opacity: $op-4;
width: $s-12;
height: $s-12;
border-radius: $br-2;
.layer-row.hidden & {
opacity: $op-1;
}
.layer-row.type-comp & {
background-color: var(--layer-row-component-foreground-color);
}
.layer-row.highlight &,
.layer-row:hover & {
opacity: $op-4;
background-color: var(--layer-row-foreground-color-hover);
}
.layer-row.selected & {
background-color: var(--layer-row-foreground-color-selected);
}
}
.toggle-content {
@include buttonStyle;
display: grid;
grid-template-columns: 1fr 1fr;
align-items: center;
height: 100%;
width: $s-24;
padding: 0 4px 0 8px;
svg {
@extend .button-icon-small;
stroke: var(--icon-foreground);
.layer-row.hidden & {
opacity: $op-7;
}
.layer-row.selected & {
stroke: var(--layer-row-foreground-color-selected);
}
.layer-row.type-comp & {
stroke: var(--layer-row-component-foreground-color);
}
.layer-row.highlight &,
.layer-row:hover & {
opacity: $op-10;
stroke: var(--layer-row-foreground-color-hover);
}
}
.layer-row.selected & {
background-color: var(--layer-row-background-color-selected);
}
&.inverse svg {
transform: rotate(90deg);
}
}
.toggle-element,
.block-element {
@include buttonStyle;
@include flexCenter;
height: 100%;
width: $s-24;
margin: 0;
display: none;
svg {
@extend .button-icon-small;
stroke: var(--icon-foreground);
.layer-row.hidden & {
opacity: $op-7;
}
.type-comp & {
stroke: var(--layer-row-component-foreground-color);
}
}
.element-actions.selected & {
display: flex;
opacity: $op-0;
&.selected {
opacity: $op-10;
}
.element-list-body {
.button-content {
.toggle-content {
background-color: var(--layer-row-background-color-hover);
svg {
stroke: var(--layer-row-foreground-color-hover);
}
}
.icon-shape {
svg {
stroke: var(--layer-row-foreground-color-hover);
}
.absolute {
background-color: var(--layer-row-foreground-color-hover);
}
}
}
.element-actions {
.toggle-element,
.block-element {
display: flex;
svg {
stroke: var(--layer-row-foreground-color-hover);
}
}
&.selected {
.toggle-element,
.block-element {
opacity: $op-10;
}
}
}
}
.layer-row.highlight &,
.layer-row:hover & {
display: flex;
svg {
opacity: $op-10;
stroke: var(--layer-row-foreground-color-hover);
}
.element-children :global(.layer-row) {
background-color: transparent;
color: var(--layer-row-foreground-color-hover);
&:hover {
background-color: var(--layer-row-background-color-hover);
}
}
.layer-row.selected & {
display: flex;
svg {
stroke: var(--layer-row-foreground-color-selected);
}
}
}
:global(.sticky) {
position: sticky;
top: 0px;
z-index: 3;
}
.tab-indentation {
display: block;
height: $s-16;

View file

@ -47,7 +47,7 @@
highlighted (hooks/use-equal-memo highlighted)
root (get objects uuid/zero)
new-css-system (mf/use-ctx ctx/new-css-system)]
[:ul
[:div
{:class (stl/css new-css-system :element-list)}
[:& hooks/sortable-container {}
(for [[index id] (reverse (d/enumerate (:shapes root)))]

View file

@ -24,10 +24,12 @@
[app.main.ui.components.select :refer [select]]
[app.main.ui.components.title-bar :refer [title-bar]]
[app.main.ui.context :as ctx]
[app.main.ui.formats :as fmt]
[app.main.ui.hooks :as h]
[app.main.ui.icons :as i]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]]
[app.util.keyboard :as kbd]
[cuerdas.core :as str]
[rumext.v2 :as mf]))
@ -951,20 +953,20 @@
(defn manage-values [{:keys [value type]}]
(case type
:auto "auto"
:percent (dm/str value "%")
:flex (dm/str value "fr")
:fixed (dm/str value "px")
:percent (fmt/format-percent value)
:flex (fmt/format-frs value)
:fixed (fmt/format-pixels value)
value))
(mf/defc grid-track-info
[{:keys [is-col? type index column set-column-value set-column-type remove-element reorder-track hover-track]}]
[{:keys [is-col? type index column set-column-value set-column-type remove-element reorder-track hover-track on-select-track]}]
(let [new-css-system (mf/use-ctx ctx/new-css-system)
drop-track
(mf/use-fn
(mf/deps type reorder-track index)
(fn [drop-position data]
(reorder-track type (:index data) (if (= :top drop-position) (dec index) index))))
(fn [drop-position data event]
(reorder-track type (:index data) (if (= :top drop-position) (dec index) index) (kbd/mod? event))))
pointer-enter
(mf/use-fn
@ -978,6 +980,13 @@
(fn []
(hover-track type index false)))
handle-select-track
(mf/use-fn
(mf/deps on-select-track type index)
(fn []
(when on-select-track
(on-select-track type index))))
[dprops dref]
(h/use-sortable
:data-type "penpot/grid-track"
@ -998,7 +1007,8 @@
:on-pointer-leave pointer-leave}
[:div {:class (stl/css :track-info-container)}
[:div {:class (stl/css :track-info-dir-icon)}
[:div {:class (stl/css :track-info-dir-icon)
:on-click handle-select-track}
(if is-col? i/flex-vertical-refactor i/flex-horizontal-refactor)]
[:div {:class (stl/css :track-info-value)}
@ -1057,7 +1067,8 @@
i/minus]])))
(mf/defc grid-columns-row
[{:keys [is-col? expanded? column-values toggle add-new-element set-column-value set-column-type remove-element reorder-track hover-track] :as props}]
[{:keys [is-col? expanded? column-values toggle add-new-element set-column-value set-column-type
remove-element reorder-track hover-track on-select-track] :as props}]
(let [new-css-system (mf/use-ctx ctx/new-css-system)
column-num (count column-values)
@ -1098,7 +1109,8 @@
:set-column-type set-column-type
:remove-element remove-element
:reorder-track reorder-track
:hover-track hover-track}])]])]
:hover-track hover-track
:on-select-track on-select-track}])]])]
[:div.grid-columns
[:div.grid-columns-header
@ -1119,7 +1131,8 @@
:set-column-type set-column-type
:remove-element remove-element
:reorder-track reorder-track
:hover-track hover-track}])]])])))
:hover-track hover-track
:on-select-track on-select-track}])]])])))
;; LAYOUT COMPONENT
@ -1364,12 +1377,7 @@
handle-open-grid-help
(mf/use-callback
(fn []
(st/emit! (dom/open-new-window cf/grid-help-uri))))
handle-locate-grid
(mf/use-callback
(fn []
(st/emit! (dwge/locate-board (first ids)))))]
(st/emit! (dom/open-new-window cf/grid-help-uri))))]
(if new-css-system
[:div {:class (stl/css :element-set)}
@ -1383,7 +1391,18 @@
[:div {:class (stl/css :title-actions)}
[:button {:class (stl/css :remove-layout)
:on-click on-remove-layout}
i/remove-refactor]]
i/remove-refactor]
(when ^boolean grid-enabled?
[:*
[:button {:class (stl/css :add-layout)
:on-click handle-show-layout-dropdown}
i/menu-refactor]
[:& dropdown {:show show-layout-dropdown? :on-close handle-close-layout-options}
[:div {:class (stl/css :layout-options)}
[:button {:class (stl/css :layout-option) :on-click set-flex} "Flex layout"]
[:button {:class (stl/css :layout-option) :on-click set-grid} "Grid layout"]]]])]
[:div {:class (stl/css :title-actions)}
(if ^boolean grid-enabled?
@ -1469,11 +1488,7 @@
:set-justify set-justify-grid}]
[:& justify-grid-row {:is-col? false
:justify-items grid-justify-content-row
:set-justify set-justify-grid}]
[:button {:on-click handle-locate-grid
:class (stl/css :locate-button)}
i/locate-refactor]]]
:set-justify set-justify-grid}]]]
nil)))]
[:div.element-set
@ -1686,8 +1701,8 @@
reorder-track
(mf/use-fn
(mf/deps ids)
(fn [type from-index to-index]
(st/emit! (dwsl/reorder-layout-track ids type from-index to-index))))
(fn [type from-index to-index move-content?]
(st/emit! (dwsl/reorder-layout-track ids type from-index to-index move-content?))))
hover-track
(mf/use-fn
@ -1695,6 +1710,12 @@
(fn [type index hover?]
(st/emit! (dwsl/hover-layout-track ids type index hover?))))
handle-select-track
(mf/use-fn
(mf/deps ids)
(fn [type index]
(st/emit! (dwge/select-track-cells (first ids) type index))))
set-column-value
(mf/use-fn
(mf/deps ids)
@ -1720,7 +1741,7 @@
handle-locate-grid
(mf/use-callback
(fn []
(st/emit! (dwge/locate-board (first ids))))) ]
(st/emit! (dwge/locate-board (first ids)))))]
(if new-css-system
[:div {:class (stl/css :grid-layout-menu)}
@ -1767,7 +1788,8 @@
:set-column-type set-column-type
:remove-element remove-element
:reorder-track reorder-track
:hover-track hover-track}]
:hover-track hover-track
:on-select-track handle-select-track}]
[:& grid-columns-row {:is-col? false
:expanded? @grid-rows-open?
@ -1778,7 +1800,8 @@
:set-column-type set-column-type
:remove-element remove-element
:reorder-track reorder-track
:hover-track hover-track}]]
:hover-track hover-track
:on-select-track handle-select-track}]]
[:div {:class (stl/css :row)}
[:& gap-section {:gap-selected? gap-selected?
:on-change set-gap

View file

@ -222,15 +222,18 @@
}
.track-info-dir-icon {
cursor: pointer;
border-radius: $br-8 0 0 $br-8;
background-color: var(--input-background-color);
padding-left: $s-12;
padding: 0 $s-8;
svg {
@extend .button-icon;
stroke: var(--icon-foreground);
height: 100%;
}
&:hover svg {
stroke: var(--icon-foreground-hover);
}
}
.track-info-value {

View file

@ -378,7 +378,7 @@
selection-parents-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))
selection-parents (mf/deref selection-parents-ref)
is-absolute? (:layout-item-absolute values)
is-absolute? (:layout-item-absolute values)
is-col? (every? ctl/col? selection-parents)
@ -510,7 +510,7 @@
(when open?
[:div {:class (stl/css :flex-element-menu)}
[:div {:class (stl/css :row)}
(when is-flex-parent?
(when (or is-layout-child? is-absolute?)
[:div {:class (stl/css :position-options)}
[:& radio-buttons {:selected (if is-absolute? "absolute" "static")
:on-change on-change-position

View file

@ -93,7 +93,7 @@
selection-parents (mf/deref selection-parents-ref)
flex-child? (->> selection-parents (some ctl/flex-layout?))
absolute? (ctl/layout-absolute? shape)
absolute? (ctl/item-absolute? shape)
flex-container? (ctl/flex-layout? shape)
flex-auto-width? (ctl/auto-width? shape)
flex-fill-width? (ctl/fill-width? shape)

View file

@ -41,7 +41,7 @@
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/item-absolute? shape)
ids (hooks/use-equal-memo ids)
parents-by-ids-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))

View file

@ -43,7 +43,7 @@
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/item-absolute? shape)
ids (hooks/use-equal-memo ids)
parents-by-ids-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))

View file

@ -49,7 +49,7 @@
is-grid-parent? (mf/deref is-grid-parent-ref)
is-layout-container? (ctl/any-layout? shape)
is-layout-child-absolute? (ctl/layout-absolute? shape)
is-layout-child-absolute? (ctl/item-absolute? shape)
ids (hooks/use-equal-memo ids)
parents-by-ids-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))

View file

@ -50,7 +50,7 @@
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/item-absolute? shape)
ids (hooks/use-equal-memo ids)
parents-by-ids-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))

View file

@ -43,7 +43,7 @@
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/item-absolute? shape)
ids (hooks/use-equal-memo ids)
parents-by-ids-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))

View file

@ -43,7 +43,7 @@
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/item-absolute? shape)
ids (hooks/use-equal-memo ids)
parents-by-ids-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))

View file

@ -46,7 +46,7 @@
is-grid-parent* (mf/use-memo (mf/deps ids) #(refs/grid-layout-child? ids))
is-grid-parent? (mf/deref is-grid-parent*)
is-layout-child-absolute? (ctl/layout-absolute? shape)
is-layout-child-absolute? (ctl/item-absolute? shape)
parents-by-ids* (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))
parents (mf/deref parents-by-ids*)]

View file

@ -116,7 +116,7 @@
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/item-absolute? shape)
ids (hooks/use-equal-memo ids)
parents-by-ids-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))

View file

@ -40,7 +40,7 @@
is-grid-parent? (mf/deref is-grid-parent-ref)
layout-container-values (select-keys shape layout-container-flex-attrs)
is-layout-child-absolute? (ctl/layout-absolute? shape)
is-layout-child-absolute? (ctl/item-absolute? shape)
ids (hooks/use-equal-memo ids)
parents-by-ids-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))

View file

@ -664,6 +664,7 @@
(let [target (-> event dom/get-target)
value (-> target dom/get-input-value str/upper)
value-int (d/parse-integer value)
value-int (when value-int (max 0 value-int))
[track-type value]
(cond

View file

@ -61,6 +61,6 @@
[objects shape]
;; Layout children with a transform should be wrapped
(and (ctl/any-layout-immediate-child? objects shape)
(not (ctl/layout-absolute? shape))
(not (ctl/position-absolute? shape))
(not (gmt/unit? (:transform shape)))))

View file

@ -133,7 +133,7 @@ body {
[:height height]]
(or (not (ctl/any-layout-immediate-child? objects shape))
(not (ctl/layout-absolute? shape)))
(not (ctl/position-absolute? shape)))
(conj [:position "relative"])))))
(defn shape->wrapper-child-css-properties

View file

@ -31,7 +31,7 @@
[_ shape objects]
(cond
(or (and (ctl/any-layout-immediate-child? objects shape)
(not (ctl/layout-absolute? shape))
(not (ctl/position-absolute? shape))
(or (cfh/group-like-shape? shape)
(cfh/frame-shape? shape)
(cgc/svg-markup? shape)))
@ -39,7 +39,7 @@
:relative
(and (ctl/any-layout-immediate-child? objects shape)
(not (ctl/layout-absolute? shape)))
(not (ctl/position-absolute? shape)))
nil
:else
@ -50,7 +50,7 @@
(when (and (not (cfh/root-frame? shape))
(or (not (ctl/any-layout-immediate-child? objects shape))
(ctl/layout-absolute? shape)))
(ctl/position-absolute? shape)))
(let [parent (get objects (:parent-id shape))
@ -291,7 +291,7 @@
(defn get-grid-coord
[shape objects prop span-prop]
(when (and (ctl/grid-layout-immediate-child? objects shape)
(not (ctl/layout-absolute? shape)))
(not (ctl/position-absolute? shape)))
(let [parent (get objects (:parent-id shape))
cell (ctl/get-cell-by-shape-id parent (:id shape))]
(when (and
@ -314,7 +314,7 @@
(defmethod get-value :grid-area
[_ shape objects]
(when (and (ctl/grid-layout-immediate-child? objects shape)
(not (ctl/layout-absolute? shape)))
(not (ctl/position-absolute? shape)))
(let [parent (get objects (:parent-id shape))
cell (ctl/get-cell-by-shape-id parent (:id shape))]
(when (and (= (:position cell) :area) (d/not-empty? (:area-name cell)))