Refactor routes and start updating core design UI into new components

This commit is contained in:
Andrew Kingston 2022-04-22 16:03:47 +01:00
parent 4f94430c96
commit 03be935e6c
34 changed files with 292 additions and 168 deletions

View File

@ -17,10 +17,13 @@
export let negative = false
export let disabled = false
export let active = false
export let color = null
</script>
<div
class="spectrum-StatusLight spectrum-StatusLight--size{size}"
class:custom={!!color}
style={`--color: ${color};`}
class:spectrum-StatusLight--celery={celery}
class:spectrum-StatusLight--yellow={yellow}
class:spectrum-StatusLight--fuchsia={fuchsia}
@ -39,3 +42,13 @@
>
<slot />
</div>
<style>
.spectrum-StatusLight {
display: grid;
place-items: center;
}
.custom::before {
background: var(--color) !important;
}
</style>

View File

@ -15,7 +15,7 @@ import {
tables,
} from "stores/backend"
import { API } from "api"
import { DesignTabs, FrontendTypes } from "constants"
import { FrontendTypes } from "constants"
import analytics, { Events } from "analytics"
import {
findComponentType,
@ -48,8 +48,6 @@ const INITIAL_FRONTEND_STATE = {
continueIfAction: false,
},
currentFrontEndType: "none",
selectedDesignTab: DesignTabs.SCREENS,
selectedScreenId: "",
selectedLayoutId: "",
selectedComponentId: "",
errors: [],
@ -61,6 +59,9 @@ const INITIAL_FRONTEND_STATE = {
theme: "",
customTheme: {},
previewDevice: "desktop",
// URL params
selectedScreenId: null,
}
export const getFrontendStore = () => {

View File

@ -1,5 +1,5 @@
<script>
import { Icon } from "@budibase/bbui"
import { Icon, StatusLight } from "@budibase/bbui"
import { createEventDispatcher, getContext } from "svelte"
export let icon
@ -13,6 +13,8 @@
export let draggable = false
export let iconText
export let iconColor
export let scrollable = false
export let color
const scrollApi = getContext("scroll")
const dispatch = createEventDispatcher()
@ -43,7 +45,7 @@
class="nav-item"
class:border
class:selected
style={`padding-left: ${20 + indentLevel * 14}px`}
style={`padding-left: ${14 + indentLevel * 14}px`}
{draggable}
on:dragend
on:dragstart
@ -52,6 +54,7 @@
on:click={onClick}
ondragover="return false"
ondragenter="return false"
class:scrollable
>
<div class="nav-item-content" bind:this={contentRef}>
{#if withArrow}
@ -76,6 +79,9 @@
<slot />
</div>
{/if}
{#if color}
<StatusLight size="L" {color} />
{/if}
</div>
</div>
@ -85,9 +91,14 @@
color: var(--grey-7);
transition: background-color
var(--spectrum-global-animation-duration-100, 130ms) ease-in-out;
padding: 0 var(--spacing-m) 0 var(--spacing-xl);
padding: 0 var(--spacing-m) 0 var(--spacing-l);
height: 32px;
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
}
.nav-item.scrollable {
flex-direction: column;
justify-content: center;
align-items: flex-start;
@ -136,10 +147,13 @@
font-weight: 600;
font-size: var(--spectrum-global-dimension-font-size-75);
white-space: nowrap;
max-width: 160px;
overflow: hidden;
text-overflow: ellipsis;
flex: 1 1 auto;
}
.scrollable .text {
flex: 0 0 auto;
max-width: 160px;
}
.actions {

View File

@ -0,0 +1,57 @@
<script>
import ComponentSelectionList from "./ComponentSelectionList.svelte"
import DevicePreviewSelect from "./DevicePreviewSelect.svelte"
import ThemeEditor from "./ThemeEditor.svelte"
import AppThemeSelect from "./AppThemeSelect.svelte"
import AppPreview from "./AppPreview.svelte"
import { store } from "builderStore"
</script>
<div class="app-panel">
<div class="header">
<ComponentSelectionList />
{#if $store.clientFeatures.devicePreview}
<DevicePreviewSelect />
{/if}
{#if $store.clientFeatures.customThemes}
<ThemeEditor />
{:else if $store.clientFeatures.spectrumThemes}
<AppThemeSelect />
{/if}
</div>
<div class="content">
{#key $store.version}
<AppPreview />
{/key}
</div>
</div>
<style>
.app-panel {
flex: 1 1 auto;
overflow-y: auto;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
gap: var(--spacing-m);
padding: var(--spacing-l) 24px;
}
.header {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: flex-start;
gap: 1rem;
margin-left: -6px;
}
.header > :global(*) {
flex: 0 0 auto;
}
.header > :global(*:first-child) {
flex: 1 1 auto;
}
.content {
flex: 1 1 auto;
}
</style>

View File

@ -1,7 +1,7 @@
<script>
import { get } from "svelte/store"
import { onMount, onDestroy } from "svelte"
import { store, currentAsset } from "builderStore"
import { store, currentAsset, allScreens } from "builderStore"
import iframeTemplate from "./iframeTemplate"
import { Screen } from "builderStore/store/screenTemplates/utils/Screen"
import { FrontendTypes } from "constants"
@ -49,13 +49,8 @@
// Extract data to pass to the iframe
$: {
if ($store.currentFrontEndType === FrontendTypes.LAYOUT) {
layout = $currentAsset
screen = screenPlaceholder
} else {
screen = $currentAsset
layout = $store.layouts.find(layout => layout._id === screen?.layoutId)
}
screen = $allScreens.find(x => x._id === $store.selectedScreenId)
layout = $store.layouts.find(layout => layout._id === screen?.layoutId)
}
$: selectedComponentId = $store.selectedComponentId ?? ""
$: previewData = {
@ -68,7 +63,7 @@
customTheme: $store.customTheme,
previewDevice: $store.previewDevice,
messagePassing: $store.clientFeatures.messagePassing,
isBudibaseEvent: true
isBudibaseEvent: true,
}
$: json = JSON.stringify(previewData)

View File

@ -141,9 +141,6 @@
</Modal>
<style>
.container {
padding-right: 8px;
}
.setting {
display: flex;
flex-direction: row;

View File

@ -1 +0,0 @@
export { default } from "./CurrentItemPreview.svelte"

View File

@ -7,9 +7,7 @@
} from "builderStore"
import instantiateStore from "./dragDropStore"
import ComponentTree from "./ComponentTree.svelte"
import NavItem from "components/common/NavItem.svelte"
import PathDropdownMenu from "./PathDropdownMenu.svelte"
import ScreenDropdownMenu from "./ScreenDropdownMenu.svelte"
import { get } from "svelte/store"
const ROUTE_NAME_MAP = {
@ -32,7 +30,6 @@
$: selectedScreen = $currentAsset
$: allScreens = getAllScreens(route)
$: filteredScreens = getFilteredScreens(allScreens, $screenSearchString)
$: hasSearchMatch = $screenSearchString && filteredScreens.length > 0
$: noSearchMatch = $screenSearchString && !filteredScreens.length
$: routeSelected =
@ -43,22 +40,6 @@
store.actions.screens.select(screenId)
}
const getAllScreens = route => {
let screens = []
Object.entries(route.subpaths).forEach(([route, subpath]) => {
Object.entries(subpath.screens).forEach(([role, id]) => {
screens.push({ id, route, role })
})
})
return screens
}
const getFilteredScreens = (screens, searchString) => {
return screens.filter(
screen => !searchString || screen.route.includes(searchString)
)
}
const toggleManuallyOpened = () => {
if (get(screenSearchString)) {
return
@ -79,28 +60,5 @@
<PathDropdownMenu screens={allScreens} {path} />
</NavItem>
{#if routeOpened}
{#each filteredScreens as screen (screen.id)}
<NavItem
icon="WebPage"
indentLevel={indent || 1}
selected={$store.selectedScreenId === screen.id &&
$store.currentView === "detail"}
opened={$store.selectedScreenId === screen.id}
text={ROUTE_NAME_MAP[screen.route]?.[screen.role] || screen.route}
withArrow={route.subpaths}
on:click={() => changeScreen(screen.id)}
>
<ScreenDropdownMenu screenId={screen.id} />
</NavItem>
{#if selectedScreen?._id === screen.id}
<ComponentTree
level={1}
components={selectedScreen.props._children}
currentComponent={$selectedComponent}
{dragDropStore}
/>
{/if}
{/each}
{/if}
{#if routeOpened}{/if}
{/if}

View File

@ -131,21 +131,7 @@
<Tabs {selected} on:select={navigate}>
<Tab title="Screens">
<div class="tab-content-padding">
<BBUILayout noPadding gap="XS">
<Select
on:change={updateAccessRole}
value={$selectedAccessRole}
label="Filter by Access"
getOptionLabel={role => role.name}
getOptionValue={role => role._id}
options={$roles}
/>
<Search
placeholder="Enter a route to search"
label="Search Screens"
bind:value={$screenSearchString}
/>
</BBUILayout>
<BBUILayout noPadding gap="XS" />
<div class="nav-items-container" bind:this={scrollRef}>
<ComponentNavigationTree />
</div>

View File

@ -0,0 +1,70 @@
<script>
import { Icon, Heading } from "@budibase/bbui"
export let title
export let showAddButton
export let onClickAddButton
</script>
<div class="navigation-panel">
<div class="header">
<div class="title">
<Heading size="XS">{title || ""}</Heading>
</div>
{#if showAddButton}
<div class="add-button">
<Icon name="Add" on:click={onClickAddButton} />
</div>
{/if}
</div>
<slot />
</div>
<style>
.navigation-panel {
width: 260px;
background: var(--background);
border-right: var(--border-light);
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
}
.header {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: var(--spacing-m) var(--spacing-l);
border-bottom: var(--border-light);
gap: var(--spacing-l);
}
.title {
flex: 1 1 auto;
width: 0;
}
.title :global(h1) {
overflow: hidden;
font-weight: 600;
text-overflow: ellipsis;
white-space: nowrap;
}
.add-button {
flex: 0 0 32px;
height: 32px;
display: grid;
place-items: center;
border-radius: 4px;
position: relative;
cursor: pointer;
background: var(--spectrum-semantic-cta-color-background-default);
transition: background var(--spectrum-global-animation-duration-100, 130ms)
ease-out;
}
.add-button:hover {
background: var(--spectrum-semantic-cta-color-background-hover);
}
.add-button :global(svg) {
fill: white;
}
</style>

View File

@ -1,13 +1,14 @@
import { Roles } from "./backend"
export const TableNames = {
USERS: "ta_users",
}
export const DesignTabs = {
SCREENS: "screens",
COMPONENTS: "components",
THEME: "theme",
NAVIGATION: "navigation",
LAYOUTS: "layouts",
export const RoleColours = {
[Roles.ADMIN]: "var(--spectrum-global-color-static-seafoam-400)",
[Roles.POWER]: "var(--spectrum-global-color-static-purple-400)",
[Roles.BASIC]: "var(--spectrum-global-color-static-magenta-400)",
[Roles.PUBLIC]: "var(--spectrum-global-color-static-yellow-400)",
}
export const FrontendTypes = {

View File

@ -55,7 +55,7 @@ export const syncURLToState = options => {
// Updates the URL with new state values
const mapStateToUrl = state => {
// Determine new URL while checking for changes
let url = "."
let url = ".."
let needsUpdate = false
for (let key of keys) {
const urlValue = cachedParams?.[key.url]

View File

@ -1,23 +1,15 @@
<script>
import { IconSideNav, IconSideNavItem } from "@budibase/bbui"
import { params, goto } from "@roxi/routify"
import { DesignTabs } from "constants"
import { params, goto, isActive } from "@roxi/routify"
import { store } from "builderStore"
import { syncURLToState } from "helpers/urlStateSync"
import { onDestroy } from "svelte"
const updateTab = tab => {
store.update(state => {
state.selectedDesignTab = tab
return state
})
}
const unsync = syncURLToState({
keys: [
{
url: "tab",
state: "selectedDesignTab",
url: "screenId",
state: "selectedScreenId",
},
],
store,
@ -30,37 +22,37 @@
<!-- routify:options index=1 -->
<div class="design">
<div class="side-nav">
<div class="icon-nav">
<IconSideNav>
<IconSideNavItem
icon="WebPage"
tooltip="Screens"
active={$store.selectedDesignTab === DesignTabs.SCREENS}
on:click={() => updateTab(DesignTabs.SCREENS)}
active={$isActive("./screens")}
on:click={() => $goto("./screens")}
/>
<IconSideNavItem
icon="ViewList"
tooltip="Components"
active={$store.selectedDesignTab === DesignTabs.COMPONENTS}
on:click={() => updateTab(DesignTabs.COMPONENTS)}
active={$isActive("./components")}
on:click={() => $goto("./components")}
/>
<IconSideNavItem
icon="Brush"
tooltip="Theme"
active={$store.selectedDesignTab === DesignTabs.THEME}
on:click={() => updateTab(DesignTabs.THEME)}
active={$isActive("./theme")}
on:click={() => $goto("./theme")}
/>
<IconSideNavItem
icon="Link"
tooltip="Navigation"
active={$store.selectedDesignTab === DesignTabs.NAVIGATION}
on:click={() => updateTab(DesignTabs.NAVIGATION)}
active={$isActive("./navigation")}
on:click={() => $goto("./navigation")}
/>
<IconSideNavItem
icon="Experience"
tooltip="Layouts"
active={$store.selectedDesignTab === DesignTabs.LAYOUTS}
on:click={() => updateTab(DesignTabs.LAYOUTS)}
active={$isActive("./layouts")}
on:click={() => $goto("./layouts")}
/>
</IconSideNav>
</div>
@ -77,12 +69,14 @@
grid-template-columns: auto 1fr;
align-items: stretch;
}
.side-nav {
.icon-nav {
background: var(--background);
border-right: var(--border-light);
}
.content {
display: flex;
flex-direction: column;
flex-direction: row;
justify-content: flex-start;
align-items: stretch;
}
</style>

View File

@ -0,0 +1,5 @@
<script>
import { redirect } from "@roxi/routify"
$redirect(`./screens`)
</script>

View File

@ -10,7 +10,7 @@
Helpers,
notifications,
} from "@budibase/bbui"
import ScreenDetailsModal from "../ScreenDetailsModal.svelte"
import ScreenDetailsModal from "./ScreenDetailsModal.svelte"
import sanitizeUrl from "builderStore/store/screenTemplates/utils/sanitizeUrl"
import analytics, { Events } from "analytics"
import { makeComponentUnique } from "builderStore/componentUtils"

View File

@ -156,24 +156,7 @@
</div>
<div class="preview-pane">
{#if $currentAsset}
<div class="preview-header">
<ComponentSelectionList />
{#if $store.clientFeatures.devicePreview}
<DevicePreviewSelect />
{/if}
{#if $store.clientFeatures.customThemes}
<ThemeEditor />
{:else if $store.clientFeatures.spectrumThemes}
<AppThemeSelect />
{/if}
</div>
<div class="preview-content">
{#key $store.version}
<CurrentItemPreview />
{/key}
</div>
{:else}
{#if $currentAsset}{:else}
<div class="centered">
<div class="main">
<Layout gap="S" justifyItems="center">
@ -247,34 +230,6 @@
border-right: var(--border-light);
}
.preview-pane {
grid-column: 2;
overflow-y: auto;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
gap: var(--spacing-m);
padding: var(--spacing-xl) 40px;
}
.preview-header {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: flex-start;
gap: 1rem;
}
.preview-header > :global(*) {
flex: 0 0 auto;
}
.preview-header > :global(*:first-child) {
flex: 1 1 auto;
}
.preview-content {
flex: 1 1 auto;
}
.components-pane {
grid-column: 3;
background-color: var(--background);

View File

@ -0,0 +1,69 @@
<script>
import { Search, Layout, Select } from "@budibase/bbui"
import NavigationPanel from "components/design/NavigationPanel/NavigationPanel.svelte"
import { roles } from "stores/backend"
import { store, allScreens } from "builderStore"
import NavItem from "components/common/NavItem.svelte"
import ScreenDropdownMenu from "./_components/ScreenDropdownMenu.svelte"
import AppPanel from "components/design/AppPanel/AppPanel.svelte"
import { RoleColours } from "constants"
let searchString
let accessRole
$: filteredScreens = getFilteredScreens($allScreens, searchString)
const getFilteredScreens = (screens, searchString) => {
return screens
.filter(
screen => !searchString || screen.routing.route.includes(searchString)
)
.slice()
.sort((a, b) => {
return a.routing.route < b.routing.route ? -1 : 1
})
}
const getRoleColor = roleId => {
return RoleColours[roleId] || "pink"
}
</script>
<NavigationPanel title="Screens" showAddButton onClickAddButton>
<Layout paddingX="L" paddingY="XL" gap="S">
<Search
placeholder="Enter a route to search"
value={searchString}
on:change={e => (searchString = e.detail)}
/>
<Select
bind:value={accessRole}
placeholder="All screens"
getOptionLabel={role => role.name}
getOptionValue={role => role._id}
options={$roles}
/>
</Layout>
{#each filteredScreens as screen (screen._id)}
<NavItem
icon={screen.routing.route === "/" ? "Home" : "WebPage"}
indentLevel={0}
selected={$store.selectedScreenId === screen._id}
text={screen.routing.route}
on:click={() => ($store.selectedScreenId = screen._id)}
color={getRoleColor(screen.routing.roleId)}
>
<ScreenDropdownMenu screenId={screen._id} />
</NavItem>
<!--{#if selectedScreen?._id === screen.id}-->
<!-- <ComponentTree-->
<!-- level={1}-->
<!-- components={selectedScreen.props._children}-->
<!-- currentComponent={$selectedComponent}-->
<!-- {dragDropStore}-->
<!-- />-->
<!--{/if}-->
{/each}
</NavigationPanel>
<AppPanel />

View File

@ -1,2 +0,0 @@
<!-- routify:options index=1 -->
<slot />

View File

@ -1,6 +0,0 @@
<script>
import { goto } from "@roxi/routify"
import { FrontendTypes } from "constants"
$goto(`./${FrontendTypes.SCREEN}`)
</script>

View File

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

View File

@ -276,7 +276,7 @@
.preview #app-root {
border: 1px solid var(--spectrum-global-color-gray-300);
border-radius: 4px;
border-radius: 2px;
}
/* Print styles */