Add resizable screen/component sections and remove redundant /components route

This commit is contained in:
Andrew Kingston 2023-08-23 10:27:56 +01:00
parent a83e987dcd
commit a54c5b7222
39 changed files with 117 additions and 89 deletions

View File

@ -225,7 +225,7 @@ export const getFrontendStore = () => {
// Select new screen
store.update(state => {
state.selectedScreenId = screen._id
state.selectedComponentId = screen.props?._id
state.selectedComponentId = "screen"
return state
})
},

View File

@ -0,0 +1 @@
<!-- Required to make Routify happy -->

View File

@ -1,6 +1,6 @@
<script>
import Panel from "components/design/Panel.svelte"
import { goto } from "@roxi/routify"
import { goto, url } from "@roxi/routify"
import { Layout, Search, Icon, Body, notifications } from "@budibase/bbui"
import structure from "./componentStructure.json"
import { store, selectedComponent, selectedScreen } from "builderStore"

View File

@ -9,9 +9,7 @@
<div class="app-panel">
<div class="header">
<div class="header-left">
{#if $isActive("./screens") || $isActive("./components")}
<UndoRedoControl store={screenHistoryStore} />
{/if}
</div>
<div class="header-right">
{#if $store.clientFeatures.devicePreview}

View File

@ -70,9 +70,7 @@
$: refreshContent(json)
// Determine if the add component menu is active
$: isAddingComponent = $isActive(
`./components/${$selectedComponent?._id}/new`
)
$: isAddingComponent = $isActive(`./${$selectedComponent?._id}/new`)
// Register handler to send custom to the preview
$: sendPreviewEvent = (name, payload) => {
@ -215,9 +213,9 @@
const toggleAddComponent = () => {
if (isAddingComponent) {
$goto(`./components/${$selectedComponent?._id}`)
$goto(`./${$selectedComponent?._id}`)
} else {
$goto(`./components/${$selectedComponent?._id}/new`)
$goto(`./${$selectedComponent?._id}/new`)
}
}

View File

@ -14,10 +14,10 @@
let scrolling = false
const toNewComponentRoute = () => {
if ($isActive("./new")) {
if ($isActive(`./${$store.selectedComponentId}/new`)) {
return
} else {
$goto(`./new`)
$goto(`./${$store.selectedComponentId}/new`)
}
}
@ -31,11 +31,7 @@
}
const handleScroll = e => {
if (e.target.scrollTop === 0) {
scrolling = false
} else {
scrolling = true
}
scrolling = e.target.scrollTop !== 0
}
</script>

View File

@ -0,0 +1,24 @@
<script>
import ScreenList from "./ScreenList/index.svelte"
import ComponentList from "./ComponentList/index.svelte"
import { isActive } from "@roxi/routify"
</script>
<div class="panel">
<ScreenList />
{#if $isActive("./:componentId")}
<ComponentList />
{/if}
</div>
<style>
.panel {
width: 310px;
height: 100%;
border-right: var(--border-light);
display: flex;
flex-direction: column;
background: var(--background);
position: relative;
}
</style>

View File

@ -9,29 +9,42 @@
let newScreen = false
let search = false
let resizing = false
let searchValue = ""
let searchInput
let screensContainer
let scrolling = false
let height = "210px"
let previousHeight = null
let dragOffset
$: filteredScreens = getFilteredScreens($sortedScreens, searchValue)
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))
const openSearch = async () => {
search = true
await tick()
searchInput.focus()
screensContainer.scroll({ top: 0, behavior: "smooth" })
previousHeight = height
height = "calc(100% + 1px)"
}
const closeSearch = () => {
const closeSearch = async () => {
if (previousHeight) {
// Restore previous height and wait for animation
height = previousHeight
previousHeight = null
await sleep(300)
}
search = false
searchValue = ""
}
const getFilteredScreens = (screens, search) => {
return screens.filter(screen => {
const searchMatch = !search || screen.routing.route.includes(search)
return searchMatch
return !search || screen.routing.route.includes(search)
})
}
@ -54,17 +67,33 @@
}
const handleScroll = e => {
if (e.target.scrollTop === 0) {
scrolling = false
} else {
scrolling = true
scrolling = e.target.scrollTop !== 0
}
const startResizing = e => {
resizing = true
dragOffset = parseInt(height) - e.clientY
document.addEventListener("mousemove", resize)
document.addEventListener("mouseup", stopResizing)
}
const resize = e => {
const newHeight = Math.max(0, e.clientY + dragOffset)
if (newHeight == null || isNaN(newHeight)) {
return
}
height = `${newHeight}px`
}
const stopResizing = () => {
resizing = false
document.removeEventListener("mousemove", resize)
}
</script>
<svelte:window on:keydown={onKeyDown} />
<div class="screens" class:screenSearch={search}>
<div class="header" class:headerScrolling={scrolling}>
<div class="screens" class:search class:resizing style={`height:${height};`}>
<div class="header" class:scrolling>
<input
readonly={!search}
bind:value={searchValue}
@ -113,6 +142,8 @@
</Layout>
{/if}
</div>
<div class="divider" on:mousedown={startResizing} />
</div>
<div class="newScreen" class:newScreenVisible={newScreen}>
@ -129,20 +160,24 @@
z-index: 2;
background-color: var(--background);
}
.newScreenVisible {
display: initial;
}
.screens {
height: 210px;
display: flex;
flex-direction: column;
transition: height 300ms ease-out;
min-height: 147px;
max-height: calc(100% - 147px);
position: relative;
}
.screenSearch {
height: 100%;
.screens.search {
transition: height 300ms ease-out;
max-height: none;
}
.screens.resizing {
user-select: none;
cursor: row-resize;
}
.header {
@ -154,11 +189,10 @@
display: flex;
align-items: center;
border-bottom: 2px solid transparent;
transition: border-bottom 300ms ease-out;
transition: border-bottom 130ms ease-out;
}
.headerScrolling {
border-bottom: 2px solid var(--grey-2);
.header.scrolling {
border-bottom: var(--border-light);
}
.input {
@ -178,8 +212,7 @@
.input::placeholder {
color: var(--spectrum-global-color-gray-600);
}
.screenSearch input {
.screens.search input {
display: block;
}
@ -197,6 +230,9 @@
overflow: auto;
flex-grow: 1;
}
.screens.resizing .content {
pointer-events: none;
}
.screens :global(.nav-item) {
padding-right: 8px !important;
@ -208,7 +244,6 @@
margin-right: 10px;
opacity: 1;
}
.searchButton:hover {
color: var(--ink);
}
@ -223,15 +258,14 @@
cursor: pointer;
transition: transform 300ms ease-out;
}
.addButton:hover {
color: var(--ink);
}
.closeButton {
transform: rotate(45deg);
}
.addButton:hover {
color: var(--ink);
}
.icon {
margin-left: 4px;
margin-right: 4px;
@ -240,4 +274,24 @@
.no-results {
color: var(--spectrum-global-color-gray-600);
}
.divider {
position: absolute;
bottom: 0;
transform: translateY(50%);
height: 16px;
width: 100%;
}
.divider:after {
content: "";
position: absolute;
background: var(--spectrum-global-color-gray-200);
height: 2px;
width: 100%;
top: 50%;
transform: translateY(-50%);
}
.divider:hover {
cursor: row-resize;
}
</style>

View File

@ -4,7 +4,7 @@
import { syncURLToState } from "helpers/urlStateSync"
import { store } from "builderStore"
import { onDestroy } from "svelte"
import LeftPanel from "./components/[componentId]/_components/LeftPanel/index.svelte"
import LeftPanel from "./_components/LeftPanel.svelte"
$: screenId = $store.selectedScreenId
$: store.actions.websocket.selectResource(screenId)

View File

@ -1,28 +0,0 @@
<script>
import Screens from "./Screens/index.svelte"
import Components from "./Components/index.svelte"
import { selectedScreen } from "builderStore"
</script>
<div class="panel">
<Screens />
<div class="divider" />
{#if $selectedScreen}
<Components />
{/if}
</div>
<style>
.panel {
width: 310px;
height: 100%;
border-right: var(--border-light);
display: flex;
flex-direction: column;
background: var(--background);
}
.divider {
border-bottom: var(--border-light);
}
</style>

View File

@ -1,4 +0,0 @@
<!--
Placeholder file so that routify works.
No unique content is needed in this index page.
-->

View File

@ -1,8 +0,0 @@
<script>
import { onMount } from "svelte"
import { redirect } from "@roxi/routify"
onMount(() => {
$redirect(`./screen`)
})
</script>

View File

@ -1,8 +1,5 @@
<script>
import { onMount } from "svelte"
import { redirect } from "@roxi/routify"
onMount(() => {
$redirect("./components/screen")
})
$redirect("./screen")
</script>

View File

@ -4,7 +4,7 @@
$: {
if ($frontendStore.screens.length > 0) {
$redirect(`./${$frontendStore.screens[0]._id}/components/screen`)
$redirect(`./${$frontendStore.screens[0]._id}/screen`)
} else {
$redirect("./new")
}