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
cb7b5ae306
commit
8b352fe40a
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
|
@ -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 => {
|
||||||
|
|
|
@ -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)}>
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
// }
|
// }
|
||||||
|
|
|
@ -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 />
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue