Updated approach to focus behaviour. Placeholder now updates to direct the user to the next required field they haven't entered

This commit is contained in:
Dean 2022-05-30 12:57:10 +01:00
parent e786a0d122
commit dbbf7afcd0
15 changed files with 116 additions and 176 deletions

View File

@ -33,7 +33,7 @@
let focus = false let focus = false
let pickerButton let pickerButton
$: focus = autofocus && pickerButton $: focus = autofocus && pickerButton !== null
$: sortedOptions = getSortedOptions(options, getOptionLabel, sort) $: sortedOptions = getSortedOptions(options, getOptionLabel, sort)
$: filteredOptions = getFilteredOptions( $: filteredOptions = getFilteredOptions(
@ -88,6 +88,14 @@
class:is-focused={focus} class:is-focused={focus}
bind:this={pickerButton} bind:this={pickerButton}
on:mousedown={onClick} on:mousedown={onClick}
on:keydown={e => {
var keycode = e.key
if (focus) {
if (keycode === "Enter") {
onClick(e)
}
}
}}
on:focus={() => { on:focus={() => {
focus = true focus = true
}} }}

View File

@ -417,28 +417,12 @@ export const getFrontendStore = () => {
} }
parentComponent._children.push(componentInstance) parentComponent._children.push(componentInstance)
const definition = store.actions.components.getDefinition(componentName)
// Save components and update UI // Save components and update UI
await store.actions.preview.saveSelected() await store.actions.preview.saveSelected()
store.update(state => { store.update(state => {
state.currentView = "component" state.currentView = "component"
state.selectedComponentId = componentInstance._id state.selectedComponentId = componentInstance._id
const focusSetting = definition.settings.filter(setting => {
return setting.required
})
const mappedSettings = focusSetting.map(setting => {
return {
key: setting.key,
target: state.selectedComponentId,
location: "component_settings",
}
})
if (focusSetting.length) {
state.builderFocus = mappedSettings
}
return state return state
}) })

View File

@ -6,6 +6,7 @@
import ResetFieldsButton from "./PropertyControls/ResetFieldsButton.svelte" import ResetFieldsButton from "./PropertyControls/ResetFieldsButton.svelte"
import { getComponentForSettingType } from "./PropertyControls/componentSettings" import { getComponentForSettingType } from "./PropertyControls/componentSettings"
import { Utils } from "@budibase/frontend-core" import { Utils } from "@budibase/frontend-core"
import { onMount } from "svelte"
export let componentDefinition export let componentDefinition
export let componentInstance export let componentInstance
@ -78,8 +79,41 @@
if (!$store.builderFocus) { if (!$store.builderFocus) {
return false return false
} }
return setting.required === true return (
setting.required === true && $store.builderFocus[0].key === setting.key
)
} }
onMount(() => {
const emptyFields = (definition, options) => {
if (!options) {
return []
}
return definition?.settings
? definition.settings.filter(setting => {
return (
setting.required &&
(!options[setting.key] || options[setting.key] == "")
)
})
: []
}
let target = emptyFields(componentDefinition, componentInstance)[0]
if (target) {
store.update(state => ({
...state,
builderFocus: [
{
location: "component_settings",
key: target.key,
target: componentInstance._id,
},
],
}))
}
})
</script> </script>
{#each sections as section, idx (section.name)} {#each sections as section, idx (section.name)}

View File

@ -25,6 +25,7 @@
export let otherSources export let otherSources
export let showAllQueries export let showAllQueries
export let bindings = [] export let bindings = []
export let autofocus = false
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
const arrayTypes = ["attachment", "array"] const arrayTypes = ["attachment", "array"]
@ -158,6 +159,7 @@
value={text} value={text}
options={[text]} options={[text]}
on:click={dropdownRight.show} on:click={dropdownRight.show}
{autofocus}
/> />
{#if value?.type === "query"} {#if value?.type === "query"}
<i class="ri-settings-5-line" on:click={openQueryParamsDrawer} /> <i class="ri-settings-5-line" on:click={openQueryParamsDrawer} />

View File

@ -975,7 +975,8 @@
{ {
"type": "text", "type": "text",
"label": "URL", "label": "URL",
"key": "url" "key": "url",
"required": true
}, },
{ {
"type": "select", "type": "select",
@ -3255,7 +3256,8 @@
{ {
"type": "dataSource", "type": "dataSource",
"label": "Data", "label": "Data",
"key": "dataSource" "key": "dataSource",
"required": true
}, },
{ {
"type": "filter", "type": "filter",
@ -3324,7 +3326,7 @@
], ],
"hasChildren": true, "hasChildren": true,
"showEmptyState": false, "showEmptyState": false,
"info": "Row selection is only compatible with internal or SQL tables", "info": "Row selection is only compatible with internal or SQL tables",
"settings": [ "settings": [
{ {
"type": "dataProvider", "type": "dataProvider",

View File

@ -154,6 +154,7 @@
selected, selected,
name, name,
editing, editing,
type: instance._component,
}) })
const initialise = instance => { const initialise = instance => {

View File

@ -28,25 +28,7 @@
class="placeholder" class="placeholder"
use:styleable={{ ...$component.styles, empty: true }} use:styleable={{ ...$component.styles, empty: true }}
> >
<Placeholder> <Placeholder />
<div slot="content">
Add the <mark>URL</mark> and start updating your background image&nbsp;
<span
class="showMe spectrum-Link"
on:click={() => {
builderStore.actions.setFocus([
{
location: "component_settings",
key: "url",
target: $component.id,
},
])
}}
>
Show me
</span>
</div>
</Placeholder>
</div> </div>
{/if} {/if}

View File

@ -28,25 +28,7 @@
/> />
{:else if $builderStore.inBuilder} {:else if $builderStore.inBuilder}
<div use:styleable={styles}> <div use:styleable={styles}>
<Placeholder> <Placeholder />
<div slot="content">
Add the <mark>Icon</mark> in the settings menu&nbsp;
<span
class="showMe spectrum-Link"
on:click={() => {
builderStore.actions.setFocus([
{
location: "component_settings",
key: "icon",
target: $component.id,
},
])
}}
>
Show me
</span>
</div>
</Placeholder>
</div> </div>
{/if} {/if}

View File

@ -15,25 +15,7 @@
class="placeholder" class="placeholder"
use:styleable={{ ...$component.styles, empty: true }} use:styleable={{ ...$component.styles, empty: true }}
> >
<Placeholder> <Placeholder />
<div slot="content">
Add the <mark>URL</mark> and start updating your image&nbsp;
<span
class="showMe spectrum-Link"
on:click={() => {
builderStore.actions.setFocus([
{
location: "component_settings",
key: "url",
target: $component.id,
},
])
}}
>
Show me
</span>
</div>
</Placeholder>
</div> </div>
{/if} {/if}

View File

@ -82,31 +82,7 @@
{:else if $builderStore.inBuilder || componentText} {:else if $builderStore.inBuilder || componentText}
{#if !url && !text} {#if !url && !text}
<div use:styleable={{ ...$component.styles, empty: true }}> <div use:styleable={{ ...$component.styles, empty: true }}>
<Placeholder> <Placeholder />
<div slot="content">
Add the <mark>URL</mark> and <mark>Text</mark> settings and start
using your link&nbsp;
<span
class="showMe spectrum-Link"
on:click={() => {
builderStore.actions.setFocus([
{
location: "component_settings",
key: "url",
target: $component.id,
},
{
location: "component_settings",
key: "text",
target: $component.id,
},
])
}}
>
Show me
</span>
</div>
</Placeholder>
</div> </div>
{:else if externalLink || openInNewTab} {:else if externalLink || openInNewTab}
<a <a

View File

@ -14,25 +14,6 @@
{#if value} {#if value}
<MarkdownViewer {value} {height} /> <MarkdownViewer {value} {height} />
{:else if $builderStore.inBuilder} {:else if $builderStore.inBuilder}
<Placeholder> <Placeholder />
<div slot="content">
Add some <mark>Markdown</mark> in the field setting to start using your
component&nbsp;
<span
class="showMe spectrum-Link"
on:click={() => {
builderStore.actions.setFocus([
{
location: "component_settings",
key: "value",
target: $component.id,
},
])
}}
>
Show me
</span>
</div>
</Placeholder>
{/if} {/if}
</div> </div>

View File

@ -1,18 +1,71 @@
<script> <script>
import { getContext } from "svelte" import { getContext } from "svelte"
import Manifest from "manifest.json"
import { builderStore } from "stores"
const { builderStore } = getContext("sdk") const { componentStore } = getContext("sdk")
const component = getContext("component") const component = getContext("component")
export let text export let text
$: componentInstance = componentStore.actions.getComponentById($component.id)
const getComponentKey = compId => {
if (!compId) {
return
}
const prefix = "@budibase/standard-components/"
let componentKey = compId.replace(prefix, "")
return componentKey
}
//Corify this somewhere
const emptyFields = (definition, options) => {
if (!options) {
return []
}
return definition?.settings
? definition.settings.filter(setting => {
return (
setting.required &&
(!options[setting.key] || options[setting.key] == "")
)
})
: []
}
const definition = Manifest[getComponentKey($component.type)]
$: focus_setting = emptyFields(definition, componentInstance)[0]
</script> </script>
{#if $builderStore.inBuilder} {#if $builderStore.inBuilder}
<div class="placeholder_wrap"> <div class="placeholder_wrap">
{#if !$$slots.content} {#if componentInstance && focus_setting}
<div>
<span>
Add the <mark>{focus_setting?.label}</mark> setting to start using your
component &nbsp;
</span>
<span
class="showMe spectrum-Link"
on:click={() => {
builderStore.actions.setFocus([
{
location: "component_settings",
key: focus_setting.key,
target: $component.id,
},
])
}}
>
Show me
</span>
</div>
{:else}
{text || $component.name || "Placeholder"} {text || $component.name || "Placeholder"}
<!-- {#if definition.hasChildren}
<span>: Add a component or two!</span>
{/if} -->
{/if} {/if}
<slot name="content" />
</div> </div>
{/if} {/if}

View File

@ -39,7 +39,7 @@
}} }}
> >
{#if $component.empty} {#if $component.empty}
<Placeholder text={$component.name} /> <Placeholder />
{:else} {:else}
<BlockComponent <BlockComponent
type="repeater" type="repeater"

View File

@ -13,35 +13,7 @@
<div use:chart={options} use:styleable={$component.styles} /> <div use:chart={options} use:styleable={$component.styles} />
{:else if $builderStore.inBuilder} {:else if $builderStore.inBuilder}
<div use:styleable={$component.styles}> <div use:styleable={$component.styles}>
<Placeholder> <Placeholder />
<div slot="content">
Use the settings panel start building your chart&nbsp;
<span
class="showMe spectrum-Link"
on:click={() => {
builderStore.actions.setFocus([
{
location: "component_settings",
key: "dataProvider",
target: $component.id,
},
{
location: "component_settings",
key: "valueColumns",
target: $component.id,
},
{
location: "component_settings",
key: "dataColumns",
target: $component.id,
},
])
}}
>
Show me
</span>
</div>
</Placeholder>
</div> </div>
{/if} {/if}

View File

@ -77,26 +77,7 @@
<Placeholder text="Form components need to be wrapped in a form" /> <Placeholder text="Form components need to be wrapped in a form" />
{:else if !fieldState} {:else if !fieldState}
{#if $builderStore.inBuilder} {#if $builderStore.inBuilder}
<Placeholder> <Placeholder />
<div slot="content">
Add the <mark>Field</mark> setting to start using your
component&nbsp;
<span
class="showMe spectrum-Link"
on:click={() => {
builderStore.actions.setFocus([
{
location: "component_settings",
key: "field",
target: $component.id,
},
])
}}
>
Show me
</span>
</div>
</Placeholder>
{/if} {/if}
{:else if schemaType && schemaType !== type && type !== "options"} {:else if schemaType && schemaType !== type && type !== "options"}
<Placeholder <Placeholder