diff --git a/CHANGES.md b/CHANGES.md
index b4cdb7bf3d..7e1a1760b2 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -20,11 +20,13 @@
- Select through stroke only rectangle [Taiga #5484](https://tree.taiga.io/project/penpot/issue/5484)
- Override browser Ctrl+ and Ctrl- zoom with Penpot Zoom [Taiga #3200](https://tree.taiga.io/project/penpot/us/3200)
+- Reduce handlers for the flex layout gaps and paddings
### :bug: Bugs fixed
-- Fix pixelated thumbnails [Github
- #3681](https://github.com/penpot/penpot/issues/3681) [Github #3661](https://github.com/penpot/penpot/issues/3661)
+- Fix pixelated thumbnails [Github #3681](https://github.com/penpot/penpot/issues/3681) [Github #3661](https://github.com/penpot/penpot/issues/3661)
+- Fix problem with not applying colors to boards [Github #3941](https://github.com/penpot/penpot/issues/3941)
+- Fix problem with path editor undoing changes [Github #3998](https://github.com/penpot/penpot/issues/3998)
### :arrow_up: Deps updates
diff --git a/common/src/app/common/files/changes_builder.cljc b/common/src/app/common/files/changes_builder.cljc
index fa35400bc8..edf5a0166c 100644
--- a/common/src/app/common/files/changes_builder.cljc
+++ b/common/src/app/common/files/changes_builder.cljc
@@ -465,7 +465,7 @@
{:type :add-obj
:id id
:page-id page-id
- :parent-id (:frame-id shape)
+ :parent-id (:parent-id shape)
:frame-id (:frame-id shape)
:index (cfh/get-position-on-parent objects id)
:obj (cond-> shape
diff --git a/common/src/app/common/files/helpers.cljc b/common/src/app/common/files/helpers.cljc
index 01507eea09..dce09d5dd5 100644
--- a/common/src/app/common/files/helpers.cljc
+++ b/common/src/app/common/files/helpers.cljc
@@ -72,13 +72,6 @@
(and (some? shape)
(= :bool (dm/get-prop shape :type))))
-(defn group-like-shape?
- ([objects id]
- (group-like-shape? (get objects id)))
- ([shape]
- (or ^boolean (group-shape? shape)
- ^boolean (bool-shape? shape))))
-
(defn text-shape?
[shape]
(and (some? shape)
@@ -123,6 +116,14 @@
([shape]
(d/not-empty? (:shapes shape))))
+(defn group-like-shape?
+ ([objects id]
+ (group-like-shape? (get objects id)))
+ ([shape]
+ (or ^boolean (group-shape? shape)
+ ^boolean (bool-shape? shape)
+ ^boolean (and (svg-raw-shape? shape) (has-children? shape)))))
+
;; ---- ACCESSORS
(defn get-children-ids
diff --git a/common/src/app/common/files/libraries_helpers.cljc b/common/src/app/common/files/libraries_helpers.cljc
index cf4b637c4f..bd750dee52 100644
--- a/common/src/app/common/files/libraries_helpers.cljc
+++ b/common/src/app/common/files/libraries_helpers.cljc
@@ -39,6 +39,7 @@
[it shapes objects page-id file-id components-v2 prepare-create-group prepare-create-board]
(let [changes (pcb/empty-changes it page-id)
+ from-singe-frame? (and (= 1 (count shapes)) (-> shapes first cfh/frame-shape?))
[root changes old-root-ids]
(if (and (= (count shapes) 1)
(or (and (= (:type (first shapes)) :group) (not components-v2))
@@ -72,6 +73,15 @@
[root changes (map :id shapes)]))
+ changes
+ (cond-> changes
+ (not from-singe-frame?)
+ (pcb/update-shapes
+ (:shapes root)
+ (fn [shape]
+ (-> shape
+ (assoc :constraints-h :scale :constraints-v :scale)))))
+
objects' (assoc objects (:id root) root)
[root-shape changes] (generate-add-component-changes changes root objects' file-id page-id components-v2)
diff --git a/common/src/app/common/files/shapes_helpers.cljc b/common/src/app/common/files/shapes_helpers.cljc
index 8a656886f1..03e3e89c1c 100644
--- a/common/src/app/common/files/shapes_helpers.cljc
+++ b/common/src/app/common/files/shapes_helpers.cljc
@@ -71,6 +71,23 @@
parent-id (or parent-id (dm/get-in objects [selected-id :parent-id]))
base-parent (get objects parent-id)
+ layout-props
+ (when (and (= 1 (count selected))
+ (ctl/any-layout? base-parent))
+ (let [shape (get objects selected-id)]
+ (select-keys shape ctl/layout-item-props)))
+
+ target-cell-id
+ (if (and (nil? target-cell-id)
+ (ctl/grid-layout? objects parent-id))
+ ;; Find the top-left grid cell of the selected elements
+ (let [ncols (count (:layout-grid-columns base-parent))]
+ (->> selected
+ (map #(ctl/get-cell-by-shape-id base-parent %))
+ (apply min-key (fn [{:keys [row column]}] (+ (* ncols row) column)))
+ :id))
+ target-cell-id)
+
attrs {:type :frame
:x (:x srect)
:y (:y srect)
@@ -90,12 +107,14 @@
:parent-id parent-id
:shapes (into [] selected))
- :always
- (with-meta {:index new-index})
+ (some? layout-props)
+ (d/patch-object layout-props)
(or (not= frame-id uuid/zero) without-fill?)
(assoc :fills [] :hide-in-viewer true)))
+ shape (with-meta shape {:index new-index})
+
[shape changes]
(prepare-add-shape changes shape objects)
@@ -105,15 +124,23 @@
changes
(cond-> changes
(ctl/grid-layout? objects (:parent-id shape))
- (-> (cond-> (some? target-cell-id)
- (pcb/update-shapes
- [(:parent-id shape)]
- (fn [parent]
- (-> parent
- (assoc :layout-grid-cells (:layout-grid-cells base-parent))
- (assoc-in [:layout-grid-cells target-cell-id :shapes] [id])
- (assoc :position :auto)))))
- (pcb/update-shapes [(:parent-id shape)] ctl/assign-cells {:with-objects? true})
+ (-> (pcb/update-shapes
+ [(:parent-id shape)]
+ (fn [parent objects]
+ ;; This restores the grid layout before adding and moving the shapes
+ ;; this is done because the add+move could have altered the layout and we
+ ;; want to do it after both operations are completed. Also here we could
+ ;; asign the new element to a target-cell
+ (-> parent
+ (assoc :layout-grid-cells (:layout-grid-cells base-parent))
+ (assoc :layout-grid-rows (:layout-grid-rows base-parent))
+ (assoc :layout-grid-columns (:layout-grid-columns base-parent))
+
+ (cond-> (some? target-cell-id)
+ (assoc-in [:layout-grid-cells target-cell-id :shapes] [(:id shape)]))
+ (ctl/assign-cells objects)))
+ {:with-objects? true})
+
(pcb/reorder-grid-children [(:parent-id shape)])))]
[shape changes])))))
diff --git a/common/src/app/common/geom/shapes/flex_layout/params.cljc b/common/src/app/common/geom/shapes/flex_layout/params.cljc
index 4293dd53de..7bd1ce855f 100644
--- a/common/src/app/common/geom/shapes/flex_layout/params.cljc
+++ b/common/src/app/common/geom/shapes/flex_layout/params.cljc
@@ -19,7 +19,9 @@
([objects shapes parent]
(when (d/not-empty? shapes)
- (let [points
+ (let [shapes (->> shapes (remove :hidden))
+
+ points
(->> shapes
(map :id)
(ctt/sort-z-index objects)
diff --git a/common/src/app/common/geom/shapes/grid_layout/params.cljc b/common/src/app/common/geom/shapes/grid_layout/params.cljc
index 16fcfd426b..befd93e688 100644
--- a/common/src/app/common/geom/shapes/grid_layout/params.cljc
+++ b/common/src/app/common/geom/shapes/grid_layout/params.cljc
@@ -117,7 +117,8 @@
:layout-grid-rows [ctl/default-track-value ctl/default-track-value]}
(ctl/create-cells [1 1 2 2]))
- (let [all-shapes-rect (gco/shapes->rect shapes)
+ (let [shapes (->> shapes (remove :hidden))
+ all-shapes-rect (gco/shapes->rect shapes)
shapes+bounds
(->> shapes
(map #(vector % (grc/points->rect (get % :points)))))
diff --git a/common/src/app/common/geom/shapes/transforms.cljc b/common/src/app/common/geom/shapes/transforms.cljc
index 6adf456cd4..f9be02adaf 100644
--- a/common/src/app/common/geom/shapes/transforms.cljc
+++ b/common/src/app/common/geom/shapes/transforms.cljc
@@ -414,7 +414,11 @@
(gco/transform-points shape-center (:transform group (gmt/matrix))))
;; Calculate the new selrect
- new-selrect (grc/points->rect base-points)]
+ sr-transform (gmt/transform-in (gco/points->center new-points) (:transform-inverse group (gmt/matrix)))
+ new-selrect
+ (-> new-points
+ (gco/transform-points sr-transform)
+ (grc/points->rect))]
;; Updates the shape and the applytransform-rect will update the other properties
(-> group
diff --git a/common/src/app/common/types/component.cljc b/common/src/app/common/types/component.cljc
index 9dd0a8c80f..67ffd24147 100644
--- a/common/src/app/common/types/component.cljc
+++ b/common/src/app/common/types/component.cljc
@@ -83,18 +83,19 @@
: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-type :layout-item
- :layout-item-h-sizing :layout-item
- :layout-item-v-sizing :layout-item
- :layout-item-max-h :layout-item
- :layout-item-min-h :layout-item
- :layout-item-max-w :layout-item
- :layout-item-min-w :layout-item
- :layout-item-align-self :layout-item})
+ :layout-grid-cells :layout-container})
+(def swap-keep-attrs
+ [:layout-item-margin
+ :layout-item-margin-type
+ :layout-item-h-sizing
+ :layout-item-v-sizing
+ :layout-item-max-h
+ :layout-item-min-h
+ :layout-item-max-w
+ :layout-item-min-w
+ :layout-item-absolute
+ :layout-item-z-index])
(defn instance-root?
"Check if this shape is the head of a top instance."
diff --git a/common/src/app/common/types/container.cljc b/common/src/app/common/types/container.cljc
index f70705230b..b00a41b1a4 100644
--- a/common/src/app/common/types/container.cljc
+++ b/common/src/app/common/types/container.cljc
@@ -6,6 +6,7 @@
(ns app.common.types.container
(:require
+ [app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.helpers :as cfh]
[app.common.geom.point :as gpt]
@@ -287,7 +288,8 @@
component-shape (if components-v2
(-> (get-shape component-page (:main-instance-id component))
(assoc :parent-id nil) ;; On v2 we force parent-id to nil in order to behave like v1
- (assoc :frame-id uuid/zero))
+ (assoc :frame-id uuid/zero)
+ (d/without-keys ctk/swap-keep-attrs))
(get-shape component (:id component)))
orig-pos (gpt/point (:x component-shape) (:y component-shape))
diff --git a/common/src/app/common/types/shape/layout.cljc b/common/src/app/common/types/shape/layout.cljc
index 5a48aa0162..104fb9bc71 100644
--- a/common/src/app/common/types/shape/layout.cljc
+++ b/common/src/app/common/types/shape/layout.cljc
@@ -73,6 +73,18 @@
(def justify-items-types
#{:start :end :center :stretch})
+(def layout-item-props
+ [:layout-item-margin
+ :layout-item-margin-type
+ :layout-item-h-sizing
+ :layout-item-v-sizing
+ :layout-item-max-h
+ :layout-item-min-h
+ :layout-item-max-w
+ :layout-item-min-w
+ :layout-item-absolute
+ :layout-item-z-index])
+
(sm/def! ::layout-attrs
[:map {:title "LayoutAttrs"}
[:layout {:optional true} [::sm/one-of layout-types]]
diff --git a/frontend/resources/images/cap-circle-marker.svg b/frontend/resources/images/cap-circle-marker.svg
deleted file mode 100644
index b41388e506..0000000000
--- a/frontend/resources/images/cap-circle-marker.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/frontend/resources/images/cap-diamond-marker.svg b/frontend/resources/images/cap-diamond-marker.svg
deleted file mode 100644
index 0e23183406..0000000000
--- a/frontend/resources/images/cap-diamond-marker.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/frontend/resources/images/cap-line-arrow.svg b/frontend/resources/images/cap-line-arrow.svg
deleted file mode 100644
index 0df0673ba7..0000000000
--- a/frontend/resources/images/cap-line-arrow.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/frontend/resources/images/cap-round.svg b/frontend/resources/images/cap-round.svg
deleted file mode 100644
index 594e02575f..0000000000
--- a/frontend/resources/images/cap-round.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/frontend/resources/images/cap-square-marker.svg b/frontend/resources/images/cap-square-marker.svg
deleted file mode 100644
index 2340ce571b..0000000000
--- a/frontend/resources/images/cap-square-marker.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/frontend/resources/images/cap-square.svg b/frontend/resources/images/cap-square.svg
deleted file mode 100644
index a2e3b6260b..0000000000
--- a/frontend/resources/images/cap-square.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/frontend/resources/images/cap-triangle-arrow.svg b/frontend/resources/images/cap-triangle-arrow.svg
deleted file mode 100644
index f294d01cf5..0000000000
--- a/frontend/resources/images/cap-triangle-arrow.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/frontend/resources/images/icons/cap-circle-marker.svg b/frontend/resources/images/icons/cap-circle-marker.svg
new file mode 100644
index 0000000000..3068ad7b7e
--- /dev/null
+++ b/frontend/resources/images/icons/cap-circle-marker.svg
@@ -0,0 +1,3 @@
+
diff --git a/frontend/resources/images/icons/cap-diamond-marker.svg b/frontend/resources/images/icons/cap-diamond-marker.svg
new file mode 100644
index 0000000000..0391c0ffea
--- /dev/null
+++ b/frontend/resources/images/icons/cap-diamond-marker.svg
@@ -0,0 +1,3 @@
+
diff --git a/frontend/resources/images/icons/cap-line-arrow.svg b/frontend/resources/images/icons/cap-line-arrow.svg
new file mode 100644
index 0000000000..c24e84233d
--- /dev/null
+++ b/frontend/resources/images/icons/cap-line-arrow.svg
@@ -0,0 +1,3 @@
+
diff --git a/frontend/resources/images/icons/cap-round.svg b/frontend/resources/images/icons/cap-round.svg
new file mode 100644
index 0000000000..c717c13b90
--- /dev/null
+++ b/frontend/resources/images/icons/cap-round.svg
@@ -0,0 +1,3 @@
+
diff --git a/frontend/resources/images/icons/cap-square-marker.svg b/frontend/resources/images/icons/cap-square-marker.svg
new file mode 100644
index 0000000000..00407fd379
--- /dev/null
+++ b/frontend/resources/images/icons/cap-square-marker.svg
@@ -0,0 +1,3 @@
+
diff --git a/frontend/resources/images/icons/cap-square.svg b/frontend/resources/images/icons/cap-square.svg
new file mode 100644
index 0000000000..24ad4f43f4
--- /dev/null
+++ b/frontend/resources/images/icons/cap-square.svg
@@ -0,0 +1,3 @@
+
diff --git a/frontend/resources/images/icons/cap-triangle-arrow.svg b/frontend/resources/images/icons/cap-triangle-arrow.svg
new file mode 100644
index 0000000000..6d6f159783
--- /dev/null
+++ b/frontend/resources/images/icons/cap-triangle-arrow.svg
@@ -0,0 +1,3 @@
+
diff --git a/frontend/resources/styles/common/refactor/z-index.scss b/frontend/resources/styles/common/refactor/z-index.scss
index fed8066ce4..5be8e2e5bf 100644
--- a/frontend/resources/styles/common/refactor/z-index.scss
+++ b/frontend/resources/styles/common/refactor/z-index.scss
@@ -5,7 +5,7 @@
// Copyright (c) KALEIDOS INC
$z-index-1: 1; // floating elements
-$z-index-2: 2; //sidebars
+$z-index-2: 2; // sidebars
$z-index-3: 3; // context menu
$z-index-4: 4; // modal
$z-index-10: 10;
diff --git a/frontend/resources/styles/main/partials/sidebar.scss b/frontend/resources/styles/main/partials/sidebar.scss
index 88b199e3e5..ce901a6a4d 100644
--- a/frontend/resources/styles/main/partials/sidebar.scss
+++ b/frontend/resources/styles/main/partials/sidebar.scss
@@ -159,11 +159,6 @@
}
}
}
-
- .assets-bar .tool-window {
- flex: none;
- height: auto;
- }
}
.empty {
diff --git a/frontend/src/app/main.cljs b/frontend/src/app/main.cljs
index 7ed4bd059e..868d4b9ea8 100644
--- a/frontend/src/app/main.cljs
+++ b/frontend/src/app/main.cljs
@@ -117,17 +117,17 @@
(st/emit! (initialize)))
(defn ^:export reinit
- []
- ;; NOTE: in cases of some strange behavior after hot-reload,
- ;; uncomment this lines; they make a hard-rerender instead
- ;; soft-rerender.
- ;;
- ;; (mf/unmount! app-root)
- ;; (mf/unmount! modal-root)
- ;; (set! app-root (mf/create-root (dom/get-element "app")))
- ;; (set! modal-root (mf/create-root (dom/get-element "modal")))
- (st/emit! (ev/initialize))
- (init-ui))
+ ([]
+ (reinit false))
+ ([hard?]
+ ;; The hard flag will force to unmount the whole UI and will redraw every component
+ (when hard?
+ (mf/unmount! app-root)
+ (mf/unmount! modal-root)
+ (set! app-root (mf/create-root (dom/get-element "app")))
+ (set! modal-root (mf/create-root (dom/get-element "modal"))))
+ (st/emit! (ev/initialize))
+ (init-ui)))
(defn ^:dev/after-load after-load
[]
diff --git a/frontend/src/app/main/data/dashboard.cljs b/frontend/src/app/main/data/dashboard.cljs
index 10effaf162..c782b49fae 100644
--- a/frontend/src/app/main/data/dashboard.cljs
+++ b/frontend/src/app/main/data/dashboard.cljs
@@ -320,7 +320,9 @@
(update [_ state]
(update state :dashboard-local
assoc :selected-files #{}
- :selected-project nil))))
+ :selected-project nil
+ :menu-open false
+ :menu-pos nil))))
(defn toggle-file-select
[{:keys [id project-id] :as file}]
@@ -339,6 +341,53 @@
(assoc :selected-project project-id))))
state)))))
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Show grid menu
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defn show-file-menu-with-position
+ [file-id pos]
+ (ptk/reify ::show-file-menu-with-position
+ ptk/UpdateEvent
+ (update [_ state]
+ (update state :dashboard-local
+ assoc :menu-open true
+ :menu-pos pos
+ :file-id file-id))))
+
+(defn show-file-menu
+ []
+ (ptk/reify ::show-file-menu
+ ptk/UpdateEvent
+ (update [_ state]
+ (update state :dashboard-local
+ assoc :menu-open true))))
+
+(defn hide-file-menu
+ []
+ (ptk/reify ::hide-file-menu
+ ptk/UpdateEvent
+ (update [_ state]
+ (update state :dashboard-local
+ assoc :menu-open false))))
+
+(defn start-edit-file-name
+ [file-id]
+ (ptk/reify ::start-edit-file-menu
+ ptk/UpdateEvent
+ (update [_ state]
+ (update state :dashboard-local
+ assoc :edition true
+ :file-id file-id))))
+
+(defn stop-edit-file-name
+ []
+ (ptk/reify ::stop-edit-file-name
+ ptk/UpdateEvent
+ (update [_ state]
+ (update state :dashboard-local
+ assoc :edition false))))
+
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Data Modification
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
diff --git a/frontend/src/app/main/data/workspace/colors.cljs b/frontend/src/app/main/data/workspace/colors.cljs
index e20b9019b7..2dd79469e7 100644
--- a/frontend/src/app/main/data/workspace/colors.cljs
+++ b/frontend/src/app/main/data/workspace/colors.cljs
@@ -12,7 +12,6 @@
[app.common.files.helpers :as cfh]
[app.common.schema :as sm]
[app.common.text :as txt]
- [app.common.types.component :as ctk]
[app.main.broadcast :as mbc]
[app.main.data.events :as ev]
[app.main.data.modal :as md]
@@ -427,11 +426,7 @@
(if (empty? pending)
result
(let [cur (first pending)
- ;; We treat frames that aren't components and with no fill the same as groups
- group? (or (cfh/group-shape? objects cur)
- (and (cfh/frame-shape? objects cur)
- (empty? (dm/get-in objects [cur :fills]))
- (not (ctk/instance-head? (get objects cur)))))
+ group? (cfh/group-shape? objects cur)
pending
(if group?
diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs
index ae2d666e88..54047f0e3c 100644
--- a/frontend/src/app/main/data/workspace/libraries.cljs
+++ b/frontend/src/app/main/data/workspace/libraries.cljs
@@ -829,7 +829,7 @@
0)))))
(defn- add-component-for-swap
- [shape file-id id-new-component index target-cell]
+ [shape file-id id-new-component index target-cell keep-props-values]
(dm/assert! (uuid? id-new-component))
(dm/assert! (uuid? file-id))
(ptk/reify ::add-component-for-swap
@@ -856,8 +856,13 @@
(:parent-id shape)
(:frame-id shape))
- ;; We need to set the same index as the original shape
- changes (pcb/change-parent changes (:parent-id shape) [new-shape] index {:component-swap true})]
+ changes
+ (-> changes
+ ;; Restore the properties
+ (pcb/update-shapes [(:id new-shape)] #(d/patch-object % keep-props-values))
+
+ ;; We need to set the same index as the original shape
+ (pcb/change-parent (:parent-id shape) [new-shape] index {:component-swap true}))]
;; First delete so we don't break the grid layout cells
(rx/of (dch/commit-changes changes)
@@ -880,9 +885,12 @@
target-cell (when (ctl/grid-layout? parent)
(ctl/get-cell-by-shape-id parent (:id shape)))
- index (find-shape-index objects (:parent-id shape) (:id shape))]
+ index (find-shape-index objects (:parent-id shape) (:id shape))
+
+ ;; Store the properties that need to be maintained when the component is swapped
+ keep-props-values (select-keys shape ctk/swap-keep-attrs)]
(rx/of (dwsh/delete-shapes nil (d/ordered-set (:id shape)) {:component-swap true})
- (add-component-for-swap shape file-id id-new-component index target-cell)
+ (add-component-for-swap shape file-id id-new-component index target-cell keep-props-values)
(ptk/data-event :layout/update [(:parent-id shape)]))))))
(defn component-multi-swap
@@ -1002,12 +1010,16 @@
(rx/of (dch/commit-changes (assoc changes ;; TODO a ver qué pasa con esto
:file-id file-id))))
(when-not (empty? updated-frames)
- (->> (rx/from updated-frames)
- (rx/mapcat (fn [shape]
- (rx/of
- (dwt/clear-thumbnail file-id (:page-id shape) (:id shape) "frame")
- (when-not (= (:frame-id shape) uuid/zero)
- (dwt/clear-thumbnail file-id (:page-id shape) (:frame-id shape) "frame")))))))
+ (rx/merge
+ (rx/of (ptk/data-event :layout/update (map :id updated-frames)))
+ (->> (rx/from updated-frames)
+ (rx/mapcat
+ (fn [shape]
+ (rx/of
+ (dwt/clear-thumbnail file-id (:page-id shape) (:id shape) "frame")
+ (when-not (= (:frame-id shape) uuid/zero)
+ (dwt/clear-thumbnail file-id (:page-id shape) (:frame-id shape) "frame"))))))))
+
(when (not= file-id library-id)
;; When we have just updated the library file, give some time for the
;; update to finish, before marking this file as synced.
@@ -1054,8 +1066,7 @@
ignore-until (dm/get-in state [:workspace-file :ignore-sync-until])
libraries-need-sync (filter #(seq (assets-need-sync % file-data ignore-until))
(vals (get state :workspace-libraries)))
- do-more-info #(do (modal/show! :libraries-dialog {:starting-tab :updates})
- (st/emit! msg/hide))
+ do-more-info #(modal/show! :libraries-dialog {:starting-tab :updates})
do-update #(do (apply st/emit! (map (fn [library]
(sync-file (:current-file-id state)
(:id library)))
diff --git a/frontend/src/app/main/data/workspace/path/common.cljs b/frontend/src/app/main/data/workspace/path/common.cljs
index 6892484127..8edd06ffe6 100644
--- a/frontend/src/app/main/data/workspace/path/common.cljs
+++ b/frontend/src/app/main/data/workspace/path/common.cljs
@@ -7,6 +7,7 @@
(ns app.main.data.workspace.path.common
(:require
[app.common.schema :as sm]
+ [app.common.svg.path.subpath :as ups]
[app.main.data.workspace.path.state :as st]
[potok.v2.core :as ptk]))
@@ -52,10 +53,11 @@
(dissoc state :last-point :prev-handler :drag-handler :preview))
(defn finish-path
- [_source]
+ []
(ptk/reify ::finish-path
ptk/UpdateEvent
(update [_ state]
(let [id (st/get-path-id state)]
(-> state
- (update-in [:workspace-local :edit-path id] clean-edit-state))))))
+ (update-in [:workspace-local :edit-path id] clean-edit-state)
+ (update-in (st/get-path-location state :content) ups/close-subpaths))))))
diff --git a/frontend/src/app/main/data/workspace/path/drawing.cljs b/frontend/src/app/main/data/workspace/path/drawing.cljs
index 64007cc8fa..2e926e3154 100644
--- a/frontend/src/app/main/data/workspace/path/drawing.cljs
+++ b/frontend/src/app/main/data/workspace/path/drawing.cljs
@@ -153,7 +153,7 @@
drag-events-stream
(rx/of (finish-drag))
(rx/of (close-path-drag-end))))
- (rx/of (common/finish-path "close-path")))))))
+ (rx/of (common/finish-path)))))))
(defn close-path-drag-end []
(ptk/reify ::close-path-drag-end
@@ -253,7 +253,7 @@
(rx/of (common/init-path))
(rx/merge mousemove-events
mousedown-events)
- (rx/of (common/finish-path "after-events")))))))
+ (rx/of (common/finish-path)))))))
(defn setup-frame []
(ptk/reify ::setup-frame
@@ -362,7 +362,7 @@
(not= content old-content) (rx/of (changes/save-path-content)
(start-draw-mode))
(= mode :draw) (rx/of :interrupt)
- :else (rx/of (common/finish-path "changed-content")))))))
+ :else (rx/of (common/finish-path)))))))
(defn change-edit-mode [mode]
(ptk/reify ::change-edit-mode
@@ -376,7 +376,7 @@
(watch [_ state _]
(let [id (st/get-path-id state)]
(cond
- (and id (= :move mode)) (rx/of (common/finish-path "change-edit-mode"))
+ (and id (= :move mode)) (rx/of (common/finish-path))
(and id (= :draw mode)) (rx/of (dwc/hide-toolbar)
(start-draw-mode))
:else (rx/empty))))))
diff --git a/frontend/src/app/main/data/workspace/path/helpers.cljs b/frontend/src/app/main/data/workspace/path/helpers.cljs
index de955f0f15..2facaf53a6 100644
--- a/frontend/src/app/main/data/workspace/path/helpers.cljs
+++ b/frontend/src/app/main/data/workspace/path/helpers.cljs
@@ -12,7 +12,6 @@
[app.common.geom.shapes :as gsh]
[app.common.math :as mth]
[app.common.svg.path.command :as upc]
- [app.common.svg.path.subpath :as ups]
[app.main.data.workspace.path.common :as common]
[app.util.mouse :as mse]
[potok.v2.core :as ptk]))
@@ -117,7 +116,6 @@
(let [command (next-node shape position prev-point prev-handler)]
(-> shape
(update :content (fnil conj []) command)
- (update :content ups/close-subpaths)
(update-selrect))))
(defn angle-points [common p1 p2]
diff --git a/frontend/src/app/main/data/workspace/persistence.cljs b/frontend/src/app/main/data/workspace/persistence.cljs
index b506c13bc5..8cfc5a87f4 100644
--- a/frontend/src/app/main/data/workspace/persistence.cljs
+++ b/frontend/src/app/main/data/workspace/persistence.cljs
@@ -234,7 +234,7 @@
(= (ptk/type event) ::changes-persisted))
(defn shapes-changes-persisted
- [file-id {:keys [revn changes]}]
+ [file-id {:keys [revn changes] persisted-session-id :session-id}]
(dm/assert! (uuid? file-id))
(dm/assert! (int? revn))
(dm/assert! (cpc/check-changes! changes))
@@ -245,24 +245,28 @@
;; NOTE: we don't set the file features context here because
;; there are no useful context for code that need to be executed
;; on the frontend side
-
- (if-let [current-file-id (:current-file-id state)]
- (if (= file-id current-file-id)
- (let [changes (group-by :page-id changes)]
+ (let [current-file-id (:current-file-id state)
+ current-session-id (:session-id state)]
+ (if (and (some? current-file-id)
+ ;; If the remote change is from teh current session we skip
+ (not= persisted-session-id current-session-id))
+ (if (= file-id current-file-id)
+ (let [changes (group-by :page-id changes)]
+ (-> state
+ (update-in [:workspace-file :revn] max revn)
+ (update :workspace-data
+ (fn [file]
+ (loop [fdata file
+ entries (seq changes)]
+ (if-let [[page-id changes] (first entries)]
+ (recur (-> fdata
+ (cpc/process-changes changes)
+ (cond-> (some? page-id)
+ (ctst/update-object-indices page-id)))
+ (rest entries))
+ fdata))))))
(-> state
- (update-in [:workspace-file :revn] max revn)
- (update :workspace-data (fn [file]
- (loop [fdata file
- entries (seq changes)]
- (if-let [[page-id changes] (first entries)]
- (recur (-> fdata
- (cpc/process-changes changes)
- (cond-> (some? page-id)
- (ctst/update-object-indices page-id)))
- (rest entries))
- fdata))))))
- (-> state
- (update-in [:workspace-libraries file-id :revn] max revn)
- (update-in [:workspace-libraries file-id :data] cpc/process-changes changes)))
+ (update-in [:workspace-libraries file-id :revn] max revn)
+ (update-in [:workspace-libraries file-id :data] cpc/process-changes changes)))
- state))))
+ state)))))
diff --git a/frontend/src/app/main/data/workspace/texts.cljs b/frontend/src/app/main/data/workspace/texts.cljs
index 5f7bfb4997..c3e98739ba 100644
--- a/frontend/src/app/main/data/workspace/texts.cljs
+++ b/frontend/src/app/main/data/workspace/texts.cljs
@@ -52,6 +52,14 @@
(when-let [editor (:workspace-editor state)]
(ts/schedule #(.focus ^js editor))))))
+(defn gen-name
+ [editor]
+ (when (some? editor)
+ (let [result
+ (-> (ted/get-editor-current-plain-text editor)
+ (txt/generate-shape-name))]
+ (when (not= result "") result))))
+
(defn update-editor-state
[{:keys [id] :as shape} editor-state]
(ptk/reify ::update-editor-state
@@ -62,7 +70,7 @@
(update state :workspace-editor-state dissoc id)))))
(defn finalize-editor-state
- [id]
+ [id update-name?]
(ptk/reify ::finalize-editor-state
ptk/WatchEvent
(watch [_ state _]
@@ -72,8 +80,8 @@
editor-state (get-in state [:workspace-editor-state id])
content (-> editor-state
(ted/get-editor-current-content))
- text (-> (ted/get-editor-current-plain-text editor-state)
- (txt/generate-shape-name))
+ name (gen-name editor-state)
+
new-shape? (nil? (:content shape))]
(if (ted/content-has-text? content)
(let [content (d/merge (ted/export-content content)
@@ -93,8 +101,8 @@
(assoc :content content)
(cond-> position-data
(assoc :position-data position-data))
- (cond-> new-shape?
- (assoc :name text))
+ (cond-> (and update-name? (some? name))
+ (assoc :name name))
(cond-> (or (some? width) (some? height))
(gsh/transform-shape (ctm/change-size shape width height))))))
{:undo-group (when new-shape? id)})))))
@@ -104,29 +112,31 @@
(dwsh/delete-shapes #{id})))))))))
(defn initialize-editor-state
- [{:keys [id content] :as shape} decorator]
+ [{:keys [id name content] :as shape} decorator]
(ptk/reify ::initialize-editor-state
ptk/UpdateEvent
(update [_ state]
- (let [text-state (some->> content ted/import-content)
- attrs (d/merge txt/default-text-attrs
- (get-in state [:workspace-global :default-font]))
- editor (cond-> (ted/create-editor-state text-state decorator)
- (and (nil? content) (some? attrs))
- (ted/update-editor-current-block-data attrs))]
+ (let [text-state (some->> content ted/import-content)
+ attrs (d/merge txt/default-text-attrs
+ (get-in state [:workspace-global :default-font]))
+ editor (cond-> (ted/create-editor-state text-state decorator)
+ (and (nil? content) (some? attrs))
+ (ted/update-editor-current-block-data attrs))]
(-> state
(assoc-in [:workspace-editor-state id] editor))))
ptk/WatchEvent
- (watch [_ _ stream]
+ (watch [_ state stream]
;; We need to finalize editor on two main events: (1) when user
;; explicitly navigates to other section or page; (2) when user
;; leaves the editor.
- (->> (rx/merge
- (rx/filter (ptk/type? ::rt/navigate) stream)
- (rx/filter #(= ::finalize-editor-state %) stream))
- (rx/take 1)
- (rx/map #(finalize-editor-state id))))))
+ (let [editor (dm/get-in state [:workspace-editor-state id])
+ update-name? (or (nil? content) (= name (gen-name editor)))]
+ (->> (rx/merge
+ (rx/filter (ptk/type? ::rt/navigate) stream)
+ (rx/filter #(= ::finalize-editor-state %) stream))
+ (rx/take 1)
+ (rx/map #(finalize-editor-state id update-name?)))))))
(defn select-all
"Select all content of the current editor. When not editor found this
diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs
index 33502346b6..58fce57916 100644
--- a/frontend/src/app/main/refs.cljs
+++ b/frontend/src/app/main/refs.cljs
@@ -82,15 +82,27 @@
(dm/get-in state [:dashboard-local :selected-project]))
st/state))
+(defn- dashboard-extract-selected
+ [files selected]
+ (let [get-file #(get files %)
+ sim-file #(select-keys % [:id :name :project-id :is-shared])
+ xform (comp (map get-file)
+ (map sim-file))]
+ (->> (into #{} xform selected)
+ (d/index-by :id))))
+
+(def dashboard-selected-search
+ (l/derived (fn [state]
+ ;; we need to this because :dashboard-search-result is a list
+ ;; of maps and we need a map of maps (using :id as key).
+ (let [files (d/index-by :id (:dashboard-search-result state))]
+ (dashboard-extract-selected files (dm/get-in state [:dashboard-local :selected-files]))))
+ st/state))
+
(def dashboard-selected-files
(l/derived (fn [state]
- (let [get-file #(dm/get-in state [:dashboard-files %])
- sim-file #(select-keys % [:id :name :project-id :is-shared])
- selected (get-in state [:dashboard-local :selected-files])
- xform (comp (map get-file)
- (map sim-file))]
- (->> (into #{} xform selected)
- (d/index-by :id))))
+ (dashboard-extract-selected (:dashboard-files state)
+ (dm/get-in state [:dashboard-local :selected-files])))
st/state =))
;; ---- Workspace refs
diff --git a/frontend/src/app/main/render.cljs b/frontend/src/app/main/render.cljs
index 861935a54c..0a19c8a84c 100644
--- a/frontend/src/app/main/render.cljs
+++ b/frontend/src/app/main/render.cljs
@@ -44,6 +44,7 @@
[app.main.ui.shapes.svg-raw :as svg-raw]
[app.main.ui.shapes.text :as text]
[app.main.ui.shapes.text.fontfaces :as ff]
+ [app.util.dom :as dom]
[app.util.http :as http]
[app.util.strings :as ust]
[app.util.thumbnails :as th]
@@ -626,7 +627,7 @@
bounds (gsb/get-object-bounds objects shape)
background (when (str/ends-with? object-id "component")
- (or (:background options) "#aab5ba"))
+ (or (:background options) (dom/get-css-variable "--assets-component-background-color") "#fff"))
x (dm/get-prop bounds :x)
y (dm/get-prop bounds :y)
diff --git a/frontend/src/app/main/ui/components/color_bullet_new.cljs b/frontend/src/app/main/ui/components/color_bullet_new.cljs
index f15f3549c8..690a0dd794 100644
--- a/frontend/src/app/main/ui/components/color_bullet_new.cljs
+++ b/frontend/src/app/main/ui/components/color_bullet_new.cljs
@@ -26,7 +26,9 @@
(^function on-click color event))))]
(if (uc/multiple? color)
- [:div {:on-click on-click :class (stl/css :color-bullet :multiple)}]
+ [:div {:class (stl/css :color-bullet :multiple)
+ :on-click on-click
+ :title (:color color)}]
;; No multiple selection
(let [color (if (string? color) {:color color :opacity 1} color)
id (:id color)
@@ -44,7 +46,8 @@
:grid-area area
:read-only read-only?)
:data-readonly (str read-only?)
- :on-click on-click}
+ :on-click on-click
+ :title (:color color)}
(cond
(some? gradient)
@@ -72,6 +75,7 @@
:color-text (< size 72)
:small-text (and (>= size 64) (< size 72))
:big-text (>= size 72))
+ :title name
:on-click on-click
:on-double-click on-double-click}
(if (some? image)
diff --git a/frontend/src/app/main/ui/components/color_bullet_new.scss b/frontend/src/app/main/ui/components/color_bullet_new.scss
index 9da0251159..d6e617251a 100644
--- a/frontend/src/app/main/ui/components/color_bullet_new.scss
+++ b/frontend/src/app/main/ui/components/color_bullet_new.scss
@@ -85,8 +85,9 @@
.big-text {
@include inspectValue;
+ @include twoLineTextEllipsis;
color: var(--palette-text-color);
- height: $s-16;
+ height: $s-28;
text-align: center;
}
diff --git a/frontend/src/app/main/ui/components/select.scss b/frontend/src/app/main/ui/components/select.scss
index 6e47f82d18..1f6621c914 100644
--- a/frontend/src/app/main/ui/components/select.scss
+++ b/frontend/src/app/main/ui/components/select.scss
@@ -28,7 +28,6 @@
.current-icon {
@include flexCenter;
- height: $s-24;
width: $s-24;
padding-right: $s-4;
svg {
@@ -49,6 +48,7 @@
.separator {
margin: 0;
height: $s-12;
+ border-top: 1px solid $db-primary;
}
}
.checked-element {
diff --git a/frontend/src/app/main/ui/components/shape_icon_refactor.cljs b/frontend/src/app/main/ui/components/shape_icon_refactor.cljs
index 3b856e929c..d4a9cf9274 100644
--- a/frontend/src/app/main/ui/components/shape_icon_refactor.cljs
+++ b/frontend/src/app/main/ui/components/shape_icon_refactor.cljs
@@ -47,7 +47,7 @@
:exclude i/boolean-exclude-refactor
:intersection i/boolean-intersection-refactor
#_:default i/boolean-union-refactor)
- :svg-raw i/path-refactor
+ :svg-raw i/img-refactor
nil)))
diff --git a/frontend/src/app/main/ui/components/tab_container.cljs b/frontend/src/app/main/ui/components/tab_container.cljs
index 9373027413..495ceeec21 100644
--- a/frontend/src/app/main/ui/components/tab_container.cljs
+++ b/frontend/src/app/main/ui/components/tab_container.cljs
@@ -19,7 +19,7 @@
(mf/defc tab-element
{::mf/wrap-props false}
[{:keys [children]}]
- [:div {:class (stl/css :tab-element)} children])
+ children)
(mf/defc tab-container
{::mf/wrap-props false}
@@ -41,8 +41,8 @@
(when (fn? on-change-tab)
(on-change-tab id)))))]
- [:div {:class (stl/css :tab-container)}
- [:div {:class (dm/str header-class " " (stl/css :tab-container-tabs))}
+ [:section {:class (stl/css :tab-container)}
+ [:header {:class (dm/str header-class " " (stl/css :tab-container-tabs))}
(when ^boolean collapsable
[:button
{:on-click handle-collapse
diff --git a/frontend/src/app/main/ui/components/tab_container.scss b/frontend/src/app/main/ui/components/tab_container.scss
index 06ac807ebb..2db5c9df33 100644
--- a/frontend/src/app/main/ui/components/tab_container.scss
+++ b/frontend/src/app/main/ui/components/tab_container.scss
@@ -7,33 +7,21 @@
.tab-container {
display: grid;
- grid-template-rows: auto 1fr;
- grid-template-columns: 100%;
+ grid-template-rows: $s-32 1fr;
height: 100%;
-
- .tab-container-content {
- overflow-y: auto;
- overflow-x: hidden;
- display: flex;
- flex-direction: column;
- }
-
- .tab-element {
- flex: 1;
- height: 100%;
- }
}
+
.tab-container-tabs {
display: flex;
align-items: center;
flex-direction: row;
gap: $s-2;
- height: $s-32;
border-radius: $br-8;
background: var(--color-background-secondary);
padding: $s-2;
cursor: pointer;
font-size: $fs-12;
+ height: 100%;
.tab-container-tab-wrapper {
@include flexCenter;
flex-direction: row;
@@ -72,6 +60,7 @@
}
}
}
+
.collapse-sidebar {
@include flexCenter;
@include buttonStyle;
@@ -103,3 +92,10 @@
}
}
}
+
+.tab-container-content {
+ overflow-y: auto;
+ overflow-x: hidden;
+ display: flex;
+ flex-direction: column;
+}
diff --git a/frontend/src/app/main/ui/dashboard.cljs b/frontend/src/app/main/ui/dashboard.cljs
index 36aa4b7efd..a568735159 100644
--- a/frontend/src/app/main/ui/dashboard.cljs
+++ b/frontend/src/app/main/ui/dashboard.cljs
@@ -30,6 +30,7 @@
[app.util.keyboard :as kbd]
[app.util.object :as obj]
[goog.events :as events]
+ [okulary.core :as l]
[rumext.v2 :as mf]))
(defn ^boolean uuid-str?
@@ -81,6 +82,7 @@
(mf/use-effect on-resize)
+
[:div {:class (stl/css :dashboard-content)
:on-click clear-selected-fn :ref container}
(case section
@@ -137,6 +139,9 @@
nil)]))
+(def dashboard-initialized
+ (l/derived :current-team-id st/state))
+
(mf/defc dashboard
[{:keys [route profile] :as props}]
(let [section (get-in route [:data :name])
@@ -150,7 +155,9 @@
team (get teams team-id)
projects (mf/deref refs/dashboard-projects)
- project (get projects project-id)]
+ project (get projects project-id)
+
+ initialized? (mf/deref dashboard-initialized)]
(hooks/use-shortcuts ::dashboard sc/shortcuts)
@@ -177,7 +184,7 @@
;; team-id because we want to completely refresh all the
;; components on team change. Many components assumes that the
;; team is already set so don't put the team into mf/deps.
- (when team
+ (when (and team initialized?)
[:main {:class (stl/css :dashboard)
:key (:id team)}
[:& sidebar
diff --git a/frontend/src/app/main/ui/dashboard/file_menu.cljs b/frontend/src/app/main/ui/dashboard/file_menu.cljs
index edda4f2f48..283460530b 100644
--- a/frontend/src/app/main/ui/dashboard/file_menu.cljs
+++ b/frontend/src/app/main/ui/dashboard/file_menu.cljs
@@ -75,6 +75,7 @@
other-teams (remove #(= (:id %) current-team-id) (vals @teams))
current-projects (remove #(= (:id %) (:project-id file))
(:projects current-team))
+
on-new-tab
(fn [_]
(let [path-params {:project-id (:project-id file)
diff --git a/frontend/src/app/main/ui/dashboard/grid.cljs b/frontend/src/app/main/ui/dashboard/grid.cljs
index 4badcf1172..172f4f4ec8 100644
--- a/frontend/src/app/main/ui/dashboard/grid.cljs
+++ b/frontend/src/app/main/ui/dashboard/grid.cljs
@@ -215,31 +215,34 @@
(mf/defc grid-item
{:wrap [mf/memo]}
- [{:keys [file navigate? origin library-view?] :as props}]
+ [{:keys [file origin library-view?] :as props}]
(let [file-id (:id file)
- local (mf/use-state {:menu-open false
- :menu-pos nil
- :edition false})
- selected-files (mf/deref refs/dashboard-selected-files)
+ selected-files (if (= origin :search)
+ (mf/deref refs/dashboard-selected-search)
+ (mf/deref refs/dashboard-selected-files))
+
dashboard-local (mf/deref refs/dashboard-local)
+ file-menu-open? (:menu-open dashboard-local)
+
+ selected? (contains? selected-files file-id)
+
node-ref (mf/use-ref)
menu-ref (mf/use-ref)
- selected? (contains? selected-files file-id)
-
on-menu-close
(mf/use-fn
- #(swap! local assoc :menu-open false))
+ (fn [_]
+ (st/emit! (dd/hide-file-menu))))
on-select
- (fn [event]
- (when (and (or (not selected?) (> (count selected-files) 1))
- (not (:menu-open @local)))
- (dom/stop-propagation event)
- (let [shift? (kbd/shift? event)]
- (when-not shift?
- (st/emit! (dd/clear-selected-files)))
- (st/emit! (dd/toggle-file-select file)))))
+ (mf/use-fn
+ (fn [event]
+ (when (or (not selected?) (> (count selected-files) 1))
+ (dom/stop-propagation event)
+ (let [shift? (kbd/shift? event)]
+ (when-not shift?
+ (st/emit! (dd/clear-selected-files)))
+ (st/emit! (dd/toggle-file-select file))))))
on-navigate
(mf/use-fn
@@ -254,6 +257,7 @@
(mf/use-fn
(mf/deps selected-files)
(fn [event]
+ (st/emit! (dd/hide-file-menu))
(let [offset (dom/get-offset-position (.-nativeEvent event))
select-current? (not (contains? selected-files (:id file)))
@@ -283,6 +287,7 @@
(mf/use-fn
(mf/deps file selected?)
(fn [event]
+ (dom/stop-propagation event)
(dom/prevent-default event)
(when-not selected?
(when-not (kbd/shift? event)
@@ -297,9 +302,7 @@
x (:left points)]
(gpt/point x y))
client-position)]
- (swap! local assoc
- :menu-open true
- :menu-pos position))))
+ (st/emit! (dd/show-file-menu-with-position file-id position)))))
edit
(mf/use-fn
@@ -308,16 +311,14 @@
(let [name (str/trim name)]
(when (not= name "")
(st/emit! (dd/rename-file (assoc file :name name)))))
- (swap! local assoc :edition false)))
+ (st/emit! (dd/stop-edit-file-name))))
on-edit
(mf/use-fn
(mf/deps file)
(fn [event]
(dom/stop-propagation event)
- (swap! local assoc
- :edition true
- :menu-open false)))
+ (st/emit! (dd/start-edit-file-name file-id))))
handle-key-down
(mf/use-callback
@@ -331,10 +332,6 @@
(on-select event)) ;; TODO Fix this
)))]
- (mf/with-effect [selected? local]
- (when (and (not selected?) (:menu-open @local))
- (swap! local assoc :menu-open false)))
-
[:li
{:class (stl/css-case :grid-item true :project-th true :library library-view?)}
[:button
@@ -363,13 +360,13 @@
[:div {:class (stl/css :info-wrapper)}
[:div {:class (stl/css :item-info)}
- (if (:edition @local)
+ (if (and (= file-id (:file-id dashboard-local)) (:edition dashboard-local))
[:& inline-edition {:content (:name file)
:on-end edit}]
[:h3 (:name file)])
[:& grid-item-metadata {:modified-at (:modified-at file)}]]
- [:div {:class (stl/css-case :project-th-actions true :force-display (:menu-open @local))}
+ [:div {:class (stl/css-case :project-th-actions true :force-display (:menu-open dashboard-local))}
[:div
{:class (stl/css :project-th-icon :menu)
:tab-index "0"
@@ -381,16 +378,15 @@
(dom/stop-propagation event)
(on-menu-click event)))}
i/actions
- (when selected?
+ (when (and selected? file-menu-open?)
[:& file-menu {:files (vals selected-files)
- :show? (:menu-open @local)
- :left (+ 24 (:x (:menu-pos @local)))
- :top (:y (:menu-pos @local))
- :navigate? navigate?
+ :show? (:menu-open dashboard-local)
+ :left (+ 24 (:x (:menu-pos dashboard-local)))
+ :top (:y (:menu-pos dashboard-local))
+ :navigate? true
:on-edit on-edit
:on-menu-close on-menu-close
:origin origin
- :dashboard-local dashboard-local
:parent-id (str file-id "-action-menu")}])]]]]]))
(mf/defc grid
diff --git a/frontend/src/app/main/ui/dashboard/team.cljs b/frontend/src/app/main/ui/dashboard/team.cljs
index 50a83cb308..1cb240817c 100644
--- a/frontend/src/app/main/ui/dashboard/team.cljs
+++ b/frontend/src/app/main/ui/dashboard/team.cljs
@@ -692,8 +692,11 @@
[:& header {:section :dashboard-team-invitations
:team team}]
[:section {:class (stl/css :dashboard-container :dashboard-team-invitations)}
- [:& invitation-section {:team team
- :invitations invitations}]]]))
+ ;; TODO: We should consider adding a "loading state" here
+ ;; with an (if (nil? invitations) [:& loading-state] [:& invitations])
+ (when-not (nil? invitations)
+ [:& invitation-section {:team team
+ :invitations invitations}])]]))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; WEBHOOKS SECTION
diff --git a/frontend/src/app/main/ui/flex_controls.cljs b/frontend/src/app/main/ui/flex_controls.cljs
new file mode 100644
index 0000000000..a21c6b9d90
--- /dev/null
+++ b/frontend/src/app/main/ui/flex_controls.cljs
@@ -0,0 +1,16 @@
+;; 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.flex-controls
+ (:require
+ [app.common.data.macros :as dm]
+ [app.main.ui.flex-controls.gap :as fcg]
+ [app.main.ui.flex-controls.margin :as fcm]
+ [app.main.ui.flex-controls.padding :as fcp]))
+
+(dm/export fcg/gap-control)
+(dm/export fcm/margin-control)
+(dm/export fcp/padding-control)
diff --git a/frontend/src/app/main/ui/flex_controls/common.cljs b/frontend/src/app/main/ui/flex_controls/common.cljs
new file mode 100644
index 0000000000..052e501ee2
--- /dev/null
+++ b/frontend/src/app/main/ui/flex_controls/common.cljs
@@ -0,0 +1,35 @@
+(ns app.main.ui.flex-controls.common
+ (:require
+ [app.main.ui.formats :as fmt]
+ [rumext.v2 :as mf]))
+
+;; ------------------------------------------------
+;; CONSTANTS
+;; ------------------------------------------------
+
+(def font-size 11)
+(def distance-color "var(--color-distance)")
+(def distance-text-color "var(--color-white)")
+(def warning-color "var(--color-warning)")
+(def flex-display-pill-width 40)
+(def flex-display-pill-height 20)
+(def flex-display-pill-border-radius 4)
+
+(mf/defc flex-display-pill
+ [{:keys [x y width height font-size border-radius value color]}]
+ [:g.distance-pill
+ [:rect {:x x
+ :y y
+ :width width
+ :height height
+ :rx border-radius
+ :ry border-radius
+ :style {:fill color}}]
+
+ [:text {:x (+ x (/ width 2))
+ :y (+ y (/ height 2))
+ :text-anchor "middle"
+ :dominant-baseline "central"
+ :style {:fill distance-text-color
+ :font-size font-size}}
+ (fmt/format-number (or value 0))]])
diff --git a/frontend/src/app/main/ui/flex_controls/gap.cljs b/frontend/src/app/main/ui/flex_controls/gap.cljs
new file mode 100644
index 0000000000..bd38eba107
--- /dev/null
+++ b/frontend/src/app/main/ui/flex_controls/gap.cljs
@@ -0,0 +1,311 @@
+;; 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.flex-controls.gap
+ (:require
+ [app.common.data :as d]
+ [app.common.files.helpers :as cfh]
+ [app.common.geom.point :as gpt]
+ [app.common.geom.shapes :as gsh]
+ [app.common.geom.shapes.flex-layout :as gsl]
+ [app.common.geom.shapes.points :as gpo]
+ [app.common.types.modifiers :as ctm]
+ [app.common.types.shape.layout :as ctl]
+ [app.main.data.workspace.modifiers :as dwm]
+ [app.main.data.workspace.state-helpers :as wsh]
+ [app.main.refs :as refs]
+ [app.main.store :as st]
+ [app.main.ui.css-cursors :as cur]
+ [app.main.ui.flex-controls.common :as fcc]
+ [app.main.ui.workspace.viewport.viewport-ref :refer [point->viewport]]
+ [app.util.dom :as dom]
+ [rumext.v2 :as mf]))
+
+(mf/defc gap-display
+ [{:keys [frame-id zoom gap-type gap on-pointer-enter on-pointer-leave
+ rect-data hover? selected? mouse-pos hover-value
+ on-move-selected on-context-menu]}]
+ (let [resizing (mf/use-var nil)
+ start (mf/use-var nil)
+ original-value (mf/use-var 0)
+ negate? (:resize-negate? rect-data)
+ axis (:resize-axis rect-data)
+
+ on-pointer-down
+ (mf/use-fn
+ (mf/deps frame-id gap-type gap)
+ (fn [event]
+ (dom/capture-pointer event)
+ (reset! resizing gap-type)
+ (reset! start (dom/get-client-position event))
+ (reset! original-value (:initial-value rect-data))))
+
+ on-lost-pointer-capture
+ (mf/use-fn
+ (mf/deps frame-id gap-type gap)
+ (fn [event]
+ (dom/release-pointer event)
+ (reset! resizing nil)
+ (reset! start nil)
+ (reset! original-value 0)
+ (st/emit! (dwm/apply-modifiers))))
+
+ on-pointer-move
+ (mf/use-fn
+ (mf/deps frame-id gap-type gap)
+ (fn [event]
+ (let [pos (dom/get-client-position event)]
+ (reset! mouse-pos (point->viewport pos))
+ (when (= @resizing gap-type)
+ (let [delta (-> (gpt/to-vec @start pos)
+ (cond-> negate? gpt/negate)
+ (get axis))
+ val (int (max (+ @original-value (/ delta zoom)) 0))
+ layout-gap (assoc gap gap-type val)
+ modifiers (dwm/create-modif-tree [frame-id] (ctm/change-property (ctm/empty) :layout-gap layout-gap))]
+
+ (reset! hover-value val)
+ (st/emit! (dwm/set-modifiers modifiers)))))))]
+
+ [:g.gap-rect
+ [:rect.info-area
+ {:x (:x rect-data)
+ :y (:y rect-data)
+ :width (:width rect-data)
+ :height (:height rect-data)
+ :on-pointer-enter on-pointer-enter
+ :on-pointer-leave on-pointer-leave
+ :on-pointer-move on-pointer-move
+ :on-pointer-down on-move-selected
+ :on-context-menu on-context-menu
+
+ :style {:fill (if (or hover? selected?) fcc/distance-color "none")
+ :opacity (if selected? 0.5 0.25)}}]
+
+ (let [handle-width
+ (if (= axis :x)
+ (/ 2 zoom)
+ (min (* (:width rect-data) 0.5) (/ 20 zoom)))
+
+ handle-height
+ (if (= axis :y)
+ (/ 2 zoom)
+ (min (* (:height rect-data) 0.5) (/ 30 zoom)))]
+ [:rect.handle
+ {:x (+ (:x rect-data) (/ (- (:width rect-data) handle-width) 2))
+ :y (+ (:y rect-data) (/ (- (:height rect-data) handle-height) 2))
+ :width handle-width
+ :height handle-height
+ :on-pointer-enter on-pointer-enter
+ :on-pointer-leave on-pointer-leave
+ :on-pointer-down on-pointer-down
+ :on-lost-pointer-capture on-lost-pointer-capture
+ :on-pointer-move on-pointer-move
+ :on-context-menu on-context-menu
+ :class (when (or hover? selected?)
+ (if (= (:resize-axis rect-data) :x) (cur/get-dynamic "resize-ew" 0) (cur/get-dynamic "resize-ew" 90)))
+ :style {:fill (if (or hover? selected?) fcc/distance-color "none")
+ :opacity (if selected? 0 1)}}])]))
+
+(mf/defc gap-rects
+ [{:keys [frame zoom on-move-selected on-context-menu]}]
+ (let [frame-id (:id frame)
+ saved-dir (:layout-flex-dir frame)
+ is-col? (or (= :column saved-dir) (= :column-reverse saved-dir))
+ flip-x (:flip-x frame)
+ flip-y (:flip-y frame)
+ pill-width (/ fcc/flex-display-pill-width zoom)
+ pill-height (/ fcc/flex-display-pill-height zoom)
+ workspace-modifiers (mf/deref refs/workspace-modifiers)
+ gap-selected (mf/deref refs/workspace-gap-selected)
+ hover (mf/use-var nil)
+ hover-value (mf/use-var 0)
+ mouse-pos (mf/use-var nil)
+ padding (:layout-padding frame)
+ gap (:layout-gap frame)
+ {:keys [width height x1 y1]} (:selrect frame)
+ on-pointer-enter (fn [hover-type val]
+ (reset! hover hover-type)
+ (reset! hover-value val))
+
+ on-pointer-leave #(reset! hover nil)
+ negate {:column-gap (if flip-x true false)
+ :row-gap (if flip-y true false)}
+
+ objects (wsh/lookup-page-objects @st/state)
+ children (->> (cfh/get-immediate-children objects frame-id)
+ (remove ctl/position-absolute?))
+
+ children-to-display (if (or (= :row-reverse saved-dir)
+ (= :column-reverse saved-dir))
+ (drop-last children)
+ (rest children))
+ children-to-display (->> children-to-display
+ (map #(gsh/transform-shape % (get-in workspace-modifiers [(:id %) :modifiers]))))
+
+ wrap-blocks
+ (let [block-children (->> children
+ (map #(vector (gpo/parent-coords-bounds (:points %) (:points frame)) %)))
+ bounds (d/lazy-map (keys objects) #(gsh/shape->points (get objects %)))
+
+ layout-data (gsl/calc-layout-data frame (:points frame) block-children bounds objects)
+ layout-bounds (:layout-bounds layout-data)
+ xv #(gpo/start-hv layout-bounds %)
+ yv #(gpo/start-vv layout-bounds %)]
+ (for [{:keys [start-p line-width line-height layout-gap-row layout-gap-col num-children]} (:layout-lines layout-data)]
+ (let [line-width (if is-col? line-width (+ line-width (* (dec num-children) layout-gap-row)))
+ line-height (if is-col? (+ line-height (* (dec num-children) layout-gap-col)) line-height)
+ end-p (-> start-p (gpt/add (xv line-width)) (gpt/add (yv line-height)))]
+ {:x1 (min (:x start-p) (:x end-p))
+ :y1 (min (:y start-p) (:y end-p))
+ :x2 (max (:x start-p) (:x end-p))
+ :y2 (max (:y start-p) (:y end-p))})))
+
+ block-contains
+ (fn [x y block]
+ (if is-col?
+ (<= (:x1 block) x (:x2 block))
+ (<= (:y1 block) y (:y2 block))))
+
+ get-container-block
+ (fn [shape]
+ (let [selrect (:selrect shape)
+ x (/ (+ (:x1 selrect) (:x2 selrect)) 2)
+ y (/ (+ (:y1 selrect) (:y2 selrect)) 2)]
+ (->> wrap-blocks
+ (filter #(block-contains x y %))
+ first)))
+
+ create-cgdd
+ (fn [shape]
+ (let [block (get-container-block shape)
+ x (if flip-x
+ (- (:x1 (:selrect shape))
+ (get-in shape [:layout-item-margin :m2])
+ (:column-gap gap))
+ (+ (:x2 (:selrect shape)) (get-in shape [:layout-item-margin :m2])))
+ y (:y1 block)
+ h (- (:y2 block) (:y1 block))]
+ {:x x
+ :y y
+ :height h
+ :width (:column-gap gap)
+ :initial-value (:column-gap gap)
+ :resize-type :left
+ :resize-axis :x
+ :resize-negate? (:column-gap negate)
+ :gap-type (if is-col? :row-gap :column-gap)}))
+
+ create-cgdd-block
+ (fn [block]
+ (let [x (if flip-x
+ (- (:x1 block) (:column-gap gap))
+ (:x2 block))
+ y (if flip-y
+ (+ y1 (:p3 padding))
+ (+ y1 (:p1 padding)))
+ h (- height (+ (:p1 padding) (:p3 padding)))]
+ {:x x
+ :y y
+ :width (:column-gap gap)
+ :height h
+ :initial-value (:column-gap gap)
+ :resize-type :left
+ :resize-axis :x
+ :resize-negate? (:column-gap negate)
+ :gap-type (if is-col? :column-gap :row-gap)}))
+
+ create-rgdd
+ (fn [shape]
+ (let [block (get-container-block shape)
+ x (:x1 block)
+ y (if flip-y
+ (- (:y1 (:selrect shape))
+ (get-in shape [:layout-item-margin :m3])
+ (:row-gap gap))
+ (+ (:y2 (:selrect shape)) (get-in shape [:layout-item-margin :m3])))
+ w (- (:x2 block) (:x1 block))]
+ {:x x
+ :y y
+ :width w
+ :height (:row-gap gap)
+ :initial-value (:row-gap gap)
+ :resize-type :bottom
+ :resize-axis :y
+ :resize-negate? (:row-gap negate)
+ :gap-type (if is-col? :row-gap :column-gap)}))
+
+ create-rgdd-block
+ (fn [block]
+ (let [x (if flip-x
+ (+ x1 (:p2 padding))
+ (+ x1 (:p4 padding)))
+ y (if flip-y
+ (- (:y1 block) (:row-gap gap))
+ (:y2 block))
+ w (- width (+ (:p2 padding) (:p4 padding)))]
+ {:x x
+ :y y
+ :width w
+ :height (:row-gap gap)
+ :initial-value (:row-gap gap)
+ :resize-type :bottom
+ :resize-axis :y
+ :resize-negate? (:row-gap negate)
+ :gap-type (if is-col? :column-gap :row-gap)}))
+
+ display-blocks (if is-col?
+ (->> (drop-last wrap-blocks)
+ (map create-cgdd-block))
+ (->> (drop-last wrap-blocks)
+ (map create-rgdd-block)))
+
+ display-children (if is-col?
+ (->> children-to-display
+ (map create-rgdd))
+ (->> children-to-display
+ (map create-cgdd)))]
+
+ [:g.gaps {:pointer-events "visible"}
+ (for [[index display-item] (d/enumerate (concat display-blocks display-children))]
+ (let [gap-type (:gap-type display-item)]
+ [:& gap-display {:key (str frame-id index)
+ :frame-id frame-id
+ :zoom zoom
+ :gap-type gap-type
+ :gap gap
+ :on-pointer-enter (partial on-pointer-enter gap-type (get gap gap-type))
+ :on-pointer-leave on-pointer-leave
+ :on-move-selected on-move-selected
+ :on-context-menu on-context-menu
+ :rect-data display-item
+ :hover? (= @hover gap-type)
+ :selected? (= gap-selected gap-type)
+ :mouse-pos mouse-pos
+ :hover-value hover-value}]))
+
+ (when @hover
+ [:& fcc/flex-display-pill
+ {:height pill-height
+ :width pill-width
+ :font-size (/ fcc/font-size zoom)
+ :border-radius (/ fcc/flex-display-pill-border-radius zoom)
+ :color fcc/distance-color
+ :x (:x @mouse-pos)
+ :y (- (:y @mouse-pos) pill-width)
+ :value @hover-value}])]))
+
+
+(mf/defc gap-control
+ [{:keys [frame zoom on-move-selected on-context-menu]}]
+ (when frame
+ [:g.measurement-gaps {:pointer-events "none"}
+ [:g.hover-shapes
+ [:& gap-rects
+ {:frame frame
+ :zoom zoom
+ :on-move-selected on-move-selected
+ :on-context-menu on-context-menu}]]]))
diff --git a/frontend/src/app/main/ui/flex_controls/margin.cljs b/frontend/src/app/main/ui/flex_controls/margin.cljs
new file mode 100644
index 0000000000..5ee7d33c55
--- /dev/null
+++ b/frontend/src/app/main/ui/flex_controls/margin.cljs
@@ -0,0 +1,185 @@
+;; 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.flex-controls.margin
+ (:require
+ [app.common.geom.point :as gpt]
+ [app.common.types.modifiers :as ctm]
+ [app.main.data.workspace.modifiers :as dwm]
+ [app.main.refs :as refs]
+ [app.main.store :as st]
+ [app.main.ui.css-cursors :as cur]
+ [app.main.ui.flex-controls.common :as fcc]
+ [app.main.ui.workspace.viewport.viewport-ref :refer [point->viewport]]
+ [app.util.dom :as dom]
+ [rumext.v2 :as mf]))
+
+(mf/defc margin-display [{:keys [shape-id zoom hover-all? hover-v? hover-h? margin-num margin on-pointer-enter on-pointer-leave
+ rect-data hover? selected? mouse-pos hover-value]}]
+ (let [resizing? (mf/use-var false)
+ start (mf/use-var nil)
+ original-value (mf/use-var 0)
+ negate? (true? (:resize-negate? rect-data))
+ axis (:resize-axis rect-data)
+
+ on-pointer-down
+ (mf/use-fn
+ (mf/deps shape-id margin-num margin)
+ (fn [event]
+ (dom/capture-pointer event)
+ (reset! resizing? true)
+ (reset! start (dom/get-client-position event))
+ (reset! original-value (:initial-value rect-data))))
+
+ on-lost-pointer-capture
+ (mf/use-fn
+ (mf/deps shape-id margin-num margin)
+ (fn [event]
+ (dom/release-pointer event)
+ (reset! resizing? false)
+ (reset! start nil)
+ (reset! original-value 0)
+ (st/emit! (dwm/apply-modifiers))))
+
+ on-pointer-move
+ (mf/use-fn
+ (mf/deps shape-id margin-num margin hover-all? hover-v? hover-h?)
+ (fn [event]
+ (let [pos (dom/get-client-position event)]
+ (reset! mouse-pos (point->viewport pos))
+ (when @resizing?
+ (let [delta (-> (gpt/to-vec @start pos)
+ (cond-> negate? gpt/negate)
+ (get axis))
+ val (int (max (+ @original-value (/ delta zoom)) 0))
+ layout-item-margin (cond
+ hover-all? (assoc margin :m1 val :m2 val :m3 val :m4 val)
+ hover-v? (assoc margin :m1 val :m3 val)
+ hover-h? (assoc margin :m2 val :m4 val)
+ :else (assoc margin margin-num val))
+ layout-item-margin-type (if (= (:m1 margin) (:m2 margin) (:m3 margin) (:m4 margin)) :simple :multiple)
+ modifiers (dwm/create-modif-tree [shape-id]
+ (-> (ctm/empty)
+ (ctm/change-property :layout-item-margin layout-item-margin)
+ (ctm/change-property :layout-item-margin-type layout-item-margin-type)))]
+ (reset! hover-value val)
+ (st/emit! (dwm/set-modifiers modifiers)))))))]
+
+ [:rect.margin-rect
+ {:x (:x rect-data)
+ :y (:y rect-data)
+ :width (:width rect-data)
+ :height (:height rect-data)
+ :on-pointer-enter on-pointer-enter
+ :on-pointer-leave on-pointer-leave
+ :on-pointer-down on-pointer-down
+ :on-lost-pointer-capture on-lost-pointer-capture
+ :on-pointer-move on-pointer-move
+ :class (when (or hover? selected?)
+ (if (= (:resize-axis rect-data) :x) (cur/get-dynamic "resize-ew" 0) (cur/get-dynamic "resize-ew" 90)))
+ :style {:fill (if (or hover? selected?) fcc/warning-color "none")
+ :opacity (if selected? 0.5 0.25)}}]))
+
+
+(mf/defc margin-rects [{:keys [shape frame zoom alt? shift?]}]
+ (let [shape-id (:id shape)
+ pill-width (/ fcc/flex-display-pill-width zoom)
+ pill-height (/ fcc/flex-display-pill-height zoom)
+ margins-selected (mf/deref refs/workspace-margins-selected)
+ hover-value (mf/use-var 0)
+ mouse-pos (mf/use-var nil)
+ hover (mf/use-var nil)
+ hover-all? (and (not (nil? @hover)) alt?)
+ hover-v? (and (or (= @hover :m1) (= @hover :m3)) shift?)
+ hover-h? (and (or (= @hover :m2) (= @hover :m4)) shift?)
+ margin (:layout-item-margin shape)
+ {:keys [width height x1 x2 y1 y2]} (:selrect shape)
+ on-pointer-enter (fn [hover-type val]
+ (reset! hover hover-type)
+ (reset! hover-value val))
+ on-pointer-leave #(reset! hover nil)
+ hover? #(or hover-all?
+ (and (or (= % :m1) (= % :m3)) hover-v?)
+ (and (or (= % :m2) (= % :m4)) hover-h?)
+ (= @hover %))
+ margin-display-data {:m1 {:key (str shape-id "-m1")
+ :x x1
+ :y (if (:flip-y frame) y2 (- y1 (:m1 margin)))
+ :width width
+ :height (:m1 margin)
+ :initial-value (:m1 margin)
+ :resize-type :top
+ :resize-axis :y
+ :resize-negate? (:flip-y frame)}
+ :m2 {:key (str shape-id "-m2")
+ :x (if (:flip-x frame) (- x1 (:m2 margin)) x2)
+ :y y1
+ :width (:m2 margin)
+ :height height
+ :initial-value (:m2 margin)
+ :resize-type :left
+ :resize-axis :x
+ :resize-negate? (:flip-x frame)}
+ :m3 {:key (str shape-id "-m3")
+ :x x1
+ :y (if (:flip-y frame) (- y1 (:m3 margin)) y2)
+ :width width
+ :height (:m3 margin)
+ :initial-value (:m3 margin)
+ :resize-type :top
+ :resize-axis :y
+ :resize-negate? (:flip-y frame)}
+ :m4 {:key (str shape-id "-m4")
+ :x (if (:flip-x frame) x2 (- x1 (:m4 margin)))
+ :y y1
+ :width (:m4 margin)
+ :height height
+ :initial-value (:m4 margin)
+ :resize-type :left
+ :resize-axis :x
+ :resize-negate? (:flip-x frame)}}]
+
+ [:g.margins {:pointer-events "visible"}
+ (for [[margin-num rect-data] margin-display-data]
+ [:& margin-display
+ {:key (:key rect-data)
+ :shape-id shape-id
+ :zoom zoom
+ :hover-all? hover-all?
+ :hover-v? hover-v?
+ :hover-h? hover-h?
+ :margin-num margin-num
+ :margin margin
+ :on-pointer-enter (partial on-pointer-enter margin-num (get margin margin-num))
+ :on-pointer-leave on-pointer-leave
+ :rect-data rect-data
+ :hover? (hover? margin-num)
+ :selected? (get margins-selected margin-num)
+ :mouse-pos mouse-pos
+ :hover-value hover-value}])
+
+ (when @hover
+ [:& fcc/flex-display-pill
+ {:height pill-height
+ :width pill-width
+ :font-size (/ fcc/font-size zoom)
+ :border-radius (/ fcc/flex-display-pill-border-radius zoom)
+ :color fcc/warning-color
+ :x (:x @mouse-pos)
+ :y (- (:y @mouse-pos) pill-width)
+ :value @hover-value}])]))
+
+(mf/defc margin-control
+ [{:keys [shape parent zoom alt? shift?]}]
+ (when shape
+ [:g.measurement-gaps {:pointer-events "none"}
+ [:g.hover-shapes
+ [:& margin-rects
+ {:shape shape
+ :frame parent
+ :zoom zoom
+ :alt? alt?
+ :shift? shift?}]]]))
diff --git a/frontend/src/app/main/ui/flex_controls/padding.cljs b/frontend/src/app/main/ui/flex_controls/padding.cljs
new file mode 100644
index 0000000000..5b64c94fb3
--- /dev/null
+++ b/frontend/src/app/main/ui/flex_controls/padding.cljs
@@ -0,0 +1,223 @@
+;; 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.flex-controls.padding
+ (:require
+ [app.common.geom.point :as gpt]
+ [app.common.types.modifiers :as ctm]
+ [app.main.data.workspace.modifiers :as dwm]
+ [app.main.refs :as refs]
+ [app.main.store :as st]
+ [app.main.ui.css-cursors :as cur]
+ [app.main.ui.flex-controls.common :as fcc]
+ [app.main.ui.workspace.viewport.viewport-ref :refer [point->viewport]]
+ [app.util.dom :as dom]
+ [rumext.v2 :as mf]))
+
+(mf/defc padding-display
+ [{:keys [frame-id zoom hover-all? hover-v? hover-h? padding-num padding on-pointer-enter on-pointer-leave
+ rect-data hover? selected? mouse-pos hover-value on-move-selected on-context-menu]}]
+ (let [resizing? (mf/use-var false)
+ start (mf/use-var nil)
+ original-value (mf/use-var 0)
+ negate? (true? (:resize-negate? rect-data))
+ axis (:resize-axis rect-data)
+
+ on-pointer-down
+ (mf/use-fn
+ (mf/deps frame-id rect-data padding-num)
+ (fn [event]
+ (dom/capture-pointer event)
+ (reset! resizing? true)
+ (reset! start (dom/get-client-position event))
+ (reset! original-value (:initial-value rect-data))))
+
+ on-lost-pointer-capture
+ (mf/use-fn
+ (mf/deps frame-id padding-num padding)
+ (fn [event]
+ (dom/release-pointer event)
+ (reset! resizing? false)
+ (reset! start nil)
+ (reset! original-value 0)
+ (st/emit! (dwm/apply-modifiers))))
+
+ on-pointer-move
+ (mf/use-fn
+ (mf/deps frame-id padding-num padding hover-all? hover-v? hover-h?)
+ (fn [event]
+ (let [pos (dom/get-client-position event)]
+ (reset! mouse-pos (point->viewport pos))
+ (when @resizing?
+ (let [delta (-> (gpt/to-vec @start pos)
+ (cond-> negate? gpt/negate)
+ (get axis))
+ val (int (max (+ @original-value (/ delta zoom)) 0))
+ layout-padding (cond
+ hover-all? (assoc padding :p1 val :p2 val :p3 val :p4 val)
+ hover-v? (assoc padding :p1 val :p3 val)
+ hover-h? (assoc padding :p2 val :p4 val)
+ :else (assoc padding padding-num val))
+
+
+ layout-padding-type (if (= (:p1 padding) (:p2 padding) (:p3 padding) (:p4 padding)) :simple :multiple)
+ modifiers (dwm/create-modif-tree [frame-id]
+ (-> (ctm/empty)
+ (ctm/change-property :layout-padding layout-padding)
+ (ctm/change-property :layout-padding-type layout-padding-type)))]
+ (reset! hover-value val)
+ (st/emit! (dwm/set-modifiers modifiers)))))))]
+
+ [:g.padding-rect
+ [:rect.info-area
+ {:x (:x rect-data)
+ :y (:y rect-data)
+ :width (max 0 (:width rect-data))
+ :height (max 0 (:height rect-data))
+
+ :on-pointer-enter on-pointer-enter
+ :on-pointer-leave on-pointer-leave
+ :on-pointer-move on-pointer-move
+ :on-pointer-down on-move-selected
+ :on-context-menu on-context-menu
+ :style {:fill (if (or hover? selected?) fcc/distance-color "none")
+ :opacity (if selected? 0.5 0.25)}}]
+
+ (let [handle-width
+ (if (= axis :x)
+ (/ 2 zoom)
+ (min (* (:width rect-data) 0.5) (/ 20 zoom)))
+
+ handle-height
+ (if (= axis :y)
+ (/ 2 zoom)
+ (min (* (:height rect-data) 0.5) (/ 30 zoom)))]
+ [:rect.handle
+ {:x (+ (:x rect-data) (/ (- (:width rect-data) handle-width) 2))
+ :y (+ (:y rect-data) (/ (- (:height rect-data) handle-height) 2))
+ :width handle-width
+ :height handle-height
+ :on-pointer-enter on-pointer-enter
+ :on-pointer-leave on-pointer-leave
+ :on-pointer-down on-pointer-down
+ :on-lost-pointer-capture on-lost-pointer-capture
+ :on-pointer-move on-pointer-move
+ :on-context-menu on-context-menu
+ :class (when (or hover? selected?)
+ (if (= (:resize-axis rect-data) :x) (cur/get-dynamic "resize-ew" 0) (cur/get-dynamic "resize-ew" 90)))
+ :style {:fill (if (or hover? selected?) fcc/distance-color "none")
+ :opacity (if selected? 0 1)}}])]))
+
+(mf/defc padding-rects
+ [{:keys [frame zoom alt? shift? on-move-selected on-context-menu]}]
+ (let [frame-id (:id frame)
+ paddings-selected (mf/deref refs/workspace-paddings-selected)
+ hover-value (mf/use-var 0)
+ mouse-pos (mf/use-var nil)
+ hover (mf/use-var nil)
+ hover-all? (and (not (nil? @hover)) alt?)
+ hover-v? (and (or (= @hover :p1) (= @hover :p3)) shift?)
+ hover-h? (and (or (= @hover :p2) (= @hover :p4)) shift?)
+ padding (:layout-padding frame)
+ {:keys [width height x1 x2 y1 y2]} (:selrect frame)
+ on-pointer-enter (fn [hover-type val]
+ (reset! hover hover-type)
+ (reset! hover-value val))
+ on-pointer-leave #(reset! hover nil)
+ pill-width (/ fcc/flex-display-pill-width zoom)
+ pill-height (/ fcc/flex-display-pill-height zoom)
+ hover? #(or hover-all?
+ (and (or (= % :p1) (= % :p3)) hover-v?)
+ (and (or (= % :p2) (= % :p4)) hover-h?)
+ (= @hover %))
+ negate {:p1 (if (:flip-y frame) true false)
+ :p2 (if (:flip-x frame) true false)
+ :p3 (if (:flip-y frame) true false)
+ :p4 (if (:flip-x frame) true false)}
+ negate (cond-> negate
+ (not= :auto (:layout-item-h-sizing frame)) (assoc :p2 (not (:p2 negate)))
+ (not= :auto (:layout-item-v-sizing frame)) (assoc :p3 (not (:p3 negate))))
+
+ padding-rect-data {:p1 {:key (str frame-id "-p1")
+ :x x1
+ :y (if (:flip-y frame) (- y2 (:p1 padding)) y1)
+ :width width
+ :height (:p1 padding)
+ :initial-value (:p1 padding)
+ :resize-type (if (:flip-y frame) :bottom :top)
+ :resize-axis :y
+ :resize-negate? (:p1 negate)}
+ :p2 {:key (str frame-id "-p2")
+ :x (if (:flip-x frame) x1 (- x2 (:p2 padding)))
+ :y y1
+ :width (:p2 padding)
+ :height height
+ :initial-value (:p2 padding)
+ :resize-type :left
+ :resize-axis :x
+ :resize-negate? (:p2 negate)}
+ :p3 {:key (str frame-id "-p3")
+ :x x1
+ :y (if (:flip-y frame) y1 (- y2 (:p3 padding)))
+ :width width
+ :height (:p3 padding)
+ :initial-value (:p3 padding)
+ :resize-type :bottom
+ :resize-axis :y
+ :resize-negate? (:p3 negate)}
+ :p4 {:key (str frame-id "-p4")
+ :x (if (:flip-x frame) (- x2 (:p4 padding)) x1)
+ :y y1
+ :width (:p4 padding)
+ :height height
+ :initial-value (:p4 padding)
+ :resize-type (if (:flip-x frame) :right :left)
+ :resize-axis :x
+ :resize-negate? (:p4 negate)}}]
+
+ [:g.paddings {:pointer-events "visible"}
+ (for [[padding-num rect-data] padding-rect-data]
+ [:& padding-display
+ {:key (:key rect-data)
+ :frame-id frame-id
+ :zoom zoom
+ :hover-all? hover-all?
+ :hover-v? hover-v?
+ :hover-h? hover-h?
+ :padding padding
+ :mouse-pos mouse-pos
+ :hover-value hover-value
+ :padding-num padding-num
+ :on-pointer-enter (partial on-pointer-enter padding-num (get padding padding-num))
+ :on-pointer-leave on-pointer-leave
+ :on-move-selected on-move-selected
+ :on-context-menu on-context-menu
+ :hover? (hover? padding-num)
+ :selected? (get paddings-selected padding-num)
+ :rect-data rect-data}])
+ (when @hover
+ [:& fcc/flex-display-pill
+ {:height pill-height
+ :width pill-width
+ :font-size (/ fcc/font-size zoom)
+ :border-radius (/ fcc/flex-display-pill-border-radius zoom)
+ :color fcc/distance-color
+ :x (:x @mouse-pos)
+ :y (- (:y @mouse-pos) pill-width)
+ :value @hover-value}])]))
+
+(mf/defc padding-control
+ [{:keys [frame zoom alt? shift? on-move-selected on-context-menu]}]
+ (when frame
+ [:g.measurement-gaps {:pointer-events "none"}
+ [:g.hover-shapes
+ [:& padding-rects
+ {:frame frame
+ :zoom zoom
+ :alt? alt?
+ :shift? shift?
+ :on-move-selected on-move-selected
+ :on-context-menu on-context-menu}]]]))
diff --git a/frontend/src/app/main/ui/icons.cljs b/frontend/src/app/main/ui/icons.cljs
index f73fef49be..fd0c0657ff 100644
--- a/frontend/src/app/main/ui/icons.cljs
+++ b/frontend/src/app/main/ui/icons.cljs
@@ -466,6 +466,15 @@
(def ^:icon view-as-icons-refactor (icon-xref :view-as-icons-refactor))
(def ^:icon wrap-refactor (icon-xref :wrap-refactor))
(def ^:icon view-as-list-refactor (icon-xref :view-as-list-refactor))
+(def ^:icon cap-line-arrow (icon-xref :cap-line-arrow))
+(def ^:icon cap-triangle-arrow (icon-xref :cap-triangle-arrow))
+(def ^:icon cap-square-marker (icon-xref :cap-square-marker))
+(def ^:icon cap-circle-marker (icon-xref :cap-circle-marker))
+(def ^:icon cap-diamond-marker (icon-xref :cap-diamond-marker))
+(def ^:icon cap-round (icon-xref :cap-round))
+(def ^:icon cap-square (icon-xref :cap-square))
+
+
(def ^:icon loader-pencil
(mf/html
[:svg
diff --git a/frontend/src/app/main/ui/measurements.cljs b/frontend/src/app/main/ui/measurements.cljs
index 057794e307..9c4f9719c9 100644
--- a/frontend/src/app/main/ui/measurements.cljs
+++ b/frontend/src/app/main/ui/measurements.cljs
@@ -8,24 +8,12 @@
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
- [app.common.files.helpers :as cfh]
[app.common.geom.point :as gpt]
[app.common.geom.rect :as grc]
[app.common.geom.shapes :as gsh]
- [app.common.geom.shapes.flex-layout :as gsl]
- [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]
- [app.main.refs :as refs]
- [app.main.store :as st]
- [app.main.ui.css-cursors :as cur]
[app.main.ui.formats :as fmt]
- [app.main.ui.workspace.viewport.viewport-ref :refer [point->viewport]]
- [app.util.dom :as dom]
[rumext.v2 :as mf]))
;; ------------------------------------------------
@@ -57,9 +45,6 @@
(def distance-pill-height 16)
(def distance-line-stroke 1)
(def warning-color "var(--color-warning)")
-(def flex-display-pill-width 40)
-(def flex-display-pill-height 20)
-(def flex-display-pill-border-radius 4)
;; ------------------------------------------------
;; HELPERS
@@ -277,600 +262,3 @@
[:& size-display {:selrect hover-selrect :zoom zoom}]
[:& distance-display {:from hover-selrect :to selected-selrect :zoom zoom :bounds bounds-selrect}]])])))
-
-
-(mf/defc flex-display-pill [{:keys [x y width height font-size border-radius value color]}]
- [:g.distance-pill
- [:rect {:x x
- :y y
- :width width
- :height height
- :rx border-radius
- :ry border-radius
- :style {:fill color}}]
-
- [:text {:x (+ x (/ width 2))
- :y (+ y (/ height 2))
- :text-anchor "middle"
- :dominant-baseline "central"
- :style {:fill distance-text-color
- :font-size font-size}}
- (fmt/format-number (or value 0))]])
-
-
-(mf/defc padding-display [{:keys [frame-id zoom hover-all? hover-v? hover-h? padding-num padding on-pointer-enter on-pointer-leave
- rect-data hover? selected? mouse-pos hover-value]}]
- (let [resizing? (mf/use-var false)
- start (mf/use-var nil)
- original-value (mf/use-var 0)
- negate? (true? (:resize-negate? rect-data))
- axis (:resize-axis rect-data)
-
- on-pointer-down
- (mf/use-fn
- (mf/deps frame-id rect-data padding-num)
- (fn [event]
- (dom/capture-pointer event)
- (reset! resizing? true)
- (reset! start (dom/get-client-position event))
- (reset! original-value (:initial-value rect-data))))
-
- on-lost-pointer-capture
- (mf/use-fn
- (mf/deps frame-id padding-num padding)
- (fn [event]
- (dom/release-pointer event)
- (reset! resizing? false)
- (reset! start nil)
- (reset! original-value 0)
- (st/emit! (dwm/apply-modifiers))))
-
- on-pointer-move
- (mf/use-fn
- (mf/deps frame-id padding-num padding hover-all? hover-v? hover-h?)
- (fn [event]
- (let [pos (dom/get-client-position event)]
- (reset! mouse-pos (point->viewport pos))
- (when @resizing?
- (let [delta (-> (gpt/to-vec @start pos)
- (cond-> negate? gpt/negate)
- (get axis))
- val (int (max (+ @original-value (/ delta zoom)) 0))
- layout-padding (cond
- hover-all? (assoc padding :p1 val :p2 val :p3 val :p4 val)
- hover-v? (assoc padding :p1 val :p3 val)
- hover-h? (assoc padding :p2 val :p4 val)
- :else (assoc padding padding-num val))
-
-
- layout-padding-type (if (= (:p1 padding) (:p2 padding) (:p3 padding) (:p4 padding)) :simple :multiple)
- modifiers (dwm/create-modif-tree [frame-id]
- (-> (ctm/empty)
- (ctm/change-property :layout-padding layout-padding)
- (ctm/change-property :layout-padding-type layout-padding-type)))]
- (reset! hover-value val)
- (st/emit! (dwm/set-modifiers modifiers)))))))]
-
- [:rect.padding-rect {:x (:x rect-data)
- :y (:y rect-data)
- :width (max 0 (:width rect-data))
- :height (max 0 (:height rect-data))
- :on-pointer-enter on-pointer-enter
- :on-pointer-leave on-pointer-leave
- :on-pointer-down on-pointer-down
- :on-lost-pointer-capture on-lost-pointer-capture
- :on-pointer-move on-pointer-move
- :class (when (or hover? selected?)
- (if (= (:resize-axis rect-data) :x) (cur/get-dynamic "resize-ew" 0) (cur/get-dynamic "resize-ew" 90)))
- :style {:fill (if (or hover? selected?) distance-color "none")
- :opacity (if selected? 0.5 0.25)}}]))
-
-(mf/defc padding-rects [{:keys [frame zoom alt? shift?]}]
- (let [frame-id (:id frame)
- paddings-selected (mf/deref refs/workspace-paddings-selected)
- hover-value (mf/use-var 0)
- mouse-pos (mf/use-var nil)
- hover (mf/use-var nil)
- hover-all? (and (not (nil? @hover)) alt?)
- hover-v? (and (or (= @hover :p1) (= @hover :p3)) shift?)
- hover-h? (and (or (= @hover :p2) (= @hover :p4)) shift?)
- padding (:layout-padding frame)
- {:keys [width height x1 x2 y1 y2]} (:selrect frame)
- on-pointer-enter (fn [hover-type val]
- (reset! hover hover-type)
- (reset! hover-value val))
- on-pointer-leave #(reset! hover nil)
- pill-width (/ flex-display-pill-width zoom)
- pill-height (/ flex-display-pill-height zoom)
- hover? #(or hover-all?
- (and (or (= % :p1) (= % :p3)) hover-v?)
- (and (or (= % :p2) (= % :p4)) hover-h?)
- (= @hover %))
- negate {:p1 (if (:flip-y frame) true false)
- :p2 (if (:flip-x frame) true false)
- :p3 (if (:flip-y frame) true false)
- :p4 (if (:flip-x frame) true false)}
- negate (cond-> negate
- (not= :auto (:layout-item-h-sizing frame)) (assoc :p2 (not (:p2 negate)))
- (not= :auto (:layout-item-v-sizing frame)) (assoc :p3 (not (:p3 negate))))
-
- padding-rect-data {:p1 {:key (str frame-id "-p1")
- :x x1
- :y (if (:flip-y frame) (- y2 (:p1 padding)) y1)
- :width width
- :height (:p1 padding)
- :initial-value (:p1 padding)
- :resize-type (if (:flip-y frame) :bottom :top)
- :resize-axis :y
- :resize-negate? (:p1 negate)}
- :p2 {:key (str frame-id "-p2")
- :x (if (:flip-x frame) x1 (- x2 (:p2 padding)))
- :y y1
- :width (:p2 padding)
- :height height
- :initial-value (:p2 padding)
- :resize-type :left
- :resize-axis :x
- :resize-negate? (:p2 negate)}
- :p3 {:key (str frame-id "-p3")
- :x x1
- :y (if (:flip-y frame) y1 (- y2 (:p3 padding)))
- :width width
- :height (:p3 padding)
- :initial-value (:p3 padding)
- :resize-type :bottom
- :resize-axis :y
- :resize-negate? (:p3 negate)}
- :p4 {:key (str frame-id "-p4")
- :x (if (:flip-x frame) (- x2 (:p4 padding)) x1)
- :y y1
- :width (:p4 padding)
- :height height
- :initial-value (:p4 padding)
- :resize-type (if (:flip-x frame) :right :left)
- :resize-axis :x
- :resize-negate? (:p4 negate)}}]
-
- [:g.paddings {:pointer-events "visible"}
- (for [[padding-num rect-data] padding-rect-data]
- [:& padding-display {:key (:key rect-data)
- :frame-id frame-id
- :zoom zoom
- :hover-all? hover-all?
- :hover-v? hover-v?
- :hover-h? hover-h?
- :padding padding
- :mouse-pos mouse-pos
- :hover-value hover-value
- :padding-num padding-num
- :on-pointer-enter (partial on-pointer-enter padding-num (get padding padding-num))
- :on-pointer-leave on-pointer-leave
- :hover? (hover? padding-num)
- :selected? (get paddings-selected padding-num)
- :rect-data rect-data}])
- (when @hover
- [:& flex-display-pill {:height pill-height
- :width pill-width
- :font-size (/ font-size zoom)
- :border-radius (/ flex-display-pill-border-radius zoom)
- :color distance-color
- :x (:x @mouse-pos)
- :y (- (:y @mouse-pos) pill-width)
- :value @hover-value}])]))
-
-(mf/defc margin-display [{:keys [shape-id zoom hover-all? hover-v? hover-h? margin-num margin on-pointer-enter on-pointer-leave
- rect-data hover? selected? mouse-pos hover-value]}]
- (let [resizing? (mf/use-var false)
- start (mf/use-var nil)
- original-value (mf/use-var 0)
- negate? (true? (:resize-negate? rect-data))
- axis (:resize-axis rect-data)
-
- on-pointer-down
- (mf/use-fn
- (mf/deps shape-id margin-num margin)
- (fn [event]
- (dom/capture-pointer event)
- (reset! resizing? true)
- (reset! start (dom/get-client-position event))
- (reset! original-value (:initial-value rect-data))))
-
- on-lost-pointer-capture
- (mf/use-fn
- (mf/deps shape-id margin-num margin)
- (fn [event]
- (dom/release-pointer event)
- (reset! resizing? false)
- (reset! start nil)
- (reset! original-value 0)
- (st/emit! (dwm/apply-modifiers))))
-
- on-pointer-move
- (mf/use-fn
- (mf/deps shape-id margin-num margin hover-all? hover-v? hover-h?)
- (fn [event]
- (let [pos (dom/get-client-position event)]
- (reset! mouse-pos (point->viewport pos))
- (when @resizing?
- (let [delta (-> (gpt/to-vec @start pos)
- (cond-> negate? gpt/negate)
- (get axis))
- val (int (max (+ @original-value (/ delta zoom)) 0))
- layout-item-margin (cond
- hover-all? (assoc margin :m1 val :m2 val :m3 val :m4 val)
- hover-v? (assoc margin :m1 val :m3 val)
- hover-h? (assoc margin :m2 val :m4 val)
- :else (assoc margin margin-num val))
- layout-item-margin-type (if (= (:m1 margin) (:m2 margin) (:m3 margin) (:m4 margin)) :simple :multiple)
- modifiers (dwm/create-modif-tree [shape-id]
- (-> (ctm/empty)
- (ctm/change-property :layout-item-margin layout-item-margin)
- (ctm/change-property :layout-item-margin-type layout-item-margin-type)))]
- (reset! hover-value val)
- (st/emit! (dwm/set-modifiers modifiers)))))))]
-
- [:rect.margin-rect {:x (:x rect-data)
- :y (:y rect-data)
- :width (:width rect-data)
- :height (:height rect-data)
- :on-pointer-enter on-pointer-enter
- :on-pointer-leave on-pointer-leave
- :on-pointer-down on-pointer-down
- :on-lost-pointer-capture on-lost-pointer-capture
- :on-pointer-move on-pointer-move
- :class (when (or hover? selected?)
- (if (= (:resize-axis rect-data) :x) (cur/get-dynamic "resize-ew" 0) (cur/get-dynamic "resize-ew" 90)))
- :style {:fill (if (or hover? selected?) warning-color "none")
- :opacity (if selected? 0.5 0.25)}}]))
-
-(mf/defc margin-rects [{:keys [shape frame zoom alt? shift?]}]
- (let [shape-id (:id shape)
- pill-width (/ flex-display-pill-width zoom)
- pill-height (/ flex-display-pill-height zoom)
- margins-selected (mf/deref refs/workspace-margins-selected)
- hover-value (mf/use-var 0)
- mouse-pos (mf/use-var nil)
- hover (mf/use-var nil)
- hover-all? (and (not (nil? @hover)) alt?)
- hover-v? (and (or (= @hover :m1) (= @hover :m3)) shift?)
- hover-h? (and (or (= @hover :m2) (= @hover :m4)) shift?)
- margin (:layout-item-margin shape)
- {:keys [width height x1 x2 y1 y2]} (:selrect shape)
- on-pointer-enter (fn [hover-type val]
- (reset! hover hover-type)
- (reset! hover-value val))
- on-pointer-leave #(reset! hover nil)
- hover? #(or hover-all?
- (and (or (= % :m1) (= % :m3)) hover-v?)
- (and (or (= % :m2) (= % :m4)) hover-h?)
- (= @hover %))
- margin-display-data {:m1 {:key (str shape-id "-m1")
- :x x1
- :y (if (:flip-y frame) y2 (- y1 (:m1 margin)))
- :width width
- :height (:m1 margin)
- :initial-value (:m1 margin)
- :resize-type :top
- :resize-axis :y
- :resize-negate? (:flip-y frame)}
- :m2 {:key (str shape-id "-m2")
- :x (if (:flip-x frame) (- x1 (:m2 margin)) x2)
- :y y1
- :width (:m2 margin)
- :height height
- :initial-value (:m2 margin)
- :resize-type :left
- :resize-axis :x
- :resize-negate? (:flip-x frame)}
- :m3 {:key (str shape-id "-m3")
- :x x1
- :y (if (:flip-y frame) (- y1 (:m3 margin)) y2)
- :width width
- :height (:m3 margin)
- :initial-value (:m3 margin)
- :resize-type :top
- :resize-axis :y
- :resize-negate? (:flip-y frame)}
- :m4 {:key (str shape-id "-m4")
- :x (if (:flip-x frame) x2 (- x1 (:m4 margin)))
- :y y1
- :width (:m4 margin)
- :height height
- :initial-value (:m4 margin)
- :resize-type :left
- :resize-axis :x
- :resize-negate? (:flip-x frame)}}]
-
- [:g.margins {:pointer-events "visible"}
- (for [[margin-num rect-data] margin-display-data]
- [:& margin-display
- {:key (:key rect-data)
- :shape-id shape-id
- :zoom zoom
- :hover-all? hover-all?
- :hover-v? hover-v?
- :hover-h? hover-h?
- :margin-num margin-num
- :margin margin
- :on-pointer-enter (partial on-pointer-enter margin-num (get margin margin-num))
- :on-pointer-leave on-pointer-leave
- :rect-data rect-data
- :hover? (hover? margin-num)
- :selected? (get margins-selected margin-num)
- :mouse-pos mouse-pos
- :hover-value hover-value}])
-
- (when @hover
- [:& flex-display-pill {:height pill-height
- :width pill-width
- :font-size (/ font-size zoom)
- :border-radius (/ flex-display-pill-border-radius zoom)
- :color warning-color
- :x (:x @mouse-pos)
- :y (- (:y @mouse-pos) pill-width)
- :value @hover-value}])]))
-
-(mf/defc gap-display [{:keys [frame-id zoom gap-type gap on-pointer-enter on-pointer-leave
- rect-data hover? selected? mouse-pos hover-value]}]
- (let [resizing (mf/use-var nil)
- start (mf/use-var nil)
- original-value (mf/use-var 0)
- negate? (:resize-negate? rect-data)
- axis (:resize-axis rect-data)
-
- on-pointer-down
- (mf/use-fn
- (mf/deps frame-id gap-type gap)
- (fn [event]
- (dom/capture-pointer event)
- (reset! resizing gap-type)
- (reset! start (dom/get-client-position event))
- (reset! original-value (:initial-value rect-data))))
-
- on-lost-pointer-capture
- (mf/use-fn
- (mf/deps frame-id gap-type gap)
- (fn [event]
- (dom/release-pointer event)
- (reset! resizing nil)
- (reset! start nil)
- (reset! original-value 0)
- (st/emit! (dwm/apply-modifiers))))
-
- on-pointer-move
- (mf/use-fn
- (mf/deps frame-id gap-type gap)
- (fn [event]
- (let [pos (dom/get-client-position event)]
- (reset! mouse-pos (point->viewport pos))
- (when (= @resizing gap-type)
- (let [delta (-> (gpt/to-vec @start pos)
- (cond-> negate? gpt/negate)
- (get axis))
- val (int (max (+ @original-value (/ delta zoom)) 0))
- layout-gap (assoc gap gap-type val)
- modifiers (dwm/create-modif-tree [frame-id] (ctm/change-property (ctm/empty) :layout-gap layout-gap))]
-
- (reset! hover-value val)
- (st/emit! (dwm/set-modifiers modifiers)))))))]
-
- [:rect.gap-rect {:x (:x rect-data)
- :y (:y rect-data)
- :width (:width rect-data)
- :height (:height rect-data)
- :on-pointer-enter on-pointer-enter
- :on-pointer-leave on-pointer-leave
- :on-pointer-down on-pointer-down
- :on-lost-pointer-capture on-lost-pointer-capture
- :on-pointer-move on-pointer-move
- :class (when (or hover? selected?)
- (if (= (:resize-axis rect-data) :x) (cur/get-dynamic "resize-ew" 0) (cur/get-dynamic "resize-ew" 90)))
- :style {:fill (if (or hover? selected?) distance-color "none")
- :opacity (if selected? 0.5 0.25)}}]))
-
-(mf/defc gap-rects [{:keys [frame zoom]}]
- (let [frame-id (:id frame)
- saved-dir (:layout-flex-dir frame)
- is-col? (or (= :column saved-dir) (= :column-reverse saved-dir))
- flip-x (:flip-x frame)
- flip-y (:flip-y frame)
- pill-width (/ flex-display-pill-width zoom)
- pill-height (/ flex-display-pill-height zoom)
- workspace-modifiers (mf/deref refs/workspace-modifiers)
- gap-selected (mf/deref refs/workspace-gap-selected)
- hover (mf/use-var nil)
- hover-value (mf/use-var 0)
- mouse-pos (mf/use-var nil)
- padding (:layout-padding frame)
- gap (:layout-gap frame)
- {:keys [width height x1 y1]} (:selrect frame)
- on-pointer-enter (fn [hover-type val]
- (reset! hover hover-type)
- (reset! hover-value val))
-
- on-pointer-leave #(reset! hover nil)
- negate {:column-gap (if flip-x true false)
- :row-gap (if flip-y true false)}
-
- objects (wsh/lookup-page-objects @st/state)
- children (->> (cfh/get-immediate-children objects frame-id)
- (remove ctl/position-absolute?))
-
- children-to-display (if (or (= :row-reverse saved-dir)
- (= :column-reverse saved-dir))
- (drop-last children)
- (rest children))
- children-to-display (->> children-to-display
- (map #(gsh/transform-shape % (get-in workspace-modifiers [(:id %) :modifiers]))))
-
- wrap-blocks
- (let [block-children (->> children
- (map #(vector (gpo/parent-coords-bounds (:points %) (:points frame)) %)))
- bounds (d/lazy-map (keys objects) #(gsh/shape->points (get objects %)))
-
- layout-data (gsl/calc-layout-data frame (:points frame) block-children bounds objects)
- layout-bounds (:layout-bounds layout-data)
- xv #(gpo/start-hv layout-bounds %)
- yv #(gpo/start-vv layout-bounds %)]
- (for [{:keys [start-p line-width line-height layout-gap-row layout-gap-col num-children]} (:layout-lines layout-data)]
- (let [line-width (if is-col? line-width (+ line-width (* (dec num-children) layout-gap-row)))
- line-height (if is-col? (+ line-height (* (dec num-children) layout-gap-col)) line-height)
- end-p (-> start-p (gpt/add (xv line-width)) (gpt/add (yv line-height)))]
- {:x1 (min (:x start-p) (:x end-p))
- :y1 (min (:y start-p) (:y end-p))
- :x2 (max (:x start-p) (:x end-p))
- :y2 (max (:y start-p) (:y end-p))})))
-
- block-contains
- (fn [x y block]
- (if is-col?
- (<= (:x1 block) x (:x2 block))
- (<= (:y1 block) y (:y2 block))))
-
- get-container-block
- (fn [shape]
- (let [selrect (:selrect shape)
- x (/ (+ (:x1 selrect) (:x2 selrect)) 2)
- y (/ (+ (:y1 selrect) (:y2 selrect)) 2)]
- (->> wrap-blocks
- (filter #(block-contains x y %))
- first)))
-
- create-cgdd
- (fn [shape]
- (let [block (get-container-block shape)
- x (if flip-x
- (- (:x1 (:selrect shape))
- (get-in shape [:layout-item-margin :m2])
- (:column-gap gap))
- (+ (:x2 (:selrect shape)) (get-in shape [:layout-item-margin :m2])))
- y (:y1 block)
- h (- (:y2 block) (:y1 block))]
- {:x x
- :y y
- :height h
- :width (:column-gap gap)
- :initial-value (:column-gap gap)
- :resize-type :left
- :resize-axis :x
- :resize-negate? (:column-gap negate)
- :gap-type (if is-col? :row-gap :column-gap)}))
-
- create-cgdd-block
- (fn [block]
- (let [x (if flip-x
- (- (:x1 block) (:column-gap gap))
- (:x2 block))
- y (if flip-y
- (+ y1 (:p3 padding))
- (+ y1 (:p1 padding)))
- h (- height (+ (:p1 padding) (:p3 padding)))]
- {:x x
- :y y
- :width (:column-gap gap)
- :height h
- :initial-value (:column-gap gap)
- :resize-type :left
- :resize-axis :x
- :resize-negate? (:column-gap negate)
- :gap-type (if is-col? :column-gap :row-gap)}))
-
- create-rgdd
- (fn [shape]
- (let [block (get-container-block shape)
- x (:x1 block)
- y (if flip-y
- (- (:y1 (:selrect shape))
- (get-in shape [:layout-item-margin :m3])
- (:row-gap gap))
- (+ (:y2 (:selrect shape)) (get-in shape [:layout-item-margin :m3])))
- w (- (:x2 block) (:x1 block))]
- {:x x
- :y y
- :width w
- :height (:row-gap gap)
- :initial-value (:row-gap gap)
- :resize-type :bottom
- :resize-axis :y
- :resize-negate? (:row-gap negate)
- :gap-type (if is-col? :row-gap :column-gap)}))
-
- create-rgdd-block
- (fn [block]
- (let [x (if flip-x
- (+ x1 (:p2 padding))
- (+ x1 (:p4 padding)))
- y (if flip-y
- (- (:y1 block) (:row-gap gap))
- (:y2 block))
- w (- width (+ (:p2 padding) (:p4 padding)))]
- {:x x
- :y y
- :width w
- :height (:row-gap gap)
- :initial-value (:row-gap gap)
- :resize-type :bottom
- :resize-axis :y
- :resize-negate? (:row-gap negate)
- :gap-type (if is-col? :column-gap :row-gap)}))
-
- display-blocks (if is-col?
- (->> (drop-last wrap-blocks)
- (map create-cgdd-block))
- (->> (drop-last wrap-blocks)
- (map create-rgdd-block)))
-
- display-children (if is-col?
- (->> children-to-display
- (map create-rgdd))
- (->> children-to-display
- (map create-cgdd)))]
-
- [:g.gaps {:pointer-events "visible"}
- (for [[index display-item] (d/enumerate (concat display-blocks display-children))]
- (let [gap-type (:gap-type display-item)]
- [:& gap-display {:key (str frame-id index)
- :frame-id frame-id
- :zoom zoom
- :gap-type gap-type
- :gap gap
- :on-pointer-enter (partial on-pointer-enter gap-type (get gap gap-type))
- :on-pointer-leave on-pointer-leave
- :rect-data display-item
- :hover? (= @hover gap-type)
- :selected? (= gap-selected gap-type)
- :mouse-pos mouse-pos
- :hover-value hover-value}]))
-
- (when @hover
- [:& flex-display-pill {:height pill-height
- :width pill-width
- :font-size (/ font-size zoom)
- :border-radius (/ flex-display-pill-border-radius zoom)
- :color distance-color
- :x (:x @mouse-pos)
- :y (- (:y @mouse-pos) pill-width)
- :value @hover-value}])]))
-
-(mf/defc padding
- [{:keys [frame zoom alt? shift?]}]
- (when frame
- [:g.measurement-gaps {:pointer-events "none"}
- [:g.hover-shapes
- [:& padding-rects {:frame frame :zoom zoom :alt? alt? :shift? shift?}]]]))
-
-(mf/defc gap
- [{:keys [frame zoom]}]
- (when frame
- [:g.measurement-gaps {:pointer-events "none"}
- [:g.hover-shapes
- [:& gap-rects {:frame frame :zoom zoom}]]]))
-
-(mf/defc margin
- [{:keys [shape parent zoom alt? shift?]}]
- (when shape
- [:g.measurement-gaps {:pointer-events "none"}
- [:g.hover-shapes
- [:& margin-rects {:shape shape :frame parent :zoom zoom :alt? alt? :shift? shift?}]]]))
-
-
diff --git a/frontend/src/app/main/ui/modal.scss b/frontend/src/app/main/ui/modal.scss
index dc2c0e2748..6dd5b4eda0 100644
--- a/frontend/src/app/main/ui/modal.scss
+++ b/frontend/src/app/main/ui/modal.scss
@@ -2,7 +2,6 @@
:global(:root) {
--s-4: 0.25rem;
- --layer-indentation-size: calc(var(--s-4) * 5);
}
.modal-wrapper {
diff --git a/frontend/src/app/main/ui/viewer.cljs b/frontend/src/app/main/ui/viewer.cljs
index 651a7c8061..899dee3863 100644
--- a/frontend/src/app/main/ui/viewer.cljs
+++ b/frontend/src/app/main/ui/viewer.cljs
@@ -542,12 +542,11 @@
:index index
:thumbnail-data (:thumbnails file)}]
- [:section {:id "viewer-section"
- :ref viewer-section-ref
- :data-viewer-section true
- :class (stl/css-case :viewer-section true
- :fulscreen fullscreen?)
- :on-click click-on-screen}
+ [:section#viewer-section {:ref viewer-section-ref
+ :data-viewer-section true
+ :class (stl/css-case :viewer-section true
+ :fulscreen fullscreen?)
+ :on-click click-on-screen}
(cond
(empty? frames)
[:section {:class (stl/css :empty-state)}
diff --git a/frontend/src/app/main/ui/viewer.scss b/frontend/src/app/main/ui/viewer.scss
index 94f54ae5f6..7b98ecfa39 100644
--- a/frontend/src/app/main/ui/viewer.scss
+++ b/frontend/src/app/main/ui/viewer.scss
@@ -62,6 +62,7 @@
flex-wrap: nowrap;
margin-top: 0;
height: 100%;
+ overflow: hidden;
}
.viewer-go-prev,
diff --git a/frontend/src/app/main/ui/viewer/inspect/code.scss b/frontend/src/app/main/ui/viewer/inspect/code.scss
index 1e1e58666f..5b6d66c9c3 100644
--- a/frontend/src/app/main/ui/viewer/inspect/code.scss
+++ b/frontend/src/app/main/ui/viewer/inspect/code.scss
@@ -28,7 +28,6 @@
flex-direction: column;
height: 100%;
min-height: 0;
- overflow: hidden;
padding: 0 $s-4 $s-8 0;
pre {
diff --git a/frontend/src/app/main/ui/viewer/interactions.cljs b/frontend/src/app/main/ui/viewer/interactions.cljs
index c304d05451..60f1928c42 100644
--- a/frontend/src/app/main/ui/viewer/interactions.cljs
+++ b/frontend/src/app/main/ui/viewer/interactions.cljs
@@ -113,24 +113,27 @@
[:& (mf/provider shapes/base-frame-ctx) {:value base}
[:& (mf/provider shapes/frame-offset-ctx) {:value offset}
;; We have two different svgs for fixed and not fixed elements so we can emulate the sticky css attribute in svg
- [:svg.not-fixed {:view-box vbox
- :width (:width size)
- :height (:height size)
- :version "1.1"
- :xmlnsXlink "http://www.w3.org/1999/xlink"
- :xmlns "http://www.w3.org/2000/svg"
- :fill "none"}
- [:& wrapper-not-fixed {:shape frame :view-box vbox}]]
- [:svg.fixed {:view-box vbox
- :width (:width size)
- :height (:height size)
- :version "1.1"
- :xmlnsXlink "http://www.w3.org/1999/xlink"
- :xmlns "http://www.w3.org/2000/svg"
- :fill "none"
- :style {:width (:width size)
- :height (:height size)}}
- [:& wrapper-fixed {:shape fixed-frame :view-box vbox}]]]]))
+ [:svg {:class (stl/css :fixed)
+ :view-box vbox
+ :width (:width size)
+ :height (:height size)
+ :version "1.1"
+ :xmlnsXlink "http://www.w3.org/1999/xlink"
+ :xmlns "http://www.w3.org/2000/svg"
+ :fill "none"
+ :style {:width (:width size)
+ :height (:height size)}}
+ [:& wrapper-fixed {:shape fixed-frame :view-box vbox}]]
+
+ [:svg {:class (stl/css :not-fixed)
+ :view-box vbox
+ :width (:width size)
+ :height (:height size)
+ :version "1.1"
+ :xmlnsXlink "http://www.w3.org/1999/xlink"
+ :xmlns "http://www.w3.org/2000/svg"
+ :fill "none"}
+ [:& wrapper-not-fixed {:shape frame :view-box vbox}]]]]))
(mf/defc viewport
{::mf/wrap [mf/memo]
diff --git a/frontend/src/app/main/ui/viewer/interactions.scss b/frontend/src/app/main/ui/viewer/interactions.scss
index 07b12e3af1..656bc7c937 100644
--- a/frontend/src/app/main/ui/viewer/interactions.scss
+++ b/frontend/src/app/main/ui/viewer/interactions.scss
@@ -78,3 +78,12 @@
}
// breakpoint 1013px
+
+.fixed {
+ position: fixed;
+ pointer-events: none;
+
+ :global(.frame-children) g {
+ pointer-events: auto;
+ }
+}
diff --git a/frontend/src/app/main/ui/workspace.scss b/frontend/src/app/main/ui/workspace.scss
index a9e8a35a72..0c7df32056 100644
--- a/frontend/src/app/main/ui/workspace.scss
+++ b/frontend/src/app/main/ui/workspace.scss
@@ -16,13 +16,14 @@
:global(:root) {
--s-4: 0.25rem;
- --layer-indentation-size: calc(var(--s-4) * 5);
+ --layer-indentation-size: calc(var(--s-4) * 6);
}
.workspace {
@extend .new-scrollbar;
width: 100vw;
- height: 100%;
+ height: 100vh;
+ max-height: 100vh;
user-select: none;
display: grid;
grid-template-areas: "left-sidebar viewport right-sidebar";
diff --git a/frontend/src/app/main/ui/workspace/colorpicker.cljs b/frontend/src/app/main/ui/workspace/colorpicker.cljs
index 1477d2b6b3..8a361d1d06 100644
--- a/frontend/src/app/main/ui/workspace/colorpicker.cljs
+++ b/frontend/src/app/main/ui/workspace/colorpicker.cljs
@@ -378,17 +378,21 @@
[{vh :height} position x y]
(let [;; picker height in pixels
h 510
+
;; Checks for overflow outside the viewport height
- overflow-fix (max 0 (+ y (- 50) h (- vh)))
+ max-y (- vh h)
x-pos 325]
(cond
(or (nil? x) (nil? y)) {:left "auto" :right "16rem" :top "4rem"}
- (= position :left) {:left (str (- x x-pos) "px")
- :top (str (- y 50 overflow-fix) "px")}
+ (= position :left)
+ (if (> y max-y)
+ {:left (str (- x x-pos) "px")
+ :bottom "1rem"}
+ {:left (str (- x x-pos) "px")
+ :top (str (- y 70) "px")})
:else {:left (str (+ x 80) "px")
- :top (str (- y 70 overflow-fix) "px")})))
-
+ :top (str (- y 70) "px")})))
(mf/defc colorpicker-modal
{::mf/register modal/components
diff --git a/frontend/src/app/main/ui/workspace/colorpicker.scss b/frontend/src/app/main/ui/workspace/colorpicker.scss
index 4abe58b463..f3049f3878 100644
--- a/frontend/src/app/main/ui/workspace/colorpicker.scss
+++ b/frontend/src/app/main/ui/workspace/colorpicker.scss
@@ -8,7 +8,6 @@
.colorpicker-tooltip {
@extend .modal-background;
- top: $s-100;
left: calc(10 * $s-140);
width: auto;
}
diff --git a/frontend/src/app/main/ui/workspace/context_menu.cljs b/frontend/src/app/main/ui/workspace/context_menu.cljs
index cb5e635f85..da56b63388 100644
--- a/frontend/src/app/main/ui/workspace/context_menu.cljs
+++ b/frontend/src/app/main/ui/workspace/context_menu.cljs
@@ -106,8 +106,9 @@
[:span {:class (stl/css :title)} title]
(when shortcut
[:span {:class (stl/css :shortcut)}
- (for [sc (scd/split-sc shortcut)]
- [:span {:class (stl/css :shortcut-key)} sc])])
+ (for [[idx sc] (d/enumerate (scd/split-sc shortcut))]
+ [:span {:key (dm/str shortcut "-" idx)
+ :class (stl/css :shortcut-key)} sc])])
(when (> (count children) 1)
[:span {:class (stl/css :submenu-icon)} i/arrow-refactor])
diff --git a/frontend/src/app/main/ui/workspace/left_header.cljs b/frontend/src/app/main/ui/workspace/left_header.cljs
index 25c47f99f8..c5d57c29dc 100644
--- a/frontend/src/app/main/ui/workspace/left_header.cljs
+++ b/frontend/src/app/main/ui/workspace/left_header.cljs
@@ -7,6 +7,7 @@
(ns app.main.ui.workspace.left-header
(:require-macros [app.main.style :as stl])
(:require
+ [app.common.data.macros :as dm]
[app.common.files.helpers :as cfh]
[app.common.uuid :as uuid]
[app.config :as cf]
@@ -625,7 +626,9 @@
(keyword))]
(st/emit!
(-> (dw/toggle-layout-flag flag)
- (vary-meta assoc ::ev/origin "workspace-menu"))))))]
+ (vary-meta assoc ::ev/origin "workspace-menu")))
+ (reset! show-menu* false)
+ (reset! sub-menu* nil))))]
[:*
@@ -724,7 +727,7 @@
(mf/defc left-header
{::mf/wrap-props false}
- [{:keys [file layout project page-id]}]
+ [{:keys [file layout project page-id class]}]
(let [file-id (:id file)
file-name (:name file)
project-id (:id project)
@@ -780,7 +783,7 @@
(mf/with-effect [editing?]
(when ^boolean editing?
(dom/select-text! (mf/ref-val input-ref))))
- [:header {:class (stl/css :workspace-header-left)}
+ [:header {:class (dm/str class " " (stl/css :workspace-header-left))}
[:a {:on-click go-back
:class (stl/css :main-icon)} i/logo-icon]
[:div {:alt (tr "workspace.sitemap")
diff --git a/frontend/src/app/main/ui/workspace/left_header.scss b/frontend/src/app/main/ui/workspace/left_header.scss
index d527a04e9c..6b0e5016e0 100644
--- a/frontend/src/app/main/ui/workspace/left_header.scss
+++ b/frontend/src/app/main/ui/workspace/left_header.scss
@@ -9,7 +9,6 @@
.workspace-header-left {
display: flex;
align-items: center;
- height: $s-48;
padding: $s-8 $s-8 $s-4 $s-8;
}
diff --git a/frontend/src/app/main/ui/workspace/libraries.cljs b/frontend/src/app/main/ui/workspace/libraries.cljs
index 3829f7ca39..8d6b7dcc93 100644
--- a/frontend/src/app/main/ui/workspace/libraries.cljs
+++ b/frontend/src/app/main/ui/workspace/libraries.cljs
@@ -481,6 +481,11 @@
on-tab-change
(mf/use-fn #(reset! selected-tab* %))
+ close-dialog-outside
+ (mf/use-fn (fn [event]
+ (when (= (dom/get-target event) (dom/get-current-target event))
+ (modal/hide!))))
+
close-dialog
(mf/use-fn (fn [_]
(modal/hide!)
@@ -490,7 +495,7 @@
(when team-id
(st/emit! (dwl/fetch-shared-files {:team-id team-id}))))
- [:div {:class (stl/css :modal-overlay)}
+ [:div {:class (stl/css :modal-overlay) :on-click close-dialog-outside}
[:div {:class (stl/css :modal-dialog)}
[:button {:class (stl/css :close)
:on-click close-dialog}
diff --git a/frontend/src/app/main/ui/workspace/libraries.scss b/frontend/src/app/main/ui/workspace/libraries.scss
index 8ebf89b741..e91cedb36d 100644
--- a/frontend/src/app/main/ui/workspace/libraries.scss
+++ b/frontend/src/app/main/ui/workspace/libraries.scss
@@ -15,7 +15,6 @@
width: 100%;
z-index: $z-index-modal;
background-color: var(--overlay-color);
- pointer-events: none; // This is to allow outside click that closes modal.
.modal-dialog {
position: relative;
@@ -25,7 +24,6 @@
padding: $s-32;
border-radius: $br-10;
background-color: var(--modal-background-color);
- pointer-events: all;
.close {
@extend .button-tertiary;
position: absolute;
@@ -146,8 +144,10 @@
.section-title {
@include titleTipography;
+ color: var(--modal-title-foreground-color);
margin-bottom: $s-12;
}
+
.libraries-search {
margin: $s-12 0;
.search-icon {
diff --git a/frontend/src/app/main/ui/workspace/shapes.cljs b/frontend/src/app/main/ui/workspace/shapes.cljs
index ffb8c1d529..d1889e620e 100644
--- a/frontend/src/app/main/ui/workspace/shapes.cljs
+++ b/frontend/src/app/main/ui/workspace/shapes.cljs
@@ -12,9 +12,11 @@
others are defined using a generic wrapper implemented in
common."
(:require
+ [app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.helpers :as cfh]
[app.common.geom.rect :as grc]
+ [app.common.geom.shapes :as gsh]
[app.common.uuid :as uuid]
[app.main.ui.context :as ctx]
[app.main.ui.shapes.circle :as circle]
@@ -42,22 +44,34 @@
(def image-wrapper (common/generic-wrapper-factory image/image-shape))
(def rect-wrapper (common/generic-wrapper-factory rect/rect-shape))
+(defn- make-is-frame-overlap
+ [vbox objects]
+ (fn [shape]
+ (let [bounds
+ (if (dm/get-prop shape :show-content)
+ (let [children (->> (cfh/get-children-ids objects (dm/get-prop shape :id))
+ (map (d/getf objects)))]
+ (gsh/shapes->rect (cons shape children)))
+ (dm/get-prop shape :selrect))]
+ (grc/overlaps-rects? vbox bounds))))
+
(mf/defc root-shape
"Draws the root shape of the viewport and recursively all the shapes"
{::mf/wrap [mf/memo]
::mf/wrap-props false}
[props]
- (let [objects (obj/get props "objects")
- active-frames (obj/get props "active-frames")
- shapes (cfh/get-immediate-children objects)
- vbox (mf/use-ctx ctx/current-vbox)
+ (let [objects (obj/get props "objects")
+ active-frames (obj/get props "active-frames")
+ shapes (cfh/get-immediate-children objects)
+ vbox (mf/use-ctx ctx/current-vbox)
- shapes (mf/with-memo [shapes vbox]
- (if (some? vbox)
- (filter (fn [shape]
- (grc/overlaps-rects? vbox (dm/get-prop shape :selrect)))
- shapes)
- shapes))]
+ frame-overlap? (mf/with-memo [vbox objects]
+ #(make-is-frame-overlap vbox objects))
+
+ shapes (mf/with-memo [shapes vbox frame-overlap?]
+ (cond->> shapes
+ (some? vbox)
+ (filter frame-overlap?)))]
[:g {:id (dm/str "shape-" uuid/zero)}
[:& (mf/provider ctx/active-frames) {:value active-frames}
diff --git a/frontend/src/app/main/ui/workspace/shapes/bool.cljs b/frontend/src/app/main/ui/workspace/shapes/bool.cljs
index 6a07eeed85..a3080b704e 100644
--- a/frontend/src/app/main/ui/workspace/shapes/bool.cljs
+++ b/frontend/src/app/main/ui/workspace/shapes/bool.cljs
@@ -11,6 +11,7 @@
[app.main.ui.shapes.bool :as bool]
[app.main.ui.shapes.shape :refer [shape-container]]
[app.main.ui.workspace.shapes.common :refer [check-shape-props]]
+ [app.main.ui.workspace.shapes.debug :as wsd]
[rumext.v2 :as mf]))
(defn bool-wrapper-factory
@@ -38,5 +39,7 @@
[:> shape-container {:shape shape}
[:& bool-shape {:shape shape
- :childs childs}]]))))
+ :childs childs}]
+ (when *assert*
+ [:& wsd/shape-debug {:shape shape}])]))))
diff --git a/frontend/src/app/main/ui/workspace/shapes/common.cljs b/frontend/src/app/main/ui/workspace/shapes/common.cljs
index c407b8db8f..4af042e877 100644
--- a/frontend/src/app/main/ui/workspace/shapes/common.cljs
+++ b/frontend/src/app/main/ui/workspace/shapes/common.cljs
@@ -8,6 +8,7 @@
(:require
[app.common.record :as cr]
[app.main.ui.shapes.shape :refer [shape-container]]
+ [app.main.ui.workspace.shapes.debug :as wsd]
[rumext.v2 :as mf]))
(def ^:private excluded-attrs
@@ -34,4 +35,6 @@
[props]
(let [shape (unchecked-get props "shape")]
[:> shape-container {:shape shape}
- [:& component {:shape shape}]])))
+ [:& component {:shape shape}]
+ (when *assert*
+ [:& wsd/shape-debug {:shape shape}])])))
diff --git a/frontend/src/app/main/ui/workspace/shapes/debug.cljs b/frontend/src/app/main/ui/workspace/shapes/debug.cljs
new file mode 100644
index 0000000000..71cec7d268
--- /dev/null
+++ b/frontend/src/app/main/ui/workspace/shapes/debug.cljs
@@ -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.workspace.shapes.debug
+ (:require
+ [app.common.data :as d]
+ [app.common.data.macros :as dm]
+ [app.common.files.helpers :as cfh]
+ [app.common.geom.shapes :as gsh]
+ [app.common.geom.shapes.text :as gst]
+ [app.common.math :as mth]
+ [app.main.refs :as refs]
+ [app.util.color :as uc]
+ [app.util.debug :as dbg]
+ [app.util.dom :as dom]
+ [cuerdas.core :as str]
+ [rumext.v2 :as mf]))
+
+(mf/defc debug-bounding-boxes
+ [{:keys [shape]}]
+ (let [points (->> (:points shape)
+ (map #(dm/fmt "%,%" (dm/get-prop % :x) (dm/get-prop % :y)))
+ (str/join " "))
+ color (mf/use-memo #(uc/random-color))
+ sr (:selrect shape)]
+ [:g.debug-bounding-boxes
+ [:rect {:transform (gsh/transform-str shape)
+ :x (:x sr)
+ :y (:y sr)
+ :width (:width sr)
+ :height (:height sr)
+ :fill color
+ :opacity 0.2}]
+ (for [p (:points shape)]
+ [:circle {:cx (dm/get-prop p :x)
+ :cy (dm/get-prop p :y)
+ :r 2
+ :fill color}])
+ [:polygon {:points points
+ :stroke-width 1
+ :stroke color}]]))
+
+(mf/defc debug-text-bounds
+ {::mf/wrap-props false}
+ [props]
+ (let [shape (unchecked-get props "shape")
+ zoom (mf/deref refs/selected-zoom)
+ bounding-box (gst/shape->rect shape)
+ ctx (js* "document.createElement(\"canvas\").getContext(\"2d\")")]
+ [:g {:transform (gsh/transform-str shape)}
+ [:rect {:x (:x bounding-box)
+ :y (:y bounding-box)
+ :width (:width bounding-box)
+ :height (:height bounding-box)
+ :style {:fill "none"
+ :stroke "orange"
+ :stroke-width (/ 1 zoom)}}]
+
+ (for [[index data] (d/enumerate (:position-data shape))]
+ (let [{:keys [x y width height]} data
+ res (dom/measure-text ctx (:font-size data) (:font-family data) (:text data))]
+ [:g {:key (dm/str index)}
+ ;; Text fragment bounding box
+ [:rect {:x x
+ :y (- y height)
+ :width width
+ :height height
+ :style {:fill "none"
+ :stroke "red"
+ :stroke-width (/ 1 zoom)}}]
+
+ ;; Text baseline
+ [:line {:x1 (mth/round x)
+ :y1 (mth/round (- (:y data) (:height data)))
+ :x2 (mth/round (+ x width))
+ :y2 (mth/round (- (:y data) (:height data)))
+ :style {:stroke "blue"
+ :stroke-width (/ 1 zoom)}}]
+
+ [:line {:x1 (:x data)
+ :y1 (- (:y data) (:descent res))
+ :x2 (+ (:x data) (:width data))
+ :y2 (- (:y data) (:descent res))
+ :style {:stroke "green"
+ :stroke-width (/ 2 zoom)}}]]))]))
+
+(mf/defc shape-debug
+ [{:keys [shape]}]
+ [:*
+ (when ^boolean (dbg/enabled? :bounding-boxes)
+ [:& debug-bounding-boxes])
+
+ (when (and ^boolean (cfh/text-shape? shape)
+ ^boolean (dbg/enabled? :text-outline)
+ ^boolean (seq (:position-data shape)))
+ [:& debug-text-bounds {:shape shape}])])
diff --git a/frontend/src/app/main/ui/workspace/shapes/frame.cljs b/frontend/src/app/main/ui/workspace/shapes/frame.cljs
index 2dc7e32890..9ecddd1e20 100644
--- a/frontend/src/app/main/ui/workspace/shapes/frame.cljs
+++ b/frontend/src/app/main/ui/workspace/shapes/frame.cljs
@@ -20,6 +20,7 @@
[app.main.ui.shapes.frame :as frame]
[app.main.ui.shapes.shape :refer [shape-container]]
[app.main.ui.workspace.shapes.common :refer [check-shape-props]]
+ [app.main.ui.workspace.shapes.debug :as wsd]
[app.main.ui.workspace.shapes.frame.dynamic-modifiers :as fdm]
[app.util.debug :as dbg]
[app.util.dom :as dom]
@@ -193,5 +194,8 @@
[:g.frame-content
{:id (dm/str "frame-content-" frame-id)
:ref container-ref}
- [:& frame-shape {:shape shape :ref content-ref}]])]]))))
+ [:& frame-shape {:shape shape :ref content-ref}]])]
+
+ (when *assert*
+ [:& wsd/shape-debug {:shape shape}])]))))
diff --git a/frontend/src/app/main/ui/workspace/shapes/group.cljs b/frontend/src/app/main/ui/workspace/shapes/group.cljs
index 5fbd2ad16b..d98d58a2f7 100644
--- a/frontend/src/app/main/ui/workspace/shapes/group.cljs
+++ b/frontend/src/app/main/ui/workspace/shapes/group.cljs
@@ -11,6 +11,7 @@
[app.main.ui.shapes.group :as group]
[app.main.ui.shapes.shape :refer [shape-container]]
[app.main.ui.workspace.shapes.common :refer [check-shape-props]]
+ [app.main.ui.workspace.shapes.debug :as wsd]
[rumext.v2 :as mf]))
(defn group-wrapper-factory
@@ -30,5 +31,7 @@
[:> shape-container {:shape shape}
[:& group-shape
{:shape shape
- :childs childs}]]))))
+ :childs childs}]
+ (when *assert*
+ [:& wsd/shape-debug {:shape shape}])]))))
diff --git a/frontend/src/app/main/ui/workspace/shapes/path.cljs b/frontend/src/app/main/ui/workspace/shapes/path.cljs
index b20f65b1da..110238be4b 100644
--- a/frontend/src/app/main/ui/workspace/shapes/path.cljs
+++ b/frontend/src/app/main/ui/workspace/shapes/path.cljs
@@ -11,6 +11,7 @@
[app.main.refs :as refs]
[app.main.ui.shapes.path :as path]
[app.main.ui.shapes.shape :refer [shape-container]]
+ [app.main.ui.workspace.shapes.debug :as wsd]
[app.main.ui.workspace.shapes.path.common :as pc]
[rumext.v2 :as mf]))
@@ -38,4 +39,6 @@
[:> shape-container {:shape shape
:pointer-events (when editing? "none")}
- [:& path/path-shape {:shape shape}]]))
+ [:& path/path-shape {:shape shape}]
+ (when *assert*
+ [:& wsd/shape-debug {:shape shape}])]))
diff --git a/frontend/src/app/main/ui/workspace/shapes/svg_raw.cljs b/frontend/src/app/main/ui/workspace/shapes/svg_raw.cljs
index 774fa61faa..de1701e016 100644
--- a/frontend/src/app/main/ui/workspace/shapes/svg_raw.cljs
+++ b/frontend/src/app/main/ui/workspace/shapes/svg_raw.cljs
@@ -10,6 +10,7 @@
[app.main.refs :as refs]
[app.main.ui.shapes.shape :refer [shape-container]]
[app.main.ui.shapes.svg-raw :as svg-raw]
+ [app.main.ui.workspace.shapes.debug :as wsd]
[rumext.v2 :as mf]))
(defn svg-raw-wrapper-factory
@@ -26,7 +27,9 @@
(if (contains? csvg/svg-group-safe-tags svg-tag)
[:> shape-container {:shape shape}
[:& svg-raw-shape {:shape shape
- :childs childs}]]
+ :childs childs}]
+ (when *assert*
+ [:& wsd/shape-debug {:shape shape}])]
[:& svg-raw-shape {:shape shape
:childs childs}])))))
diff --git a/frontend/src/app/main/ui/workspace/shapes/text.cljs b/frontend/src/app/main/ui/workspace/shapes/text.cljs
index eb9b5e68a6..cdaeda400f 100644
--- a/frontend/src/app/main/ui/workspace/shapes/text.cljs
+++ b/frontend/src/app/main/ui/workspace/shapes/text.cljs
@@ -6,63 +6,14 @@
(ns app.main.ui.workspace.shapes.text
(:require
- [app.common.data :as d]
[app.common.data.macros :as dm]
- [app.common.geom.shapes :as gsh]
- [app.common.geom.shapes.text :as gst]
- [app.common.math :as mth]
[app.main.data.workspace.texts :as dwt]
[app.main.refs :as refs]
[app.main.ui.shapes.shape :refer [shape-container]]
[app.main.ui.shapes.text :as text]
- [app.util.debug :as dbg]
- [app.util.dom :as dom]
+ [app.main.ui.workspace.shapes.debug :as wsd]
[rumext.v2 :as mf]))
-(mf/defc debug-text-bounds
- {::mf/wrap-props false}
- [props]
- (let [shape (unchecked-get props "shape")
- zoom (mf/deref refs/selected-zoom)
- bounding-box (gst/shape->rect shape)
- ctx (js* "document.createElement(\"canvas\").getContext(\"2d\")")]
- [:g {:transform (gsh/transform-str shape)}
- [:rect {:x (:x bounding-box)
- :y (:y bounding-box)
- :width (:width bounding-box)
- :height (:height bounding-box)
- :style {:fill "none"
- :stroke "orange"
- :stroke-width (/ 1 zoom)}}]
-
- (for [[index data] (d/enumerate (:position-data shape))]
- (let [{:keys [x y width height]} data
- res (dom/measure-text ctx (:font-size data) (:font-family data) (:text data))]
- [:g {:key (dm/str index)}
- ;; Text fragment bounding box
- [:rect {:x x
- :y (- y height)
- :width width
- :height height
- :style {:fill "none"
- :stroke "red"
- :stroke-width (/ 1 zoom)}}]
-
- ;; Text baseline
- [:line {:x1 (mth/round x)
- :y1 (mth/round (- (:y data) (:height data)))
- :x2 (mth/round (+ x width))
- :y2 (mth/round (- (:y data) (:height data)))
- :style {:stroke "blue"
- :stroke-width (/ 1 zoom)}}]
-
- [:line {:x1 (:x data)
- :y1 (- (:y data) (:descent res))
- :x2 (+ (:x data) (:width data))
- :y2 (- (:y data) (:descent res))
- :style {:stroke "green"
- :stroke-width (/ 2 zoom)}}]]))]))
-
;; --- Text Wrapper for workspace
(mf/defc text-wrapper
{::mf/wrap-props false}
@@ -84,6 +35,5 @@
[:g.text-shape {:key (dm/str shape-id)}
[:& text/text-shape {:shape shape}]]
- (when (and ^boolean (dbg/enabled? :text-outline)
- ^boolean (seq (:position-data shape)))
- [:& debug-text-bounds {:shape shape}])]))
+ (when *assert*
+ [:& wsd/shape-debug {:shape shape}])]))
diff --git a/frontend/src/app/main/ui/workspace/sidebar.cljs b/frontend/src/app/main/ui/workspace/sidebar.cljs
index 22201e991f..db497e7bd0 100644
--- a/frontend/src/app/main/ui/workspace/sidebar.cljs
+++ b/frontend/src/app/main/ui/workspace/sidebar.cljs
@@ -41,6 +41,7 @@
section (cond (or mode-inspect? (contains? layout :layers)) :layers
(contains? layout :assets) :assets)
+
shortcuts? (contains? layout :shortcuts)
show-debug? (contains? layout :debug-panel)
@@ -65,44 +66,54 @@
:global/three-row (and (> size 300) (<= size 400))
:global/four-row (> size 400))
:style #js {"--width" (dm/str size "px")}}
- [:& left-header {:file file :layout layout :project project :page-id page-id}]
+
+ [:& left-header {:file file :layout layout :project project :page-id page-id
+ :class (stl/css :left-header)}]
+
[:div {:on-pointer-down on-pointer-down
:on-lost-pointer-capture on-lost-pointer-capture
:on-pointer-move on-pointer-move
:class (stl/css :resize-area)}]
- [:div {:class (stl/css :settings-bar-inside)}
+ [:*
(cond
(true? shortcuts?)
- [:& shortcuts-container]
+ [:& shortcuts-container {:class (stl/css :settings-bar-content)}]
(true? show-debug?)
- [:& debug-panel]
+ [:& debug-panel {:class (stl/css :settings-bar-content)}]
:else
- [:div {:class (stl/css :tabs-wrapper)}
+ [:div {:class (stl/css :settings-bar-content)}
[:& tab-container
{:on-change-tab on-tab-change
:selected section
:collapsable true
:handle-collapse handle-collapse
:header-class (stl/css :tab-spacing)}
- [:& tab-element {:id :layers :title (tr "workspace.sidebar.layers")}
- [:div {:class (stl/css :layers-tab)
- :style #js {"--height" (str size-pages "px")}}
+
+ [:& tab-element {:id :layers
+ :title (tr "workspace.sidebar.layers")}
+ [:article {:class (stl/css :layers-tab)
+ :style #js {"--height" (str size-pages "px")}}
+
[:& sitemap {:layout layout
:toggle-pages toggle-pages
:show-pages? @show-pages?
:size size-pages}]
+
(when @show-pages?
[:div {:class (stl/css :resize-area-horiz)
:on-pointer-down on-pointer-down-pages
:on-lost-pointer-capture on-lost-pointer-capture-pages
:on-pointer-move on-pointer-move-pages}])
+
[:& layers-toolbox {:size-parent size
:size size-pages}]]]
+
(when-not ^boolean mode-inspect?
- [:& tab-element {:id :assets :title (tr "workspace.toolbar.assets")}
+ [:& tab-element {:id :assets
+ :title (tr "workspace.toolbar.assets")}
[:& assets-toolbox]])]])]]))
;; --- Right Sidebar (Component)
diff --git a/frontend/src/app/main/ui/workspace/sidebar.scss b/frontend/src/app/main/ui/workspace/sidebar.scss
index e4775f0d74..8f006ca43e 100644
--- a/frontend/src/app/main/ui/workspace/sidebar.scss
+++ b/frontend/src/app/main/ui/workspace/sidebar.scss
@@ -10,52 +10,49 @@ $width-settings-bar: $s-276;
$width-settings-bar-max: $s-500;
.left-settings-bar {
+ display: grid;
+ grid-template-areas:
+ "header header"
+ "content resize";
+ grid-template-rows: $s-48 1fr;
+ grid-template-columns: 1fr 0;
position: relative;
grid-area: left-sidebar;
min-width: $width-settings-bar;
max-width: $width-settings-bar-max;
width: var(--width, $width-settings-bar);
- height: 100%;
background-color: var(--panel-background-color);
+ height: 100vh;
+ max-height: 100vh;
.resize-area {
- position: absolute;
- right: calc(-1 * $s-8);
- z-index: $z-index-3;
- width: $s-8;
- height: calc(100vh - $s-52);
- cursor: ew-resize;
- }
- .resize-area-horiz {
- position: absolute;
- top: calc($s-80 + var(--height, 200px));
- left: 0;
- width: 100%;
- height: $s-12;
- border-top: $s-2 solid var(--resize-area-border-color);
- background-color: var(--resize-area-background-color);
- cursor: ns-resize;
- }
- .settings-bar-inside {
- display: grid;
- grid-template-columns: 100%;
- grid-template-rows: 100%;
- height: calc(100vh - $s-52);
- overflow: hidden;
- .tabs-wrapper {
- .layers-tab {
- display: grid;
- grid-template-rows: auto 1fr;
- grid-template-columns: 100%;
- height: 100%;
- overflow: hidden;
- }
- }
+ grid-area: resize;
}
}
+
+.left-header {
+ grid-area: header;
+}
+
+.settings-bar-content {
+ grid-area: content;
+ right: calc(-1 * $s-8);
+}
+
+.resize-area {
+ position: absolute;
+ top: 0;
+ left: unset;
+ z-index: $z-index-3;
+ width: $s-8;
+ cursor: ew-resize;
+ height: 100%;
+}
+
.tab-spacing {
- margin: $s-4 $s-8 0 $s-8;
+ margin-inline: $s-8;
}
+
.right-settings-bar {
grid-area: right-sidebar;
width: $width-settings-bar;
@@ -69,14 +66,24 @@ $width-settings-bar-max: $s-500;
&.expanded {
width: var(--width, $width-settings-bar);
}
- .resize-area {
- position: absolute;
- height: 100%;
- width: $s-8;
- z-index: $z-index-3;
- cursor: ew-resize;
- }
+
.settings-bar-inside {
+ display: grid;
+ grid-template-columns: 100%;
+ grid-template-rows: 100%;
+
height: calc(100vh - $s-52);
+ overflow: hidden;
}
}
+
+.resize-area-horiz {
+ position: absolute;
+ top: calc($s-80 + var(--height, 200px));
+ left: 0;
+ width: 100%;
+ height: $s-12;
+ border-top: $s-2 solid var(--resize-area-border-color);
+ background-color: var(--resize-area-background-color);
+ cursor: ns-resize;
+}
diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs
index 09118cffa6..d82ae14e11 100644
--- a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs
+++ b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs
@@ -153,7 +153,7 @@
:option-handler on-section-filter-change
:data-test "typographies"}]))]
- [:div {:class (stl/css :assets-bar)}
+ [:article {:class (stl/css :assets-bar)}
[:div {:class (stl/css :assets-header)}
(when-not ^boolean read-only?
[:button {:class (stl/css :libraries-button)
@@ -178,7 +178,7 @@
:fixed? true
:min-width? true
:top 152
- :left 64
+ :left 18
:options options
:workspace? true}]
[:button {:class (stl/css :sort-button)
@@ -190,6 +190,6 @@
[:& (mf/provider cmm/assets-filters) {:value filters}
[:& (mf/provider cmm/assets-toggle-ordering) {:value toggle-ordering}
[:& (mf/provider cmm/assets-toggle-list-style) {:value toggle-list-style}
- [:div {:class (stl/css :libraries-wrapper)}
+ [:*
[:& assets-local-library {:filters filters}]
[:& assets-libraries {:filters filters}]]]]]]))
diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets.scss b/frontend/src/app/main/ui/workspace/sidebar/assets.scss
index dd8ebf217a..0c73974ca1 100644
--- a/frontend/src/app/main/ui/workspace/sidebar/assets.scss
+++ b/frontend/src/app/main/ui/workspace/sidebar/assets.scss
@@ -7,9 +7,11 @@
@import "refactor/common-refactor.scss";
.assets-bar {
- position: relative;
+ display: grid;
height: 100%;
- overflow: hidden;
+ grid-auto-rows: max-content;
+ // TODO: ugly hack :( Fix this! we shouldn't be hardcoding this height
+ max-height: calc(100vh - $s-80);
}
.libraries-button {
@@ -108,16 +110,6 @@
@include buttonStyle;
}
-.libraries-wrapper {
- overflow-x: hidden;
- overflow-y: auto;
- scrollbar-gutter: stable;
- display: flex;
- flex-direction: column;
- padding-left: $s-8;
- height: calc(100vh - $s-180);
-}
-
.assets-header {
padding: $s-8 $s-12 $s-12 $s-12;
}
diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.scss b/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.scss
index 0044dbcd18..a131dd26b3 100644
--- a/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.scss
+++ b/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.scss
@@ -6,7 +6,14 @@
@import "refactor/common-refactor.scss";
.tool-window {
- margin-bottom: $s-24;
+ padding-inline-start: $s-12;
+ overflow-y: auto;
+ display: grid;
+ grid-auto-rows: max-content;
+ scrollbar-gutter: stable;
+ &:last-child {
+ margin-block-end: $s-24;
+ }
}
.file-name {
@@ -38,13 +45,7 @@
}
.library-content {
- display: flex;
- flex-direction: column;
- height: calc(100% - $s-36);
width: 100%;
- overflow-y: hidden;
- overflow-x: hidden;
- margin-top: $s-4;
}
.asset-title {
diff --git a/frontend/src/app/main/ui/workspace/sidebar/debug.cljs b/frontend/src/app/main/ui/workspace/sidebar/debug.cljs
index 71adc0d998..b49371e5ff 100644
--- a/frontend/src/app/main/ui/workspace/sidebar/debug.cljs
+++ b/frontend/src/app/main/ui/workspace/sidebar/debug.cljs
@@ -8,6 +8,7 @@
(:require-macros [app.main.style :as stl])
(:require
[app.common.data :as d]
+ [app.common.data.macros :as dm]
[app.main.data.workspace :as dw]
[app.main.store :as st]
[app.main.ui.icons :as i]
@@ -16,21 +17,21 @@
[rumext.v2 :as mf]))
(mf/defc debug-panel
- []
+ [{:keys [class] :as props}]
(let [on-toggle-enabled
(mf/use-fn
(fn [event option]
(dom/prevent-default event)
(dom/stop-propagation event)
(dbg/toggle! option)
- (js* "app.main.reinit()")))
+ (js* "app.main.reinit(true)")))
handle-close
(mf/use-fn
(fn []
(st/emit! (dw/remove-layout-flag :debug-panel))))]
- [:div {:class (stl/css :debug-panel)}
+ [:div {:class (dm/str class " " (stl/css :debug-panel))}
[:div {:class (stl/css :panel-title)}
[:span "Debugging tools"]
[:div {:class (stl/css :close-button) :on-click handle-close}
diff --git a/frontend/src/app/main/ui/workspace/sidebar/layer_item.scss b/frontend/src/app/main/ui/workspace/sidebar/layer_item.scss
index 4f703090b9..68779f911b 100644
--- a/frontend/src/app/main/ui/workspace/sidebar/layer_item.scss
+++ b/frontend/src/app/main/ui/workspace/sidebar/layer_item.scss
@@ -74,11 +74,12 @@
}
.element-list-body {
- display: flex;
align-items: center;
+ display: grid;
+ grid-template-columns: auto 1fr auto;
+ column-gap: $s-4;
height: $s-32;
width: calc(100% - (var(--depth) * var(--layer-indentation-size)));
- padding-right: $s-12;
cursor: pointer;
&.filtered {
@@ -111,7 +112,7 @@
width: $s-16;
height: 100%;
width: $s-24;
- padding: 0 $s-8 0 $s-4;
+ padding-inline-start: $s-4;
svg {
@extend .button-icon-small;
stroke: var(--icon-foreground);
@@ -169,7 +170,7 @@
align-items: center;
height: 100%;
width: $s-24;
- padding: 0 $s-4 0 $s-8;
+ padding-inline-start: $s-8;
svg {
@extend .button-icon-small;
diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs
index b306434959..4ce3d31968 100644
--- a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs
+++ b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs
@@ -458,7 +458,7 @@
(mf/use-fn
#(st/emit! (dw/toggle-focus-mode)))]
- [:div#layers {:class (stl/css :layers)}
+ [:div#layers
(if (d/not-empty? focus)
[:div {:class (stl/css :tool-window-bar)}
[:button {:class (stl/css :focus-title)
diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.scss b/frontend/src/app/main/ui/workspace/sidebar/layers.scss
index 71289de1a0..e46c73d23c 100644
--- a/frontend/src/app/main/ui/workspace/sidebar/layers.scss
+++ b/frontend/src/app/main/ui/workspace/sidebar/layers.scss
@@ -6,14 +6,6 @@
@import "refactor/common-refactor.scss";
-.layers {
- position: relative;
- display: flex;
- flex-direction: column;
- overflow: auto;
- box-sizing: border-box;
-}
-
.tool-window-bar {
display: flex;
align-items: center;
@@ -246,7 +238,8 @@
}
.tool-window-content {
- --calculated-height: calc($s-128 + var(--height, $s-200));
+ // TODO: sass variables are not being interpolated here, find why
+ --calculated-height: calc(128px + var(--height, 200px));
display: flex;
flex-direction: column;
height: calc(100vh - var(--calculated-height));
diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs
index dff6d0888e..fd5e423246 100644
--- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs
+++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs
@@ -555,7 +555,8 @@
:on-change #(set-justify % type)
:name (dm/str "grid-justify-items-" (d/name type))}
(for [justify [:start :center :end :space-around :space-between :stretch]]
- [:& radio-button {:value (d/name justify)
+ [:& radio-button {:key (dm/str "justify-item-" (d/name justify))
+ :value (d/name justify)
:icon (get-layout-grid-icon-refactor :justify-items justify is-col?)
:title (dm/str "Justify items " (d/name justify))
:id (dm/str "justify-items-" (d/name justify) "-" (d/name type))}])]))
diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.scss
index 536f589c99..036c14ca10 100644
--- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.scss
+++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.scss
@@ -64,6 +64,7 @@
font-size: $fs-10;
text-transform: uppercase;
margin-inline-start: $s-4;
+ color: $df-primary;
}
.attr-row {
diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.scss
index 6f7aab290d..e8a81f607f 100644
--- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.scss
+++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.scss
@@ -356,22 +356,22 @@
flex-direction: column;
position: relative;
margin-bottom: $s-2;
+ background-color: var(--dropdown-background-color);
.title {
@include tabTitleTipography;
margin: 9px 17px;
+ color: var(--title-foreground-color);
}
}
.fonts-list {
@include menuShadow;
- position: absolute;
- top: $s-36;
- left: 0;
+ position: relative;
display: flex;
flex-direction: column;
flex: 1 1 auto;
min-height: 100%;
- height: $s-216;
width: 100%;
+ height: 100%;
padding: $s-2;
border-radius: $br-8;
background-color: var(--dropdown-background-color);
diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.scss b/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.scss
index 5285303c5f..bfa232fdad 100644
--- a/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.scss
+++ b/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.scss
@@ -49,13 +49,9 @@
}
.color-name {
@include titleTipography;
- display: flex;
- align-items: center;
- height: $s-28;
- padding-left: $s-6;
+ @include textEllipsis;
+ padding-inline: $s-6;
border-radius: $br-8;
- width: 100%;
- flex-grow: 1;
color: var(--input-foreground-color-active);
}
.detach-btn {
diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs
index ca0b4b57fe..23c70c23e5 100644
--- a/frontend/src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs
+++ b/frontend/src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs
@@ -123,14 +123,14 @@
stroke-caps-options
[{:value nil :label (tr "workspace.options.stroke-cap.none")}
:separator
- {:value :line-arrow :label (tr "workspace.options.stroke-cap.line-arrow-short")}
- {:value :triangle-arrow :label (tr "workspace.options.stroke-cap.triangle-arrow-short")}
- {:value :square-marker :label (tr "workspace.options.stroke-cap.square-marker-short")}
- {:value :circle-marker :label (tr "workspace.options.stroke-cap.circle-marker-short")}
- {:value :diamond-marker :label (tr "workspace.options.stroke-cap.diamond-marker-short")}
+ {:value :line-arrow :label (tr "workspace.options.stroke-cap.line-arrow-short") :icon :cap-line-arrow}
+ {:value :triangle-arrow :label (tr "workspace.options.stroke-cap.triangle-arrow-short") :icon :cap-triangle-arrow}
+ {:value :square-marker :label (tr "workspace.options.stroke-cap.square-marker-short") :icon :cap-square-marker}
+ {:value :circle-marker :label (tr "workspace.options.stroke-cap.circle-marker-short") :icon :cap-circle-marker}
+ {:value :diamond-marker :label (tr "workspace.options.stroke-cap.diamond-marker-short") :icon :cap-diamond-marker}
:separator
- {:value :round :label (tr "workspace.options.stroke-cap.round")}
- {:value :square :label (tr "workspace.options.stroke-cap.square")}]
+ {:value :round :label (tr "workspace.options.stroke-cap.round") :icon :cap-round}
+ {:value :square :label (tr "workspace.options.stroke-cap.square") :icon :cap-square}]
on-cap-switch
(mf/use-callback
diff --git a/frontend/src/app/main/ui/workspace/sidebar/shortcuts.cljs b/frontend/src/app/main/ui/workspace/sidebar/shortcuts.cljs
index 04748ef406..241b88445c 100644
--- a/frontend/src/app/main/ui/workspace/sidebar/shortcuts.cljs
+++ b/frontend/src/app/main/ui/workspace/sidebar/shortcuts.cljs
@@ -341,7 +341,7 @@
:filter-term filter-term}]]])))
(mf/defc shortcuts-container
- []
+ [{:keys [class] :as props}]
(let [workspace-shortcuts app.main.data.workspace.shortcuts/shortcuts
path-shortcuts app.main.data.workspace.path.shortcuts/shortcuts
all-workspace-shortcuts (->> (d/deep-merge path-shortcuts workspace-shortcuts)
@@ -468,7 +468,7 @@
(mf/with-effect []
(dom/focus! (dom/get-element "shortcut-search")))
- [:div {:class (stl/css :shortcuts)}
+ [:div {:class (dm/str class " " (stl/css :shortcuts))}
[:div {:class (stl/css :shortcuts-header)}
[:div {:class (stl/css :shortcuts-title)} (tr "shortcuts.title")]
[:div {:class (stl/css :shortcuts-close-button)
diff --git a/frontend/src/app/main/ui/workspace/top_toolbar.scss b/frontend/src/app/main/ui/workspace/top_toolbar.scss
index de44be4431..03cfac6970 100644
--- a/frontend/src/app/main/ui/workspace/top_toolbar.scss
+++ b/frontend/src/app/main/ui/workspace/top_toolbar.scss
@@ -16,7 +16,7 @@
height: $s-56;
padding: $s-8 $s-16;
border-radius: $s-8;
- z-index: $z-index-2;
+ z-index: $z-index-10;
background-color: var(--color-background-primary);
transition:
top 0.3s,
diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs
index 35cb52be76..7fdc82f6c8 100644
--- a/frontend/src/app/main/ui/workspace/viewport.cljs
+++ b/frontend/src/app/main/ui/workspace/viewport.cljs
@@ -16,6 +16,7 @@
[app.main.data.workspace.modifiers :as dwm]
[app.main.refs :as refs]
[app.main.ui.context :as ctx]
+ [app.main.ui.flex-controls :as mfc]
[app.main.ui.hooks :as ui-hooks]
[app.main.ui.measurements :as msr]
[app.main.ui.shapes.export :as use]
@@ -232,31 +233,32 @@
disabled-guides? (or drawing-tool transform drawing-path? node-editing?)
- one-selected-shape? (= (count selected-shapes) 1)
+ single-select? (= (count selected-shapes) 1)
- show-padding? (and (nil? transform)
- one-selected-shape?
- (= (:type (first selected-shapes)) :frame)
- (= (:layout (first selected-shapes)) :flex)
- (zero? (:rotation (first selected-shapes))))
+ first-shape (first selected-shapes)
+ show-padding?
+ (and (nil? transform)
+ single-select?
+ (= (:type first-shape) :frame)
+ (= (:layout first-shape) :flex)
+ (zero? (:rotation first-shape)))
- show-margin? (and (nil? transform)
- one-selected-shape?
- (= (:layout selected-frame) :flex)
- (zero? (:rotation (first selected-shapes))))
+ show-margin?
+ (and (nil? transform)
+ single-select?
+ (= (:layout selected-frame) :flex)
+ (zero? (:rotation first-shape)))
- first-selected-shape (first selected-shapes)
- selecting-first-level-frame? (and one-selected-shape?
- (cfh/root-frame? first-selected-shape))
+ selecting-first-level-frame? (and single-select? (cfh/root-frame? first-shape))
offset-x (if selecting-first-level-frame?
- (:x first-selected-shape)
+ (:x first-shape)
(:x selected-frame))
offset-y (if selecting-first-level-frame?
- (:y (first selected-shapes))
+ (:y first-shape)
(:y selected-frame))
rule-area-size (/ rules/rule-area-size zoom)]
@@ -439,24 +441,28 @@
:zoom zoom}])
(when show-padding?
- [:*
- [:& msr/padding
- {:frame (first selected-shapes)
- :hover @frame-hover
- :zoom zoom
- :alt? @alt?
- :shift? @shift?}]
+ [:& mfc/padding-control
+ {:frame first-shape
+ :hover @frame-hover
+ :zoom zoom
+ :alt? @alt?
+ :shift? @shift?
+ :on-move-selected on-move-selected
+ :on-context-menu on-menu-selected}])
- [:& msr/gap
- {:frame (first selected-shapes)
- :hover @frame-hover
- :zoom zoom
- :alt? @alt?
- :shift? @shift?}]])
+ (when show-padding?
+ [:& mfc/gap-control
+ {:frame first-shape
+ :hover @frame-hover
+ :zoom zoom
+ :alt? @alt?
+ :shift? @shift?
+ :on-move-selected on-move-selected
+ :on-context-menu on-menu-selected}])
(when show-margin?
- [:& msr/margin
- {:shape (first selected-shapes)
+ [:& mfc/margin-control
+ {:shape first-shape
:parent selected-frame
:hover @frame-hover
:zoom zoom
diff --git a/frontend/src/app/main/ui/workspace/viewport/actions.cljs b/frontend/src/app/main/ui/workspace/viewport/actions.cljs
index 64fe00e77f..3a9174a9df 100644
--- a/frontend/src/app/main/ui/workspace/viewport/actions.cljs
+++ b/frontend/src/app/main/ui/workspace/viewport/actions.cljs
@@ -43,7 +43,6 @@
(mf/deps id blocked hidden type selected edition drawing-tool text-editing?
node-editing? grid-editing? drawing-path? create-comment? @z? @space?
panning workspace-read-only?)
-
(fn [bevent]
;; We need to handle editor related stuff here because
;; handling on editor dom node does not works properly.
@@ -127,6 +126,7 @@
(not mod?)
(not shift?)
(not @space?))
+
(dom/prevent-default bevent)
(dom/stop-propagation bevent)
(when-not (or workspace-read-only? @z?)
diff --git a/frontend/src/app/main/ui/workspace/viewport/rules.cljs b/frontend/src/app/main/ui/workspace/viewport/rules.cljs
index c3af821de1..21db6a320a 100644
--- a/frontend/src/app/main/ui/workspace/viewport/rules.cljs
+++ b/frontend/src/app/main/ui/workspace/viewport/rules.cljs
@@ -23,8 +23,9 @@
(def rules-background "var(--panel-background-color)")
(def selection-area-color "var(--color-primary)")
(def selection-area-opacity 0.3)
-(def over-number-size 50)
-(def over-number-opacity 0.7)
+(def over-number-size 100)
+(def over-number-opacity 0.8)
+(def over-number-percent 0.75)
(def font-size 12)
(def font-family "worksans")
@@ -204,7 +205,29 @@
;; When using the format-number callls we consider if the guide is associated to a frame and we show the position relative to it with the offset
(let [rules-background rules-background]
[:g.selection-area
+ [:defs
+ [:linearGradient {:id "selection-gradient-start"}
+ [:stop {:offset "0%" :stop-color rules-background :stop-opacity 0}]
+ [:stop {:offset "40%" :stop-color rules-background :stop-opacity 1}]
+ [:stop {:offset "100%" :stop-color rules-background :stop-opacity 1}]]
+
+ [:linearGradient {:id "selection-gradient-end"}
+ [:stop {:offset "0%" :stop-color rules-background :stop-opacity 1}]
+ [:stop {:offset "60%" :stop-color rules-background :stop-opacity 1}]
+ [:stop {:offset "100%" :stop-color rules-background :stop-opacity 0}]]]
[:g
+ [:rect {:x (- (:x selection-rect) (* (* over-number-size over-number-percent) zoom-inverse))
+ :y (:y vbox)
+ :width (* over-number-size zoom-inverse)
+ :height (* rule-area-size zoom-inverse)
+ :fill "url('#selection-gradient-start')"}]
+
+ [:rect {:x (- (:x2 selection-rect) (* over-number-size (- 1 over-number-percent)))
+ :y (:y vbox)
+ :width (* over-number-size zoom-inverse)
+ :height (* rule-area-size zoom-inverse)
+ :fill "url('#selection-gradient-end')"}]
+
[:rect {:x (:x selection-rect)
:y (:y vbox)
:width (:width selection-rect)
@@ -212,15 +235,8 @@
:style {:fill selection-area-color
:fill-opacity selection-area-opacity}}]
- [:rect {:x (- (:x selection-rect) (* over-number-size zoom-inverse))
- :y (:y vbox)
- :width (* over-number-size zoom-inverse)
- :height (* rule-area-size zoom-inverse)
- :style {:fill rules-background
- :fill-opacity over-number-opacity}}]
-
[:text {:x (- (:x1 selection-rect) (* 4 zoom-inverse))
- :y (+ (:y vbox) (* 12 zoom-inverse))
+ :y (+ (:y vbox) (* 10.6 zoom-inverse))
:text-anchor "end"
:dominant-baseline "middle"
:style {:font-size (* font-size zoom-inverse)
@@ -228,15 +244,8 @@
:fill selection-area-color}}
(fmt/format-number (- (:x1 selection-rect) offset-x))]
- [:rect {:x (:x2 selection-rect)
- :y (:y vbox)
- :width (* over-number-size zoom-inverse)
- :height (* rule-area-size zoom-inverse)
- :style {:fill rules-background
- :fill-opacity over-number-opacity}}]
-
[:text {:x (+ (:x2 selection-rect) (* 4 zoom-inverse))
- :y (+ (:y vbox) (* 12 zoom-inverse))
+ :y (+ (:y vbox) (* 10.6 zoom-inverse))
:text-anchor "start"
:dominant-baseline "middle"
:style {:font-size (* font-size zoom-inverse)
diff --git a/frontend/src/app/util/color.cljs b/frontend/src/app/util/color.cljs
index d7fc4a2bd3..79989b3894 100644
--- a/frontend/src/app/util/color.cljs
+++ b/frontend/src/app/util/color.cljs
@@ -12,6 +12,8 @@
(:require
[app.common.colors :as cc]
[app.common.data :as d]
+ [app.common.data.macros :as dm]
+ [app.common.math :as mth]
[app.util.i18n :as i18n :refer [tr]]
[cuerdas.core :as str]))
@@ -82,3 +84,10 @@
(:name color)
(:color color)
(gradient-type->string (:type (:gradient color)))))
+
+(defn random-color
+ []
+ (dm/fmt "rgb(%, %, %)"
+ (mth/floor (* (js/Math.random) 256))
+ (mth/floor (* (js/Math.random) 256))
+ (mth/floor (* (js/Math.random) 256))))
diff --git a/frontend/src/app/util/dom.cljs b/frontend/src/app/util/dom.cljs
index c6b3947d58..efe5108685 100644
--- a/frontend/src/app/util/dom.cljs
+++ b/frontend/src/app/util/dom.cljs
@@ -433,6 +433,12 @@
[o prop]
(.getPropertyValue ^js o prop))
+(defn get-css-variable
+ ([variable element]
+ (.getPropertyValue (.getComputedStyle js/window element) variable))
+ ([variable]
+ (.getPropertyValue (.getComputedStyle js/window (.-documentElement js/document)) variable)))
+
(defn focus!
[^js node]
(when (some? node)