feat(frontend): Add select component to app builder (#1021)

* feat(frontend): Add select input to app builder
This commit is contained in:
Ádám Kovács
2022-12-15 09:51:43 +01:00
committed by GitHub
parent 054689fb10
commit dd22a3ff42
5 changed files with 120 additions and 31 deletions

View File

@@ -0,0 +1,51 @@
<script lang="ts">
import { getContext } from 'svelte'
import Select from 'svelte-select'
import type { AppInput } from '../../inputType'
import type { Output } from '../../rx'
import type { AppEditorContext } from '../../types'
import AlignWrapper from '../helpers/AlignWrapper.svelte'
import InputValue from '../helpers/InputValue.svelte'
export const staticOutputs: string[] = ['loading', 'result']
export let id: string
export let configuration: Record<string, AppInput>
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = undefined
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
const { worldStore } = getContext<AppEditorContext>('AppEditorContext')
let label: string
let items: string[]
let itemKey: string
$: outputs = $worldStore?.outputsById[id] as {
result: Output<string | undefined>
}
function onChange({detail}: CustomEvent) {
outputs?.result.set(detail?.[itemKey] || undefined)
}
</script>
<InputValue input={configuration.label} bind:value={label} />
<InputValue input={configuration.items} bind:value={items} />
<InputValue input={configuration.itemKey} bind:value={itemKey} />
<AlignWrapper {horizontalAlignment} {verticalAlignment}>
<!-- svelte-ignore a11y-label-has-associated-control -->
<label class="block w-full">
<div>
{label}
</div>
<div>
<Select
on:clear={onChange}
on:change={onChange}
{items}
class="w-full"
placeholder="Select an item"
/>
</div>
</label>
</AlignWrapper>

View File

@@ -8,6 +8,7 @@
import type { AppComponent, AppEditorContext } from '../types'
import ButtonComponent from '../components/buttons/AppButton.svelte'
import PieChartComponent from '../components/dataDisplay/AppPieChart.svelte'
import SelectComponent from '../components/selectInputs/AppSelect.svelte'
import CheckboxComponent from '../components/selectInputs/AppCheckbox.svelte'
import TextInputComponent from '../components/textInputs/AppTextInput.svelte'
import NumberInputComponent from '../components/numberInputs/AppNumberInput.svelte'
@@ -29,11 +30,12 @@
<div
class={classNames(
' border overflow-auto cursor-pointer h-full bg-white',
' border cursor-pointer h-full bg-white',
shouldDisplayOverlay ? 'border-blue-500' : 'border-white',
!selected && $mode !== 'preview' && !component.card ? 'border-gray-100' : '',
$mode !== 'preview' && !$connectingInput.opened ? 'hover:border-blue-500' : '',
component.card ? 'p-2' : '',
component.softWrap ? '' : 'overflow-auto',
'relative'
)}
>
@@ -73,6 +75,11 @@
bind:componentInput={component.componentInput}
bind:staticOutputs={$staticOutputs[component.id]}
/>
{:else if component.type === 'selectcomponent'}
<SelectComponent
{...component}
bind:staticOutputs={$staticOutputs[component.id]}
/>
{:else if component.type === 'formcomponent'}
<AppForm
{...component}

View File

@@ -15,31 +15,20 @@
const { app } = getContext<AppEditorContext>('AppEditorContext')
function getMinDimensionsByComponent(componentType: string, column: number): Size {
console.log(componentType, column)
if (componentType === 'buttoncomponent') {
return column === 3 ? { w: 1, h: 1 } : { w: 3, h: 1 }
} else if (componentType === 'formcomponent') {
return column === 3 ? { w: 2, h: 3 } : { w: 6, h: 4 }
} else if (componentType === 'textcomponent') {
return column === 3 ? { w: 1, h: 1 } : { w: 3, h: 1 }
} else if (componentType === 'textinputcomponent') {
return column === 3 ? { w: 1, h: 2 } : { w: 3, h: 2 }
} else if (componentType === 'numberinputcomponent') {
return column === 3 ? { w: 1, h: 2 } : { w: 3, h: 2 }
} else if (componentType === 'barchartcomponent') {
return column === 3 ? { w: 2, h: 4 } : { w: 6, h: 4 }
} else if (componentType === 'piechartcomponent') {
return column === 3 ? { w: 2, h: 4 } : { w: 6, h: 4 }
} else if (componentType === 'tablecomponent') {
return column === 3 ? { w: 3, h: 4 } : { w: 12, h: 4 }
} else if (componentType === 'displaycomponent') {
return column === 3 ? { w: 2, h: 2 } : { w: 6, h: 4 }
} else if (componentType === 'checkboxcomponent') {
return column === 3 ? { w: 1, h: 1 } : { w: 3, h: 1 }
} else {
return { w: 2, h: 1 }
function getMinDimensionsByComponent(componentType: AppComponent['type'], column: number): Size {
// Dimensions key formula: <mobile width>:<mobile height>-<desktop width>:<desktop height>
const dimensions: Record<`${number}:${number}-${number}:${number}`, AppComponent['type'][]> = {
'1:1-3:1': [ 'buttoncomponent', 'textcomponent', 'checkboxcomponent' ],
'1:2-3:2': [ 'textinputcomponent', 'numberinputcomponent', 'selectcomponent' ],
'2:2-6:4': [ 'displaycomponent' ],
'2:3-6:4': [ 'formcomponent' ],
'2:4-6:4': [ 'barchartcomponent', 'piechartcomponent' ],
'3:4-12:4': [ 'tablecomponent' ],
}
// Finds the key that is associated with the component type and extracts the dimensions from it
const [dimension] = Object.entries(dimensions).find(([_, value]) => value.includes(componentType)) || ['2:1-2:1']
const size = dimension.split('-')[column === 3 ? 0 : 1].split(':')
return { w: +size[0], h: +size[1] }
}
function addComponent(appComponent: AppComponent) {

View File

@@ -27,9 +27,9 @@ const inputs: ComponentSet = {
label: {
type: 'static',
visible: false,
value: 'Title',
value: 'Label',
fieldType: 'textarea',
defaultValue: 'Title'
defaultValue: 'Label'
}
},
card: false
@@ -38,17 +38,53 @@ const inputs: ComponentSet = {
...defaultAlignement,
id: 'checkboxcomponent',
type: 'checkboxcomponent',
componentInput: undefined,
configuration: {
label: {
type: 'static',
visible: true,
value: 'Lorem ipsum',
value: 'Label',
fieldType: 'textarea',
defaultValue: 'Lorem ipsum'
defaultValue: 'Label'
}
},
componentInput: undefined,
card: false
},
{
...defaultAlignement,
id: 'selectcomponent',
type: 'selectcomponent',
componentInput: undefined,
configuration: {
label: {
type: 'static',
visible: false,
value: 'Label',
fieldType: 'textarea',
defaultValue: 'Label'
},
items: {
type: 'static',
fieldType: 'array',
subFieldType: 'object',
defaultValue: [
{ value: 'foo', label: 'Foo' },
{ value: 'bar', label: 'Bar' }
],
value: [
{ value: 'foo', label: 'Foo' },
{ value: 'bar', label: 'Bar' }
]
},
itemKey: {
type: 'static',
fieldType: 'text',
defaultValue: 'value',
value: 'value'
}
},
card: false,
softWrap: true
}
]
}
@@ -138,7 +174,7 @@ const buttons: ComponentSet = {
}
const dataDisplay: ComponentSet = {
title: 'Data Display',
title: 'Data Displays',
components: [
{
...defaultAlignement,

View File

@@ -53,6 +53,12 @@ export interface BaseAppComponent extends Partial<Aligned> {
componentInput: AppInput | undefined
configuration: Record<string, StaticAppInput | ConnectedAppInput | UserAppInput>
card: boolean | undefined
/**
* If `true` then the wrapper will allow items to flow outside of it's borders.
*
* *For example when the component has a popup like `Select`*
*/
softWrap?: boolean
// TODO: add min/max width/height
}