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:
parent
a3992acfa8
commit
aa71e95597
|
@ -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
|
||||
|
|
|
@ -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
|
||||
})
|
||||
},
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -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)}>
|
||||
|
|
|
@ -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 (roleId === selectedRoleId) {
|
||||
if (screenId === selectedScreenId) {
|
||||
screenRoleId = roleId
|
||||
found = roleId === selectedRoleId
|
||||
}
|
||||
if (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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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}
|
||||
<Modal bind:this={modal}>
|
||||
<NewScreenModal />
|
||||
</Modal>
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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(
|
||||
const assetChildren =
|
||||
assetList.find(
|
||||
asset =>
|
||||
asset._id === $params.asset ||
|
||||
asset._id === decodeURIComponent($params.asset)
|
||||
).props._children
|
||||
)?.props._children ?? []
|
||||
findComponent(componentIds, assetChildren)
|
||||
}
|
||||
// }
|
||||
|
|
|
@ -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 />
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
Loading…
Reference in New Issue