Add screen settings panel
This commit is contained in:
parent
1e59576a30
commit
877791970a
|
@ -13,14 +13,7 @@ export const selectedScreen = derived(store, $store => {
|
|||
return $store.screens.find(screen => screen._id === $store.selectedScreenId)
|
||||
})
|
||||
|
||||
export const currentAsset = derived(store, $store => {
|
||||
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 currentAsset = selectedScreen
|
||||
|
||||
export const selectedComponent = derived(
|
||||
[store, currentAsset],
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
class:border
|
||||
class:selected
|
||||
class:withActions
|
||||
style={`padding-left: ${14 + indentLevel * 14}px`}
|
||||
style={`padding-left: calc(var(--spacing-l) + ${indentLevel * 14}px)`}
|
||||
{draggable}
|
||||
on:dragend
|
||||
on:dragstart
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
align-items: stretch;
|
||||
}
|
||||
.header {
|
||||
height: 55px;
|
||||
height: 48px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
|
@ -51,8 +51,8 @@
|
|||
white-space: nowrap;
|
||||
}
|
||||
.add-button {
|
||||
flex: 0 0 32px;
|
||||
height: 32px;
|
||||
flex: 0 0 30px;
|
||||
height: 30px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border-radius: 4px;
|
||||
|
|
|
@ -108,19 +108,4 @@
|
|||
]
|
||||
</script>
|
||||
|
||||
{#if $store.currentView !== "component" && $currentAsset && $store.currentFrontEndType === FrontendTypes.SCREEN}
|
||||
<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}
|
||||
{#if $store.currentView !== "component" && $currentAsset && $store.currentFrontEndType === FrontendTypes.SCREEN}{/if}
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
align-items: stretch;
|
||||
}
|
||||
.header {
|
||||
height: 55px;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
|
|
|
@ -1,45 +1,11 @@
|
|||
<script>
|
||||
import { Search, Layout, Select } from "@budibase/bbui"
|
||||
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 { store } from "builderStore"
|
||||
import { onDestroy } from "svelte"
|
||||
import { syncURLToState } from "helpers/urlStateSync"
|
||||
import { goto, params, redirect } from "@roxi/routify"
|
||||
import AppPanel from "components/design/AppPanel/AppPanel.svelte"
|
||||
import SettingsPanel from "components/design/SettingsPanel/SettingsPanel.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"
|
||||
}
|
||||
import ScreenNavigationPanel from "./_components/ScreenNavigationPanel.svelte"
|
||||
import ScreenSettingsPanel from "./_components/ScreenSettingsPanel.svelte"
|
||||
|
||||
// Keep URL and state in sync for selected screen ID
|
||||
const stopSyncing = syncURLToState({
|
||||
|
@ -60,44 +26,6 @@
|
|||
onDestroy(stopSyncing)
|
||||
</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.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>
|
||||
|
||||
<ScreenNavigationPanel />
|
||||
<AppPanel />
|
||||
|
||||
<SettingsPanel
|
||||
title={$selectedScreen?.routing.route}
|
||||
icon={$selectedScreen.routing.route === "/" ? "Home" : "WebPage"}
|
||||
/>
|
||||
|
||||
<ScreenWizard bind:showModal={showNewScreenModal} />
|
||||
<ScreenSettingsPanel />
|
||||
|
|
|
@ -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} />
|
|
@ -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>
|
Loading…
Reference in New Issue