Resizeable panels

This commit is contained in:
alonso.torres 2022-01-26 15:16:21 +01:00
parent acc3d00fd5
commit 1599b2644a
19 changed files with 307 additions and 118 deletions

View file

@ -11,6 +11,13 @@ body {
display: flex;
flex-direction: column;
font-family: "worksans", sans-serif;
width: 100vw;
height: 100vh;
overflow: hidden;
}
#app {
width: 100vw;
height: 100vh;
overflow: hidden;
}

View file

@ -1,4 +1,5 @@
$width-settings-bar: 16rem;
$width-left-toolbar: 48px;
$width-settings-bar: 256px;
.handoff-layout {
height: 100vh;

View file

@ -10,10 +10,7 @@
background-color: $color-gray-50;
border-top: 1px solid $color-gray-60;
display: flex;
position: absolute;
bottom: 0;
left: 0;
width: 100%;
z-index: 11;
& .right-arrow,
@ -46,16 +43,21 @@
@include animation(0, 0.5s, fadeOutDown);
}
&.left-sidebar-open {
left: 303px;
width: calc(100% - 303px);
}
& .context-menu-items {
bottom: 1.5rem;
top: initial;
min-width: 10rem;
}
& .resize-area {
position: absolute;
height: 8px;
width: 100%;
z-index: 10;
cursor: ns-resize;
top: 0;
left: 0;
}
}
.color-palette-actions {
@ -119,7 +121,6 @@
display: flex;
overflow: hidden;
width: 100%;
height: 5rem;
padding: 0.25rem;
&.size-small {

View file

@ -5,16 +5,8 @@
// Copyright (c) 2015-2020 Andrey Antukh <niwi@niwi.nz>
// Copyright (c) 2015-2020 Juan de la Cruz <delacruzgarciajuan@gmail.com>
$width-left-toolbar: 48px;
.left-toolbar {
background-color: $color-gray-50;
bottom: 0;
height: 100%;
position: fixed;
left: 0;
width: $width-left-toolbar;
z-index: 11;
}
.left-toolbar-inside {
@ -23,7 +15,6 @@ $width-left-toolbar: 48px;
display: flex;
flex-direction: column;
overflow: visible;
padding-top: 48px;
height: 100%;
}

View file

@ -5,29 +5,14 @@
// Copyright (c) 2015-2020 Andrey Antukh <niwi@niwi.nz>
// 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 {
background-color: $color-gray-50;
border-left: 1px solid $color-gray-60;
bottom: 0;
height: 100%;
position: fixed;
right: 0;
width: $width-settings-bar;
&.expanded {
width: $width-settings-bar * 3;
}
z-index: 10;
overflow-y: auto;
position: relative;
&.settings-bar-left {
border-left: none;
border-right: 1px solid $color-gray-60;
left: 48px;
}
.settings-bar-inside {
@ -64,7 +49,6 @@ $width-settings-bar: 16rem;
}
flex-direction: column;
padding-top: 48px;
height: 100%;
.tool-window {
@ -163,6 +147,22 @@ $width-settings-bar: 16rem;
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 {
@ -204,6 +204,7 @@ $width-settings-bar: 16rem;
}
}
}
}
.element-list.pages-list {

View file

@ -5,14 +5,12 @@
// Copyright (c) UXBOX Labs SL
.workspace-header {
position: relative;
align-items: center;
background-color: $color-gray-50;
border-bottom: 1px solid $color-gray-60;
display: flex;
height: 48px;
padding: $size-1 $size-4 $size-1 55px;
position: relative;
z-index: 12;
justify-content: space-between;
.main-icon {

View file

@ -5,10 +5,70 @@
// Copyright (c) 2015-2016 Andrey Antukh <niwi@niwi.nz>
// 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 {
width: 100vw;
height: 100vh;
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 {
background-color: $color-white;
border-radius: $br-small;
@ -97,7 +157,6 @@
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
svg#loader-pencil {
fill: $color-gray-50;
@ -107,12 +166,8 @@
.workspace-content {
background-color: $color-canvas;
display: flex;
height: 100%;
width: calc(100% - #{$width-left-toolbar} - 2 * #{$width-settings-bar});
padding: 0;
margin: 0;
position: fixed;
right: $width-settings-bar;
&.scrolling {
cursor: grab;
@ -171,14 +226,12 @@
}
.workspace-viewport {
height: calc(100% - 40px);
overflow: hidden;
transition: none;
width: 100%;
display: grid;
grid-template-rows: 20px 100%;
grid-template-columns: 20px 100%;
grid-template-rows: 20px 1fr;
grid-template-columns: 20px 1fr;
flex: 1;
}
.viewport {
@ -209,10 +262,14 @@
.render-shapes {
position: absolute;
width: 100%;
height: 100%;
}
.viewport-controls {
position: absolute;
width: 100%;
height: 100%;
}
}

View file

@ -481,7 +481,7 @@
(cond
(or (not (mth/finite? (:width 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)
(> (:height srect) height))
@ -522,25 +522,44 @@
(update :y y)))))))
(defn update-viewport-size
[{:keys [width height] :as size}]
[resize-type {:keys [width height] :as size}]
(ptk/reify ::update-viewport-size
ptk/UpdateEvent
(update [_ state]
(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 we have a resize to zero just keep the old value
local
(let [wprop (/ (:width vport) width)
hprop (/ (:height vport) height)
left-offset (if left-sidebar? 0 (/ (* -1 15 16) zoom))]
(-> local ;; This matches $width-settings-bar
(assoc :vport size) ;; in frontend/resources/styles/main/partials/sidebar.scss
(update :vbox (fn [vbox]
(-> vbox
(update :width #(/ % wprop))
(update :height #(/ % hprop))
(assoc :left-offset left-offset))))))))))))
vbox (:vbox local)
vbox-x (:x vbox)
vbox-y (:y vbox)
vbox-width (:width vbox)
vbox-height (:height vbox)
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 []
(ptk/reify ::start-panning
@ -596,14 +615,12 @@
(defn- impl-update-zoom
[{:keys [vbox] :as local} center zoom]
(let [vbox (update vbox :x + (:left-offset vbox))
new-zoom (if (fn? zoom) (zoom (:zoom local)) zoom)
(let [new-zoom (if (fn? zoom) (zoom (:zoom local)) zoom)
old-zoom (:zoom local)
center (if center center (gsh/center-rect vbox))
scale (/ old-zoom new-zoom)
mtx (gmt/scale-matrix (gpt/point scale) center)
vbox' (gsh/transform-rect vbox mtx)
vbox' (update vbox' :x - (:left-offset vbox))]
vbox' (gsh/transform-rect vbox mtx)]
(-> local
(assoc :zoom new-zoom)
(update :vbox merge (select-keys vbox' [:x :y :width :height])))))

View 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))

View file

@ -12,6 +12,7 @@
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.context :as ctx]
[app.main.ui.hooks.resize :refer [use-resize-observer]]
[app.main.ui.icons :as i]
[app.main.ui.workspace.colorpalette :refer [colorpalette]]
[app.main.ui.workspace.colorpicker]
@ -39,11 +40,19 @@
{:keys [options-mode]} local
file (obj/get props "file")
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])
[:section.workspace-content
[:section.workspace-content {:ref node-ref}
[:section.workspace-viewport
[:& coordinates/coordinates {:colorpalette? colorpalette?}]
@ -131,3 +140,5 @@
:layout layout}]
[:& workspace-loader])]]]]]))

View file

@ -12,6 +12,7 @@
[app.main.store :as st]
[app.main.ui.components.color-bullet :as cb]
[app.main.ui.components.dropdown :refer [dropdown]]
[app.main.ui.hooks.resize :refer [use-resize-hook]]
[app.main.ui.icons :as i]
[app.util.color :as uc]
[app.util.i18n :refer [tr]]
@ -52,7 +53,7 @@
[:& cb/color-name {:color color :size size}]]))
(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 })
width (:width @state 0)
@ -64,6 +65,9 @@
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
(mf/use-callback
(mf/deps max-offset visible)
@ -111,7 +115,11 @@
(fn []
(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)
:on-close #(swap! state assoc :show-menu false)}
[:ul.workspace-context-menu.palette-menu
@ -170,8 +178,7 @@
[:div.color-palette-inside {:style {:position "relative"
:right (str (* 66 offset) "px")}}
(for [[idx item] (map-indexed vector current-colors)]
[:& palette-item {:size size
:color item
[:& palette-item {:color item
:key idx}])]]
[:span.right-arrow {:on-click on-right-arrow-click} i/arrow-slide]]))
@ -188,8 +195,6 @@
file-colors (mf/deref refs/workspace-file-colors)
shared-libs (mf/deref refs/workspace-libraries)
selected (or (mf/deref selected-palette-ref) :recent)
size (or (mf/deref selected-palette-size-ref) :big)
current-library-colors (mf/use-state [])]
(mf/use-effect
@ -219,5 +224,4 @@
:recent-colors recent-colors
:file-colors file-colors
:shared-libs shared-libs
:selected selected
:size size}]))
:selected selected}]))

View file

@ -14,6 +14,7 @@
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.components.file-uploader :refer [file-uploader]]
[app.main.ui.hooks.resize :as r]
[app.main.ui.icons :as i]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]]
@ -136,5 +137,7 @@
[:li.tooltip.tooltip-right
{:alt (tr "workspace.toolbar.color-palette" (sc/get-tooltip :toggle-palette))
: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]]]]))

View file

@ -7,6 +7,7 @@
(ns app.main.ui.workspace.sidebar
(:require
[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.sidebar.assets :refer [assets-toolbox]]
[app.main.ui.workspace.sidebar.history :refer [history-toolbox]]
@ -21,19 +22,26 @@
(mf/defc left-sidebar
{:wrap [mf/memo]}
[{:keys [layout ] :as props}]
[:aside.settings-bar.settings-bar-left
[:div.settings-bar-inside
{:data-layout (str/join "," layout)}
(when (contains? layout :layers)
[:*
[:& sitemap {:layout layout}]
[:& layers-toolbox]])
(let [{:keys [on-pointer-down on-lost-pointer-capture on-mouse-move parent-ref size]}
(use-resize-hook 255 255 500 :x false :left)]
(when (contains? layout :document-history)
[:& history-toolbox])
[:aside.settings-bar.settings-bar-left {: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
{:data-layout (str/join "," layout)}
(when (contains? layout :layers)
[:*
[:& sitemap {:layout layout}]
[:& layers-toolbox]])
(when (contains? layout :assets)
[:& assets-toolbox])]])
(when (contains? layout :document-history)
[:& history-toolbox])
(when (contains? layout :assets)
[:& assets-toolbox])]]))
;; --- Right Sidebar (Component)
@ -41,8 +49,15 @@
{::mf/wrap-props false
::mf/wrap [mf/memo]}
[props]
(let [drawing-tool (:tool (mf/deref refs/workspace-drawing))]
[:aside.settings-bar
(let [{:keys [on-pointer-down on-lost-pointer-capture on-mouse-move parent-ref size]}
(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
(if (= drawing-tool :comments)
[:& comments-sidebar]

View file

@ -163,7 +163,6 @@
(hooks/setup-dom-events viewport-ref zoom disable-paste in-viewport?)
(hooks/setup-viewport-size viewport-ref)
(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-hover-shapes page-id move-stream base-objects transform selected ctrl? hover hover-ids @hover-disabled? zoom)
(hooks/setup-viewport-modifiers modifiers base-objects)
@ -222,8 +221,6 @@
:xmlnsXlink "http://www.w3.org/1999/xlink"
:preserveAspectRatio "xMidYMid meet"
:key (str "viewport" page-id)
:width (:width vport 0)
:height (:height vport 0)
:view-box (utils/format-viewbox vbox)
:ref viewport-ref
:class (when drawing-tool "drawing")

View file

@ -492,11 +492,3 @@
(when (and (not (#{"INPUT" "TEXTAREA"} tag-name)) (not @disable-paste))
(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)))))))

View file

@ -220,12 +220,12 @@
(defn guide-creation-area
[vbox zoom axis]
(if (= axis :x)
{:x (:x vbox)
{:x (+ (:x vbox) (/ 8 zoom))
:y (:y vbox)
:width (/ 24 zoom)
:width (/ 16 zoom)
:height (:height vbox)}
{:x (:x vbox)
{:x (+ (:x vbox) (+ 28 zoom))
:y (:y vbox)
:width (:width vbox)
:height (/ 24 zoom)}))

View file

@ -31,10 +31,9 @@
on-key-up (actions/on-key-up)
on-mouse-move (actions/on-mouse-move 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?)]
(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 []
(let [node (mf/ref-val viewport-ref)
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
;; https://stackoverflow.com/a/57582286/3219895
(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)]]
(fn []
@ -52,12 +50,12 @@
(defn setup-viewport-size [viewport-ref]
(mf/use-layout-effect
(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/initialize-viewport size)))))))
(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/initialize-viewport size)))))))
(defn setup-cursor [cursor alt? panning drawing-tool drawing-path? path-editing?]
(mf/use-effect
@ -80,10 +78,6 @@
(when (not= @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?]
(hooks/use-stream ms/keyboard-alt #(reset! alt? %))
(hooks/use-stream ms/keyboard-ctrl #(reset! ctrl? %))

View file

@ -25,7 +25,7 @@
(:import goog.events.EventType))
(defn format-viewbox [vbox]
(str/join " " [(+ (:x vbox 0) (:left-offset vbox 0))
(str/join " " [(:x vbox 0)
(:y vbox 0)
(:width vbox 0)
(:height vbox 0)]))

View file

@ -148,7 +148,7 @@
(dom/remove-attribute node "transform")))))))
(defn format-viewbox [vbox]
(str/join " " [(+ (:x vbox 0) (:left-offset vbox 0))
(str/join " " [(:x vbox 0)
(:y vbox 0)
(:width vbox 0)
(:height vbox 0)]))