Merge pull request #9951 from Budibase/settings-panel-update
Settings panel layout update
This commit is contained in:
commit
bcbed08a40
|
@ -7,7 +7,7 @@
|
||||||
export let title
|
export let title
|
||||||
export let fillWidth
|
export let fillWidth
|
||||||
export let left = "314px"
|
export let left = "314px"
|
||||||
export let width = "calc(100% - 576px)"
|
export let width = "calc(100% - 626px)"
|
||||||
|
|
||||||
let visible = false
|
let visible = false
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
export let title
|
export let title
|
||||||
export let icon
|
export let icon
|
||||||
export let expandable = false
|
|
||||||
export let showAddButton = false
|
export let showAddButton = false
|
||||||
export let showBackButton = false
|
export let showBackButton = false
|
||||||
export let showCloseButton = false
|
export let showCloseButton = false
|
||||||
|
@ -12,8 +11,8 @@
|
||||||
export let onClickCloseButton
|
export let onClickCloseButton
|
||||||
export let borderLeft = false
|
export let borderLeft = false
|
||||||
export let borderRight = false
|
export let borderRight = false
|
||||||
|
export let wide = false
|
||||||
|
|
||||||
let wide = false
|
|
||||||
$: customHeaderContent = $$slots["panel-header-content"]
|
$: customHeaderContent = $$slots["panel-header-content"]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -28,13 +27,6 @@
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<Heading size="XXS">{title || ""}</Heading>
|
<Heading size="XXS">{title || ""}</Heading>
|
||||||
</div>
|
</div>
|
||||||
{#if expandable}
|
|
||||||
<Icon
|
|
||||||
name={wide ? "Minimize" : "Maximize"}
|
|
||||||
hoverable
|
|
||||||
on:click={() => (wide = !wide)}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
{#if showAddButton}
|
{#if showAddButton}
|
||||||
<div class="add-button" on:click={onClickAddButton}>
|
<div class="add-button" on:click={onClickAddButton}>
|
||||||
<Icon name="Add" />
|
<Icon name="Add" />
|
||||||
|
@ -74,8 +66,8 @@
|
||||||
border-right: var(--border-light);
|
border-right: var(--border-light);
|
||||||
}
|
}
|
||||||
.panel.wide {
|
.panel.wide {
|
||||||
width: 420px;
|
width: 310px;
|
||||||
flex: 0 0 420px;
|
flex: 0 0 310px;
|
||||||
}
|
}
|
||||||
.header {
|
.header {
|
||||||
flex: 0 0 48px;
|
flex: 0 0 48px;
|
||||||
|
|
|
@ -74,11 +74,13 @@
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="property-control" class:highlighted={highlighted && nullishValue}>
|
<div
|
||||||
{#if type !== "boolean" && label}
|
class="property-control"
|
||||||
<div class="label">
|
class:wide={!label}
|
||||||
<Label>{label}</Label>
|
class:highlighted={highlighted && nullishValue}
|
||||||
</div>
|
>
|
||||||
|
{#if label}
|
||||||
|
<Label size="M">{label}</Label>
|
||||||
{/if}
|
{/if}
|
||||||
<div id={`${key}-prop-control`} class="control">
|
<div id={`${key}-prop-control`} class="control">
|
||||||
<svelte:component
|
<svelte:component
|
||||||
|
@ -90,7 +92,6 @@
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
bindings={allBindings}
|
bindings={allBindings}
|
||||||
name={key}
|
name={key}
|
||||||
text={label}
|
|
||||||
{nested}
|
{nested}
|
||||||
{key}
|
{key}
|
||||||
{type}
|
{type}
|
||||||
|
@ -105,28 +106,34 @@
|
||||||
<style>
|
<style>
|
||||||
.property-control {
|
.property-control {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: grid;
|
||||||
flex-direction: column;
|
grid-template-columns: 90px 1fr;
|
||||||
justify-content: flex-start;
|
align-items: center;
|
||||||
align-items: stretch;
|
|
||||||
transition: background 130ms ease-out, border-color 130ms ease-out;
|
transition: background 130ms ease-out, border-color 130ms ease-out;
|
||||||
border-left: 4px solid transparent;
|
border-left: 4px solid transparent;
|
||||||
margin: -6px calc(-1 * var(--spacing-xl));
|
margin: 0 calc(-1 * var(--spacing-xl));
|
||||||
padding: 6px var(--spacing-xl) 6px calc(var(--spacing-xl) - 4px);
|
padding: 0 var(--spacing-xl) 0 calc(var(--spacing-xl) - 4px);
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.property-control :global(.spectrum-FieldLabel) {
|
||||||
|
white-space: normal;
|
||||||
}
|
}
|
||||||
.property-control.highlighted {
|
.property-control.highlighted {
|
||||||
background: var(--spectrum-global-color-gray-300);
|
background: var(--spectrum-global-color-gray-300);
|
||||||
border-color: var(--spectrum-global-color-blue-400);
|
border-color: var(--spectrum-global-color-blue-400);
|
||||||
}
|
}
|
||||||
.label {
|
|
||||||
padding-bottom: var(--spectrum-global-dimension-size-65);
|
|
||||||
}
|
|
||||||
.control {
|
.control {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
.property-control.wide .control {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
.text {
|
.text {
|
||||||
margin-top: var(--spectrum-global-dimension-size-65);
|
|
||||||
font-size: var(--spectrum-global-dimension-font-size-75);
|
font-size: var(--spectrum-global-dimension-font-size-75);
|
||||||
color: var(--grey-6);
|
color: var(--grey-6);
|
||||||
|
grid-column: 2 / 2;
|
||||||
|
}
|
||||||
|
.property-control.wide .text {
|
||||||
|
grid-column: 1 / -1;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
|
|
||||||
{#if $selectedComponent}
|
{#if $selectedComponent}
|
||||||
{#key $selectedComponent._id}
|
{#key $selectedComponent._id}
|
||||||
<Panel {title} icon={componentDefinition?.icon} borderLeft>
|
<Panel {title} icon={componentDefinition?.icon} borderLeft wide>
|
||||||
<span slot="panel-header-content">
|
<span slot="panel-header-content">
|
||||||
<div class="settings-tabs">
|
<div class="settings-tabs">
|
||||||
{#each tabs as tab}
|
{#each tabs as tab}
|
||||||
|
|
|
@ -117,49 +117,51 @@
|
||||||
{#each sections as section, idx (section.name)}
|
{#each sections as section, idx (section.name)}
|
||||||
{#if section.visible}
|
{#if section.visible}
|
||||||
<DetailSummary name={section.name} collapsible={false}>
|
<DetailSummary name={section.name} collapsible={false}>
|
||||||
{#if idx === 0 && !componentInstance._component.endsWith("/layout") && !isScreen}
|
<div class="settings">
|
||||||
<PropertyControl
|
{#if idx === 0 && !componentInstance._component.endsWith("/layout") && !isScreen}
|
||||||
control={Input}
|
|
||||||
label="Name"
|
|
||||||
key="_instanceName"
|
|
||||||
value={componentInstance._instanceName}
|
|
||||||
onChange={val => updateSetting({ key: "_instanceName" }, val)}
|
|
||||||
/>
|
|
||||||
{/if}
|
|
||||||
{#each section.settings as setting (setting.key)}
|
|
||||||
{#if setting.visible}
|
|
||||||
<PropertyControl
|
<PropertyControl
|
||||||
type={setting.type}
|
control={Input}
|
||||||
control={getComponentForSetting(setting)}
|
label="Name"
|
||||||
label={setting.label}
|
key="_instanceName"
|
||||||
key={setting.key}
|
value={componentInstance._instanceName}
|
||||||
value={componentInstance[setting.key]}
|
onChange={val => updateSetting({ key: "_instanceName" }, val)}
|
||||||
defaultValue={setting.defaultValue}
|
|
||||||
nested={setting.nested}
|
|
||||||
onChange={val => updateSetting(setting, val)}
|
|
||||||
highlighted={$store.highlightedSettingKey === setting.key}
|
|
||||||
info={setting.info}
|
|
||||||
props={{
|
|
||||||
// Generic settings
|
|
||||||
placeholder: setting.placeholder || null,
|
|
||||||
|
|
||||||
// Select settings
|
|
||||||
options: setting.options || [],
|
|
||||||
|
|
||||||
// Number fields
|
|
||||||
min: setting.min ?? null,
|
|
||||||
max: setting.max ?? null,
|
|
||||||
}}
|
|
||||||
{bindings}
|
|
||||||
{componentBindings}
|
|
||||||
{componentInstance}
|
|
||||||
{componentDefinition}
|
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{#each section.settings as setting (setting.key)}
|
||||||
{#if idx === 0 && componentDefinition?.component?.endsWith("/fieldgroup")}
|
{#if setting.visible}
|
||||||
<ResetFieldsButton {componentInstance} />
|
<PropertyControl
|
||||||
{/if}
|
type={setting.type}
|
||||||
|
control={getComponentForSetting(setting)}
|
||||||
|
label={setting.label}
|
||||||
|
key={setting.key}
|
||||||
|
value={componentInstance[setting.key]}
|
||||||
|
defaultValue={setting.defaultValue}
|
||||||
|
nested={setting.nested}
|
||||||
|
onChange={val => updateSetting(setting, val)}
|
||||||
|
highlighted={$store.highlightedSettingKey === setting.key}
|
||||||
|
info={setting.info}
|
||||||
|
props={{
|
||||||
|
// Generic settings
|
||||||
|
placeholder: setting.placeholder || null,
|
||||||
|
|
||||||
|
// Select settings
|
||||||
|
options: setting.options || [],
|
||||||
|
|
||||||
|
// Number fields
|
||||||
|
min: setting.min ?? null,
|
||||||
|
max: setting.max ?? null,
|
||||||
|
}}
|
||||||
|
{bindings}
|
||||||
|
{componentBindings}
|
||||||
|
{componentInstance}
|
||||||
|
{componentDefinition}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
{#if idx === 0 && componentDefinition?.component?.endsWith("/fieldgroup")}
|
||||||
|
<ResetFieldsButton {componentInstance} />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
</DetailSummary>
|
</DetailSummary>
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
|
@ -168,3 +170,13 @@
|
||||||
<EjectBlockButton />
|
<EjectBlockButton />
|
||||||
</DetailSummary>
|
</DetailSummary>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.settings {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -27,7 +27,6 @@
|
||||||
<StyleSection
|
<StyleSection
|
||||||
{style}
|
{style}
|
||||||
name={style.label}
|
name={style.label}
|
||||||
columns={style.columns}
|
|
||||||
properties={style.settings}
|
properties={style.settings}
|
||||||
{componentInstance}
|
{componentInstance}
|
||||||
{bindings}
|
{bindings}
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
import { store } from "builderStore"
|
import { store } from "builderStore"
|
||||||
|
|
||||||
export let name
|
export let name
|
||||||
export let columns
|
|
||||||
export let properties
|
export let properties
|
||||||
export let componentInstance
|
export let componentInstance
|
||||||
export let bindings = []
|
export let bindings = []
|
||||||
|
@ -34,27 +33,27 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<DetailSummary collapsible={false} name={`${name}${changed ? " *" : ""}`}>
|
<DetailSummary collapsible={false} name={`${name}${changed ? " *" : ""}`}>
|
||||||
<div class="group-content" style="grid-template-columns: {columns || '1fr'}">
|
<div class="styles">
|
||||||
{#each properties as prop (`${componentInstance._id}-${prop.key}-${prop.label}`)}
|
{#each properties as prop (`${componentInstance._id}-${prop.key}-${prop.label}`)}
|
||||||
<div style="grid-column: {prop.column || 'auto'}">
|
<PropertyControl
|
||||||
<PropertyControl
|
label={`${prop.label}${hasPropChanged(style, prop) ? " *" : ""}`}
|
||||||
label={`${prop.label}${hasPropChanged(style, prop) ? " *" : ""}`}
|
control={prop.control}
|
||||||
control={prop.control}
|
key={prop.key}
|
||||||
key={prop.key}
|
value={style[prop.key]}
|
||||||
value={style[prop.key]}
|
onChange={val => updateStyle(prop.key, val)}
|
||||||
onChange={val => updateStyle(prop.key, val)}
|
props={getControlProps(prop)}
|
||||||
props={getControlProps(prop)}
|
{bindings}
|
||||||
{bindings}
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
</DetailSummary>
|
</DetailSummary>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.group-content {
|
.styles {
|
||||||
display: grid;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
gap: var(--spacing-l);
|
gap: 8px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -3,7 +3,6 @@ import ColorPicker from "components/design/settings/controls/ColorPicker.svelte"
|
||||||
|
|
||||||
export const margin = {
|
export const margin = {
|
||||||
label: "Margin",
|
label: "Margin",
|
||||||
columns: "1fr 1fr",
|
|
||||||
settings: [
|
settings: [
|
||||||
{
|
{
|
||||||
label: "Top",
|
label: "Top",
|
||||||
|
@ -90,7 +89,6 @@ export const margin = {
|
||||||
|
|
||||||
export const padding = {
|
export const padding = {
|
||||||
label: "Padding",
|
label: "Padding",
|
||||||
columns: "1fr 1fr",
|
|
||||||
settings: [
|
settings: [
|
||||||
{
|
{
|
||||||
label: "Top",
|
label: "Top",
|
||||||
|
@ -177,7 +175,6 @@ export const padding = {
|
||||||
|
|
||||||
export const size = {
|
export const size = {
|
||||||
label: "Size",
|
label: "Size",
|
||||||
columns: "1fr 1fr",
|
|
||||||
settings: [
|
settings: [
|
||||||
{
|
{
|
||||||
label: "Width",
|
label: "Width",
|
||||||
|
@ -196,7 +193,6 @@ export const size = {
|
||||||
|
|
||||||
export const background = {
|
export const background = {
|
||||||
label: "Background",
|
label: "Background",
|
||||||
columns: "auto 1fr",
|
|
||||||
settings: [
|
settings: [
|
||||||
{
|
{
|
||||||
label: "Color",
|
label: "Color",
|
||||||
|
@ -285,7 +281,6 @@ export const background = {
|
||||||
|
|
||||||
export const border = {
|
export const border = {
|
||||||
label: "Border",
|
label: "Border",
|
||||||
columns: "1fr 1fr",
|
|
||||||
settings: [
|
settings: [
|
||||||
{
|
{
|
||||||
label: "Color",
|
label: "Color",
|
||||||
|
|
|
@ -1,22 +1,13 @@
|
||||||
<script>
|
<script>
|
||||||
import Panel from "components/design/Panel.svelte"
|
import Panel from "components/design/Panel.svelte"
|
||||||
import { goto } from "@roxi/routify"
|
import { goto } from "@roxi/routify"
|
||||||
import {
|
import { Layout, Search, Icon, Body, notifications } from "@budibase/bbui"
|
||||||
Layout,
|
|
||||||
ActionGroup,
|
|
||||||
ActionButton,
|
|
||||||
Search,
|
|
||||||
Icon,
|
|
||||||
Body,
|
|
||||||
notifications,
|
|
||||||
} from "@budibase/bbui"
|
|
||||||
import structure from "./componentStructure.json"
|
import structure from "./componentStructure.json"
|
||||||
import { store, selectedComponent, selectedScreen } from "builderStore"
|
import { store, selectedComponent, selectedScreen } from "builderStore"
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
import { fly } from "svelte/transition"
|
import { fly } from "svelte/transition"
|
||||||
import { findComponentPath } from "builderStore/componentUtils"
|
import { findComponentPath } from "builderStore/componentUtils"
|
||||||
|
|
||||||
let section = "components"
|
|
||||||
let searchString
|
let searchString
|
||||||
let searchRef
|
let searchRef
|
||||||
let selectedIndex
|
let selectedIndex
|
||||||
|
@ -37,7 +28,6 @@
|
||||||
allowedComponents,
|
allowedComponents,
|
||||||
searchString
|
searchString
|
||||||
)
|
)
|
||||||
$: blocks = enrichedStructure.find(x => x.name === "Blocks").children
|
|
||||||
$: orderMap = createComponentOrderMap(componentList)
|
$: orderMap = createComponentOrderMap(componentList)
|
||||||
|
|
||||||
const getAllowedComponents = (allComponents, screen, component) => {
|
const getAllowedComponents = (allComponents, screen, component) => {
|
||||||
|
@ -127,6 +117,11 @@
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Swap blocks and plugins
|
||||||
|
let tmp = enrichedStructure[1]
|
||||||
|
enrichedStructure[1] = enrichedStructure[0]
|
||||||
|
enrichedStructure[0] = tmp
|
||||||
|
|
||||||
return enrichedStructure
|
return enrichedStructure
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,11 +132,6 @@
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove blocks if there is no search string
|
|
||||||
if (!search) {
|
|
||||||
structure = structure.filter(category => category.name !== "Blocks")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return only items which match the search string
|
// Return only items which match the search string
|
||||||
let filteredStructure = []
|
let filteredStructure = []
|
||||||
structure.forEach(category => {
|
structure.forEach(category => {
|
||||||
|
@ -225,6 +215,7 @@
|
||||||
showCloseButton
|
showCloseButton
|
||||||
onClickCloseButton={() => $goto("../")}
|
onClickCloseButton={() => $goto("../")}
|
||||||
borderLeft
|
borderLeft
|
||||||
|
wide
|
||||||
>
|
>
|
||||||
<Layout paddingX="L" paddingY="XL" gap="S">
|
<Layout paddingX="L" paddingY="XL" gap="S">
|
||||||
<Search
|
<Search
|
||||||
|
@ -233,64 +224,31 @@
|
||||||
on:change={e => (searchString = e.detail)}
|
on:change={e => (searchString = e.detail)}
|
||||||
bind:inputRef={searchRef}
|
bind:inputRef={searchRef}
|
||||||
/>
|
/>
|
||||||
{#if !searchString}
|
{#if filteredStructure.length}
|
||||||
<ActionGroup compact justified>
|
{#each filteredStructure as category}
|
||||||
<ActionButton
|
<Layout noPadding gap="XS">
|
||||||
fullWidth
|
<div class="category-label">{category.name}</div>
|
||||||
selected={section === "components"}
|
{#each category.children as component}
|
||||||
on:click={() => (section = "components")}>Components</ActionButton
|
<div
|
||||||
>
|
draggable="true"
|
||||||
<ActionButton
|
on:dragstart={() => onDragStart(component.component)}
|
||||||
fullWidth
|
on:dragend={onDragEnd}
|
||||||
selected={section === "blocks"}
|
class="component"
|
||||||
on:click={() => (section = "blocks")}>Blocks</ActionButton
|
class:selected={selectedIndex === orderMap[component.component]}
|
||||||
>
|
on:click={() => addComponent(component.component)}
|
||||||
</ActionGroup>
|
on:mouseover={() => (selectedIndex = null)}
|
||||||
{/if}
|
on:focus
|
||||||
{#if searchString || section === "components"}
|
>
|
||||||
{#if filteredStructure.length}
|
<Icon name={component.icon} />
|
||||||
{#each filteredStructure as category}
|
<Body size="XS">{component.name}</Body>
|
||||||
<Layout noPadding gap="XS">
|
</div>
|
||||||
<div class="category-label">{category.name}</div>
|
{/each}
|
||||||
{#each category.children as component}
|
</Layout>
|
||||||
<div
|
{/each}
|
||||||
draggable="true"
|
|
||||||
on:dragstart={() => onDragStart(component.component)}
|
|
||||||
on:dragend={onDragEnd}
|
|
||||||
class="component"
|
|
||||||
class:selected={selectedIndex ===
|
|
||||||
orderMap[component.component]}
|
|
||||||
on:click={() => addComponent(component.component)}
|
|
||||||
on:mouseover={() => (selectedIndex = null)}
|
|
||||||
on:focus
|
|
||||||
>
|
|
||||||
<Icon name={component.icon} />
|
|
||||||
<Body size="XS">{component.name}</Body>
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</Layout>
|
|
||||||
{/each}
|
|
||||||
{:else}
|
|
||||||
<Body size="S">
|
|
||||||
There aren't any components matching the current filter
|
|
||||||
</Body>
|
|
||||||
{/if}
|
|
||||||
{:else}
|
{:else}
|
||||||
<Body size="S">Blocks are collections of pre-built components</Body>
|
<Body size="S">
|
||||||
<Layout noPadding gap="XS">
|
There aren't any components matching the current filter
|
||||||
{#each blocks as block}
|
</Body>
|
||||||
<div
|
|
||||||
draggable="true"
|
|
||||||
class="component"
|
|
||||||
on:click={() => addComponent(block.component)}
|
|
||||||
on:dragstart={() => onDragStart(block.component)}
|
|
||||||
on:dragend={onDragEnd}
|
|
||||||
>
|
|
||||||
<Icon name={block.icon} />
|
|
||||||
<Body size="XS">{block.name}</Body>
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</Layout>
|
|
||||||
{/if}
|
{/if}
|
||||||
</Layout>
|
</Layout>
|
||||||
</Panel>
|
</Panel>
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
let width = window.innerWidth
|
let width = window.innerWidth
|
||||||
let height = window.innerHeight
|
let height = window.innerHeight
|
||||||
const tabletBreakpoint = 768
|
const tabletBreakpoint = 720
|
||||||
const desktopBreakpoint = 1280
|
const desktopBreakpoint = 1280
|
||||||
const resizeObserver = new ResizeObserver(entries => {
|
const resizeObserver = new ResizeObserver(entries => {
|
||||||
if (entries?.[0]) {
|
if (entries?.[0]) {
|
||||||
|
|
Loading…
Reference in New Issue