Add back in working component tree with scrolling and dropdown menus
This commit is contained in:
parent
c46501709b
commit
53f3a4755b
|
@ -2,7 +2,7 @@ import { getFrontendStore } from "./store/frontend"
|
|||
import { getAutomationStore } from "./store/automation"
|
||||
import { getThemeStore } from "./store/theme"
|
||||
import { derived, writable } from "svelte/store"
|
||||
import { FrontendTypes, LAYOUT_NAMES } from "../constants"
|
||||
import { LAYOUT_NAMES } from "../constants"
|
||||
import { findComponent, findComponentPath } from "./componentUtils"
|
||||
|
||||
export const store = getFrontendStore()
|
||||
|
@ -13,32 +13,26 @@ export const selectedScreen = derived(store, $store => {
|
|||
return $store.screens.find(screen => screen._id === $store.selectedScreenId)
|
||||
})
|
||||
|
||||
export const currentAsset = selectedScreen
|
||||
|
||||
export const selectedComponent = derived(
|
||||
[store, currentAsset],
|
||||
([$store, $currentAsset]) => {
|
||||
if (!$currentAsset || !$store.selectedComponentId) {
|
||||
[store, selectedScreen],
|
||||
([$store, $selectedScreen]) => {
|
||||
if (!$selectedScreen || !$store.selectedComponentId) {
|
||||
return null
|
||||
}
|
||||
return findComponent($currentAsset?.props, $store.selectedComponentId)
|
||||
return findComponent($selectedScreen?.props, $store.selectedComponentId)
|
||||
}
|
||||
)
|
||||
|
||||
export const selectedComponentPath = derived(
|
||||
[store, currentAsset],
|
||||
([$store, $currentAsset]) => {
|
||||
[store, selectedScreen],
|
||||
([$store, $selectedScreen]) => {
|
||||
return findComponentPath(
|
||||
$currentAsset?.props,
|
||||
$selectedScreen?.props,
|
||||
$store.selectedComponentId
|
||||
).map(component => component._id)
|
||||
}
|
||||
)
|
||||
|
||||
export const currentAssetName = derived(currentAsset, $currentAsset => {
|
||||
return $currentAsset?.name
|
||||
})
|
||||
|
||||
export const mainLayout = derived(store, $store => {
|
||||
return $store.layouts?.find(
|
||||
layout => layout._id === LAYOUT_NAMES.MASTER.PRIVATE
|
||||
|
@ -47,4 +41,5 @@ export const mainLayout = derived(store, $store => {
|
|||
|
||||
export const selectedAccessRole = writable("BASIC")
|
||||
|
||||
export const screenSearchString = writable(null)
|
||||
// For compatibility
|
||||
export const currentAsset = selectedScreen
|
||||
|
|
|
@ -48,7 +48,6 @@ const INITIAL_FRONTEND_STATE = {
|
|||
},
|
||||
currentFrontEndType: "none",
|
||||
selectedLayoutId: "",
|
||||
selectedComponentId: "",
|
||||
errors: [],
|
||||
hasAppPackage: false,
|
||||
libraries: null,
|
||||
|
@ -61,6 +60,7 @@ const INITIAL_FRONTEND_STATE = {
|
|||
|
||||
// URL params
|
||||
selectedScreenId: null,
|
||||
selectedComponentId: null,
|
||||
}
|
||||
|
||||
export const getFrontendStore = () => {
|
||||
|
@ -300,32 +300,6 @@ export const getFrontendStore = () => {
|
|||
},
|
||||
},
|
||||
components: {
|
||||
select: component => {
|
||||
const asset = get(currentAsset)
|
||||
if (!asset || !component) {
|
||||
return
|
||||
}
|
||||
|
||||
// If this is the root component, select the asset instead
|
||||
const parent = findComponentParent(asset.props, component._id)
|
||||
if (parent == null) {
|
||||
const state = get(store)
|
||||
const isLayout = state.currentFrontEndType === FrontendTypes.LAYOUT
|
||||
if (isLayout) {
|
||||
store.actions.layouts.select(asset._id)
|
||||
} else {
|
||||
store.actions.screens.select(asset._id)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Otherwise select the component
|
||||
store.update(state => {
|
||||
state.selectedComponentId = component._id
|
||||
state.currentView = "component"
|
||||
return state
|
||||
})
|
||||
},
|
||||
getDefinition: componentName => {
|
||||
if (!componentName) {
|
||||
return null
|
||||
|
@ -464,7 +438,10 @@ export const getFrontendStore = () => {
|
|||
parent._children = parent._children.filter(
|
||||
child => child._id !== component._id
|
||||
)
|
||||
store.actions.components.select(parent)
|
||||
store.update(state => {
|
||||
state.selectedComponentId = parent._id
|
||||
return state
|
||||
})
|
||||
}
|
||||
await store.actions.preview.saveSelected()
|
||||
},
|
||||
|
@ -488,7 +465,10 @@ export const getFrontendStore = () => {
|
|||
parent._children = parent._children.filter(
|
||||
child => child._id !== component._id
|
||||
)
|
||||
store.actions.components.select(parent)
|
||||
store.update(state => {
|
||||
state.selectedComponentId = parent._id
|
||||
return state
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -539,7 +519,7 @@ export const getFrontendStore = () => {
|
|||
|
||||
// Save and select the new component
|
||||
promises.push(store.actions.preview.saveSelected())
|
||||
store.actions.components.select(componentToPaste)
|
||||
state.selectedComponentId = componentToPaste._id
|
||||
return state
|
||||
})
|
||||
await Promise.all(promises)
|
||||
|
|
|
@ -1,114 +0,0 @@
|
|||
<script>
|
||||
import {
|
||||
ActionMenu,
|
||||
ActionButton,
|
||||
MenuItem,
|
||||
Icon,
|
||||
notifications,
|
||||
} from "@budibase/bbui"
|
||||
import { store, currentAssetName, selectedComponent } from "builderStore"
|
||||
import structure from "./componentStructure.json"
|
||||
|
||||
$: enrichedStructure = enrichStructure(structure, $store.components)
|
||||
|
||||
const isChildAllowed = ({ name }, selectedComponent) => {
|
||||
const currentComponent = store.actions.components.getDefinition(
|
||||
selectedComponent?._component
|
||||
)
|
||||
return currentComponent?.illegalChildren?.includes(name.toLowerCase())
|
||||
}
|
||||
|
||||
const enrichStructure = (structure, definitions) => {
|
||||
let enrichedStructure = []
|
||||
structure.forEach(item => {
|
||||
if (typeof item === "string") {
|
||||
const def = definitions[`@budibase/standard-components/${item}`]
|
||||
if (def) {
|
||||
enrichedStructure.push({
|
||||
...def,
|
||||
isCategory: false,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
enrichedStructure.push({
|
||||
...item,
|
||||
isCategory: true,
|
||||
children: enrichStructure(item.children || [], definitions),
|
||||
})
|
||||
}
|
||||
})
|
||||
return enrichedStructure
|
||||
}
|
||||
|
||||
const onItemChosen = async item => {
|
||||
if (!item.isCategory) {
|
||||
try {
|
||||
await store.actions.components.create(item.component)
|
||||
} catch (error) {
|
||||
notifications.error("Error creating component")
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="components">
|
||||
{#each enrichedStructure as item}
|
||||
<ActionMenu disabled={!item.isCategory}>
|
||||
<ActionButton
|
||||
icon={item.icon}
|
||||
disabled={!item.isCategory && isChildAllowed(item, $selectedComponent)}
|
||||
quiet
|
||||
size="S"
|
||||
slot="control"
|
||||
dataCy={`category-${item.name}`}
|
||||
on:click={() => onItemChosen(item)}
|
||||
>
|
||||
<div class="buttonContent">
|
||||
{item.name}
|
||||
{#if item.isCategory}
|
||||
<Icon size="S" name="ChevronDown" />
|
||||
{/if}
|
||||
</div>
|
||||
</ActionButton>
|
||||
{#each item.children || [] as item}
|
||||
{#if !item.showOnAsset || item.showOnAsset.includes($currentAssetName)}
|
||||
<MenuItem
|
||||
dataCy={`component-${item.name}`}
|
||||
icon={item.icon}
|
||||
on:click={() => onItemChosen(item)}
|
||||
disabled={isChildAllowed(item, $selectedComponent)}
|
||||
>
|
||||
{item.name}
|
||||
</MenuItem>
|
||||
{/if}
|
||||
{/each}
|
||||
</ActionMenu>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.components {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.components :global(> *) {
|
||||
height: 32px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
|
||||
.buttonContent {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: flex-end;
|
||||
}
|
||||
.buttonContent :global(svg) {
|
||||
margin-left: 2px !important;
|
||||
margin-right: 0 !important;
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
</style>
|
|
@ -127,9 +127,6 @@
|
|||
<Tab title="Screens">
|
||||
<div class="tab-content-padding">
|
||||
<BBUILayout noPadding gap="XS" />
|
||||
<div class="nav-items-container" bind:this={scrollRef}>
|
||||
<ComponentNavigationTree />
|
||||
</div>
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab title="Layouts">
|
||||
|
@ -190,15 +187,6 @@
|
|||
gap: var(--spacing-xl);
|
||||
}
|
||||
|
||||
.nav-items-container {
|
||||
border-top: var(--border-light);
|
||||
margin: 0 calc(-1 * var(--spacing-xl));
|
||||
padding: var(--spacing-m) 0;
|
||||
flex: 1 1 auto;
|
||||
overflow: auto;
|
||||
height: 0;
|
||||
position: relative;
|
||||
}
|
||||
.nav-items-container--layouts {
|
||||
border-top: none;
|
||||
margin-top: calc(-1 * var(--spectrum-global-dimension-static-size-150));
|
||||
|
|
|
@ -23,7 +23,9 @@
|
|||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<slot />
|
||||
<div class="body">
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
@ -35,7 +37,6 @@
|
|||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: stretch;
|
||||
overflow: auto;
|
||||
}
|
||||
.navigation-panel.wide {
|
||||
width: 360px;
|
||||
|
@ -78,4 +79,13 @@
|
|||
.add-button :global(svg) {
|
||||
fill: white;
|
||||
}
|
||||
.body {
|
||||
flex: 1 1 auto;
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: stretch;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -64,20 +64,11 @@
|
|||
{#each paths as path, idx (path)}
|
||||
<PathTree border={idx > 0} {path} route={routes[path]} />
|
||||
{/each}
|
||||
{#if !paths.length}
|
||||
<div class="empty">
|
||||
There aren't any screens configured with this access role.
|
||||
</div>
|
||||
{/if}
|
||||
{#if !paths.length}{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.root.has-screens {
|
||||
min-width: max-content;
|
||||
}
|
||||
div.empty {
|
||||
font-size: var(--font-size-s);
|
||||
color: var(--grey-5);
|
||||
padding: var(--spacing-xs) var(--spacing-xl);
|
||||
}
|
||||
</style>
|
|
@ -1,10 +1,5 @@
|
|||
<script>
|
||||
import {
|
||||
store,
|
||||
selectedComponent,
|
||||
currentAsset,
|
||||
screenSearchString,
|
||||
} from "builderStore"
|
||||
import { store, selectedComponent, currentAsset } from "builderStore"
|
||||
import instantiateStore from "./dragDropStore"
|
||||
import ComponentTree from "./ComponentTree.svelte"
|
||||
import PathDropdownMenu from "./PathDropdownMenu.svelte"
|
|
@ -12,7 +12,7 @@
|
|||
$: roleName = $roles.find(x => x._id === roleId)?.name || "Unknown"
|
||||
|
||||
// Needs to be absolute as we embed this component from multiple different URLs
|
||||
$: newComponentUrl = `/builder/app/${store.appId}/design/components/${$selectedScreen?._id}/new`
|
||||
$: newComponentUrl = `/builder/app/${$store.appId}/design/components/${$selectedScreen?._id}/new`
|
||||
|
||||
const getRoleColor = roleId => {
|
||||
return RoleColours[roleId] || "#ffa500"
|
||||
|
|
|
@ -140,7 +140,10 @@
|
|||
|
||||
try {
|
||||
if (type === "select-component" && data.id) {
|
||||
store.actions.components.select({ _id: data.id })
|
||||
store.update(state => {
|
||||
state.selectedComponentId = data.id
|
||||
return state
|
||||
})
|
||||
} else if (type === "update-prop") {
|
||||
await store.actions.components.updateProp(data.prop, data.value)
|
||||
} else if (type === "delete-component" && data.id) {
|
||||
|
|
|
@ -1,9 +1,63 @@
|
|||
<script>
|
||||
import NavigationPanel from "components/design/NavigationPanel/NavigationPanel.svelte"
|
||||
import ComponentTree from "./ComponentTree.svelte"
|
||||
import instantiateStore from "./dragDropStore.js"
|
||||
import { goto } from "@roxi/routify"
|
||||
import { Layout, Search } from "@budibase/bbui"
|
||||
import { store, selectedScreen, selectedComponent } from "builderStore"
|
||||
import NavItem from "components/common/NavItem.svelte"
|
||||
import ScreenslotDropdownMenu from "./ScreenslotDropdownMenu.svelte"
|
||||
import { setContext } from "svelte"
|
||||
|
||||
let searchString
|
||||
const dragDropStore = instantiateStore()
|
||||
let scrollRef
|
||||
|
||||
const scrollTo = bounds => {
|
||||
if (!bounds) {
|
||||
return
|
||||
}
|
||||
const sidebarWidth = 259
|
||||
const navItemHeight = 32
|
||||
const { scrollLeft, scrollTop, offsetHeight } = scrollRef
|
||||
let scrollBounds = scrollRef.getBoundingClientRect()
|
||||
let newOffsets = {}
|
||||
|
||||
// Calculate left offset
|
||||
const offsetX = bounds.left + bounds.width + scrollLeft - 40
|
||||
if (offsetX > sidebarWidth) {
|
||||
newOffsets.left = offsetX - sidebarWidth
|
||||
} else {
|
||||
newOffsets.left = 0
|
||||
}
|
||||
if (newOffsets.left === scrollLeft) {
|
||||
delete newOffsets.left
|
||||
}
|
||||
|
||||
// Calculate top offset
|
||||
const offsetY = bounds.top - scrollBounds?.top + scrollTop
|
||||
if (offsetY > scrollTop + offsetHeight - 2 * navItemHeight) {
|
||||
newOffsets.top = offsetY - offsetHeight + 2 * navItemHeight
|
||||
} else if (offsetY < scrollTop + navItemHeight) {
|
||||
newOffsets.top = offsetY - navItemHeight
|
||||
} else {
|
||||
delete newOffsets.top
|
||||
}
|
||||
|
||||
// Skip if offset is unchanged
|
||||
if (newOffsets.left == null && newOffsets.top == null) {
|
||||
return
|
||||
}
|
||||
|
||||
// Smoothly scroll to the offset
|
||||
scrollRef.scroll({
|
||||
...newOffsets,
|
||||
behavior: "smooth",
|
||||
})
|
||||
}
|
||||
|
||||
// Set scroll context so components can invoke scrolling when selected
|
||||
setContext("scroll", {
|
||||
scrollTo,
|
||||
})
|
||||
</script>
|
||||
|
||||
<NavigationPanel
|
||||
|
@ -11,11 +65,36 @@
|
|||
showAddButton
|
||||
onClickAddButton={() => $goto("../new")}
|
||||
>
|
||||
<Layout paddingX="L" paddingY="XL" gap="S">
|
||||
<Search
|
||||
placeholder="Search"
|
||||
value={searchString}
|
||||
on:change={e => (searchString = e.detail)}
|
||||
<div class="nav-items-container" bind:this={scrollRef}>
|
||||
<NavItem
|
||||
text="Screen"
|
||||
withArrow
|
||||
indentLevel={0}
|
||||
selected={$store.selectedComponentId === $selectedScreen?.props._id}
|
||||
opened
|
||||
scrollable
|
||||
on:click={() => {
|
||||
$store.selectedComponentId = $selectedScreen?.props._id
|
||||
}}
|
||||
>
|
||||
<ScreenslotDropdownMenu component={$selectedScreen?.props} />
|
||||
</NavItem>
|
||||
<ComponentTree
|
||||
level={0}
|
||||
components={$selectedScreen?.props._children}
|
||||
currentComponent={$selectedComponent}
|
||||
{dragDropStore}
|
||||
/>
|
||||
</Layout>
|
||||
</div>
|
||||
</NavigationPanel>
|
||||
|
||||
<style>
|
||||
.nav-items-container {
|
||||
margin: 0 calc(-1 * var(--spacing-l));
|
||||
padding: var(--spacing-xl) var(--spacing-l);
|
||||
flex: 1 1 auto;
|
||||
overflow: auto;
|
||||
height: 0;
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -9,16 +9,11 @@
|
|||
|
||||
export let components = []
|
||||
export let currentComponent
|
||||
export let onSelect = () => {}
|
||||
export let level = 0
|
||||
export let dragDropStore
|
||||
|
||||
let closedNodes = {}
|
||||
|
||||
const selectComponent = component => {
|
||||
store.actions.components.select(component)
|
||||
}
|
||||
|
||||
const dragstart = component => e => {
|
||||
e.dataTransfer.dropEffect = DropEffect.MOVE
|
||||
dragDropStore.actions.dragstart(component)
|
||||
|
@ -86,7 +81,11 @@
|
|||
|
||||
<ul>
|
||||
{#each components || [] as component, index (component._id)}
|
||||
<li on:click|stopPropagation={() => selectComponent(component)}>
|
||||
<li
|
||||
on:click|stopPropagation={() => {
|
||||
$store.selectedComponentId = component._id
|
||||
}}
|
||||
>
|
||||
{#if $dragDropStore?.targetComponent === component && $dragDropStore.dropPosition === DropPosition.ABOVE}
|
||||
<div
|
||||
on:drop={onDrop}
|
||||
|
@ -98,6 +97,7 @@
|
|||
{/if}
|
||||
|
||||
<NavItem
|
||||
scrollable
|
||||
draggable
|
||||
on:dragend={dragDropStore.actions.reset}
|
||||
on:dragstart={dragstart(component)}
|
||||
|
@ -117,7 +117,6 @@
|
|||
<svelte:self
|
||||
components={component._children}
|
||||
{currentComponent}
|
||||
{onSelect}
|
||||
{dragDropStore}
|
||||
level={level + 1}
|
||||
/>
|
|
@ -0,0 +1,52 @@
|
|||
<script>
|
||||
import { store } from "builderStore"
|
||||
import { ActionMenu, MenuItem, Icon, notifications } from "@budibase/bbui"
|
||||
|
||||
export let component
|
||||
|
||||
$: definition = store.actions.components.getDefinition(component?._component)
|
||||
$: noPaste = !$store.componentToPaste
|
||||
|
||||
// "editable" has been repurposed for inline text editing.
|
||||
// It remains here for legacy compatibility.
|
||||
// Future components should define "static": true for indicate they should
|
||||
// not show a context menu.
|
||||
$: showMenu = definition?.editable !== false && definition?.static !== true
|
||||
|
||||
const storeComponentForCopy = (cut = false) => {
|
||||
store.actions.components.copy(component, cut)
|
||||
}
|
||||
|
||||
const pasteComponent = mode => {
|
||||
try {
|
||||
store.actions.components.paste(component, mode)
|
||||
} catch (error) {
|
||||
notifications.error("Error saving component")
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if showMenu}
|
||||
<ActionMenu>
|
||||
<div slot="control" class="icon">
|
||||
<Icon size="S" hoverable name="MoreSmallList" />
|
||||
</div>
|
||||
<MenuItem icon="Copy" on:click={() => storeComponentForCopy(false)}>
|
||||
Copy
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
icon="ShowOneLayer"
|
||||
on:click={() => pasteComponent("inside")}
|
||||
disabled={noPaste}
|
||||
>
|
||||
Paste inside
|
||||
</MenuItem>
|
||||
</ActionMenu>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.icon {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,30 @@
|
|||
<script>
|
||||
import { syncURLToState } from "helpers/urlStateSync"
|
||||
import { store, selectedScreen } from "builderStore"
|
||||
import { goto, params, redirect } from "@roxi/routify"
|
||||
import { onDestroy } from "svelte"
|
||||
import { findComponent } from "builderStore/componentUtils"
|
||||
|
||||
// Keep URL and state in sync for selected component ID
|
||||
const stopSyncing = syncURLToState({
|
||||
keys: [
|
||||
{
|
||||
url: "componentId",
|
||||
state: "selectedComponentId",
|
||||
validate: componentId => {
|
||||
return !!findComponent($selectedScreen.props, componentId)
|
||||
},
|
||||
fallbackUrl: "../",
|
||||
},
|
||||
],
|
||||
store,
|
||||
params,
|
||||
goto,
|
||||
redirect,
|
||||
baseUrl: "..",
|
||||
})
|
||||
|
||||
onDestroy(stopSyncing)
|
||||
</script>
|
||||
|
||||
<slot />
|
|
@ -18,6 +18,7 @@
|
|||
params,
|
||||
goto,
|
||||
redirect,
|
||||
baseUrl: "..",
|
||||
})
|
||||
|
||||
onDestroy(stopSyncing)
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
onMount(() => {
|
||||
if ($selectedScreen) {
|
||||
// Select the screen slot if a screen exists
|
||||
$redirect(`./slot`)
|
||||
$redirect(`./${$selectedScreen.props._id}`)
|
||||
} else {
|
||||
// Otherwise go up so we can select a new valid screen
|
||||
$redirect("../")
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
Icon,
|
||||
Body,
|
||||
Divider,
|
||||
notifications,
|
||||
} from "@budibase/bbui"
|
||||
import structure from "./componentStructure.json"
|
||||
import { store } from "builderStore"
|
||||
|
@ -75,7 +76,20 @@
|
|||
return structure
|
||||
}
|
||||
|
||||
const addComponent = () => {}
|
||||
const isChildAllowed = ({ name }, selectedComponent) => {
|
||||
const currentComponent = store.actions.components.getDefinition(
|
||||
selectedComponent?._component
|
||||
)
|
||||
return currentComponent?.illegalChildren?.includes(name.toLowerCase())
|
||||
}
|
||||
|
||||
const addComponent = async item => {
|
||||
try {
|
||||
await store.actions.components.create(item.component)
|
||||
} catch (error) {
|
||||
notifications.error("Error creating component")
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<NavigationPanel
|
||||
|
@ -125,7 +139,7 @@
|
|||
{/each}
|
||||
{:else}
|
||||
<Layout paddingX="L" paddingY="XL" gap="S">
|
||||
<Body size="S">Blocks are a collection of pre-built components</Body>
|
||||
<Body>Blocks are a collection of pre-built components</Body>
|
||||
<Layout noPadding gap="XS">
|
||||
{#each blocks as block}
|
||||
<div class="component block">
|
|
@ -0,0 +1,10 @@
|
|||
<script>
|
||||
import SettingsPanel from "components/design/SettingsPanel/SettingsPanel.svelte"
|
||||
import { Body, Layout } from "@budibase/bbui"
|
||||
</script>
|
||||
|
||||
<SettingsPanel title="Screen" icon="WebPage">
|
||||
<Layout paddingX="L" paddingY="XL">
|
||||
<Body>The component you add will be placed inside Screen</Body>
|
||||
</Layout>
|
||||
</SettingsPanel>
|
|
@ -1,5 +1,7 @@
|
|||
<script>
|
||||
import ComponentListPanel from "./_components/ComponentListPanel.svelte"
|
||||
import NewComponentPanel from "./_components/NewComponentPanel.svelte"
|
||||
import NewComponentTargetPanel from "./_components/NewComponentTargetPanel.svelte"
|
||||
</script>
|
||||
|
||||
<ComponentListPanel />
|
||||
<NewComponentPanel />
|
||||
<NewComponentTargetPanel />
|
||||
|
|
|
@ -67,6 +67,19 @@
|
|||
<ScreenDropdownMenu screenId={screen._id} />
|
||||
</NavItem>
|
||||
{/each}
|
||||
{#if !filteredScreens?.length}
|
||||
<div class="empty">
|
||||
There aren't any screens matching the current filters
|
||||
</div>
|
||||
{/if}
|
||||
</NavigationPanel>
|
||||
|
||||
<ScreenWizard bind:showModal={showNewScreenModal} />
|
||||
|
||||
<style>
|
||||
.empty {
|
||||
font-size: var(--spectrum-global-dimension-font-size-75);
|
||||
color: var(--grey-7);
|
||||
padding: 0 var(--spacing-l);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -6,9 +6,7 @@
|
|||
|
||||
<NavigationPanel title="Theme">
|
||||
<Layout paddingX="L" paddingY="XL" gap="S">
|
||||
<Body size="S">
|
||||
Your theme is set across all the screens within your app
|
||||
</Body>
|
||||
<Body>Your theme is set across all the screens within your app</Body>
|
||||
<ThemeEditor />
|
||||
</Layout>
|
||||
</NavigationPanel>
|
||||
|
|
Loading…
Reference in New Issue