Moved screen settings/theme into the new rightpanel structure. Refactord the color picker to help with positioning. Removed dead home link

This commit is contained in:
Dean 2023-07-20 11:32:48 +01:00
parent 1d0ab398cd
commit d94fe7c2eb
16 changed files with 354 additions and 136 deletions

View File

@ -32,11 +32,10 @@ export default function positionDropdown(element, opts) {
left: null,
top: null,
}
// Determine vertical styles
if (align === "right-outside") {
styles.top = anchorBounds.top
} else if (window.innerHeight - anchorBounds.bottom < 100) {
} else if (window.innerHeight - anchorBounds.bottom < (maxHeight || 100)) {
styles.top = anchorBounds.top - elementBounds.height - offset
styles.maxHeight = maxHeight || 240
} else {

View File

@ -1,8 +1,8 @@
<script>
import Popover from "../Popover/Popover.svelte"
import Layout from "../Layout/Layout.svelte"
import { createEventDispatcher } from "svelte"
import "@spectrum-css/popover/dist/index-vars.css"
import clickOutside from "../Actions/click_outside"
import { fly } from "svelte/transition"
import Icon from "../Icon/Icon.svelte"
import Input from "../Form/Input.svelte"
import { capitalise } from "../helpers"
@ -10,9 +10,11 @@
export let value
export let size = "M"
export let spectrumTheme
export let alignRight = false
export let offset
export let align
let open = false
let dropdown
let preview
$: customValue = getCustomValue(value)
$: checkColor = getCheckColor(value)
@ -82,7 +84,7 @@
const onChange = value => {
dispatch("change", value)
open = false
dropdown.hide()
}
const getCustomValue = value => {
@ -119,30 +121,25 @@
return "var(--spectrum-global-color-static-gray-900)"
}
const handleOutsideClick = event => {
if (open) {
event.stopPropagation()
open = false
}
}
</script>
<div class="container">
<div class="preview size--{size || 'M'}" on:click={() => (open = true)}>
<div
class="fill {spectrumTheme || ''}"
style={value ? `background: ${value};` : ""}
class:placeholder={!value}
/>
</div>
{#if open}
<div
use:clickOutside={handleOutsideClick}
transition:fly|local={{ y: -20, duration: 200 }}
class="spectrum-Popover spectrum-Popover--bottom spectrum-Picker-popover is-open"
class:spectrum-Popover--align-right={alignRight}
>
<div
bind:this={preview}
class="preview size--{size || 'M'}"
on:click={() => {
dropdown.toggle()
}}
>
<div
class="fill {spectrumTheme || ''}"
style={value ? `background: ${value};` : ""}
class:placeholder={!value}
/>
</div>
<Popover bind:this={dropdown} anchor={preview} maxHeight={320} {offset} {align}>
<Layout paddingX="XL" paddingY="L">
<div class="container">
{#each categories as category}
<div class="category">
<div class="heading">{category.label}</div>
@ -187,8 +184,8 @@
</div>
</div>
</div>
{/if}
</div>
</Layout>
</Popover>
<style>
.container {
@ -248,20 +245,6 @@
width: 48px;
height: 48px;
}
.spectrum-Popover {
width: 210px;
z-index: 999;
top: 100%;
padding: var(--spacing-l) var(--spacing-xl);
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
gap: var(--spacing-xl);
}
.spectrum-Popover--align-right {
right: 0;
}
.colors {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr;
@ -297,7 +280,11 @@
.category--custom .heading {
margin-bottom: var(--spacing-xs);
}
.container {
display: flex;
flex-direction: column;
gap: var(--spacing-xl);
}
.spectrum-wrapper {
background-color: transparent;
}

View File

@ -34,6 +34,14 @@
open = false
}
export const toggle = () => {
if (!open) {
show()
} else {
hide()
}
}
const handleOutsideClick = e => {
if (open) {
// Stop propagation if the source is the anchor

View File

@ -16,6 +16,6 @@
<style>
.pane {
padding: 13px;
padding: var(--spacing-l);
}
</style>

View File

@ -42,7 +42,6 @@
<ColorPicker
value={column.background}
on:change={e => (column.background = e.detail)}
alignRight
spectrumTheme={$store.theme}
/>
</Layout>
@ -51,7 +50,6 @@
<ColorPicker
value={column.color}
on:change={e => (column.color = e.detail)}
alignRight
spectrumTheme={$store.theme}
/>
</Layout>

View File

@ -38,12 +38,6 @@
active={$isActive("./components")}
on:click={() => $goto("./components")}
/>
<IconSideNavItem
icon="Brush"
tooltip="Theme"
active={$isActive("./theme")}
on:click={() => $goto("./theme")}
/>
<IconSideNavItem
icon="Link"
tooltip="Navigation"

View File

@ -0,0 +1,198 @@
<!--
Nav Setting
/design/:screen/navigation
Current screen setting paths
/design/:screen/screens
/design/:screen/theme
-->
<script>
import Pane from "components/design/Pane.svelte"
import { get } from "svelte/store"
import { Helpers } from "@budibase/bbui"
import {
Input,
Layout,
Button,
Toggle,
Checkbox,
Banner,
Select,
notifications,
} from "@budibase/bbui"
import PropertyControl from "components/design/settings/controls/PropertyControl.svelte"
import RoleSelect from "components/design/settings/controls/RoleSelect.svelte"
import { selectedScreen, store } from "builderStore"
import sanitizeUrl from "builderStore/store/screenTemplates/utils/sanitizeUrl"
import { goto } from "@roxi/routify"
import ButtonActionEditor from "components/design/settings/controls/ButtonActionEditor/ButtonActionEditor.svelte"
import { getBindableProperties } from "builderStore/dataBinding"
$: bindings = getBindableProperties($selectedScreen, null)
let errors = {}
const routeTaken = url => {
const roleId = get(selectedScreen).routing.roleId || "BASIC"
return get(store).screens.some(
screen =>
screen.routing.route.toLowerCase() === url.toLowerCase() &&
screen.routing.roleId === roleId
)
}
const roleTaken = roleId => {
const url = get(selectedScreen).routing.route
return get(store).screens.some(
screen =>
screen.routing.route.toLowerCase() === url.toLowerCase() &&
screen.routing.roleId === roleId
)
}
const setScreenSetting = async (setting, value) => {
const { key, parser, validate } = setting
// Parse value if required
if (parser) {
value = parser(value)
}
// Validate value if required and determine errors
if (validate) {
const error = validate(value)
errors = {
...errors,
[key]: error,
}
if (error) {
return
}
} else {
errors = {
...errors,
[key]: null,
}
}
// Update screen setting
try {
await store.actions.screens.updateSetting(get(selectedScreen), key, value)
} catch (error) {
console.log(error)
notifications.error("Error saving screen settings")
}
}
$: 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: route => {
const existingRoute = get(selectedScreen).routing.route
if (route !== existingRoute && routeTaken(route)) {
return "That URL is already in use for this role"
}
return null
},
},
{
key: "routing.roleId",
label: "Access",
control: RoleSelect,
validate: role => {
const existingRole = get(selectedScreen).routing.roleId
if (role !== existingRole && roleTaken(role)) {
return "That role is already in use for this URL"
}
return null
},
},
{
key: "onLoad",
label: "On screen load",
control: ButtonActionEditor,
},
{
key: "showNavigation",
label: "Navigation",
control: Toggle,
props: {
text: "Show nav",
disabled: !!$selectedScreen.layoutId,
},
},
{
key: "width",
label: "Width",
control: Select,
props: {
options: ["Extra small", "Small", "Medium", "Large", "Max"],
placeholder: "Default",
disabled: !!$selectedScreen.layoutId,
},
},
]
const removeCustomLayout = async () => {
return store.actions.screens.removeCustomLayout(get(selectedScreen))
}
</script>
<Pane title="General">
<Layout gap="S" noPadding>
{#if $selectedScreen.layoutId}
<Banner
type="warning"
extraButtonText="Detach custom layout"
extraButtonAction={removeCustomLayout}
showCloseButton={false}
>
This screen uses a custom layout, which is deprecated
</Banner>
{/if}
<span class="screen-settings">
{#each screenSettings as setting (setting.key)}
<PropertyControl
control={setting.control}
label={setting.label}
key={setting.key}
value={Helpers.deepGet($selectedScreen, setting.key)}
onChange={val => setScreenSetting(setting, val)}
props={{ ...setting.props, error: errors[setting.key] }}
{bindings}
/>
{/each}
</span>
<Button secondary on:click={() => $goto("../components")}>
View components
</Button>
</Layout>
</Pane>
<style>
.screen-settings {
display: contents;
}
.screen-settings :global(.property-control) {
padding: 0px;
margin: 0px;
border: 0px;
}
</style>

View File

@ -0,0 +1,98 @@
<script>
import Pane from "components/design/Pane.svelte"
import {
Layout,
Label,
ColorPicker,
notifications,
Icon,
Body,
} from "@budibase/bbui"
import { store } from "builderStore"
import { get } from "svelte/store"
import { DefaultAppTheme } from "constants"
//These were shifted technically the /theme url is unnecessary
import AppThemeSelect from "./theme/AppThemeSelect.svelte"
import ButtonRoundnessSelect from "./theme/ButtonRoundnessSelect.svelte"
import PropertyControl from "components/design/settings/controls/PropertyControl.svelte"
$: customTheme = $store.customTheme || {}
const update = async (property, value) => {
try {
store.actions.customTheme.save({
...get(store).customTheme,
[property]: value,
})
} catch (error) {
notifications.error("Error updating custom theme")
}
}
</script>
<Pane title="Theme">
<div class="info">
<div class="infoHeader">
<Icon name="InfoOutline" size="S" />
<Body size="XS">CHANGES WILL APPLY TO ALL SCREENS</Body>
</div>
<Body size="S">
Your navigation is configured for all the screens within your app.
</Body>
</div>
<Layout noPadding gap="S">
<Layout noPadding gap="XS">
<AppThemeSelect />
</Layout>
<Layout noPadding gap="XS">
<Label>Button roundness</Label>
<ButtonRoundnessSelect
{customTheme}
on:change={e => update("buttonBorderRadius", e.detail)}
/>
</Layout>
<PropertyControl
label="Accent color"
control={ColorPicker}
value={customTheme.primaryColor || DefaultAppTheme.primaryColor}
onChange={val => update("primaryColor", val)}
props={{
spectrumTheme: $store.theme,
}}
/>
<PropertyControl
label="Hover"
control={ColorPicker}
value={customTheme.primaryColorHover || DefaultAppTheme.primaryColorHover}
onChange={val => update("primaryColorHover", val)}
props={{
spectrumTheme: $store.theme,
}}
/>
</Layout>
</Pane>
<!-- Add this to its own component -->
<style>
.infoHeader {
display: flex;
margin-bottom: 5px;
border-radius: 4px;
}
.infoHeader :global(svg) {
margin-right: 5px;
color: var(--grey-6);
}
.infoHeader :global(p) {
color: var(--grey-6);
}
.info {
background-color: var(--background-alt);
padding: 12px;
margin-bottom: 12px;
}
</style>

View File

@ -0,0 +1,15 @@
<script>
import RightPanel from "components/design/RightPanel.svelte"
import GeneralPane from "./GeneralPane.svelte"
import ThemePane from "./ThemePane.svelte"
import { selectedScreen } from "builderStore"
</script>
<RightPanel
title={$selectedScreen.routing.route}
icon={$selectedScreen.routing.route === "/" ? "Home" : "WebPage"}
>
<GeneralPane />
<ThemePane />
</RightPanel>

View File

@ -1,7 +1,7 @@
<script>
import { selectedScreen } from "builderStore"
import ScreenListPanel from "./_components/ScreenListPanel.svelte"
import ScreenSettingsPanel from "./_components/ScreenSettingsPanel.svelte"
import ScreenSettingsPanel from "./_components/ScreenSettingsPanel/index.svelte"
</script>
<ScreenListPanel />

View File

@ -1,12 +0,0 @@
<script>
import Panel from "components/design/Panel.svelte"
import { Body, Layout } from "@budibase/bbui"
</script>
<Panel borderLeft title="Theme" icon="InfoOutline" wide>
<Layout paddingX="L" paddingY="XL">
<Body size="S">
Your theme is set across all the screens within your app.
</Body>
</Layout>
</Panel>

View File

@ -1,55 +0,0 @@
<script>
import Panel from "components/design/Panel.svelte"
import { Layout, Label, ColorPicker, notifications } from "@budibase/bbui"
import { store } from "builderStore"
import { get } from "svelte/store"
import { DefaultAppTheme } from "constants"
import AppThemeSelect from "./AppThemeSelect.svelte"
import ButtonRoundnessSelect from "./ButtonRoundnessSelect.svelte"
$: customTheme = $store.customTheme || {}
const update = async (property, value) => {
try {
store.actions.customTheme.save({
...get(store).customTheme,
[property]: value,
})
} catch (error) {
notifications.error("Error updating custom theme")
}
}
</script>
<Panel title="Theme" borderRight>
<Layout paddingX="L" paddingY="XL" gap="S">
<Layout noPadding gap="XS">
<Label>Theme</Label>
<AppThemeSelect />
</Layout>
<Layout noPadding gap="XS">
<Label>Button roundness</Label>
<ButtonRoundnessSelect
{customTheme}
on:change={e => update("buttonBorderRadius", e.detail)}
/>
</Layout>
<Layout noPadding gap="XS">
<Label>Accent color</Label>
<ColorPicker
spectrumTheme={$store.theme}
value={customTheme.primaryColor || DefaultAppTheme.primaryColor}
on:change={e => update("primaryColor", e.detail)}
/>
</Layout>
<Layout noPadding gap="XS">
<Label>Accent color (hover)</Label>
<ColorPicker
spectrumTheme={$store.theme}
value={customTheme.primaryColorHover ||
DefaultAppTheme.primaryColorHover}
on:change={e => update("primaryColorHover", e.detail)}
/>
</Layout>
</Layout>
</Panel>

View File

@ -1,7 +0,0 @@
<script>
import ThemeSettingsPanel from "./_components/ThemeSettingsPanel.svelte"
import ThemeInfoPanel from "./_components/ThemeInfoPanel.svelte"
</script>
<ThemeSettingsPanel />
<ThemeInfoPanel />

View File

@ -298,12 +298,7 @@ async function performAppCreate(ctx: UserCtx) {
title: name,
navWidth: "Large",
navBackground: "var(--spectrum-global-color-gray-100)",
links: [
{
url: "/home",
text: "Home",
},
],
links: [],
},
theme: "spectrum--light",
customTheme: {