Merge remote-tracking branch 'origin/staging' into develop
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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])))))
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)))))
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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."
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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]]
|
||||
|
||||
@ -1 +0,0 @@
|
||||
<svg width="16" height="6" xmlns="http://www.w3.org/2000/svg"><rect rx="6" ry="6" x="10" width="6" height="6"/><path d="M0 3h14.5" fill="none" stroke="#000"/></svg>
|
||||
|
Before Width: | Height: | Size: 165 B |
@ -1 +0,0 @@
|
||||
<svg width="16" height="6" xmlns="http://www.w3.org/2000/svg"><rect rx="0" ry="0" x="11" y="1" transform="rotate(45 13 3)" width="4" height="4"/><path d="M0 3h14.5" fill="none" stroke="#000"/></svg>
|
||||
|
Before Width: | Height: | Size: 199 B |
@ -1 +0,0 @@
|
||||
<svg width="16" height="6" xmlns="http://www.w3.org/2000/svg"><path d="M0 3h14.5M11.7 0l1 1 1.6 2-2.6 3" fill="none" stroke="#000"/></svg>
|
||||
|
Before Width: | Height: | Size: 139 B |
@ -1 +0,0 @@
|
||||
<svg viewBox="1863 1374 16 8" width="16" height="8" xmlns="http://www.w3.org/2000/svg"><path d="M1879 1374h-12s-4 0-4 4 4 4 4 4h12" fill="none" stroke="#000"/></svg>
|
||||
|
Before Width: | Height: | Size: 166 B |
@ -1 +0,0 @@
|
||||
<svg width="16" height="6" xmlns="http://www.w3.org/2000/svg"><rect rx="0" ry="0" x="10" width="6" height="6" fill="#070707"/><path d="M0 3h14.5" fill="none" stroke="#000"/></svg>
|
||||
|
Before Width: | Height: | Size: 180 B |
@ -1 +0,0 @@
|
||||
<svg viewBox="1863 1407 16 8" width="16" height="8" xmlns="http://www.w3.org/2000/svg"><path d="M1879 1407h-16v8h16" fill="none" stroke="#000"/></svg>
|
||||
|
Before Width: | Height: | Size: 151 B |
@ -1 +0,0 @@
|
||||
<svg width="16" height="6" xmlns="http://www.w3.org/2000/svg"><path d="M0 3h14.5" fill="none" stroke="#000"/><path d="M13 0l2.9 3L13 6V0z"/></svg>
|
||||
|
Before Width: | Height: | Size: 147 B |
3
frontend/resources/images/icons/cap-circle-marker.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M8 8H1m7 0a3.5 3.5 0 107 0 3.5 3.5 0 00-7 0zm1 1.25h.295m.882 1.25h.294M9 6.75h.295M10.177 8h.294m.882 1.25h.294m.882 1.25h.295m-2.647-5h.294m.882 1.25h.294M12.529 8h.295m.882 1.25H14M12.53 5.5h.294m.882 1.25H14"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 343 B |
3
frontend/resources/images/icons/cap-diamond-marker.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M7.5 8H1m8 1.25h.295m.882 1.25h.294M9 6.75h.295M10.177 8h.294m.882 1.25h.294m.882 1.25h.295m-2.647-5h.294m.882 1.25h.294M12.529 8h.295m.882 1.25H14M12.53 5.5h.294m.882 1.25H14M7.556 8.136l3.808 3.808a.192.192 0 00.272 0l3.808-3.808a.192.192 0 000-.272l-3.808-3.808a.192.192 0 00-.272 0L7.556 7.864a.192.192 0 000 .272z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 450 B |
3
frontend/resources/images/icons/cap-line-arrow.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M14.667 8H1.333M9.5 4l5.278 3.838a.2.2 0 010 .324L9.5 12.5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 190 B |
3
frontend/resources/images/icons/cap-round.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M2 5h9.823C13.578 5 15 6.343 15 8s-1.422 3-3.177 3H2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 184 B |
3
frontend/resources/images/icons/cap-square-marker.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M8 8H1m8 1.25h.295m.882 1.25h.294M9 6.75h.295M10.177 8h.294m.882 1.25h.294m.882 1.25h.295m-2.647-5h.294m.882 1.25h.294M12.529 8h.295m.882 1.25H14M12.53 5.5h.294m.882 1.25H14M8.5 5h6v6h-6V5z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 321 B |
3
frontend/resources/images/icons/cap-square.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M2 5h13v6H2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 143 B |
3
frontend/resources/images/icons/cap-triangle-arrow.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M9 9.25h.295m.882 1.25h.294M9 6.75h.295M10.177 8h.294m.882 1.25h.294m-1.47-3.75h.294m.882 1.25h.294M12.529 8h.295M8 8H1m13.723-.17L8.805 4.188a.2.2 0 00-.305.17v7.284a.2.2 0 00.305.17l5.918-3.642a.2.2 0 000-.34z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 343 B |
@ -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;
|
||||
|
||||
@ -159,11 +159,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.assets-bar .tool-window {
|
||||
flex: none;
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.empty {
|
||||
|
||||
@ -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
|
||||
[]
|
||||
|
||||
@ -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
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
@ -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?
|
||||
|
||||
@ -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)))
|
||||
|
||||
@ -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))))))
|
||||
|
||||
@ -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))))))
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -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)))))
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -85,8 +85,9 @@
|
||||
|
||||
.big-text {
|
||||
@include inspectValue;
|
||||
@include twoLineTextEllipsis;
|
||||
color: var(--palette-text-color);
|
||||
height: $s-16;
|
||||
height: $s-28;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)))
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
16
frontend/src/app/main/ui/flex_controls.cljs
Normal file
@ -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)
|
||||
35
frontend/src/app/main/ui/flex_controls/common.cljs
Normal file
@ -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))]])
|
||||
311
frontend/src/app/main/ui/flex_controls/gap.cljs
Normal file
@ -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}]]]))
|
||||
185
frontend/src/app/main/ui/flex_controls/margin.cljs
Normal file
@ -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?}]]]))
|
||||
223
frontend/src/app/main/ui/flex_controls/padding.cljs
Normal file
@ -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}]]]))
|
||||
@ -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
|
||||
|
||||
@ -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?}]]]))
|
||||
|
||||
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
:global(:root) {
|
||||
--s-4: 0.25rem;
|
||||
--layer-indentation-size: calc(var(--s-4) * 5);
|
||||
}
|
||||
|
||||
.modal-wrapper {
|
||||
|
||||
@ -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)}
|
||||
|
||||
@ -62,6 +62,7 @@
|
||||
flex-wrap: nowrap;
|
||||
margin-top: 0;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.viewer-go-prev,
|
||||
|
||||
@ -28,7 +28,6 @@
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
padding: 0 $s-4 $s-8 0;
|
||||
|
||||
pre {
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -78,3 +78,12 @@
|
||||
}
|
||||
|
||||
// breakpoint 1013px
|
||||
|
||||
.fixed {
|
||||
position: fixed;
|
||||
pointer-events: none;
|
||||
|
||||
:global(.frame-children) g {
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -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
|
||||
|
||||
@ -8,7 +8,6 @@
|
||||
|
||||
.colorpicker-tooltip {
|
||||
@extend .modal-background;
|
||||
top: $s-100;
|
||||
left: calc(10 * $s-140);
|
||||
width: auto;
|
||||
}
|
||||
|
||||
@ -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])
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -9,7 +9,6 @@
|
||||
.workspace-header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: $s-48;
|
||||
padding: $s-8 $s-8 $s-4 $s-8;
|
||||
}
|
||||
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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}])]))))
|
||||
|
||||
|
||||
@ -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}])])))
|
||||
|
||||
99
frontend/src/app/main/ui/workspace/shapes/debug.cljs
Normal file
@ -0,0 +1,99 @@
|
||||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) KALEIDOS INC
|
||||
|
||||
(ns app.main.ui.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}])])
|
||||
@ -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}])]))))
|
||||
|
||||
|
||||
@ -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}])]))))
|
||||
|
||||
|
||||
@ -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}])]))
|
||||
|
||||
@ -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}])))))
|
||||
|
||||
@ -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}])]))
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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}]]]]]]))
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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))}])]))
|
||||
|
||||
@ -64,6 +64,7 @@
|
||||
font-size: $fs-10;
|
||||
text-transform: uppercase;
|
||||
margin-inline-start: $s-4;
|
||||
color: $df-primary;
|
||||
}
|
||||
|
||||
.attr-row {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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?)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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))))
|
||||
|
||||
@ -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)
|
||||
|
||||