mirror of
https://github.com/penpot/penpot.git
synced 2025-06-03 00:31:40 +02:00
✨ Resizeable panels
This commit is contained in:
parent
acc3d00fd5
commit
1599b2644a
19 changed files with 307 additions and 118 deletions
|
@ -11,6 +11,13 @@ body {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
font-family: "worksans", sans-serif;
|
font-family: "worksans", sans-serif;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#app {
|
||||||
|
width: 100vw;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
$width-settings-bar: 16rem;
|
$width-left-toolbar: 48px;
|
||||||
|
$width-settings-bar: 256px;
|
||||||
|
|
||||||
.handoff-layout {
|
.handoff-layout {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
|
|
@ -10,10 +10,7 @@
|
||||||
background-color: $color-gray-50;
|
background-color: $color-gray-50;
|
||||||
border-top: 1px solid $color-gray-60;
|
border-top: 1px solid $color-gray-60;
|
||||||
display: flex;
|
display: flex;
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
z-index: 11;
|
z-index: 11;
|
||||||
|
|
||||||
& .right-arrow,
|
& .right-arrow,
|
||||||
|
@ -46,16 +43,21 @@
|
||||||
@include animation(0, 0.5s, fadeOutDown);
|
@include animation(0, 0.5s, fadeOutDown);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.left-sidebar-open {
|
|
||||||
left: 303px;
|
|
||||||
width: calc(100% - 303px);
|
|
||||||
}
|
|
||||||
|
|
||||||
& .context-menu-items {
|
& .context-menu-items {
|
||||||
bottom: 1.5rem;
|
bottom: 1.5rem;
|
||||||
top: initial;
|
top: initial;
|
||||||
min-width: 10rem;
|
min-width: 10rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& .resize-area {
|
||||||
|
position: absolute;
|
||||||
|
height: 8px;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 10;
|
||||||
|
cursor: ns-resize;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-palette-actions {
|
.color-palette-actions {
|
||||||
|
@ -119,7 +121,6 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 5rem;
|
|
||||||
padding: 0.25rem;
|
padding: 0.25rem;
|
||||||
|
|
||||||
&.size-small {
|
&.size-small {
|
||||||
|
|
|
@ -5,16 +5,8 @@
|
||||||
// Copyright (c) 2015-2020 Andrey Antukh <niwi@niwi.nz>
|
// Copyright (c) 2015-2020 Andrey Antukh <niwi@niwi.nz>
|
||||||
// Copyright (c) 2015-2020 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
// Copyright (c) 2015-2020 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||||
|
|
||||||
$width-left-toolbar: 48px;
|
|
||||||
|
|
||||||
.left-toolbar {
|
.left-toolbar {
|
||||||
background-color: $color-gray-50;
|
background-color: $color-gray-50;
|
||||||
bottom: 0;
|
|
||||||
height: 100%;
|
|
||||||
position: fixed;
|
|
||||||
left: 0;
|
|
||||||
width: $width-left-toolbar;
|
|
||||||
z-index: 11;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.left-toolbar-inside {
|
.left-toolbar-inside {
|
||||||
|
@ -23,7 +15,6 @@ $width-left-toolbar: 48px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
padding-top: 48px;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,29 +5,14 @@
|
||||||
// Copyright (c) 2015-2020 Andrey Antukh <niwi@niwi.nz>
|
// Copyright (c) 2015-2020 Andrey Antukh <niwi@niwi.nz>
|
||||||
// Copyright (c) 2015-2020 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
// Copyright (c) 2015-2020 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||||
|
|
||||||
$width-settings-bar: 16rem;
|
|
||||||
// This width is also used in update-viewport-size at frontend/src/app/main/data/workspace.cljs
|
|
||||||
|
|
||||||
.settings-bar {
|
.settings-bar {
|
||||||
background-color: $color-gray-50;
|
background-color: $color-gray-50;
|
||||||
border-left: 1px solid $color-gray-60;
|
border-left: 1px solid $color-gray-60;
|
||||||
bottom: 0;
|
position: relative;
|
||||||
height: 100%;
|
|
||||||
position: fixed;
|
|
||||||
right: 0;
|
|
||||||
width: $width-settings-bar;
|
|
||||||
|
|
||||||
&.expanded {
|
|
||||||
width: $width-settings-bar * 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
z-index: 10;
|
|
||||||
overflow-y: auto;
|
|
||||||
|
|
||||||
&.settings-bar-left {
|
&.settings-bar-left {
|
||||||
border-left: none;
|
border-left: none;
|
||||||
border-right: 1px solid $color-gray-60;
|
border-right: 1px solid $color-gray-60;
|
||||||
left: 48px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-bar-inside {
|
.settings-bar-inside {
|
||||||
|
@ -64,7 +49,6 @@ $width-settings-bar: 16rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding-top: 48px;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
.tool-window {
|
.tool-window {
|
||||||
|
@ -163,6 +147,22 @@ $width-settings-bar: 16rem;
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& .resize-area {
|
||||||
|
position: absolute;
|
||||||
|
width: 8px;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 10;
|
||||||
|
cursor: ew-resize;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.settings-bar-left .resize-area {
|
||||||
|
right: -8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.settings-bar-right .resize-area {
|
||||||
|
left: -8px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tool-window-content {
|
.tool-window-content {
|
||||||
|
@ -204,6 +204,7 @@ $width-settings-bar: 16rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.element-list.pages-list {
|
.element-list.pages-list {
|
||||||
|
|
|
@ -5,14 +5,12 @@
|
||||||
// Copyright (c) UXBOX Labs SL
|
// Copyright (c) UXBOX Labs SL
|
||||||
|
|
||||||
.workspace-header {
|
.workspace-header {
|
||||||
|
position: relative;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background-color: $color-gray-50;
|
background-color: $color-gray-50;
|
||||||
border-bottom: 1px solid $color-gray-60;
|
border-bottom: 1px solid $color-gray-60;
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 48px;
|
|
||||||
padding: $size-1 $size-4 $size-1 55px;
|
padding: $size-1 $size-4 $size-1 55px;
|
||||||
position: relative;
|
|
||||||
z-index: 12;
|
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
||||||
.main-icon {
|
.main-icon {
|
||||||
|
|
|
@ -5,10 +5,70 @@
|
||||||
// Copyright (c) 2015-2016 Andrey Antukh <niwi@niwi.nz>
|
// Copyright (c) 2015-2016 Andrey Antukh <niwi@niwi.nz>
|
||||||
// Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
// Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||||
|
|
||||||
|
$width-left-toolbar: 48px;
|
||||||
|
|
||||||
|
$width-settings-bar: 256px;
|
||||||
|
$width-settings-bar-min: 255px;
|
||||||
|
$width-settings-bar-max: 500px;
|
||||||
|
|
||||||
|
$height-palette: 79px;
|
||||||
|
$height-palette-min: 54px;
|
||||||
|
$height-palette-max: 80px;
|
||||||
|
|
||||||
#workspace {
|
#workspace {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
||||||
|
display: grid;
|
||||||
|
grid-template-areas: "header header header header"
|
||||||
|
"toolbar left-sidebar viewport right-sidebar"
|
||||||
|
"toolbar left-sidebar color-palette right-sidebar";
|
||||||
|
|
||||||
|
grid-template-rows: auto 1fr auto;
|
||||||
|
grid-template-columns: auto auto 1fr auto;
|
||||||
|
|
||||||
|
.workspace-header {
|
||||||
|
grid-area: header;
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-toolbar {
|
||||||
|
grid-area: toolbar;
|
||||||
|
width: $width-left-toolbar;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-bar.settings-bar-left {
|
||||||
|
min-width: $width-settings-bar;
|
||||||
|
max-width: 500px;
|
||||||
|
width: var(--width, $width-settings-bar);
|
||||||
|
grid-area: left-sidebar;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-bar.settings-bar-right {
|
||||||
|
min-width: $width-settings-bar;
|
||||||
|
max-width: 500px;
|
||||||
|
width: var(--width, $width-settings-bar);
|
||||||
|
grid-area: right-sidebar;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-loader {
|
||||||
|
grid-area: viewport;
|
||||||
|
}
|
||||||
|
|
||||||
|
.workspace-content {
|
||||||
|
grid-area: viewport;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-palette {
|
||||||
|
grid-area: color-palette;
|
||||||
|
min-height: $height-palette-min;
|
||||||
|
max-height: $height-palette-max;
|
||||||
|
height: var(--height, $height-palette);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.workspace-context-menu {
|
.workspace-context-menu {
|
||||||
background-color: $color-white;
|
background-color: $color-white;
|
||||||
border-radius: $br-small;
|
border-radius: $br-small;
|
||||||
|
@ -97,7 +157,6 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 100vh;
|
|
||||||
|
|
||||||
svg#loader-pencil {
|
svg#loader-pencil {
|
||||||
fill: $color-gray-50;
|
fill: $color-gray-50;
|
||||||
|
@ -107,12 +166,8 @@
|
||||||
.workspace-content {
|
.workspace-content {
|
||||||
background-color: $color-canvas;
|
background-color: $color-canvas;
|
||||||
display: flex;
|
display: flex;
|
||||||
height: 100%;
|
|
||||||
width: calc(100% - #{$width-left-toolbar} - 2 * #{$width-settings-bar});
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
position: fixed;
|
|
||||||
right: $width-settings-bar;
|
|
||||||
|
|
||||||
&.scrolling {
|
&.scrolling {
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
|
@ -171,14 +226,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.workspace-viewport {
|
.workspace-viewport {
|
||||||
height: calc(100% - 40px);
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transition: none;
|
transition: none;
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-rows: 20px 100%;
|
grid-template-rows: 20px 1fr;
|
||||||
grid-template-columns: 20px 100%;
|
grid-template-columns: 20px 1fr;
|
||||||
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.viewport {
|
.viewport {
|
||||||
|
@ -209,10 +262,14 @@
|
||||||
|
|
||||||
.render-shapes {
|
.render-shapes {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.viewport-controls {
|
.viewport-controls {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -481,7 +481,7 @@
|
||||||
(cond
|
(cond
|
||||||
(or (not (mth/finite? (:width srect)))
|
(or (not (mth/finite? (:width srect)))
|
||||||
(not (mth/finite? (:height srect))))
|
(not (mth/finite? (:height srect))))
|
||||||
(assoc local :vbox (assoc size :x 0 :y 0 :left-offset 0))
|
(assoc local :vbox (assoc size :x 0 :y 0))
|
||||||
|
|
||||||
(or (> (:width srect) width)
|
(or (> (:width srect) width)
|
||||||
(> (:height srect) height))
|
(> (:height srect) height))
|
||||||
|
@ -522,25 +522,44 @@
|
||||||
(update :y y)))))))
|
(update :y y)))))))
|
||||||
|
|
||||||
(defn update-viewport-size
|
(defn update-viewport-size
|
||||||
[{:keys [width height] :as size}]
|
[resize-type {:keys [width height] :as size}]
|
||||||
(ptk/reify ::update-viewport-size
|
(ptk/reify ::update-viewport-size
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(update state :workspace-local
|
(update state :workspace-local
|
||||||
(fn [{:keys [vport left-sidebar? zoom] :as local}]
|
(fn [{:keys [vport] :as local}]
|
||||||
(if (or (mth/almost-zero? width) (mth/almost-zero? height))
|
(if (or (mth/almost-zero? width) (mth/almost-zero? height))
|
||||||
;; If we have a resize to zero just keep the old value
|
;; If we have a resize to zero just keep the old value
|
||||||
local
|
local
|
||||||
(let [wprop (/ (:width vport) width)
|
(let [wprop (/ (:width vport) width)
|
||||||
hprop (/ (:height vport) height)
|
hprop (/ (:height vport) height)
|
||||||
left-offset (if left-sidebar? 0 (/ (* -1 15 16) zoom))]
|
|
||||||
(-> local ;; This matches $width-settings-bar
|
vbox (:vbox local)
|
||||||
(assoc :vport size) ;; in frontend/resources/styles/main/partials/sidebar.scss
|
vbox-x (:x vbox)
|
||||||
(update :vbox (fn [vbox]
|
vbox-y (:y vbox)
|
||||||
(-> vbox
|
vbox-width (:width vbox)
|
||||||
(update :width #(/ % wprop))
|
vbox-height (:height vbox)
|
||||||
(update :height #(/ % hprop))
|
|
||||||
(assoc :left-offset left-offset))))))))))))
|
vbox-width' (/ vbox-width wprop)
|
||||||
|
vbox-height' (/ vbox-height hprop)
|
||||||
|
|
||||||
|
vbox-x'
|
||||||
|
(case resize-type
|
||||||
|
:left (+ vbox-x (- vbox-width vbox-width'))
|
||||||
|
:right vbox-x
|
||||||
|
(+ vbox-x (/ (- vbox-width vbox-width') 2)))
|
||||||
|
|
||||||
|
vbox-y'
|
||||||
|
(case resize-type
|
||||||
|
:top (+ vbox-y (- vbox-height vbox-height'))
|
||||||
|
:bottom vbox-y
|
||||||
|
(+ vbox-y (/ (- vbox-height vbox-height') 2)))]
|
||||||
|
(-> local
|
||||||
|
(assoc :vport size)
|
||||||
|
(assoc-in [:vbox :x] vbox-x')
|
||||||
|
(assoc-in [:vbox :y] vbox-y')
|
||||||
|
(assoc-in [:vbox :width] vbox-width')
|
||||||
|
(assoc-in [:vbox :height] vbox-height')))))))))
|
||||||
|
|
||||||
(defn start-panning []
|
(defn start-panning []
|
||||||
(ptk/reify ::start-panning
|
(ptk/reify ::start-panning
|
||||||
|
@ -596,14 +615,12 @@
|
||||||
|
|
||||||
(defn- impl-update-zoom
|
(defn- impl-update-zoom
|
||||||
[{:keys [vbox] :as local} center zoom]
|
[{:keys [vbox] :as local} center zoom]
|
||||||
(let [vbox (update vbox :x + (:left-offset vbox))
|
(let [new-zoom (if (fn? zoom) (zoom (:zoom local)) zoom)
|
||||||
new-zoom (if (fn? zoom) (zoom (:zoom local)) zoom)
|
|
||||||
old-zoom (:zoom local)
|
old-zoom (:zoom local)
|
||||||
center (if center center (gsh/center-rect vbox))
|
center (if center center (gsh/center-rect vbox))
|
||||||
scale (/ old-zoom new-zoom)
|
scale (/ old-zoom new-zoom)
|
||||||
mtx (gmt/scale-matrix (gpt/point scale) center)
|
mtx (gmt/scale-matrix (gpt/point scale) center)
|
||||||
vbox' (gsh/transform-rect vbox mtx)
|
vbox' (gsh/transform-rect vbox mtx)]
|
||||||
vbox' (update vbox' :x - (:left-offset vbox))]
|
|
||||||
(-> local
|
(-> local
|
||||||
(assoc :zoom new-zoom)
|
(assoc :zoom new-zoom)
|
||||||
(update :vbox merge (select-keys vbox' [:x :y :width :height])))))
|
(update :vbox merge (select-keys vbox' [:x :y :width :height])))))
|
||||||
|
|
100
frontend/src/app/main/ui/hooks/resize.cljs
Normal file
100
frontend/src/app/main/ui/hooks/resize.cljs
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
;; 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) UXBOX Labs SL
|
||||||
|
|
||||||
|
(ns app.main.ui.hooks.resize
|
||||||
|
(:require
|
||||||
|
[app.common.geom.point :as gpt]
|
||||||
|
[app.common.logging :as log]
|
||||||
|
[app.util.dom :as dom]
|
||||||
|
[rumext.alpha :as mf]))
|
||||||
|
|
||||||
|
(log/set-level! :warn)
|
||||||
|
|
||||||
|
(def last-resize-type nil)
|
||||||
|
|
||||||
|
(defn set-resize-type! [type]
|
||||||
|
(set! last-resize-type type))
|
||||||
|
|
||||||
|
(defn use-resize-hook
|
||||||
|
[initial min-val max-val axis negate? resize-type]
|
||||||
|
(let [size-state (mf/use-state initial)
|
||||||
|
parent-ref (mf/use-ref nil)
|
||||||
|
|
||||||
|
dragging-ref (mf/use-ref false)
|
||||||
|
start-size-ref (mf/use-ref nil)
|
||||||
|
start-ref (mf/use-ref nil)
|
||||||
|
|
||||||
|
on-pointer-down
|
||||||
|
(fn [event]
|
||||||
|
(dom/capture-pointer event)
|
||||||
|
(mf/set-ref-val! start-size-ref @size-state)
|
||||||
|
(mf/set-ref-val! dragging-ref true)
|
||||||
|
(mf/set-ref-val! start-ref (dom/get-client-position event))
|
||||||
|
(set! last-resize-type resize-type))
|
||||||
|
|
||||||
|
on-lost-pointer-capture
|
||||||
|
(fn [event]
|
||||||
|
(dom/release-pointer event)
|
||||||
|
(mf/set-ref-val! start-size-ref nil)
|
||||||
|
(mf/set-ref-val! dragging-ref false)
|
||||||
|
(mf/set-ref-val! start-ref nil)
|
||||||
|
(set! last-resize-type nil))
|
||||||
|
|
||||||
|
on-mouse-move
|
||||||
|
(fn [event]
|
||||||
|
(when (mf/ref-val dragging-ref)
|
||||||
|
(let [start (mf/ref-val start-ref)
|
||||||
|
pos (dom/get-client-position event)
|
||||||
|
delta (-> (gpt/to-vec start pos)
|
||||||
|
(cond-> negate? gpt/negate)
|
||||||
|
(get axis))
|
||||||
|
start-size (mf/ref-val start-size-ref)
|
||||||
|
new-size (-> (+ start-size delta) (max min-val) (min max-val))]
|
||||||
|
|
||||||
|
(reset! size-state new-size))))]
|
||||||
|
{:on-pointer-down on-pointer-down
|
||||||
|
:on-lost-pointer-capture on-lost-pointer-capture
|
||||||
|
:on-mouse-move on-mouse-move
|
||||||
|
:parent-ref parent-ref
|
||||||
|
:size @size-state}))
|
||||||
|
|
||||||
|
(defn use-resize-observer
|
||||||
|
[callback]
|
||||||
|
|
||||||
|
(let [prev-val-ref (mf/use-ref nil)
|
||||||
|
current-observer-ref (mf/use-ref nil)
|
||||||
|
|
||||||
|
node-ref
|
||||||
|
(mf/use-callback
|
||||||
|
(mf/deps callback)
|
||||||
|
(fn [node]
|
||||||
|
(let [current-observer (mf/ref-val current-observer-ref)
|
||||||
|
prev-val (mf/ref-val prev-val-ref)]
|
||||||
|
|
||||||
|
(when (and (not= prev-val node) (some? current-observer))
|
||||||
|
(log/debug :action "disconnect" :js/prev-val prev-val :js/node node)
|
||||||
|
(.disconnect current-observer)
|
||||||
|
(mf/set-ref-val! current-observer-ref nil))
|
||||||
|
|
||||||
|
(when (and (not= prev-val node) (some? node))
|
||||||
|
(let [observer
|
||||||
|
(js/ResizeObserver.
|
||||||
|
(fn []
|
||||||
|
(let [size (dom/get-client-size node)]
|
||||||
|
(when callback (callback last-resize-type size)))))]
|
||||||
|
(mf/set-ref-val! current-observer-ref observer)
|
||||||
|
(log/debug :action "observe" :js/node node)
|
||||||
|
(.observe observer node))))
|
||||||
|
(mf/set-ref-val! prev-val-ref node)))]
|
||||||
|
|
||||||
|
(mf/use-effect
|
||||||
|
(fn []
|
||||||
|
(fn []
|
||||||
|
(let [current-observer (mf/ref-val current-observer-ref)]
|
||||||
|
(when (some? current-observer)
|
||||||
|
(log/debug :action "disconnect")
|
||||||
|
(.disconnect current-observer))))))
|
||||||
|
node-ref))
|
|
@ -12,6 +12,7 @@
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.context :as ctx]
|
[app.main.ui.context :as ctx]
|
||||||
|
[app.main.ui.hooks.resize :refer [use-resize-observer]]
|
||||||
[app.main.ui.icons :as i]
|
[app.main.ui.icons :as i]
|
||||||
[app.main.ui.workspace.colorpalette :refer [colorpalette]]
|
[app.main.ui.workspace.colorpalette :refer [colorpalette]]
|
||||||
[app.main.ui.workspace.colorpicker]
|
[app.main.ui.workspace.colorpicker]
|
||||||
|
@ -39,11 +40,19 @@
|
||||||
{:keys [options-mode]} local
|
{:keys [options-mode]} local
|
||||||
file (obj/get props "file")
|
file (obj/get props "file")
|
||||||
layout (obj/get props "layout")
|
layout (obj/get props "layout")
|
||||||
colorpalette? (:colorpalette layout)]
|
colorpalette? (:colorpalette layout)
|
||||||
|
|
||||||
|
on-resize
|
||||||
|
(mf/use-callback
|
||||||
|
(fn [resize-type size]
|
||||||
|
(when (:vport local)
|
||||||
|
(st/emit! (dw/update-viewport-size resize-type size)))))
|
||||||
|
|
||||||
|
node-ref (use-resize-observer on-resize)]
|
||||||
[:*
|
[:*
|
||||||
(when colorpalette? [:& colorpalette])
|
(when colorpalette? [:& colorpalette])
|
||||||
|
|
||||||
[:section.workspace-content
|
[:section.workspace-content {:ref node-ref}
|
||||||
[:section.workspace-viewport
|
[:section.workspace-viewport
|
||||||
[:& coordinates/coordinates {:colorpalette? colorpalette?}]
|
[:& coordinates/coordinates {:colorpalette? colorpalette?}]
|
||||||
|
|
||||||
|
@ -131,3 +140,5 @@
|
||||||
:layout layout}]
|
:layout layout}]
|
||||||
[:& workspace-loader])]]]]]))
|
[:& workspace-loader])]]]]]))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.components.color-bullet :as cb]
|
[app.main.ui.components.color-bullet :as cb]
|
||||||
[app.main.ui.components.dropdown :refer [dropdown]]
|
[app.main.ui.components.dropdown :refer [dropdown]]
|
||||||
|
[app.main.ui.hooks.resize :refer [use-resize-hook]]
|
||||||
[app.main.ui.icons :as i]
|
[app.main.ui.icons :as i]
|
||||||
[app.util.color :as uc]
|
[app.util.color :as uc]
|
||||||
[app.util.i18n :refer [tr]]
|
[app.util.i18n :refer [tr]]
|
||||||
|
@ -52,7 +53,7 @@
|
||||||
[:& cb/color-name {:color color :size size}]]))
|
[:& cb/color-name {:color color :size size}]]))
|
||||||
|
|
||||||
(mf/defc palette
|
(mf/defc palette
|
||||||
[{:keys [current-colors recent-colors file-colors shared-libs selected size]}]
|
[{:keys [current-colors recent-colors file-colors shared-libs selected]}]
|
||||||
(let [state (mf/use-state {:show-menu false })
|
(let [state (mf/use-state {:show-menu false })
|
||||||
|
|
||||||
width (:width @state 0)
|
width (:width @state 0)
|
||||||
|
@ -64,6 +65,9 @@
|
||||||
|
|
||||||
container (mf/use-ref nil)
|
container (mf/use-ref nil)
|
||||||
|
|
||||||
|
{:keys [on-pointer-down on-lost-pointer-capture on-mouse-move parent-ref size]}
|
||||||
|
(use-resize-hook 72 54 80 :y true :bottom)
|
||||||
|
|
||||||
on-left-arrow-click
|
on-left-arrow-click
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
(mf/deps max-offset visible)
|
(mf/deps max-offset visible)
|
||||||
|
@ -111,7 +115,11 @@
|
||||||
(fn []
|
(fn []
|
||||||
(events/unlistenByKey key1))))
|
(events/unlistenByKey key1))))
|
||||||
|
|
||||||
[:div.color-palette.left-sidebar-open
|
[:div.color-palette {:ref parent-ref
|
||||||
|
:style #js {"--height" (str size "px")}}
|
||||||
|
[:div.resize-area {:on-pointer-down on-pointer-down
|
||||||
|
:on-lost-pointer-capture on-lost-pointer-capture
|
||||||
|
:on-mouse-move on-mouse-move}]
|
||||||
[:& dropdown {:show (:show-menu @state)
|
[:& dropdown {:show (:show-menu @state)
|
||||||
:on-close #(swap! state assoc :show-menu false)}
|
:on-close #(swap! state assoc :show-menu false)}
|
||||||
[:ul.workspace-context-menu.palette-menu
|
[:ul.workspace-context-menu.palette-menu
|
||||||
|
@ -170,8 +178,7 @@
|
||||||
[:div.color-palette-inside {:style {:position "relative"
|
[:div.color-palette-inside {:style {:position "relative"
|
||||||
:right (str (* 66 offset) "px")}}
|
:right (str (* 66 offset) "px")}}
|
||||||
(for [[idx item] (map-indexed vector current-colors)]
|
(for [[idx item] (map-indexed vector current-colors)]
|
||||||
[:& palette-item {:size size
|
[:& palette-item {:color item
|
||||||
:color item
|
|
||||||
:key idx}])]]
|
:key idx}])]]
|
||||||
|
|
||||||
[:span.right-arrow {:on-click on-right-arrow-click} i/arrow-slide]]))
|
[:span.right-arrow {:on-click on-right-arrow-click} i/arrow-slide]]))
|
||||||
|
@ -188,8 +195,6 @@
|
||||||
file-colors (mf/deref refs/workspace-file-colors)
|
file-colors (mf/deref refs/workspace-file-colors)
|
||||||
shared-libs (mf/deref refs/workspace-libraries)
|
shared-libs (mf/deref refs/workspace-libraries)
|
||||||
selected (or (mf/deref selected-palette-ref) :recent)
|
selected (or (mf/deref selected-palette-ref) :recent)
|
||||||
size (or (mf/deref selected-palette-size-ref) :big)
|
|
||||||
|
|
||||||
current-library-colors (mf/use-state [])]
|
current-library-colors (mf/use-state [])]
|
||||||
|
|
||||||
(mf/use-effect
|
(mf/use-effect
|
||||||
|
@ -219,5 +224,4 @@
|
||||||
:recent-colors recent-colors
|
:recent-colors recent-colors
|
||||||
:file-colors file-colors
|
:file-colors file-colors
|
||||||
:shared-libs shared-libs
|
:shared-libs shared-libs
|
||||||
:selected selected
|
:selected selected}]))
|
||||||
:size size}]))
|
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.components.file-uploader :refer [file-uploader]]
|
[app.main.ui.components.file-uploader :refer [file-uploader]]
|
||||||
|
[app.main.ui.hooks.resize :as r]
|
||||||
[app.main.ui.icons :as i]
|
[app.main.ui.icons :as i]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
|
@ -136,5 +137,7 @@
|
||||||
[:li.tooltip.tooltip-right
|
[:li.tooltip.tooltip-right
|
||||||
{:alt (tr "workspace.toolbar.color-palette" (sc/get-tooltip :toggle-palette))
|
{:alt (tr "workspace.toolbar.color-palette" (sc/get-tooltip :toggle-palette))
|
||||||
:class (when (contains? layout :colorpalette) "selected")
|
:class (when (contains? layout :colorpalette) "selected")
|
||||||
:on-click (st/emitf (dw/toggle-layout-flags :colorpalette))}
|
:on-click (do
|
||||||
|
(r/set-resize-type! :bottom)
|
||||||
|
(st/emitf (dw/toggle-layout-flags :colorpalette)))}
|
||||||
i/palette]]]]))
|
i/palette]]]]))
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
(ns app.main.ui.workspace.sidebar
|
(ns app.main.ui.workspace.sidebar
|
||||||
(:require
|
(:require
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
|
[app.main.ui.hooks.resize :refer [use-resize-hook]]
|
||||||
[app.main.ui.workspace.comments :refer [comments-sidebar]]
|
[app.main.ui.workspace.comments :refer [comments-sidebar]]
|
||||||
[app.main.ui.workspace.sidebar.assets :refer [assets-toolbox]]
|
[app.main.ui.workspace.sidebar.assets :refer [assets-toolbox]]
|
||||||
[app.main.ui.workspace.sidebar.history :refer [history-toolbox]]
|
[app.main.ui.workspace.sidebar.history :refer [history-toolbox]]
|
||||||
|
@ -21,19 +22,26 @@
|
||||||
(mf/defc left-sidebar
|
(mf/defc left-sidebar
|
||||||
{:wrap [mf/memo]}
|
{:wrap [mf/memo]}
|
||||||
[{:keys [layout ] :as props}]
|
[{:keys [layout ] :as props}]
|
||||||
[:aside.settings-bar.settings-bar-left
|
(let [{:keys [on-pointer-down on-lost-pointer-capture on-mouse-move parent-ref size]}
|
||||||
[:div.settings-bar-inside
|
(use-resize-hook 255 255 500 :x false :left)]
|
||||||
{:data-layout (str/join "," layout)}
|
|
||||||
(when (contains? layout :layers)
|
|
||||||
[:*
|
|
||||||
[:& sitemap {:layout layout}]
|
|
||||||
[:& layers-toolbox]])
|
|
||||||
|
|
||||||
(when (contains? layout :document-history)
|
[:aside.settings-bar.settings-bar-left {:ref parent-ref
|
||||||
[:& history-toolbox])
|
:style #js {"--width" (str size "px")}}
|
||||||
|
[:div.resize-area {:on-pointer-down on-pointer-down
|
||||||
|
:on-lost-pointer-capture on-lost-pointer-capture
|
||||||
|
:on-mouse-move on-mouse-move}]
|
||||||
|
[:div.settings-bar-inside
|
||||||
|
{:data-layout (str/join "," layout)}
|
||||||
|
(when (contains? layout :layers)
|
||||||
|
[:*
|
||||||
|
[:& sitemap {:layout layout}]
|
||||||
|
[:& layers-toolbox]])
|
||||||
|
|
||||||
(when (contains? layout :assets)
|
(when (contains? layout :document-history)
|
||||||
[:& assets-toolbox])]])
|
[:& history-toolbox])
|
||||||
|
|
||||||
|
(when (contains? layout :assets)
|
||||||
|
[:& assets-toolbox])]]))
|
||||||
|
|
||||||
;; --- Right Sidebar (Component)
|
;; --- Right Sidebar (Component)
|
||||||
|
|
||||||
|
@ -41,8 +49,15 @@
|
||||||
{::mf/wrap-props false
|
{::mf/wrap-props false
|
||||||
::mf/wrap [mf/memo]}
|
::mf/wrap [mf/memo]}
|
||||||
[props]
|
[props]
|
||||||
(let [drawing-tool (:tool (mf/deref refs/workspace-drawing))]
|
(let [{:keys [on-pointer-down on-lost-pointer-capture on-mouse-move parent-ref size]}
|
||||||
[:aside.settings-bar
|
(use-resize-hook 255 255 500 :x true :right)
|
||||||
|
|
||||||
|
drawing-tool (:tool (mf/deref refs/workspace-drawing))]
|
||||||
|
[:aside.settings-bar.settings-bar-right {:ref parent-ref
|
||||||
|
:style #js {"--width" (str size "px")}}
|
||||||
|
[:div.resize-area {:on-pointer-down on-pointer-down
|
||||||
|
:on-lost-pointer-capture on-lost-pointer-capture
|
||||||
|
:on-mouse-move on-mouse-move}]
|
||||||
[:div.settings-bar-inside
|
[:div.settings-bar-inside
|
||||||
(if (= drawing-tool :comments)
|
(if (= drawing-tool :comments)
|
||||||
[:& comments-sidebar]
|
[:& comments-sidebar]
|
||||||
|
|
|
@ -163,7 +163,6 @@
|
||||||
(hooks/setup-dom-events viewport-ref zoom disable-paste in-viewport?)
|
(hooks/setup-dom-events viewport-ref zoom disable-paste in-viewport?)
|
||||||
(hooks/setup-viewport-size viewport-ref)
|
(hooks/setup-viewport-size viewport-ref)
|
||||||
(hooks/setup-cursor cursor alt? panning drawing-tool drawing-path? node-editing?)
|
(hooks/setup-cursor cursor alt? panning drawing-tool drawing-path? node-editing?)
|
||||||
(hooks/setup-resize layout viewport-ref)
|
|
||||||
(hooks/setup-keyboard alt? ctrl? space?)
|
(hooks/setup-keyboard alt? ctrl? space?)
|
||||||
(hooks/setup-hover-shapes page-id move-stream base-objects transform selected ctrl? hover hover-ids @hover-disabled? zoom)
|
(hooks/setup-hover-shapes page-id move-stream base-objects transform selected ctrl? hover hover-ids @hover-disabled? zoom)
|
||||||
(hooks/setup-viewport-modifiers modifiers base-objects)
|
(hooks/setup-viewport-modifiers modifiers base-objects)
|
||||||
|
@ -222,8 +221,6 @@
|
||||||
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
||||||
:preserveAspectRatio "xMidYMid meet"
|
:preserveAspectRatio "xMidYMid meet"
|
||||||
:key (str "viewport" page-id)
|
:key (str "viewport" page-id)
|
||||||
:width (:width vport 0)
|
|
||||||
:height (:height vport 0)
|
|
||||||
:view-box (utils/format-viewbox vbox)
|
:view-box (utils/format-viewbox vbox)
|
||||||
:ref viewport-ref
|
:ref viewport-ref
|
||||||
:class (when drawing-tool "drawing")
|
:class (when drawing-tool "drawing")
|
||||||
|
|
|
@ -492,11 +492,3 @@
|
||||||
(when (and (not (#{"INPUT" "TEXTAREA"} tag-name)) (not @disable-paste))
|
(when (and (not (#{"INPUT" "TEXTAREA"} tag-name)) (not @disable-paste))
|
||||||
(st/emit! (dw/paste-from-event event @in-viewport?)))))))
|
(st/emit! (dw/paste-from-event event @in-viewport?)))))))
|
||||||
|
|
||||||
(defn on-resize [viewport-ref]
|
|
||||||
(mf/use-callback
|
|
||||||
(fn [_]
|
|
||||||
(let [node (mf/ref-val viewport-ref)
|
|
||||||
prnt (dom/get-parent node)
|
|
||||||
size (dom/get-client-size prnt)]
|
|
||||||
;; We schedule the event so it fires after `initialize-page` event
|
|
||||||
(timers/schedule #(st/emit! (dw/update-viewport-size size)))))))
|
|
||||||
|
|
|
@ -220,12 +220,12 @@
|
||||||
(defn guide-creation-area
|
(defn guide-creation-area
|
||||||
[vbox zoom axis]
|
[vbox zoom axis]
|
||||||
(if (= axis :x)
|
(if (= axis :x)
|
||||||
{:x (:x vbox)
|
{:x (+ (:x vbox) (/ 8 zoom))
|
||||||
:y (:y vbox)
|
:y (:y vbox)
|
||||||
:width (/ 24 zoom)
|
:width (/ 16 zoom)
|
||||||
:height (:height vbox)}
|
:height (:height vbox)}
|
||||||
|
|
||||||
{:x (:x vbox)
|
{:x (+ (:x vbox) (+ 28 zoom))
|
||||||
:y (:y vbox)
|
:y (:y vbox)
|
||||||
:width (:width vbox)
|
:width (:width vbox)
|
||||||
:height (/ 24 zoom)}))
|
:height (/ 24 zoom)}))
|
||||||
|
|
|
@ -31,10 +31,9 @@
|
||||||
on-key-up (actions/on-key-up)
|
on-key-up (actions/on-key-up)
|
||||||
on-mouse-move (actions/on-mouse-move viewport-ref zoom)
|
on-mouse-move (actions/on-mouse-move viewport-ref zoom)
|
||||||
on-mouse-wheel (actions/on-mouse-wheel viewport-ref zoom)
|
on-mouse-wheel (actions/on-mouse-wheel viewport-ref zoom)
|
||||||
on-resize (actions/on-resize viewport-ref)
|
|
||||||
on-paste (actions/on-paste disable-paste in-viewport?)]
|
on-paste (actions/on-paste disable-paste in-viewport?)]
|
||||||
(mf/use-layout-effect
|
(mf/use-layout-effect
|
||||||
(mf/deps on-key-down on-key-up on-mouse-move on-mouse-wheel on-resize on-paste)
|
(mf/deps on-key-down on-key-up on-mouse-move on-mouse-wheel on-paste)
|
||||||
(fn []
|
(fn []
|
||||||
(let [node (mf/ref-val viewport-ref)
|
(let [node (mf/ref-val viewport-ref)
|
||||||
keys [(events/listen js/document EventType.KEYDOWN on-key-down)
|
keys [(events/listen js/document EventType.KEYDOWN on-key-down)
|
||||||
|
@ -43,7 +42,6 @@
|
||||||
;; bind with passive=false to allow the event to be cancelled
|
;; bind with passive=false to allow the event to be cancelled
|
||||||
;; https://stackoverflow.com/a/57582286/3219895
|
;; https://stackoverflow.com/a/57582286/3219895
|
||||||
(events/listen js/window EventType.WHEEL on-mouse-wheel #js {:passive false})
|
(events/listen js/window EventType.WHEEL on-mouse-wheel #js {:passive false})
|
||||||
(events/listen js/window EventType.RESIZE on-resize)
|
|
||||||
(events/listen js/window EventType.PASTE on-paste)]]
|
(events/listen js/window EventType.PASTE on-paste)]]
|
||||||
|
|
||||||
(fn []
|
(fn []
|
||||||
|
@ -52,12 +50,12 @@
|
||||||
|
|
||||||
(defn setup-viewport-size [viewport-ref]
|
(defn setup-viewport-size [viewport-ref]
|
||||||
(mf/use-layout-effect
|
(mf/use-layout-effect
|
||||||
(fn []
|
(fn []
|
||||||
(let [node (mf/ref-val viewport-ref)
|
(let [node (mf/ref-val viewport-ref)
|
||||||
prnt (dom/get-parent node)
|
prnt (dom/get-parent node)
|
||||||
size (dom/get-client-size prnt)]
|
size (dom/get-client-size prnt)]
|
||||||
;; We schedule the event so it fires after `initialize-page` event
|
;; We schedule the event so it fires after `initialize-page` event
|
||||||
(timers/schedule #(st/emit! (dw/initialize-viewport size)))))))
|
(timers/schedule #(st/emit! (dw/initialize-viewport size)))))))
|
||||||
|
|
||||||
(defn setup-cursor [cursor alt? panning drawing-tool drawing-path? path-editing?]
|
(defn setup-cursor [cursor alt? panning drawing-tool drawing-path? path-editing?]
|
||||||
(mf/use-effect
|
(mf/use-effect
|
||||||
|
@ -80,10 +78,6 @@
|
||||||
(when (not= @cursor new-cursor)
|
(when (not= @cursor new-cursor)
|
||||||
(reset! cursor new-cursor))))))
|
(reset! cursor new-cursor))))))
|
||||||
|
|
||||||
(defn setup-resize [layout viewport-ref]
|
|
||||||
(let [on-resize (actions/on-resize viewport-ref)]
|
|
||||||
(mf/use-layout-effect (mf/deps layout) on-resize)))
|
|
||||||
|
|
||||||
(defn setup-keyboard [alt? ctrl? space?]
|
(defn setup-keyboard [alt? ctrl? space?]
|
||||||
(hooks/use-stream ms/keyboard-alt #(reset! alt? %))
|
(hooks/use-stream ms/keyboard-alt #(reset! alt? %))
|
||||||
(hooks/use-stream ms/keyboard-ctrl #(reset! ctrl? %))
|
(hooks/use-stream ms/keyboard-ctrl #(reset! ctrl? %))
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
(:import goog.events.EventType))
|
(:import goog.events.EventType))
|
||||||
|
|
||||||
(defn format-viewbox [vbox]
|
(defn format-viewbox [vbox]
|
||||||
(str/join " " [(+ (:x vbox 0) (:left-offset vbox 0))
|
(str/join " " [(:x vbox 0)
|
||||||
(:y vbox 0)
|
(:y vbox 0)
|
||||||
(:width vbox 0)
|
(:width vbox 0)
|
||||||
(:height vbox 0)]))
|
(:height vbox 0)]))
|
||||||
|
|
|
@ -148,7 +148,7 @@
|
||||||
(dom/remove-attribute node "transform")))))))
|
(dom/remove-attribute node "transform")))))))
|
||||||
|
|
||||||
(defn format-viewbox [vbox]
|
(defn format-viewbox [vbox]
|
||||||
(str/join " " [(+ (:x vbox 0) (:left-offset vbox 0))
|
(str/join " " [(:x vbox 0)
|
||||||
(:y vbox 0)
|
(:y vbox 0)
|
||||||
(:width vbox 0)
|
(:width vbox 0)
|
||||||
(:height vbox 0)]))
|
(:height vbox 0)]))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue