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

View File

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

View File

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

View File

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

View File

@ -9,21 +9,23 @@
const allRoutes = $store.routes
const sortedPaths = Object.keys(allRoutes).sort()
const selectedRoleId = $selectedAccessRole
const selectedScreenId = $store.currentAssetId
const selectedScreenId = $store.selectedScreenId
let found = false
let firstValidScreenId
let filteredRoutes = {}
let screenRoleId
// Filter all routes down to only those which match the current role
sortedPaths.forEach(path => {
const config = allRoutes[path]
Object.entries(config.subpaths).forEach(([subpath, pathConfig]) => {
Object.entries(pathConfig.screens).forEach(([roleId, screenId]) => {
if (screenId === selectedScreenId) {
screenRoleId = roleId
found = roleId === selectedRoleId
}
if (roleId === selectedRoleId) {
if (screenId === selectedScreenId) {
found = roleId === selectedRoleId
}
if (!firstValidScreenId) {
firstValidScreenId = screenId
}
@ -41,8 +43,13 @@
})
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 (!found && firstValidScreenId) {
else if (!found && firstValidScreenId) {
store.actions.screens.select(firstValidScreenId)
}
}

View File

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

View File

@ -3,6 +3,7 @@
import { goto, params, url } from "@sveltech/routify"
import {
store,
allScreens,
currentAsset,
backendUiStore,
selectedAccessRole,
@ -29,11 +30,38 @@
let routes = {}
let tab = $params.assetType
function navigate({ detail }) {
if (!detail) return
const navigate = ({ detail }) => {
if (!detail) {
return
}
$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(() => {
store.actions.routing.fetch()
})
@ -46,24 +74,21 @@
on:click={modal.show}
data-cy="new-screen"
class="ri-add-circle-fill" />
<div class="role-select">
<Select
extraThin
secondary
bind:value={$selectedAccessRole}
on:change={updateAccessRole}
value={$selectedAccessRole}
label="Filter by Access">
{#each $backendUiStore.roles as role}
<option value={role._id}>{role.name}</option>
{/each}
</Select>
</div>
{#if $currentAsset}
<div class="nav-items-container">
<ComponentNavigationTree />
</div>
{/if}
<div class="nav-items-container">
<ComponentNavigationTree />
</div>
<Modal bind:this={modal}>
<NewScreenModal />
</Modal>

View File

@ -28,13 +28,13 @@
icon="ri-layout-3-line"
text={layout.name}
withArrow
selected={$store.currentAssetId === layout._id}
opened={$store.currentAssetId === layout._id}
selected={$store.selectedLayoutId === layout._id}
opened={$store.selectedLayoutId === layout._id}
on:click={selectLayout}>
<LayoutDropdownMenu {layout} />
</NavItem>
{#if $store.currentAssetId === layout._id && layout.props?._children}
{#if $store.selectedLayoutId === layout._id && layout.props?._children}
<ComponentTree
components={layout.props._children}
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.
if ($leftover) {
// Get the correct screen children.
const assetChildren = assetList.find(
asset =>
asset._id === $params.asset ||
asset._id === decodeURIComponent($params.asset)
).props._children
const assetChildren =
assetList.find(
asset =>
asset._id === $params.asset ||
asset._id === decodeURIComponent($params.asset)
)?.props._children ?? []
findComponent(componentIds, assetChildren)
}
// }

View File

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

View File

@ -5,12 +5,32 @@
// Go to first 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
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>