Add screen settings panel

This commit is contained in:
Andrew Kingston 2022-04-25 19:59:30 +01:00
parent 1e59576a30
commit 877791970a
11 changed files with 230 additions and 106 deletions

View File

@ -13,14 +13,7 @@ export const selectedScreen = derived(store, $store => {
return $store.screens.find(screen => screen._id === $store.selectedScreenId) return $store.screens.find(screen => screen._id === $store.selectedScreenId)
}) })
export const currentAsset = derived(store, $store => { export const currentAsset = selectedScreen
const type = $store.currentFrontEndType
if (type === FrontendTypes.SCREEN) {
} else if (type === FrontendTypes.LAYOUT) {
return $store.layouts.find(layout => layout._id === $store.selectedLayoutId)
}
return null
})
export const selectedComponent = derived( export const selectedComponent = derived(
[store, currentAsset], [store, currentAsset],

View File

@ -46,7 +46,7 @@
class:border class:border
class:selected class:selected
class:withActions class:withActions
style={`padding-left: ${14 + indentLevel * 14}px`} style={`padding-left: calc(var(--spacing-l) + ${indentLevel * 14}px)`}
{draggable} {draggable}
on:dragend on:dragend
on:dragstart on:dragstart

View File

@ -31,7 +31,7 @@
align-items: stretch; align-items: stretch;
} }
.header { .header {
height: 55px; height: 48px;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
@ -51,8 +51,8 @@
white-space: nowrap; white-space: nowrap;
} }
.add-button { .add-button {
flex: 0 0 32px; flex: 0 0 30px;
height: 32px; height: 30px;
display: grid; display: grid;
place-items: center; place-items: center;
border-radius: 4px; border-radius: 4px;

View File

@ -108,19 +108,4 @@
] ]
</script> </script>
{#if $store.currentView !== "component" && $currentAsset && $store.currentFrontEndType === FrontendTypes.SCREEN} {#if $store.currentView !== "component" && $currentAsset && $store.currentFrontEndType === FrontendTypes.SCREEN}{/if}
<DetailSummary name="Screen" collapsible={false}>
{#each screenSettings as def (`${componentInstance._id}-${def.key}`)}
<PropertyControl
control={def.control}
label={def.label}
key={def.key}
error="asdasds"
value={deepGet($currentAsset, def.key)}
onChange={val => setAssetProps(def.key, val, def.parser, def.validate)}
{bindings}
props={{ error: errors[def.key] }}
/>
{/each}
</DetailSummary>
{/if}

View File

@ -30,7 +30,7 @@
align-items: stretch; align-items: stretch;
} }
.header { .header {
height: 55px; height: 50px;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: flex-start; justify-content: flex-start;

View File

@ -1,45 +1,11 @@
<script> <script>
import { Search, Layout, Select } from "@budibase/bbui" import { store } from "builderStore"
import NavigationPanel from "components/design/NavigationPanel/NavigationPanel.svelte"
import { roles } from "stores/backend"
import { store, selectedScreen } from "builderStore"
import NavItem from "components/common/NavItem.svelte"
import ScreenDropdownMenu from "./_components/ScreenDropdownMenu.svelte"
import { RoleColours } from "constants"
import ScreenWizard from "./_components/ScreenWizard.svelte"
import { onDestroy } from "svelte" import { onDestroy } from "svelte"
import { syncURLToState } from "helpers/urlStateSync" import { syncURLToState } from "helpers/urlStateSync"
import { goto, params, redirect } from "@roxi/routify" import { goto, params, redirect } from "@roxi/routify"
import AppPanel from "components/design/AppPanel/AppPanel.svelte" import AppPanel from "components/design/AppPanel/AppPanel.svelte"
import SettingsPanel from "components/design/SettingsPanel/SettingsPanel.svelte" import ScreenNavigationPanel from "./_components/ScreenNavigationPanel.svelte"
import ScreenSettingsPanel from "./_components/ScreenSettingsPanel.svelte"
let searchString
let accessRole = "all"
let showNewScreenModal
$: filteredScreens = getFilteredScreens(
$store.screens,
searchString,
accessRole
)
const getFilteredScreens = (screens, search, role) => {
return screens
.filter(screen => {
const searchMatch = !search || screen.routing.route.includes(search)
const roleMatch =
!role || role === "all" || screen.routing.roleId === role
return searchMatch && roleMatch
})
.slice()
.sort((a, b) => {
return a.routing.route < b.routing.route ? -1 : 1
})
}
const getRoleColor = roleId => {
return RoleColours[roleId] || "pink"
}
// Keep URL and state in sync for selected screen ID // Keep URL and state in sync for selected screen ID
const stopSyncing = syncURLToState({ const stopSyncing = syncURLToState({
@ -60,44 +26,6 @@
onDestroy(stopSyncing) onDestroy(stopSyncing)
</script> </script>
<NavigationPanel <ScreenNavigationPanel />
title="Screens"
showAddButton
onClickAddButton={showNewScreenModal}
>
<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={null}
getOptionLabel={role => role.name}
getOptionValue={role => role._id}
options={[{ name: "All screens", _id: "all" }, ...$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>
{/each}
</NavigationPanel>
<AppPanel /> <AppPanel />
<ScreenSettingsPanel />
<SettingsPanel
title={$selectedScreen?.routing.route}
icon={$selectedScreen.routing.route === "/" ? "Home" : "WebPage"}
/>
<ScreenWizard bind:showModal={showNewScreenModal} />

View File

@ -0,0 +1,72 @@
<script>
import { Search, Layout, Select } from "@budibase/bbui"
import NavigationPanel from "components/design/NavigationPanel/NavigationPanel.svelte"
import { roles } from "stores/backend"
import { store } from "builderStore"
import NavItem from "components/common/NavItem.svelte"
import ScreenDropdownMenu from "./ScreenDropdownMenu.svelte"
import { RoleColours } from "constants"
import ScreenWizard from "./ScreenWizard.svelte"
let searchString
let accessRole = "all"
let showNewScreenModal
$: filteredScreens = getFilteredScreens(
$store.screens,
searchString,
accessRole
)
const getFilteredScreens = (screens, search, role) => {
return screens
.filter(screen => {
const searchMatch = !search || screen.routing.route.includes(search)
const roleMatch =
!role || role === "all" || screen.routing.roleId === role
return searchMatch && roleMatch
})
.slice()
.sort((a, b) => {
return a.routing.route < b.routing.route ? -1 : 1
})
}
const getRoleColor = roleId => {
return RoleColours[roleId] || "#ffa500"
}
</script>
<NavigationPanel
title="Screens"
showAddButton
onClickAddButton={showNewScreenModal}
>
<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={null}
getOptionLabel={role => role.name}
getOptionValue={role => role._id}
options={[{ name: "All screens", _id: "all" }, ...$roles]}
/>
</Layout>
{#each filteredScreens as screen (screen._id)}
<NavItem
icon={screen.routing.homeScreen ? "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>
{/each}
</NavigationPanel>
<ScreenWizard bind:showModal={showNewScreenModal} />

View File

@ -0,0 +1,146 @@
<script>
import { selectedScreen } from "builderStore"
import SettingsPanel from "components/design/SettingsPanel/SettingsPanel.svelte"
import { get } from "svelte/store"
import { get as deepGet, setWith } from "lodash"
import {
Input,
Layout,
Button,
Toggle,
Checkbox,
notifications,
} from "@budibase/bbui"
import PropertyControl from "components/design/PropertiesPanel/PropertyControls/PropertyControl.svelte"
import RoleSelect from "components/design/PropertiesPanel/PropertyControls/RoleSelect.svelte"
import { currentAsset, store, selectedAccessRole } from "builderStore"
import { FrontendTypes } from "constants"
import sanitizeUrl from "builderStore/store/screenTemplates/utils/sanitizeUrl"
let errors = {}
const routeTaken = url => {
const roleId = get(selectedAccessRole) || "BASIC"
return get(store).screens.some(
screen =>
screen.routing.route.toLowerCase() === url.toLowerCase() &&
screen.routing.roleId === roleId
)
}
const roleTaken = roleId => {
const url = get(currentAsset)?.routing.route
return get(store).screens.some(
screen =>
screen.routing.route.toLowerCase() === url.toLowerCase() &&
screen.routing.roleId === roleId
)
}
const setAssetProps = (name, value, parser, validate) => {
if (parser) {
value = parser(value)
}
if (validate) {
const error = validate(value)
errors = {
...errors,
[name]: error,
}
if (error) {
return
}
} else {
errors = {
...errors,
[name]: null,
}
}
const selectedAsset = get(currentAsset)
store.update(state => {
if (
name === "_instanceName" &&
state.currentFrontEndType === FrontendTypes.SCREEN
) {
selectedAsset.props._instanceName = value
} else {
setWith(selectedAsset, name.split("."), value, Object)
}
return state
})
try {
store.actions.preview.saveSelected()
} catch (error) {
notifications.error("Error saving settings")
}
}
const screenSettings = [
{
key: "routing.homeScreen",
control: Checkbox,
props: {
text: "Set as home screen",
},
},
{
key: "routing.route",
label: "Route",
control: Input,
parser: val => {
if (!val.startsWith("/")) {
val = "/" + val
}
return sanitizeUrl(val)
},
validate: val => {
const exisingValue = get(currentAsset)?.routing.route
if (val !== exisingValue && routeTaken(val)) {
return "That URL is already in use for this role"
}
return null
},
},
{
key: "routing.roleId",
label: "Access",
control: RoleSelect,
validate: val => {
const exisingValue = get(currentAsset)?.routing.roleId
if (val !== exisingValue && roleTaken(val)) {
return "That role is already in use for this URL"
}
return null
},
},
{
key: "showNavigation",
label: "Navigation",
control: Toggle,
props: {
text: "Show navigation",
},
},
]
</script>
<SettingsPanel
title={$selectedScreen?.routing.route}
icon={$selectedScreen.routing.route === "/" ? "Home" : "WebPage"}
>
<Layout gap="S" paddingX="L" paddingY="XL">
{#each screenSettings as def (def.key)}
<PropertyControl
control={def.control}
label={def.label}
key={def.key}
value={deepGet($currentAsset, def.key)}
onChange={val => setAssetProps(def.key, val, def.parser, def.validate)}
props={{ ...def.props, error: errors[def.key] }}
/>
{/each}
<Button cta>View components</Button>
</Layout>
</SettingsPanel>