mirror of
https://github.com/penpot/penpot.git
synced 2026-07-22 22:17:58 +00:00
✨ Add new debugger tool components-debugger (#10757)
This commit is contained in:
parent
f9439d2942
commit
3a0aca52c3
105
frontend/src/app/main/ui/hooks/floating_drag.cljs
Normal file
105
frontend/src/app/main/ui/hooks/floating_drag.cljs
Normal file
@ -0,0 +1,105 @@
|
||||
;; 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 Sucursal en España SL
|
||||
|
||||
(ns app.main.ui.hooks.floating-drag
|
||||
"Pointer drag hook for floating panels, mirroring the plugin modal drag
|
||||
handler in plugins-runtime."
|
||||
(:require
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.util.dom :as dom]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(defn- parse-translate
|
||||
"Reads the current translate offset from an element's computed transform."
|
||||
[^js el]
|
||||
(if (and el (.-DOMMatrixReadOnly js/window))
|
||||
(let [cs (.getComputedStyle js/window el nil)
|
||||
matrix (js/DOMMatrixReadOnly. (.-transform cs))]
|
||||
{:x (.-m41 matrix)
|
||||
:y (.-m42 matrix)})
|
||||
{:x 0 :y 0}))
|
||||
|
||||
(defn- set-dragging-class!
|
||||
"Toggle `dragging-class` on `target` while a drag is active."
|
||||
[^js target dragging-class dragging?]
|
||||
(when (and target dragging-class)
|
||||
(if dragging?
|
||||
(dom/add-class! target dragging-class)
|
||||
(dom/remove-class! target dragging-class))))
|
||||
|
||||
(defn use-floating-drag
|
||||
"Returns pointer handlers to drag `target-ref` by its header.
|
||||
|
||||
Optional `on-move` is called on pointer down (e.g. to raise z-index).
|
||||
Optional `dragging-class` is toggled on `target-ref` while dragging."
|
||||
([target-ref]
|
||||
(use-floating-drag target-ref nil nil))
|
||||
([target-ref on-move]
|
||||
(use-floating-drag target-ref on-move nil))
|
||||
([target-ref on-move dragging-class]
|
||||
(let [dragging-ref (mf/use-ref false)
|
||||
pointer-id-ref (mf/use-ref nil)
|
||||
initial-translate-ref (mf/use-ref {:x 0 :y 0})
|
||||
initial-client-ref (mf/use-ref (gpt/point 0 0))
|
||||
|
||||
end-drag
|
||||
(mf/use-fn
|
||||
(mf/deps dragging-class)
|
||||
(fn [event]
|
||||
(when (mf/ref-val dragging-ref)
|
||||
(mf/set-ref-val! dragging-ref false)
|
||||
(mf/set-ref-val! pointer-id-ref nil)
|
||||
(set-dragging-class! (mf/ref-val target-ref) dragging-class false)
|
||||
(when event (dom/release-pointer event)))))
|
||||
|
||||
handle-lost-pointer-capture
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(end-drag event)))
|
||||
|
||||
handle-pointer-up
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(when (= (.-pointerId event) (mf/ref-val pointer-id-ref))
|
||||
(end-drag event))))
|
||||
|
||||
handle-pointer-move
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(when (and (mf/ref-val dragging-ref)
|
||||
(= (.-pointerId event) (mf/ref-val pointer-id-ref)))
|
||||
(let [target (mf/ref-val target-ref)
|
||||
start (mf/ref-val initial-client-ref)
|
||||
pos (dom/get-client-position event)
|
||||
{:keys [x y]} (mf/ref-val initial-translate-ref)
|
||||
delta-x (+ x (- (:x pos) (:x start)))
|
||||
delta-y (+ y (- (:y pos) (:y start)))]
|
||||
(when target
|
||||
(dom/set-css-property! target "transform"
|
||||
(str "translate(" delta-x "px, " delta-y "px)")))))))
|
||||
|
||||
handle-pointer-down
|
||||
(mf/use-fn
|
||||
(mf/deps on-move dragging-class)
|
||||
(fn [event]
|
||||
(when (and (= (.-button event) 0)
|
||||
(not (and (instance? js/Element (.-target event))
|
||||
(.closest (.-target event) "button"))))
|
||||
(dom/prevent-default event)
|
||||
(let [target (mf/ref-val target-ref)]
|
||||
(when target
|
||||
(mf/set-ref-val! pointer-id-ref (.-pointerId event))
|
||||
(mf/set-ref-val! initial-client-ref (dom/get-client-position event))
|
||||
(mf/set-ref-val! initial-translate-ref (parse-translate target))
|
||||
(mf/set-ref-val! dragging-ref true)
|
||||
(set-dragging-class! target dragging-class true)
|
||||
(dom/capture-pointer event)
|
||||
(when on-move (on-move)))))))]
|
||||
|
||||
{:on-pointer-down handle-pointer-down
|
||||
:on-pointer-move handle-pointer-move
|
||||
:on-pointer-up handle-pointer-up
|
||||
:on-lost-pointer-capture handle-lost-pointer-capture})))
|
||||
@ -25,6 +25,7 @@
|
||||
[app.main.ui.hooks.resize :refer [use-resize-observer]]
|
||||
[app.main.ui.modal :refer [modal-container*]]
|
||||
[app.main.ui.workspace.colorpicker]
|
||||
[app.main.ui.workspace.components-debugger :refer [components-debugger*]]
|
||||
[app.main.ui.workspace.context-menu :refer [context-menu*]]
|
||||
[app.main.ui.workspace.coordinates :as coordinates]
|
||||
[app.main.ui.workspace.libraries]
|
||||
@ -271,6 +272,7 @@
|
||||
[:> (mf/provider ctx/design-tokens) {:value design-tokens?}
|
||||
[:> (mf/provider ctx/workspace-read-only?) {:value read-only?}
|
||||
[:> modal-container*]
|
||||
[:> components-debugger*]
|
||||
[:section {:class (stl/css :workspace)
|
||||
:style {:background-color background-color
|
||||
:touch-action "none"
|
||||
|
||||
1189
frontend/src/app/main/ui/workspace/components_debugger.cljs
Normal file
1189
frontend/src/app/main/ui/workspace/components_debugger.cljs
Normal file
File diff suppressed because it is too large
Load Diff
224
frontend/src/app/main/ui/workspace/components_debugger.scss
Normal file
224
frontend/src/app/main/ui/workspace/components_debugger.scss
Normal file
@ -0,0 +1,224 @@
|
||||
// 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 Sucursal en España SL
|
||||
|
||||
@use "ds/_borders.scss" as *;
|
||||
@use "ds/_sizes.scss" as *;
|
||||
@use "ds/_utils.scss" as *;
|
||||
@use "ds/typography.scss" as t;
|
||||
|
||||
.wrapper {
|
||||
position: fixed;
|
||||
padding: var(--sp-s);
|
||||
border-radius: $br-8;
|
||||
border: $b-2 solid var(--color-background-quaternary);
|
||||
box-shadow: 0 0 var(--sp-s) 0 var(--color-shadow-light);
|
||||
overflow: hidden;
|
||||
min-inline-size: $sz-24;
|
||||
min-block-size: $sz-200;
|
||||
max-inline-size: 90vw;
|
||||
max-block-size: 90vh;
|
||||
resize: both;
|
||||
user-select: none;
|
||||
background-color: var(--color-background-primary);
|
||||
color: var(--color-foreground-secondary);
|
||||
z-index: var(--z-index-set);
|
||||
}
|
||||
|
||||
.inner {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
block-size: 100%;
|
||||
padding: var(--sp-s);
|
||||
}
|
||||
|
||||
.header {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
justify-content: space-between;
|
||||
border-block-end: $b-1 solid var(--color-background-quaternary);
|
||||
padding-block-end: var(--sp-xxs);
|
||||
cursor: grab;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
.wrapper.is-dragging .header {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.title {
|
||||
@include t.use-typography("body-small");
|
||||
|
||||
font-weight: var(--font-weight-medium);
|
||||
margin: 0;
|
||||
margin-inline-end: var(--sp-xxs);
|
||||
}
|
||||
|
||||
.canvas {
|
||||
flex: 1;
|
||||
min-block-size: 0;
|
||||
overflow: auto;
|
||||
padding-block-start: var(--sp-s);
|
||||
}
|
||||
|
||||
.canvas-svg {
|
||||
display: block;
|
||||
min-block-size: 100%;
|
||||
min-inline-size: 100%;
|
||||
}
|
||||
|
||||
.selection-preview-box {
|
||||
--stroke-color: var(--color-background-quaternary);
|
||||
|
||||
fill: var(--color-background-primary);
|
||||
stroke: var(--stroke-color);
|
||||
stroke-width: $b-2;
|
||||
|
||||
&.selected {
|
||||
--stroke-color: var(--color-accent-secondary);
|
||||
}
|
||||
|
||||
&.swap {
|
||||
--stroke-color: var(--color-accent-success);
|
||||
}
|
||||
|
||||
&.deleted {
|
||||
--stroke-color: var(--color-accent-error);
|
||||
}
|
||||
}
|
||||
|
||||
.selection-preview-icon {
|
||||
--icon-stroke-color: var(--color-foreground-secondary);
|
||||
|
||||
display: block;
|
||||
|
||||
&.component {
|
||||
--icon-stroke-color: var(--color-accent-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
.selection-preview-text {
|
||||
@include t.use-typography("body-small");
|
||||
|
||||
fill: var(--color-foreground-primary);
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.deleted-header-bg {
|
||||
fill: var(--color-accent-error);
|
||||
}
|
||||
|
||||
.deleted-header-text {
|
||||
@include t.use-typography("body-small");
|
||||
|
||||
fill: var(--color-static-white);
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
|
||||
.info-labels-bg {
|
||||
fill: var(--color-background-quaternary);
|
||||
}
|
||||
|
||||
.info-text {
|
||||
@include t.use-typography("body-small");
|
||||
|
||||
fill: var(--color-foreground-primary);
|
||||
text-anchor: start;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.shape-thumbnail-bg {
|
||||
fill: var(--color-background-primary);
|
||||
stroke: var(--color-background-quaternary);
|
||||
stroke-width: $b-1;
|
||||
|
||||
&.is-loading {
|
||||
animation: thumbnail-loading-pulse 1.2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
&.unavailable {
|
||||
fill: var(--color-background-quaternary);
|
||||
}
|
||||
}
|
||||
|
||||
.shape-thumbnail-image {
|
||||
opacity: 0.95;
|
||||
}
|
||||
|
||||
.shape-thumbnail-loading {
|
||||
@include t.use-typography("body-small");
|
||||
|
||||
fill: var(--color-foreground-secondary);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.shape-thumbnail-unavailable {
|
||||
@include t.use-typography("body-small");
|
||||
|
||||
fill: var(--color-foreground-secondary);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@keyframes thumbnail-loading-pulse {
|
||||
0%,
|
||||
100% {
|
||||
fill: var(--color-background-primary);
|
||||
}
|
||||
|
||||
50% {
|
||||
fill: var(--color-background-quaternary);
|
||||
}
|
||||
}
|
||||
|
||||
.highlight-arrows {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.highlight-arrow {
|
||||
stroke: var(--color-accent-secondary);
|
||||
stroke-width: $b-2;
|
||||
}
|
||||
|
||||
.highlight-arrow-marker-circle {
|
||||
fill: var(--color-accent-secondary);
|
||||
}
|
||||
|
||||
.highlight-arrow-marker-icon {
|
||||
fill: var(--color-accent-secondary);
|
||||
stroke: var(--color-background-primary);
|
||||
stroke-width: $b-2;
|
||||
}
|
||||
|
||||
.swap-arrow {
|
||||
stroke: var(--color-accent-success);
|
||||
stroke-width: $b-2;
|
||||
}
|
||||
|
||||
.swap-arrow-marker-circle {
|
||||
fill: var(--color-accent-success);
|
||||
}
|
||||
|
||||
.swap-arrow-marker-icon {
|
||||
fill: var(--color-accent-success);
|
||||
stroke: var(--color-background-primary);
|
||||
stroke-width: $b-2;
|
||||
}
|
||||
|
||||
.legend-text {
|
||||
@include t.use-typography("body-small");
|
||||
|
||||
fill: var(--color-foreground-primary);
|
||||
}
|
||||
|
||||
.legend-bg {
|
||||
fill: var(--color-background-quaternary);
|
||||
}
|
||||
|
||||
::-webkit-resizer {
|
||||
display: none;
|
||||
}
|
||||
@ -23,7 +23,8 @@
|
||||
These options are handled reactively via okulary subscriptions."
|
||||
#{:shape-panel
|
||||
:show-ids
|
||||
:show-touched})
|
||||
:show-touched
|
||||
:components-debugger})
|
||||
|
||||
(mf/defc debug-panel*
|
||||
[{:keys [class]}]
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
variant-id variant-name variant-properties variant-error
|
||||
on-tab-press ref]}]
|
||||
(let [;; Subscribe to dbg/state so the component re-renders when
|
||||
;; :show-ids or :show-touched are toggled without a page reload.
|
||||
;; debug options are toggled without a page reload.
|
||||
_dbg (mf/deref dbg/state)
|
||||
|
||||
edition* (mf/use-state false)
|
||||
|
||||
@ -89,6 +89,9 @@
|
||||
;; Show info about shapes
|
||||
:shape-panel
|
||||
|
||||
;; Show the floating components debugger window
|
||||
:components-debugger
|
||||
|
||||
;; Show what is touched in copies
|
||||
:display-touched
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user