Add initial new 'add component' panel

This commit is contained in:
Andrew Kingston 2022-04-26 08:23:53 +01:00
parent 5d426b25a7
commit c127703a6c
10 changed files with 208 additions and 13 deletions

View File

@ -1,5 +1,4 @@
<script> <script>
import ComponentSelectionList from "./ComponentSelectionList.svelte"
import DevicePreviewSelect from "./DevicePreviewSelect.svelte" import DevicePreviewSelect from "./DevicePreviewSelect.svelte"
import AppPreview from "./AppPreview.svelte" import AppPreview from "./AppPreview.svelte"
import { store } from "builderStore" import { store } from "builderStore"
@ -7,7 +6,6 @@
<div class="app-panel"> <div class="app-panel">
<div class="header"> <div class="header">
<ComponentSelectionList />
{#if $store.clientFeatures.devicePreview} {#if $store.clientFeatures.devicePreview}
<DevicePreviewSelect /> <DevicePreviewSelect />
{/if} {/if}

View File

@ -2,12 +2,18 @@
import { Icon, Heading } from "@budibase/bbui" import { Icon, Heading } from "@budibase/bbui"
export let title export let title
export let showAddButton export let showAddButton = false
export let showBackButton = false
export let onClickAddButton export let onClickAddButton
export let onClickBackButton
export let wide = false
</script> </script>
<div class="navigation-panel"> <div class="navigation-panel" class:wide>
<div class="header"> <div class="header">
{#if showBackButton}
<Icon name="ArrowLeft" hoverable on:click={onClickBackButton} />
{/if}
<div class="title"> <div class="title">
<Heading size="XS">{title || ""}</Heading> <Heading size="XS">{title || ""}</Heading>
</div> </div>
@ -29,9 +35,13 @@
flex-direction: column; flex-direction: column;
justify-content: flex-start; justify-content: flex-start;
align-items: stretch; align-items: stretch;
overflow: auto;
}
.navigation-panel.wide {
width: 360px;
} }
.header { .header {
height: 48px; flex: 0 0 48px;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;

View File

@ -9,7 +9,7 @@
<NavigationPanel <NavigationPanel
title="Components" title="Components"
showAddButton showAddButton
onClickAddButton={() => $goto("./new")} onClickAddButton={() => $goto("../new")}
> >
<Layout paddingX="L" paddingY="XL" gap="S"> <Layout paddingX="L" paddingY="XL" gap="S">
<Search <Search

View File

@ -1,5 +1,4 @@
<script> <script>
import { store } from "builderStore"
import AppPanel from "components/design/AppPanel/AppPanel.svelte" import AppPanel from "components/design/AppPanel/AppPanel.svelte"
import ComponentNavigationPanel from "./_components/ComponentNavigationPanel.svelte" import ComponentNavigationPanel from "./_components/ComponentNavigationPanel.svelte"
import ComponentSettingsPanel from "./_components/ComponentSettingsPanel.svelte" import ComponentSettingsPanel from "./_components/ComponentSettingsPanel.svelte"

View File

@ -0,0 +1,26 @@
<script>
import { store } from "builderStore"
import { onDestroy } from "svelte"
import { syncURLToState } from "helpers/urlStateSync"
import { goto, params, redirect } from "@roxi/routify"
// Keep URL and state in sync for selected screen ID
const stopSyncing = syncURLToState({
keys: [
{
url: "screenId",
state: "selectedScreenId",
validate: screenId => $store.screens.some(x => x._id === screenId),
fallbackUrl: "../",
},
],
store,
params,
goto,
redirect,
})
onDestroy(stopSyncing)
</script>
<slot />

View File

@ -0,0 +1,15 @@
<script>
import { selectedScreen } from "builderStore"
import { onMount } from "svelte"
import { redirect } from "@roxi/routify"
onMount(() => {
if ($selectedScreen) {
// Select the screen slot if a screen exists
$redirect(`./slot`)
} else {
// Otherwise go up so we can select a new valid screen
$redirect("../")
}
})
</script>

View File

@ -1,21 +1,143 @@
<script> <script>
import NavigationPanel from "components/design/NavigationPanel/NavigationPanel.svelte" import NavigationPanel from "components/design/NavigationPanel/NavigationPanel.svelte"
import { goto } from "@roxi/routify" import { goto } from "@roxi/routify"
import { Layout, Search } from "@budibase/bbui" import {
Layout,
ActionGroup,
ActionButton,
Search,
DetailSummary,
Icon,
Body,
} from "@budibase/bbui"
import structure from "./componentStructure.json"
import { store } from "builderStore"
let section = "components"
let searchString let searchString
$: enrichedStructure = enrichStructure(structure, $store.components)
$: filteredStructure = filterStructure(
enrichedStructure,
section,
searchString
)
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 filterStructure = (structure, section, search) => {
if (!structure?.length) {
return []
}
if (section === "components") {
structure = structure.filter(category => category.name !== "Blocks")
} else {
structure = structure.filter(category => category.name === "Blocks")
}
if (search) {
let filteredStructure = []
structure.forEach(category => {
let matchedChildren = category.children.filter(child => {
return child.name.toLowerCase().includes(search.toLowerCase())
})
if (matchedChildren.length) {
filteredStructure.push({
...category,
children: matchedChildren,
})
}
})
structure = filteredStructure
}
return structure
}
const addComponent = () => {}
</script> </script>
<NavigationPanel <NavigationPanel
title="Components" title="Add component"
showAddButton showBackButton
onClickAddButton={() => $goto("../new")} onClickBackButton={() => $goto("../slot")}
wide
> >
<Layout paddingX="L" paddingY="XL" gap="S"> <Layout paddingX="L" paddingY="XL" gap="S">
<ActionGroup compact justified>
<ActionButton
fullWidth
selected={section === "components"}
on:click={() => (section = "components")}>Components</ActionButton
>
<ActionButton
fullWidth
selected={section === "blocks"}
on:click={() => (section = "blocks")}>Blocks</ActionButton
>
</ActionGroup>
<Search <Search
placeholder="Search" placeholder="Search"
value={searchString} value={searchString}
on:change={e => (searchString = e.detail)} on:change={e => (searchString = e.detail)}
/> />
</Layout> </Layout>
{#each filteredStructure as category}
<DetailSummary name={category.name} collapsible={false}>
<div class="component-grid">
{#each category.children as component}
<div class="component" on:click={() => addComponent(component)}>
<Icon name={component.icon} />
<Body size="XS">{component.name}</Body>
</div>
{/each}
</div>
</DetailSummary>
{/each}
</NavigationPanel> </NavigationPanel>
<style>
.component-grid {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: var(--spacing-s);
}
.component {
background-color: var(--spectrum-global-color-gray-200);
border-radius: 4px;
height: 72px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
padding: 0 var(--spacing-s);
gap: var(--spacing-s);
padding-top: 4px;
transition: background 130ms ease-out;
line-height: 1.2;
}
.component:hover {
cursor: pointer;
background: var(--spectrum-alias-background-color-tertiary);
}
</style>

View File

@ -0,0 +1,7 @@
<script>
import ComponentListPanel from "./_components/ComponentListPanel.svelte"
import AppPanel from "components/design/AppPanel/AppPanel.svelte"
</script>
<ComponentListPanel />
<AppPanel />

View File

@ -1 +1,19 @@
Components <script>
import { store } from "builderStore"
import { onMount } from "svelte"
import { redirect } from "@roxi/routify"
let loaded = false
onMount(() => {
if ($store.screens?.length) {
$redirect(`./${$store.screens[0]._id}`)
} else {
loaded = true
}
})
</script>
{#if loaded}
You need to create a screen!
{/if}

View File

@ -43,7 +43,7 @@
> >
<Layout paddingX="L" paddingY="XL" gap="S"> <Layout paddingX="L" paddingY="XL" gap="S">
<Search <Search
placeholder="Enter a route to search" placeholder="Search"
value={searchString} value={searchString}
on:change={e => (searchString = e.detail)} on:change={e => (searchString = e.detail)}
/> />