feat(front): Rework how summaries are edited in the flow editor (#632)

* feat(front): Rework how summaries are edited in the flow editor

* feat(front): add IconOnly mode, to better handle responsivness

* feat(front): Fix FlowModule summary input width

* feat(front): simplify summary placeholder

* feat(front): Fix minimap resizing issues + fix minimap delete
This commit is contained in:
Faton Ramadani
2022-09-28 19:31:24 +02:00
committed by GitHub
parent 537ac2b7a0
commit b0ac674f46
13 changed files with 148 additions and 85 deletions
+9 -6
View File
@@ -18,6 +18,7 @@
export let lang: 'python3' | 'deno' | 'go'
export let editor: Editor
export let websocketAlive: { pyright: boolean; black: boolean; deno: boolean }
export let iconOnly: boolean = false
let variablePicker: ItemPicker
let resourcePicker: ItemPicker
@@ -192,6 +193,7 @@
size="xs"
spacingSize="md"
startIcon={{ icon: faFile }}
{iconOnly}
>
Insert variable
</Button>
@@ -203,8 +205,9 @@
spacingSize="md"
color="light"
on:click={resourcePicker.openModal}
{iconOnly}
startIcon={{ icon: faCube }}
>
<Icon data={faCube} class="mr-2" />
Insert resource
</Button>
</div>
@@ -216,8 +219,9 @@
spacingSize="md"
color="light"
on:click={scriptPicker.openModal}
{iconOnly}
startIcon={{ icon: faCode }}
>
<Icon data={faCode} class="mr-2" />
Search script
</Button>
</div>
@@ -229,8 +233,9 @@
spacingSize="md"
color="light"
on:click={editor.clearContent}
{iconOnly}
startIcon={{ icon: faRotateLeft }}
>
<Icon data={faRotateLeft} class="mr-2" />
Reset content
</Button>
</div>
@@ -241,16 +246,14 @@
spacingSize="md"
color="light"
on:click={editor.reloadWebsocket}
startIcon={{ icon: faRotate }}
>
<Icon data={faRotate} class="h-4 w-4 mr-2" />
Reload assistants
<span class="ml-1">
{#if lang == 'deno'}
(<span class={websocketAlive.deno ? 'text-green-600' : 'text-red-700'}>Deno</span>)
{:else if lang == 'python3'}
(<span class={websocketAlive.pyright ? 'text-green-600' : 'text-red-700'}>Pyright</span>
&nbsp;
<span class={websocketAlive.black ? 'text-green-600' : 'text-red-700'}>Black</span>)
{/if}
</span>
@@ -1,14 +1,16 @@
<script lang="ts">
import Badge from './common/badge/Badge.svelte'
import IconedResourceType from './IconedResourceType.svelte'
export let path: string
</script>
{#if path.startsWith('hub/')}
<span class="inline-flex flex-row gap-x-2 items-center">
<div class="flex space-x-2 items-center">
{#if path.startsWith('hub/')}
<IconedResourceType name={path.split('/')[2]} silent={true} />
<span>{path}</span>
</span>
{:else}
{path}
{/if}
{:else}
<Badge color="blue">Workspace</Badge>
<span>{path}</span>
{/if}
</div>
@@ -89,7 +89,7 @@
classes: 'animate-spin'
}}
>
'Cancel'
Cancel
</Button>
{/if}
</div>
@@ -101,14 +101,15 @@
<LogViewer content={testJob?.logs} isLoading={testIsLoading} />
</top>
<down slot="down">
<pre
class="overflow-x-auto break-all relative h-full p-2 text-sm">{#if testJob && 'result' in testJob && testJob.result}<DisplayResult
result={testJob.result}
/>
{:else if testIsLoading}Waiting for Result...
{:else}Test to see result here
<pre class="overflow-x-auto break-all relative h-full p-2 text-sm">
{#if testJob && 'result' in testJob && testJob.result}
<DisplayResult result={testJob.result} />
{:else if testIsLoading}
Waiting for Result...
{:else}
Test to see result here
{/if}
</pre>
</pre>
</down>
</VSplitPane>
</div>
@@ -11,6 +11,7 @@
export let disabled: boolean = false
export let href: string | undefined = undefined
export let target: Button.Target = '_self'
export let iconOnly: boolean = false
export let startIcon: { icon: any; classes?: string } | undefined = undefined
export let endIcon: { icon: any; classes?: string } | undefined = undefined
@@ -70,7 +71,7 @@
spacingClasses[spacingSize],
'focus:ring-4 font-medium',
'rounded-md',
'flex justify-center items-center text-center',
'flex justify-center items-center text-center whitespace-nowrap',
btnClasses,
disabled ? 'pointer-events-none cursor-default filter grayscale' : ''
),
@@ -83,15 +84,17 @@
{#if startIcon}
<Icon
data={startIcon.icon}
class={classNames('mr-2', startIcon.classes)}
class={classNames(iconOnly ? undefined : 'mr-2', startIcon.classes)}
scale={iconScale[size]}
/>
{/if}
<slot />
{#if !iconOnly}
<slot />
{/if}
{#if endIcon}
<Icon
data={endIcon.icon}
class={classNames('ml-2', endIcon.classes)}
class={classNames(iconOnly ? undefined : 'ml-2', endIcon.classes)}
scale={iconScale[size]}
/>
{/if}
@@ -101,15 +104,17 @@
{#if startIcon}
<Icon
data={startIcon.icon}
class={classNames('mr-2', startIcon.classes)}
class={classNames(iconOnly ? undefined : 'mr-2', startIcon.classes)}
scale={iconScale[size]}
/>
{/if}
<slot />
{#if !iconOnly}
<slot />
{/if}
{#if endIcon}
<Icon
data={endIcon.icon}
class={classNames('ml-2', endIcon.classes)}
class={classNames(iconOnly ? undefined : 'ml-2', endIcon.classes)}
scale={iconScale[size]}
/>
{/if}
@@ -11,7 +11,7 @@
<FlowEditorHeader />
<div class="h-full overflow-hidden">
<HSplitPane leftPaneSize="25%" rightPaneSize="75%" minLeftPaneSize="20%" minRightPaneSize="20%">
<HSplitPane leftPaneSize="25%" rightPaneSize="75%" minLeftPaneSize="20%" minRightPaneSize="40%">
<left slot="left" class="h-full ">
<div class="h-full overflow-auto p-4 bg-gray-50">
{#if $flowStore.value.modules && $flowStateStore.modules}
@@ -6,7 +6,7 @@
export let flowModule: FlowModule | undefined = undefined
</script>
<FlowCardHeader {title} {flowModule}>
<FlowCardHeader {title} bind:flowModule>
<slot name="header" />
</FlowCardHeader>
<slot />
@@ -1,27 +1,40 @@
<script lang="ts">
import type { BadgeColor } from '$lib/components/common'
import Badge from '$lib/components/common/badge/Badge.svelte'
import IconedPath from '$lib/components/IconedPath.svelte'
import type { FlowModule } from '$lib/gen'
import { RawScript, type FlowModule } from '$lib/gen'
import { isEmptyFlowModule } from '../flowStateUtils'
export let flowModule: FlowModule | undefined = undefined
export let title: string | undefined = undefined
$: flowModuleTitle =
flowModule?.summary ||
(flowModule?.value.type === 'rawscript'
? `Inline ${flowModule?.value.language}`
: 'Select a script')
$: shouldPick = flowModule && isEmptyFlowModule(flowModule)
const languageColors: Record<RawScript.language, BadgeColor> = {
[RawScript.language.GO]: 'dark-indigo',
[RawScript.language.DENO]: 'dark-blue',
[RawScript.language.PYTHON3]: 'dark-green'
}
</script>
<div
class="flex items-center justify-between flex-wrap py-2 px-4 border-b bg-gray-50 shadow-sm h-12"
class="flex items-center justify-between py-2 px-4 border-b bg-gray-50 shadow-sm space-x-2 h-12 flex-nowrap"
>
{#if flowModule}
<span class="text-xs font-bold text-gray-900 flex flex-col shrink">
{#if 'path' in flowModule.value && flowModule.value.path}
<IconedPath path={flowModule.value.path} />
{:else if 'language' in flowModule.value && flowModule.value.language}
{flowModuleTitle}
{/if}
<span class="text-sm w-full">
<div class="flex items-center space-x-2">
{#if shouldPick}
<span class="font-bold text-xs">Select a script</span>
{:else if flowModule?.value.type === 'rawscript'}
<Badge color={languageColors[flowModule?.value.language] ?? 'gray'} capitalize>
{flowModule?.value.language}
</Badge>
<input bind:value={flowModule.summary} placeholder={'Summary'} />
{:else if flowModule?.value.type === 'script' && 'path' in flowModule.value && flowModule.value.path}
<IconedPath path={flowModule.value.path} />
<input bind:value={flowModule.summary} placeholder="Summary" class="ml-2" />
{/if}
</div>
</span>
{/if}
{#if title}
@@ -1,3 +1,10 @@
<script context="module" lang="ts">
export type FlowModuleWidthContext = {
width: Writable<number>
threshold: number
}
</script>
<script lang="ts">
import { VSplitPane } from 'svelte-split-pane'
@@ -26,10 +33,11 @@
import { flowStateStore, type FlowModuleState } from '../flowState'
import { scriptLangToEditorLang } from '$lib/utils'
import PropPickerWrapper from '../propPicker/PropPickerWrapper.svelte'
import { getContext } from 'svelte'
import { getContext, setContext } from 'svelte'
import type { FlowEditorContext } from '../types'
import FlowModuleAdvancedSettings from './FlowModuleAdvancedSettings.svelte'
import { loadSchemaFromModule } from '../utils'
import { writable, type Writable } from 'svelte/store'
import FlowModuleScript from './FlowModuleScript.svelte'
const { selectedId, select } = getContext<FlowEditorContext>('FlowEditorContext')
@@ -83,27 +91,33 @@
async function applyCreateLoop() {
await apply(createLoop, null)
}
export const FLOW_MODULE_WIDTH_THRESHOLD = 768
const width = writable<number>(0)
setContext<FlowModuleWidthContext>('FlowModuleWidth', {
width,
threshold: FLOW_MODULE_WIDTH_THRESHOLD
})
</script>
<svelte:window on:keydown={onKeyDown} />
<div class="flex flex-col h-full ">
<FlowCard {flowModule}>
<div class="flex flex-col h-full" bind:clientWidth={$width}>
<FlowCard bind:flowModule>
<svelte:fragment slot="header">
<div class="flex-shrink-0">
<FlowModuleHeader
bind:module={flowModule}
on:delete
on:fork={() => apply(fork, flowModule)}
on:createScriptFromInlineScript={() => {
apply(createScriptFromInlineScript, {
flowModule: flowModule,
suffix: $selectedId,
schema: flowModuleState.schema
})
}}
/>
</div>
<FlowModuleHeader
bind:module={flowModule}
on:delete
on:fork={() => apply(fork, flowModule)}
on:createScriptFromInlineScript={() => {
apply(createScriptFromInlineScript, {
flowModule: flowModule,
suffix: $selectedId,
schema: flowModuleState.schema
})
}}
/>
</svelte:fragment>
{#if shouldPick}
<FlowInputs
@@ -126,7 +140,12 @@
{:else}
{#if flowModule.value.type === 'rawscript'}
<div class="flex-shrink-0 border-b p-1">
<EditorBar {editor} lang={flowModule.value['language'] ?? 'deno'} {websocketAlive} />
<EditorBar
{editor}
lang={flowModule.value['language'] ?? 'deno'}
{websocketAlive}
iconOnly={$width < FLOW_MODULE_WIDTH_THRESHOLD}
/>
</div>
{/if}
@@ -1,12 +1,13 @@
<script lang="ts">
import Button from '$lib/components/common/button/Button.svelte'
import { isEmptyFlowModule } from '$lib/components/flows/flowStateUtils'
import type { FlowModule } from '$lib/gen'
import { faCodeBranch, faSave, faTrashAlt } from '@fortawesome/free-solid-svg-icons'
import { Button } from 'flowbite-svelte'
import { createEventDispatcher, getContext } from 'svelte'
import Icon from 'svelte-awesome'
import type { FlowEditorContext } from '../types'
import type { FlowModuleWidthContext } from './FlowModule.svelte'
import RemoveStepConfirmationModal from './RemoveStepConfirmationModal.svelte'
export let module: FlowModule
@@ -17,25 +18,42 @@
const dispatch = createEventDispatcher()
const { selectedId, select } = getContext<FlowEditorContext>('FlowEditorContext')
const { width, threshold } = getContext<FlowModuleWidthContext>('FlowModuleWidth')
$: iconOnly = $width < threshold
</script>
<div class="flex flex-row space-x-2" on:click|stopPropagation={() => undefined}>
<div class="flex flex-row space-x-2">
{#if module.value.type === 'script' && !shouldPick}
<Button size="xs" color="alternative" on:click={() => dispatch('fork')}>
<Icon data={faCodeBranch} class="mr-2" />
<Button
size="xs"
color="light"
variant="border"
on:click={() => dispatch('fork')}
startIcon={{ icon: faCodeBranch }}
{iconOnly}
>
Fork
</Button>
{/if}
{#if module.value.type === 'rawscript' && !shouldPick}
<Button size="xs" color="alternative" on:click={() => dispatch('createScriptFromInlineScript')}>
<Icon data={faSave} class="mr-2" />
<Button
size="xs"
color="light"
variant="border"
startIcon={{ icon: faSave }}
on:click={() => dispatch('createScriptFromInlineScript')}
{iconOnly}
>
Save to workspace
</Button>
{/if}
<Button
size="xs"
color="alternative"
color="light"
variant="border"
startIcon={{ icon: faTrashAlt }}
{iconOnly}
on:click={(event) => {
if (event.shiftKey || shouldPick) {
dispatch('delete')
@@ -45,7 +63,6 @@
}
}}
>
<Icon data={faTrashAlt} class="mr-2" />
{$selectedId.includes('failure') ? 'Delete error handler' : 'Remove step'}
</Button>
</div>
@@ -14,7 +14,7 @@
<div class="flex justify-between items-center border-y p-2 px-4">
<div id="flow_title" class="flex justify-between items-center">
<button on:click={() => select('settings')}>
<span class="font-mono text-sm "> {$flowStore.path}</span>
<span class="font-mono text-sm"> {$flowStore.path}</span>
<Icon
data={faPen}
scale={0.8}
@@ -1,8 +1,8 @@
<script lang="ts">
import Button from '$lib/components/common/button/Button.svelte'
import { classNames } from '$lib/utils'
import { faTrashAlt } from '@fortawesome/free-solid-svg-icons'
import { createEventDispatcher } from 'svelte'
import Icon from 'svelte-awesome'
export let color: 'blue' | 'orange' = 'blue'
export let isFirst: boolean = false
@@ -35,20 +35,21 @@
</div>
<div
class={classNames(
'border w-full rounded-sm p-2 bg-white text-sm cursor-pointer flex justify-between items-center space-x-2',
'border w-full rounded-sm p-2 bg-white text-sm cursor-pointer flex justify-between items-center space-x-2 overflow-hidden',
margin,
selected ? 'outline outline-offset-1 outline-2 outline-gray-600' : ''
)}
>
<slot name="content" />
{#if deletable}
<button
type="button"
<Button
on:click={(event) => dispatch('delete', { event })}
class="text-gray-900 bg-white border border-gray-300 focus:outline-none hover:bg-gray-100 focus:ring-4 focus:ring-gray-200 font-medium rounded-sm text-xs px-2 py-1"
>
<Icon data={faTrashAlt} scale={0.8} />
</button>
startIcon={{ icon: faTrashAlt }}
iconOnly={true}
color="light"
variant="border"
size="xs"
/>
{/if}
</div>
</div>
@@ -130,15 +130,14 @@
<div slot="icon">
<span>{index + 1}</span>
</div>
<div slot="content" class="w-full">
<input
bind:value={mod.summary}
placeholder={mod.summary ||
<div slot="content" class="w-full truncate block">
<span
>{mod.summary ||
mod.value.path ||
(mod.value.type === 'rawscript'
? `Inline ${mod.value.language}`
: 'Select a script')}
/>
: 'Select a script')}</span
>
</div>
</FlowModuleSchemaItem>
</li>
@@ -160,6 +159,9 @@
<RemoveStepConfirmationModal
bind:open={confirmationModalOpen}
on:canceled={() => {
indexToRemove = undefined
}}
on:confirmed={() => {
if (indexToRemove !== undefined) {
removeAtIndex(indexToRemove)
@@ -43,7 +43,7 @@
<HSplitPane leftPaneSize="50%" rightPaneSize="50%" minLeftPaneSize="20%" minRightPaneSize="20%">
<left slot="left" class="relative">
<div class=" overflow-auto h-full p-4">
<div class="overflow-auto h-full p-4">
<slot />
</div>
</left>