Allow independent selection of screen IDs and layout IDs so that selections persist between toggling tabs. Update logic around role selection and screen filtering

This commit is contained in:
Andrew Kingston 2020-12-14 11:14:16 +00:00
parent cb7b5ae306
commit 8b352fe40a
11 changed files with 124 additions and 60 deletions

View File

@ -4,7 +4,7 @@ import { getAutomationStore } from "./store/automation/"
import { getThemeStore } from "./store/theme" import { getThemeStore } from "./store/theme"
import { derived, writable } from "svelte/store" import { derived, writable } from "svelte/store"
import analytics from "analytics" import analytics from "analytics"
import { LAYOUT_NAMES } from "../constants" import { FrontendTypes, LAYOUT_NAMES } from "../constants"
import { makePropsSafe } from "components/userInterface/assetParsing/createProps" import { makePropsSafe } from "components/userInterface/assetParsing/createProps"
export const store = getFrontendStore() export const store = getFrontendStore()
@ -13,18 +13,12 @@ export const automationStore = getAutomationStore()
export const themeStore = getThemeStore() export const themeStore = getThemeStore()
export const currentAsset = derived(store, $store => { export const currentAsset = derived(store, $store => {
const layout = $store.layouts const type = $store.currentFrontEndType
? $store.layouts.find(layout => layout._id === $store.currentAssetId) if (type === FrontendTypes.SCREEN) {
: null return $store.screens.find(screen => screen._id === $store.selectedScreenId)
} else if (type === FrontendTypes.LAYOUT) {
if (layout) return layout return $store.layouts.find(layout => layout._id === $store.selectedLayoutId)
}
const screen = $store.screens
? $store.screens.find(screen => screen._id === $store.currentAssetId)
: null
if (screen) return screen
return null return null
}) })
@ -59,8 +53,14 @@ export const selectedComponent = derived(
} }
) )
export const currentAssetName = derived(store, () => { export const currentAssetId = derived(store, $store => {
return currentAsset.name return $store.currentFrontEndType === FrontendTypes.SCREEN
? $store.selectedScreenId
: $store.selectedLayoutId
})
export const currentAssetName = derived(currentAsset, $currentAsset => {
return $currentAsset?.name
}) })
// leave this as before for consistency // leave this as before for consistency

View File

@ -32,7 +32,8 @@ const INITIAL_FRONTEND_STATE = {
screens: [], screens: [],
components: [], components: [],
currentFrontEndType: "none", currentFrontEndType: "none",
currentAssetId: "", selectedScreenId: "",
selectedLayoutId: "",
selectedComponentId: "", selectedComponentId: "",
errors: [], errors: [],
hasAppPackage: false, hasAppPackage: false,
@ -89,8 +90,13 @@ export const getFrontendStore = () => {
let screen = let screen =
screens.find(screen => screen._id === screenId) || screens[0] screens.find(screen => screen._id === screenId) || screens[0]
if (!screen) return state if (!screen) return state
// Update role to the screen's role setting so that it will always
// be visible
selectedAccessRole.set(screen.routing.roleId)
state.currentFrontEndType = FrontendTypes.SCREEN state.currentFrontEndType = FrontendTypes.SCREEN
state.currentAssetId = screen._id state.selectedScreenId = screen._id
state.currentView = "detail" state.currentView = "detail"
state.selectedComponentId = screen.props?._id state.selectedComponentId = screen.props?._id
return state return state
@ -99,7 +105,7 @@ export const getFrontendStore = () => {
create: async screen => { create: async screen => {
screen = await store.actions.screens.save(screen) screen = await store.actions.screens.save(screen)
store.update(state => { store.update(state => {
state.currentAssetId = screen._id state.selectedScreenId = screen._id
state.selectedComponentId = screen.props._id state.selectedComponentId = screen.props._id
state.currentFrontEndType = FrontendTypes.SCREEN state.currentFrontEndType = FrontendTypes.SCREEN
selectedAccessRole.set(screen.routing.roleId) selectedAccessRole.set(screen.routing.roleId)
@ -144,8 +150,8 @@ export const getFrontendStore = () => {
`/api/screens/${screenToDelete._id}/${screenToDelete._rev}` `/api/screens/${screenToDelete._id}/${screenToDelete._rev}`
) )
) )
if (screenToDelete._id === state.currentAssetId) { if (screenToDelete._id === state.selectedScreenId) {
state.currentAssetId = "" state.selectedScreenId = null
} }
} }
return state return state
@ -168,11 +174,12 @@ export const getFrontendStore = () => {
layouts: { layouts: {
select: layoutId => { select: layoutId => {
store.update(state => { store.update(state => {
const layout = store.actions.layouts.find(layoutId) const layout =
store.actions.layouts.find(layoutId) || get(store).layouts[0]
if (!layout) return if (!layout) return
state.currentFrontEndType = FrontendTypes.LAYOUT state.currentFrontEndType = FrontendTypes.LAYOUT
state.currentView = "detail" state.currentView = "detail"
state.currentAssetId = layout._id state.selectedLayoutId = layout._id
state.selectedComponentId = layout.props?._id state.selectedComponentId = layout.props?._id
return state return state
}) })
@ -215,16 +222,17 @@ export const getFrontendStore = () => {
const response = await api.delete( const response = await api.delete(
`/api/layouts/${layoutToDelete._id}/${layoutToDelete._rev}` `/api/layouts/${layoutToDelete._id}/${layoutToDelete._rev}`
) )
if (response.status !== 200) { if (response.status !== 200) {
const json = await response.json() const json = await response.json()
throw new Error(json.message) throw new Error(json.message)
} }
store.update(state => { store.update(state => {
state.layouts = state.layouts.filter( state.layouts = state.layouts.filter(
layout => layout._id !== layoutToDelete._id layout => layout._id !== layoutToDelete._id
) )
if (layoutToDelete._id === state.selectedLayoutId) {
state.selectedLayoutId = get(mainLayout)._id
}
return state return state
}) })
}, },

View File

@ -1,6 +1,6 @@
<script> <script>
import { goto } from "@sveltech/routify" import { goto } from "@sveltech/routify"
import { store, currentAsset } from "builderStore" import { store, currentAssetId } from "builderStore"
import { getComponentDefinition } from "builderStore/storeUtils" import { getComponentDefinition } from "builderStore/storeUtils"
import { DropEffect, DropPosition } from "./dragDropStore" import { DropEffect, DropPosition } from "./dragDropStore"
import ComponentDropdownMenu from "../ComponentDropdownMenu.svelte" import ComponentDropdownMenu from "../ComponentDropdownMenu.svelte"
@ -22,7 +22,7 @@
const path = store.actions.components.findRoute(component) const path = store.actions.components.findRoute(component)
// Go to correct URL // Go to correct URL
$goto(`./${$store.currentAssetId}/${path}`) $goto(`./${$currentAssetId}/${path}`)
} }
const dragstart = component => e => { const dragstart = component => e => {

View File

@ -1,5 +1,4 @@
<script> <script>
import { writable } from "svelte/store"
import { goto } from "@sveltech/routify" import { goto } from "@sveltech/routify"
import { store, selectedComponent, currentAsset } from "builderStore" import { store, selectedComponent, currentAsset } from "builderStore"
import instantiateStore from "./dragDropStore" import instantiateStore from "./dragDropStore"
@ -43,8 +42,8 @@
<NavItem <NavItem
icon="ri-artboard-2-line" icon="ri-artboard-2-line"
indentLevel={indent || 1} indentLevel={indent || 1}
selected={$store.currentAssetId === screenId} selected={$store.selectedScreenId === screenId}
opened={$store.currentAssetId === screenId} opened={$store.selectedScreenId === screenId}
text={ROUTE_NAME_MAP[url]?.[role] || url} text={ROUTE_NAME_MAP[url]?.[role] || url}
withArrow={route.subpaths} withArrow={route.subpaths}
on:click={() => changeScreen(screenId)}> on:click={() => changeScreen(screenId)}>

View File

@ -9,21 +9,23 @@
const allRoutes = $store.routes const allRoutes = $store.routes
const sortedPaths = Object.keys(allRoutes).sort() const sortedPaths = Object.keys(allRoutes).sort()
const selectedRoleId = $selectedAccessRole const selectedRoleId = $selectedAccessRole
const selectedScreenId = $store.currentAssetId const selectedScreenId = $store.selectedScreenId
let found = false let found = false
let firstValidScreenId let firstValidScreenId
let filteredRoutes = {} let filteredRoutes = {}
let screenRoleId
// Filter all routes down to only those which match the current role // Filter all routes down to only those which match the current role
sortedPaths.forEach(path => { sortedPaths.forEach(path => {
const config = allRoutes[path] const config = allRoutes[path]
Object.entries(config.subpaths).forEach(([subpath, pathConfig]) => { Object.entries(config.subpaths).forEach(([subpath, pathConfig]) => {
Object.entries(pathConfig.screens).forEach(([roleId, screenId]) => { Object.entries(pathConfig.screens).forEach(([roleId, screenId]) => {
if (screenId === selectedScreenId) {
screenRoleId = roleId
found = roleId === selectedRoleId
}
if (roleId === selectedRoleId) { if (roleId === selectedRoleId) {
if (screenId === selectedScreenId) {
found = roleId === selectedRoleId
}
if (!firstValidScreenId) { if (!firstValidScreenId) {
firstValidScreenId = screenId firstValidScreenId = screenId
} }
@ -41,8 +43,13 @@
}) })
routes = filteredRoutes routes = filteredRoutes
// Select the correct role for the current screen ID
if (!found && screenRoleId) {
selectedAccessRole.set(screenRoleId)
}
// If the selected screen isn't in this filtered list, select the first one // If the selected screen isn't in this filtered list, select the first one
if (!found && firstValidScreenId) { else if (!found && firstValidScreenId) {
store.actions.screens.select(firstValidScreenId) store.actions.screens.select(firstValidScreenId)
} }
} }

View File

@ -1,6 +1,11 @@
<script> <script>
import { goto, url } from "@sveltech/routify" import { goto } from "@sveltech/routify"
import { store, currentAssetName, selectedComponent } from "builderStore" import {
store,
currentAssetName,
selectedComponent,
currentAssetId,
} from "builderStore"
import components from "./temporaryPanelStructure.js" import components from "./temporaryPanelStructure.js"
import { DropdownMenu } from "@budibase/bbui" import { DropdownMenu } from "@budibase/bbui"
import { DropdownContainer, DropdownItem } from "components/common/Dropdowns" import { DropdownContainer, DropdownItem } from "components/common/Dropdowns"
@ -27,7 +32,7 @@
const onComponentChosen = component => { const onComponentChosen = component => {
store.actions.components.create(component._component, component.presetProps) store.actions.components.create(component._component, component.presetProps)
const path = store.actions.components.findRoute($selectedComponent) const path = store.actions.components.findRoute($selectedComponent)
$goto(`./${$store.currentAssetId}/${path}`) $goto(`./${$currentAssetId}/${path}`)
close() close()
} }
</script> </script>

View File

@ -3,6 +3,7 @@
import { goto, params, url } from "@sveltech/routify" import { goto, params, url } from "@sveltech/routify"
import { import {
store, store,
allScreens,
currentAsset, currentAsset,
backendUiStore, backendUiStore,
selectedAccessRole, selectedAccessRole,
@ -29,11 +30,38 @@
let routes = {} let routes = {}
let tab = $params.assetType let tab = $params.assetType
function navigate({ detail }) { const navigate = ({ detail }) => {
if (!detail) return if (!detail) {
return
}
$goto(`../${detail.heading.key}`) $goto(`../${detail.heading.key}`)
} }
const updateAccessRole = event => {
const role = event.target.value
// Select a valid screen with this new role - otherwise we'll not be
// able to change role at all because ComponentNavigationTree will kick us
// back the current role again because the same screen ID is still selected
const firstValidScreenId = $allScreens.find(
screen => screen.routing.roleId === role
)?._id
if (firstValidScreenId) {
store.actions.screens.select(firstValidScreenId)
}
// Otherwise clear the selected screen ID so that the first new valid screen
// can be selected by ComponentNavigationTree
else {
store.update(state => {
state.selectedScreenId = null
return state
})
}
selectedAccessRole.set(role)
}
onMount(() => { onMount(() => {
store.actions.routing.fetch() store.actions.routing.fetch()
}) })
@ -46,24 +74,21 @@
on:click={modal.show} on:click={modal.show}
data-cy="new-screen" data-cy="new-screen"
class="ri-add-circle-fill" /> class="ri-add-circle-fill" />
<div class="role-select"> <div class="role-select">
<Select <Select
extraThin extraThin
secondary secondary
bind:value={$selectedAccessRole} on:change={updateAccessRole}
value={$selectedAccessRole}
label="Filter by Access"> label="Filter by Access">
{#each $backendUiStore.roles as role} {#each $backendUiStore.roles as role}
<option value={role._id}>{role.name}</option> <option value={role._id}>{role.name}</option>
{/each} {/each}
</Select> </Select>
</div> </div>
<div class="nav-items-container">
{#if $currentAsset} <ComponentNavigationTree />
<div class="nav-items-container"> </div>
<ComponentNavigationTree />
</div>
{/if}
<Modal bind:this={modal}> <Modal bind:this={modal}>
<NewScreenModal /> <NewScreenModal />
</Modal> </Modal>

View File

@ -28,13 +28,13 @@
icon="ri-layout-3-line" icon="ri-layout-3-line"
text={layout.name} text={layout.name}
withArrow withArrow
selected={$store.currentAssetId === layout._id} selected={$store.selectedLayoutId === layout._id}
opened={$store.currentAssetId === layout._id} opened={$store.selectedLayoutId === layout._id}
on:click={selectLayout}> on:click={selectLayout}>
<LayoutDropdownMenu {layout} /> <LayoutDropdownMenu {layout} />
</NavItem> </NavItem>
{#if $store.currentAssetId === layout._id && layout.props?._children} {#if $store.selectedLayoutId === layout._id && layout.props?._children}
<ComponentTree <ComponentTree
components={layout.props._children} components={layout.props._children}
currentComponent={$selectedComponent} currentComponent={$selectedComponent}

View File

@ -26,11 +26,12 @@
// There are leftover stuff, like IDs, so navigate the components and find the ID and select it. // There are leftover stuff, like IDs, so navigate the components and find the ID and select it.
if ($leftover) { if ($leftover) {
// Get the correct screen children. // Get the correct screen children.
const assetChildren = assetList.find( const assetChildren =
asset => assetList.find(
asset._id === $params.asset || asset =>
asset._id === decodeURIComponent($params.asset) asset._id === $params.asset ||
).props._children asset._id === decodeURIComponent($params.asset)
)?.props._children ?? []
findComponent(componentIds, assetChildren) findComponent(componentIds, assetChildren)
} }
// } // }

View File

@ -1,11 +1,10 @@
<script> <script>
import { store, backendUiStore } from "builderStore" import { store, backendUiStore, currentAsset } from "builderStore"
import { onMount } from "svelte" import { onMount } from "svelte"
import { FrontendTypes } from "constants" import { FrontendTypes } from "constants"
import CurrentItemPreview from "components/userInterface/AppPreview" import CurrentItemPreview from "components/userInterface/AppPreview"
import ComponentPropertiesPanel from "components/userInterface/ComponentPropertiesPanel.svelte" import ComponentPropertiesPanel from "components/userInterface/ComponentPropertiesPanel.svelte"
import ComponentSelectionList from "components/userInterface/ComponentSelectionList.svelte" import ComponentSelectionList from "components/userInterface/ComponentSelectionList.svelte"
import { last } from "lodash/fp"
import FrontendNavigatePane from "components/userInterface/FrontendNavigatePane.svelte" import FrontendNavigatePane from "components/userInterface/FrontendNavigatePane.svelte"
$: instance = $store.appInstance $: instance = $store.appInstance
@ -36,7 +35,7 @@
</div> </div>
<div class="preview-pane"> <div class="preview-pane">
{#if $store.currentAssetId && $store.currentAssetId.length > 0} {#if $currentAsset}
<ComponentSelectionList /> <ComponentSelectionList />
<div class="preview-content"> <div class="preview-content">
<CurrentItemPreview /> <CurrentItemPreview />

View File

@ -5,12 +5,32 @@
// Go to first layout // Go to first layout
if ($params.assetType === FrontendTypes.LAYOUT) { if ($params.assetType === FrontendTypes.LAYOUT) {
$goto(`../${$store.layouts[0]?._id}`) // Try to use previously selected layout first
let id
if (
$store.selectedLayoutId &&
$store.layouts.find(layout => layout._id === $store.selectedLayoutId)
) {
id = $store.selectedLayoutId
} else {
id = $store.layouts[0]?._id
}
$goto(`../${id}`)
} }
// Go to first screen // Go to first screen
if ($params.assetType === FrontendTypes.SCREEN) { if ($params.assetType === FrontendTypes.SCREEN) {
$goto(`../${$allScreens[0]?._id}`) // Try to use previously selected layout first
let id
if (
$store.selectedScreenId &&
$allScreens.find(screen => screen._id === $store.selectedScreenId)
) {
id = $store.selectedScreenId
} else {
id = $allScreens[0]?._id
}
$goto(`../${id}`)
} }
</script> </script>