Add initial layouts panel and add explicit panels for theme and navigation tabs
This commit is contained in:
parent
876cb4784d
commit
88018aff4e
|
@ -1,7 +1,7 @@
|
|||
import { getFrontendStore } from "./store/frontend"
|
||||
import { getAutomationStore } from "./store/automation"
|
||||
import { getThemeStore } from "./store/theme"
|
||||
import { derived, writable } from "svelte/store"
|
||||
import { derived } from "svelte/store"
|
||||
import { LAYOUT_NAMES } from "../constants"
|
||||
import { findComponent, findComponentPath } from "./componentUtils"
|
||||
import { RoleUtils } from "@budibase/frontend-core"
|
||||
|
@ -14,6 +14,20 @@ export const selectedScreen = derived(store, $store => {
|
|||
return $store.screens.find(screen => screen._id === $store.selectedScreenId)
|
||||
})
|
||||
|
||||
export const selectedLayout = derived(store, $store => {
|
||||
return $store.layouts?.find(layout => layout._id === $store.selectedLayoutId)
|
||||
})
|
||||
|
||||
export const selectedComponent = derived(
|
||||
[store, selectedScreen],
|
||||
([$store, $selectedScreen]) => {
|
||||
if (!$selectedScreen || !$store.selectedComponentId) {
|
||||
return null
|
||||
}
|
||||
return findComponent($selectedScreen?.props, $store.selectedComponentId)
|
||||
}
|
||||
)
|
||||
|
||||
export const sortedScreens = derived(store, $store => {
|
||||
return $store.screens.slice().sort((a, b) => {
|
||||
// Sort by role first
|
||||
|
@ -43,16 +57,6 @@ export const sortedScreens = derived(store, $store => {
|
|||
})
|
||||
})
|
||||
|
||||
export const selectedComponent = derived(
|
||||
[store, selectedScreen],
|
||||
([$store, $selectedScreen]) => {
|
||||
if (!$selectedScreen || !$store.selectedComponentId) {
|
||||
return null
|
||||
}
|
||||
return findComponent($selectedScreen?.props, $store.selectedComponentId)
|
||||
}
|
||||
)
|
||||
|
||||
export const selectedComponentPath = derived(
|
||||
[store, selectedScreen],
|
||||
([$store, $selectedScreen]) => {
|
||||
|
|
|
@ -43,7 +43,6 @@ const INITIAL_FRONTEND_STATE = {
|
|||
continueIfAction: false,
|
||||
},
|
||||
currentFrontEndType: "none",
|
||||
selectedLayoutId: "",
|
||||
errors: [],
|
||||
hasAppPackage: false,
|
||||
libraries: null,
|
||||
|
@ -57,6 +56,7 @@ const INITIAL_FRONTEND_STATE = {
|
|||
// URL params
|
||||
selectedScreenId: null,
|
||||
selectedComponentId: null,
|
||||
selectedLayoutId: null,
|
||||
}
|
||||
|
||||
export const getFrontendStore = () => {
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
<script>
|
||||
import { get } from "svelte/store"
|
||||
import { onMount, onDestroy } from "svelte"
|
||||
import { store, selectedScreen, currentAsset } from "builderStore"
|
||||
import {
|
||||
store,
|
||||
selectedScreen,
|
||||
selectedLayout,
|
||||
currentAsset,
|
||||
} from "builderStore"
|
||||
import iframeTemplate from "./iframeTemplate"
|
||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
||||
import {
|
||||
|
@ -40,8 +45,15 @@
|
|||
// Extract data to pass to the iframe
|
||||
$: {
|
||||
screen = $selectedScreen
|
||||
|
||||
// If viewing legacy layouts, always show the custom layout
|
||||
if ($isActive("./layouts")) {
|
||||
layout = $selectedLayout
|
||||
} else {
|
||||
layout = $store.layouts.find(layout => layout._id === screen?.layoutId)
|
||||
}
|
||||
}
|
||||
|
||||
// Don't show selected components unless on the components tab
|
||||
$: selectedComponentId = $isActive("./components")
|
||||
? $store.selectedComponentId
|
||||
|
@ -59,7 +71,10 @@
|
|||
navigation: $store.navigation,
|
||||
isBudibaseEvent: true,
|
||||
}
|
||||
|
||||
// Refresh the preview when required
|
||||
$: json = JSON.stringify(previewData)
|
||||
$: refreshContent(json)
|
||||
|
||||
// Update the iframe with the builder info to render the correct preview
|
||||
const refreshContent = message => {
|
||||
|
@ -68,10 +83,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Refresh the preview when required
|
||||
$: refreshContent(json)
|
||||
|
||||
function receiveMessage(message) {
|
||||
const receiveMessage = message => {
|
||||
const handlers = {
|
||||
[MessageTypes.READY]: () => {
|
||||
// Initialise the app when mounted
|
||||
|
@ -97,46 +109,6 @@
|
|||
messageHandler(message)
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
window.addEventListener("message", receiveMessage)
|
||||
if (!$store.clientFeatures.messagePassing) {
|
||||
// Legacy - remove in later versions of BB
|
||||
iframe.contentWindow.addEventListener(
|
||||
"ready",
|
||||
() => {
|
||||
receiveMessage({ data: { type: MessageTypes.READY } })
|
||||
},
|
||||
{ once: true }
|
||||
)
|
||||
iframe.contentWindow.addEventListener(
|
||||
"error",
|
||||
event => {
|
||||
receiveMessage({
|
||||
data: { type: MessageTypes.ERROR, error: event.detail },
|
||||
})
|
||||
},
|
||||
{ once: true }
|
||||
)
|
||||
// Add listener for events sent by client library in preview
|
||||
iframe.contentWindow.addEventListener("bb-event", handleBudibaseEvent)
|
||||
}
|
||||
})
|
||||
|
||||
// Remove all iframe event listeners on component destroy
|
||||
onDestroy(() => {
|
||||
window.removeEventListener("message", receiveMessage)
|
||||
|
||||
if (iframe.contentWindow) {
|
||||
if (!$store.clientFeatures.messagePassing) {
|
||||
// Legacy - remove in later versions of BB
|
||||
iframe.contentWindow.removeEventListener(
|
||||
"bb-event",
|
||||
handleBudibaseEvent
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const handleBudibaseEvent = async event => {
|
||||
const { type, data } = event.data || event.detail
|
||||
if (!type) {
|
||||
|
@ -212,6 +184,46 @@
|
|||
const cancelDeleteComponent = () => {
|
||||
idToDelete = null
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
window.addEventListener("message", receiveMessage)
|
||||
if (!$store.clientFeatures.messagePassing) {
|
||||
// Legacy - remove in later versions of BB
|
||||
iframe.contentWindow.addEventListener(
|
||||
"ready",
|
||||
() => {
|
||||
receiveMessage({ data: { type: MessageTypes.READY } })
|
||||
},
|
||||
{ once: true }
|
||||
)
|
||||
iframe.contentWindow.addEventListener(
|
||||
"error",
|
||||
event => {
|
||||
receiveMessage({
|
||||
data: { type: MessageTypes.ERROR, error: event.detail },
|
||||
})
|
||||
},
|
||||
{ once: true }
|
||||
)
|
||||
// Add listener for events sent by client library in preview
|
||||
iframe.contentWindow.addEventListener("bb-event", handleBudibaseEvent)
|
||||
}
|
||||
})
|
||||
|
||||
// Remove all iframe event listeners on component destroy
|
||||
onDestroy(() => {
|
||||
window.removeEventListener("message", receiveMessage)
|
||||
|
||||
if (iframe.contentWindow) {
|
||||
if (!$store.clientFeatures.messagePassing) {
|
||||
// Legacy - remove in later versions of BB
|
||||
iframe.contentWindow.removeEventListener(
|
||||
"bb-event",
|
||||
handleBudibaseEvent
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="component-container">
|
||||
|
|
|
@ -2,21 +2,11 @@
|
|||
import { store } from "builderStore"
|
||||
import { notifications } from "@budibase/bbui"
|
||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
||||
import {
|
||||
ActionMenu,
|
||||
MenuItem,
|
||||
Icon,
|
||||
Modal,
|
||||
ModalContent,
|
||||
Input,
|
||||
} from "@budibase/bbui"
|
||||
import { cloneDeep } from "lodash/fp"
|
||||
import { ActionMenu, MenuItem, Icon } from "@budibase/bbui"
|
||||
|
||||
export let layout
|
||||
|
||||
let confirmDeleteDialog
|
||||
let editLayoutNameModal
|
||||
let name = layout.name
|
||||
|
||||
const deleteLayout = async () => {
|
||||
try {
|
||||
|
@ -26,24 +16,12 @@
|
|||
notifications.error("Error deleting layout")
|
||||
}
|
||||
}
|
||||
|
||||
const saveLayout = async () => {
|
||||
try {
|
||||
const layoutToSave = cloneDeep(layout)
|
||||
layoutToSave.name = name
|
||||
await store.actions.layouts.save(layoutToSave)
|
||||
notifications.success("Layout saved successfully")
|
||||
} catch (err) {
|
||||
notifications.error("Error saving layout")
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<ActionMenu>
|
||||
<div slot="control" class="icon">
|
||||
<Icon size="S" hoverable name="MoreSmallList" />
|
||||
</div>
|
||||
<MenuItem icon="Edit" on:click={editLayoutNameModal.show}>Edit</MenuItem>
|
||||
<MenuItem icon="Delete" on:click={confirmDeleteDialog.show}>Delete</MenuItem>
|
||||
</ActionMenu>
|
||||
|
||||
|
@ -55,17 +33,6 @@
|
|||
onOk={deleteLayout}
|
||||
/>
|
||||
|
||||
<Modal bind:this={editLayoutNameModal}>
|
||||
<ModalContent
|
||||
title="Edit Layout Name"
|
||||
confirmText="Save"
|
||||
onConfirm={saveLayout}
|
||||
disabled={!name}
|
||||
>
|
||||
<Input thin type="text" label="Name" bind:value={name} />
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
|
||||
<style>
|
||||
.icon {
|
||||
display: grid;
|
|
@ -0,0 +1,34 @@
|
|||
<script>
|
||||
import NavigationPanel from "components/design/navigation/NavigationPanel.svelte"
|
||||
import { Banner, Layout } from "@budibase/bbui"
|
||||
import NavItem from "components/common/NavItem.svelte"
|
||||
import { store } from "builderStore"
|
||||
import LayoutDropdownMenu from "./LayoutDropdownMenu.svelte"
|
||||
</script>
|
||||
|
||||
<NavigationPanel title="Layouts">
|
||||
<Layout paddingX="L" paddingY="XL" gap="S">
|
||||
<Banner
|
||||
type="warning"
|
||||
showCloseButton={false}
|
||||
extraButtonText="View details"
|
||||
extraButtonAction={() => {}}
|
||||
>
|
||||
Custom layouts are being deprecated. They will be removed from June 1st.
|
||||
</Banner>
|
||||
</Layout>
|
||||
{#each $store.layouts as layout (layout._id)}
|
||||
<NavItem
|
||||
icon="Experience"
|
||||
indentLevel={0}
|
||||
selected={$store.selectedLayoutId === layout._id}
|
||||
text={layout.name}
|
||||
on:click={() => ($store.selectedLayoutId = layout._id)}
|
||||
>
|
||||
<LayoutDropdownMenu {layout} />
|
||||
</NavItem>
|
||||
{/each}
|
||||
</NavigationPanel>
|
||||
|
||||
<!-- -->
|
||||
<!-- color={RoleUtils.getRoleColour(screen.routing.roleId)}-->
|
|
@ -0,0 +1,5 @@
|
|||
<script>
|
||||
import SettingsPanel from "components/design/settings/SettingsPanel.svelte"
|
||||
</script>
|
||||
|
||||
<
|
|
@ -0,0 +1,20 @@
|
|||
<script>
|
||||
import { syncURLToState } from "helpers/urlStateSync"
|
||||
import { store } from "builderStore"
|
||||
import * as routify from "@roxi/routify"
|
||||
import { onDestroy } from "svelte"
|
||||
|
||||
// Keep URL and state in sync for selected component ID
|
||||
const stopSyncing = syncURLToState({
|
||||
urlParam: "layoutId",
|
||||
stateKey: "selectedLayoutId",
|
||||
validate: id => $store.layouts?.some(layout => layout._id === id),
|
||||
fallbackUrl: "../",
|
||||
store,
|
||||
routify,
|
||||
})
|
||||
|
||||
onDestroy(stopSyncing)
|
||||
</script>
|
||||
|
||||
<slot />
|
|
@ -0,0 +1,5 @@
|
|||
<script>
|
||||
import LayoutNavigationPanel from "./_components/LayoutNavigationPanel.svelte"
|
||||
</script>
|
||||
|
||||
<LayoutNavigationPanel />
|
|
@ -1,5 +1,13 @@
|
|||
<script>
|
||||
import NavigationPanel from "components/design/navigation/NavigationPanel.svelte"
|
||||
import { store } from "builderStore"
|
||||
import { onMount } from "svelte"
|
||||
import { redirect } from "@roxi/routify"
|
||||
|
||||
onMount(() => {
|
||||
if ($store.layouts?.length) {
|
||||
$redirect(`./${$store.layouts[0]._id}`)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<NavigationPanel title="Layouts" />
|
||||
You don't have any layouts
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
<script>
|
||||
import NavigationPanel from "components/design/navigation/NavigationPanel.svelte"
|
||||
import {
|
||||
Body,
|
||||
Layout,
|
||||
Label,
|
||||
ActionGroup,
|
||||
ActionButton,
|
||||
Checkbox,
|
||||
Select,
|
||||
ColorPicker,
|
||||
Input,
|
||||
notifications,
|
||||
} from "@budibase/bbui"
|
||||
import NavigationLinksEditor from "./NavigationLinksEditor.svelte"
|
||||
import { store } from "builderStore"
|
||||
import { DefaultAppTheme } from "constants"
|
||||
|
||||
const update = async (key, value) => {
|
||||
try {
|
||||
let navigation = $store.navigation
|
||||
navigation[key] = value
|
||||
await store.actions.navigation.save(navigation)
|
||||
} catch (error) {
|
||||
notifications.error("Error updating navigation settings")
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<NavigationPanel title="Navigation">
|
||||
<Layout paddingX="L" paddingY="XL" gap="S">
|
||||
<Body size="S">
|
||||
Your navigation is configured for all the screens within your app
|
||||
</Body>
|
||||
<Body size="S">
|
||||
You can hide and show your navigation for each screen in the screen
|
||||
settings
|
||||
</Body>
|
||||
<NavigationLinksEditor />
|
||||
<Layout noPadding gap="XS">
|
||||
<Label>Position</Label>
|
||||
<ActionGroup quiet>
|
||||
<ActionButton
|
||||
selected={$store.navigation.navigation === "Top"}
|
||||
quiet={$store.navigation.navigation !== "Top"}
|
||||
icon="PaddingTop"
|
||||
on:click={() => update("navigation", "Top")}
|
||||
/>
|
||||
<ActionButton
|
||||
selected={$store.navigation.navigation === "Left"}
|
||||
quiet={$store.navigation.navigation !== "Left"}
|
||||
icon="PaddingLeft"
|
||||
on:click={() => update("navigation", "Left")}
|
||||
/>
|
||||
</ActionGroup>
|
||||
</Layout>
|
||||
{#if $store.navigation.navigation === "Top"}
|
||||
<Checkbox
|
||||
text="Sticky header"
|
||||
value={$store.navigation.sticky}
|
||||
on:change={e => update("sticky", e.detail)}
|
||||
/>
|
||||
<Select
|
||||
label="Width"
|
||||
options={["Max", "Large", "Medium", "Small"]}
|
||||
plaveholder={null}
|
||||
value={$store.navigation.navWidth}
|
||||
on:change={e => update("navWidth", e.detail)}
|
||||
/>
|
||||
{/if}
|
||||
<Layout noPadding gap="XS">
|
||||
<Checkbox
|
||||
text="Logo"
|
||||
value={!$store.navigation.hideLogo}
|
||||
on:change={e => update("hideLogo", !e.detail)}
|
||||
/>
|
||||
{#if !$store.navigation.hideLogo}
|
||||
<Input
|
||||
value={$store.navigation.logoUrl}
|
||||
on:change={e => update("logoUrl", e.detail)}
|
||||
placeholder="Add logo URL"
|
||||
updateOnChange={false}
|
||||
/>
|
||||
{/if}
|
||||
</Layout>
|
||||
<Layout noPadding gap="XS">
|
||||
<Checkbox
|
||||
text="Title"
|
||||
value={!$store.navigation.hideTitle}
|
||||
on:change={e => update("hideTitle", !e.detail)}
|
||||
/>
|
||||
{#if !$store.navigation.hideTitle}
|
||||
<Input
|
||||
value={$store.navigation.title}
|
||||
on:change={e => update("title", e.detail)}
|
||||
placeholder="Add title"
|
||||
updateOnChange={false}
|
||||
/>
|
||||
{/if}
|
||||
</Layout>
|
||||
<Layout noPadding gap="XS">
|
||||
<Label>Background color</Label>
|
||||
<ColorPicker
|
||||
spectrumTheme={$store.theme}
|
||||
value={$store.navigation.navBackground || DefaultAppTheme.navBackground}
|
||||
on:change={e => update("navBackground", e.detail)}
|
||||
/>
|
||||
</Layout>
|
||||
<Layout noPadding gap="XS">
|
||||
<Label>Text color</Label>
|
||||
<ColorPicker
|
||||
spectrumTheme={$store.theme}
|
||||
value={$store.navigation.navTextColor || DefaultAppTheme.navTextColor}
|
||||
on:change={e => update("navTextColor", e.detail)}
|
||||
/>
|
||||
</Layout>
|
||||
</Layout>
|
||||
</NavigationPanel>
|
|
@ -1,118 +1,5 @@
|
|||
<script>
|
||||
import NavigationPanel from "components/design/navigation/NavigationPanel.svelte"
|
||||
import {
|
||||
Body,
|
||||
Layout,
|
||||
Label,
|
||||
ActionGroup,
|
||||
ActionButton,
|
||||
Checkbox,
|
||||
Select,
|
||||
ColorPicker,
|
||||
Input,
|
||||
notifications,
|
||||
} from "@budibase/bbui"
|
||||
import NavigationLinksEditor from "./_components/NavigationLinksEditor.svelte"
|
||||
import { store } from "builderStore"
|
||||
import { DefaultAppTheme } from "constants"
|
||||
|
||||
const update = async (key, value) => {
|
||||
try {
|
||||
let navigation = $store.navigation
|
||||
navigation[key] = value
|
||||
await store.actions.navigation.save(navigation)
|
||||
} catch (error) {
|
||||
notifications.error("Error updating navigation settings")
|
||||
}
|
||||
}
|
||||
import NavigationSettingsPanel from "./_components/NavigationSettingsPanel.svelte"
|
||||
</script>
|
||||
|
||||
<NavigationPanel title="Navigation">
|
||||
<Layout paddingX="L" paddingY="XL" gap="S">
|
||||
<Body size="S">
|
||||
Your navigation is configured for all the screens within your app
|
||||
</Body>
|
||||
<Body size="S">
|
||||
You can hide and show your navigation for each screen in the screen
|
||||
settings
|
||||
</Body>
|
||||
<NavigationLinksEditor />
|
||||
<Layout noPadding gap="XS">
|
||||
<Label>Position</Label>
|
||||
<ActionGroup quiet>
|
||||
<ActionButton
|
||||
selected={$store.navigation.navigation === "Top"}
|
||||
quiet={$store.navigation.navigation !== "Top"}
|
||||
icon="PaddingTop"
|
||||
on:click={() => update("navigation", "Top")}
|
||||
/>
|
||||
<ActionButton
|
||||
selected={$store.navigation.navigation === "Left"}
|
||||
quiet={$store.navigation.navigation !== "Left"}
|
||||
icon="PaddingLeft"
|
||||
on:click={() => update("navigation", "Left")}
|
||||
/>
|
||||
</ActionGroup>
|
||||
</Layout>
|
||||
{#if $store.navigation.navigation === "Top"}
|
||||
<Checkbox
|
||||
text="Sticky header"
|
||||
value={$store.navigation.sticky}
|
||||
on:change={e => update("sticky", e.detail)}
|
||||
/>
|
||||
<Select
|
||||
label="Width"
|
||||
options={["Max", "Large", "Medium", "Small"]}
|
||||
plaveholder={null}
|
||||
value={$store.navigation.navWidth}
|
||||
on:change={e => update("navWidth", e.detail)}
|
||||
/>
|
||||
{/if}
|
||||
<Layout noPadding gap="XS">
|
||||
<Checkbox
|
||||
text="Logo"
|
||||
value={!$store.navigation.hideLogo}
|
||||
on:change={e => update("hideLogo", !e.detail)}
|
||||
/>
|
||||
{#if !$store.navigation.hideLogo}
|
||||
<Input
|
||||
value={$store.navigation.logoUrl}
|
||||
on:change={e => update("logoUrl", e.detail)}
|
||||
placeholder="Add logo URL"
|
||||
updateOnChange={false}
|
||||
/>
|
||||
{/if}
|
||||
</Layout>
|
||||
<Layout noPadding gap="XS">
|
||||
<Checkbox
|
||||
text="Title"
|
||||
value={!$store.navigation.hideTitle}
|
||||
on:change={e => update("hideTitle", !e.detail)}
|
||||
/>
|
||||
{#if !$store.navigation.hideTitle}
|
||||
<Input
|
||||
value={$store.navigation.title}
|
||||
on:change={e => update("title", e.detail)}
|
||||
placeholder="Add title"
|
||||
updateOnChange={false}
|
||||
/>
|
||||
{/if}
|
||||
</Layout>
|
||||
<Layout noPadding gap="XS">
|
||||
<Label>Background color</Label>
|
||||
<ColorPicker
|
||||
spectrumTheme={$store.theme}
|
||||
value={$store.navigation.navBackground || DefaultAppTheme.navBackground}
|
||||
on:change={e => update("navBackground", e.detail)}
|
||||
/>
|
||||
</Layout>
|
||||
<Layout noPadding gap="XS">
|
||||
<Label>Text color</Label>
|
||||
<ColorPicker
|
||||
spectrumTheme={$store.theme}
|
||||
value={$store.navigation.navTextColor || DefaultAppTheme.navTextColor}
|
||||
on:change={e => update("navTextColor", e.detail)}
|
||||
/>
|
||||
</Layout>
|
||||
</Layout>
|
||||
</NavigationPanel>
|
||||
<NavigationSettingsPanel />
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
<script>
|
||||
import NavigationPanel from "components/design/navigation/NavigationPanel.svelte"
|
||||
import {
|
||||
Body,
|
||||
Layout,
|
||||
Label,
|
||||
ColorPicker,
|
||||
Button,
|
||||
notifications,
|
||||
} from "@budibase/bbui"
|
||||
import { store } from "builderStore"
|
||||
import { get } from "svelte/store"
|
||||
import { DefaultAppTheme } from "constants"
|
||||
import AppThemeSelect from "./AppThemeSelect.svelte"
|
||||
|
||||
const ButtonBorderRadiusOptions = [
|
||||
{
|
||||
label: "Square",
|
||||
value: "0",
|
||||
},
|
||||
{
|
||||
label: "Soft edge",
|
||||
value: "4px",
|
||||
},
|
||||
{
|
||||
label: "Curved",
|
||||
value: "8px",
|
||||
},
|
||||
{
|
||||
label: "Round",
|
||||
value: "16px",
|
||||
},
|
||||
]
|
||||
|
||||
$: 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>
|
||||
|
||||
<NavigationPanel title="Theme">
|
||||
<Layout paddingX="L" paddingY="XL" gap="S">
|
||||
<Body size="S">
|
||||
Your theme is set across all the screens within your app
|
||||
</Body>
|
||||
<Layout noPadding gap="XS">
|
||||
<Label>Theme</Label>
|
||||
<AppThemeSelect />
|
||||
</Layout>
|
||||
<Layout noPadding gap="XS">
|
||||
<Label>Buttons</Label>
|
||||
<div class="buttons">
|
||||
{#each ButtonBorderRadiusOptions as option}
|
||||
<div
|
||||
class:active={customTheme.buttonBorderRadius === option.value}
|
||||
style={`--radius: ${option.value}`}
|
||||
>
|
||||
<Button
|
||||
secondary
|
||||
on:click={() => update("buttonBorderRadius", option.value)}
|
||||
>
|
||||
{option.label}
|
||||
</Button>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</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>
|
||||
</NavigationPanel>
|
||||
|
||||
<style>
|
||||
.buttons {
|
||||
display: grid;
|
||||
grid-template-columns: 100px 100px;
|
||||
gap: var(--spacing-m);
|
||||
}
|
||||
.buttons > div {
|
||||
display: contents;
|
||||
}
|
||||
.buttons > div :global(.spectrum-Button) {
|
||||
border-radius: var(--radius) !important;
|
||||
border-width: 1px;
|
||||
border-color: var(--spectrum-global-color-gray-400);
|
||||
font-weight: 600;
|
||||
}
|
||||
.buttons > div:hover :global(.spectrum-Button) {
|
||||
background: var(--spectrum-global-color-gray-700);
|
||||
border-color: var(--spectrum-global-color-gray-700);
|
||||
}
|
||||
.buttons > div.active :global(.spectrum-Button) {
|
||||
background: var(--spectrum-global-color-gray-200);
|
||||
color: var(--spectrum-global-color-gray-800);
|
||||
border-color: var(--spectrum-global-color-gray-600);
|
||||
}
|
||||
</style>
|
|
@ -1,120 +1,5 @@
|
|||
<script>
|
||||
import NavigationPanel from "components/design/navigation/NavigationPanel.svelte"
|
||||
import {
|
||||
Body,
|
||||
Layout,
|
||||
Label,
|
||||
ColorPicker,
|
||||
Button,
|
||||
notifications,
|
||||
} from "@budibase/bbui"
|
||||
import { store } from "builderStore"
|
||||
import { get } from "svelte/store"
|
||||
import { DefaultAppTheme } from "constants"
|
||||
import AppThemeSelect from "./_components/AppThemeSelect.svelte"
|
||||
|
||||
const ButtonBorderRadiusOptions = [
|
||||
{
|
||||
label: "Square",
|
||||
value: "0",
|
||||
},
|
||||
{
|
||||
label: "Soft edge",
|
||||
value: "4px",
|
||||
},
|
||||
{
|
||||
label: "Curved",
|
||||
value: "8px",
|
||||
},
|
||||
{
|
||||
label: "Round",
|
||||
value: "16px",
|
||||
},
|
||||
]
|
||||
|
||||
$: 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")
|
||||
}
|
||||
}
|
||||
import ThemeSettingsPanel from "./_components/ThemeSettingsPanel.svelte"
|
||||
</script>
|
||||
|
||||
<NavigationPanel title="Theme">
|
||||
<Layout paddingX="L" paddingY="XL" gap="S">
|
||||
<Body size="S">
|
||||
Your theme is set across all the screens within your app
|
||||
</Body>
|
||||
<Layout noPadding gap="XS">
|
||||
<Label>Theme</Label>
|
||||
<AppThemeSelect />
|
||||
</Layout>
|
||||
<Layout noPadding gap="XS">
|
||||
<Label>Buttons</Label>
|
||||
<div class="buttons">
|
||||
{#each ButtonBorderRadiusOptions as option}
|
||||
<div
|
||||
class:active={customTheme.buttonBorderRadius === option.value}
|
||||
style={`--radius: ${option.value}`}
|
||||
>
|
||||
<Button
|
||||
secondary
|
||||
on:click={() => update("buttonBorderRadius", option.value)}
|
||||
>
|
||||
{option.label}
|
||||
</Button>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</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>
|
||||
</NavigationPanel>
|
||||
|
||||
<style>
|
||||
.buttons {
|
||||
display: grid;
|
||||
grid-template-columns: 100px 100px;
|
||||
gap: var(--spacing-m);
|
||||
}
|
||||
.buttons > div {
|
||||
display: contents;
|
||||
}
|
||||
.buttons > div :global(.spectrum-Button) {
|
||||
border-radius: var(--radius) !important;
|
||||
border-width: 1px;
|
||||
border-color: var(--spectrum-global-color-gray-400);
|
||||
font-weight: 600;
|
||||
}
|
||||
.buttons > div:hover :global(.spectrum-Button) {
|
||||
background: var(--spectrum-global-color-gray-700);
|
||||
border-color: var(--spectrum-global-color-gray-700);
|
||||
}
|
||||
.buttons > div.active :global(.spectrum-Button) {
|
||||
background: var(--spectrum-global-color-gray-200);
|
||||
color: var(--spectrum-global-color-gray-800);
|
||||
border-color: var(--spectrum-global-color-gray-600);
|
||||
}
|
||||
</style>
|
||||
<ThemeSettingsPanel />
|
||||
|
|
Loading…
Reference in New Issue