App UI improvements (#1072)

* fix(frontend): Improve App UI

* fix(frontend):Align typing

* fix(frontend): Improve App UI

* fix(frontend): remove debug string

* fix(frontend): remove debug string

* fix(frontend): restore code from main
This commit is contained in:
Faton Ramadani
2023-01-06 17:46:45 +01:00
committed by GitHub
parent 099c4fd10d
commit b5edb7ed98
17 changed files with 127 additions and 182 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
import Popover from './Popover.svelte'
</script>
<Popover notClickable>
<Popover notClickable class="flex">
<Icon
class="text-gray-500 font-thin inline-block align-middle w-4"
data={faInfoCircle}
@@ -60,9 +60,13 @@
<div class="text-gray-400 bg-gray-100 flex justify-center items-center h-full w-full">
No text
</div>
{:else}<svelte:element this={component} class="whitespace-pre {classes}" style={extraStyle}
>{String(result)}</svelte:element
{:else}<svelte:element
this={component}
class="whitespace-pre-wrap {classes}"
style={extraStyle}
>
{String(result)}
</svelte:element>
{/if}
</AlignWrapper>
</RunnableWrapper>
@@ -1,6 +1,5 @@
<script lang="ts">
import { Button, type ButtonType } from '$lib/components/common'
import { faArrowRight, faRefresh } from '@fortawesome/free-solid-svg-icons'
import { getContext } from 'svelte'
import type { AppInput } from '../../inputType'
import type { Output } from '../../rx'
@@ -66,10 +65,10 @@
<div class="flex flex-col gap-2 px-4 w-full">
<div>
{#if componentInput?.type != 'runnable' || Object.values(componentInput?.fields ?? {}).filter((x) => x.type == 'user').length == 0}
<span class="text-gray-600 italic text-sm py-2"
>Run forms are meant to be associated with a runnable with some user inputs. Pick a
runnable and set some 'Runnable Inputs' to 'User Input'</span
>
<span class="text-gray-600 italic text-sm py-2">
Run forms are meant to be associated with a runnable with some user inputs. Pick a
runnable and set some 'Runnable Inputs' to 'User Input'
</span>
{/if}
</div>
<div class="flex justify-end">
@@ -81,6 +80,7 @@
window.dispatchEvent(new Event('pointerup'))
}}
on:click={() => {
ownClick = true
runnableComponent?.runComponent()
if (recomputeIds) {
@@ -30,8 +30,8 @@
$runnableComponents[id] = async () => {
await executeComponent()
}
executeComponent()
}
executeComponent()
})
let args: Record<string, any> = {}
@@ -61,7 +61,10 @@
$: fields && (currentStaticValues = computeStaticValues())
$: if (JSON.stringify(currentStaticValues) != JSON.stringify(lazyStaticValues)) {
lazyStaticValues = currentStaticValues
setDebouncedExecute()
if (autoRefresh) {
setDebouncedExecute()
}
}
$: fields && (lazyStaticValues = computeStaticValues())
@@ -288,6 +291,7 @@
<Alert type="error" title="Error during execution">
<pre title={result.error} class="text-2xs whitespace-pre-wrap">{result.error}</pre>
</Alert>
<slot />
</div>
{:else}
<div class="grow min-w-1/2 min-h-[66%]">
@@ -14,7 +14,6 @@
import JobArgs from '$lib/components/JobArgs.svelte'
import LogViewer from '$lib/components/LogViewer.svelte'
import Path from '$lib/components/Path.svelte'
import SplitPanesWrapper from '$lib/components/splitPanes/SplitPanesWrapper.svelte'
import TestJobLoader from '$lib/components/TestJobLoader.svelte'
import Toggle from '$lib/components/Toggle.svelte'
import Tooltip from '$lib/components/Tooltip.svelte'
@@ -276,8 +275,9 @@
<Drawer bind:open={publishDrawerOpen} size="800px">
<DrawerContent title="Publish an App" on:close={() => (publishDrawerOpen = false)}>
{#if appPath == ''}
<Alert title="Require saving" type="error">Save this app once before you can publish it</Alert
>
<Alert title="Require saving" type="error">
Save this app once before you can publish it
</Alert>
{:else}
<Alert title="App executed on behalf of publisher">
A viewer of the app will execute the runnables of the app on behalf of the publisher
@@ -285,8 +285,8 @@
guarantee tight security, a policy is computed at time of saving of the app which only allow
the scripts/flows referred to in the app to be called on behalf of. Furthermore, static
parameters are not overridable. Hence, users will only be able to use the app as intended by
the publisher without risk for leaking resources not used in the app.</Alert
>
the publisher without risk for leaking resources not used in the app.
</Alert>
<div class="mt-4" />
<Toggle
options={{
@@ -348,8 +348,8 @@
</div>
</ToggleButton>
<ToggleButton position="right" value="preview" size="xs">
<div class="inline-flex gap-1 items-center"> <Eye size={14} /> Preview</div></ToggleButton
>
<div class="inline-flex gap-1 items-center"> <Eye size={14} /> Preview</div>
</ToggleButton>
</ToggleButtonGroup>
</div>
<div>
@@ -357,23 +357,30 @@
<ToggleButton position="left" value="sm" size="xs">
<Smartphone size={14} />
</ToggleButton>
<ToggleButton position="right" value="lg" size="xs"><Laptop2 size={14} /></ToggleButton>
<ToggleButton position="right" value="lg" size="xs">
<Laptop2 size={14} />
</ToggleButton>
</ToggleButtonGroup>
</div>
<span class="hidden lg:block">
<ToggleButtonGroup bind:selected={$app.fullscreen}>
<ToggleButton position="left" value={false} size="xs"
><AlignHorizontalSpaceAround size={14} /> &nbsp; <Tooltip
>The max width is 1168px and the content stay centered instead of taking the full page
width</Tooltip
></ToggleButton
>
<ToggleButton position="right" value={true} size="xs"><Expand size={14} /></ToggleButton>
<ToggleButton position="left" value={false} size="xs">
<div class="flex gap-1 justify-start">
<AlignHorizontalSpaceAround size={14} />
<Tooltip>
The max width is 1168px and the content stay centered instead of taking the full page
width
</Tooltip>
</div>
</ToggleButton>
<ToggleButton position="right" value={true} size="xs">
<Expand size={14} />
</ToggleButton>
</ToggleButtonGroup>
</span>
</div>
<div class="flex flex-row grow gap-4 justify-end ">
<div class="flex flex-row grow gap-2 justify-end items-center">
<Button
on:click={() => (jobsDrawerOpen = true)}
color="light"
@@ -389,7 +396,7 @@
<Button
on:click={() => (publishDrawerOpen = true)}
color="dark"
color="light"
size="xs"
variant="border"
startIcon={{ icon: faExternalLink }}
@@ -401,7 +408,9 @@
startIcon={{ icon: faSave }}
on:click={save}
color="dark"
size="xs">Save</Button
size="xs"
>
Save
</Button>
</div>
</div>
@@ -16,7 +16,7 @@
export let app: App
</script>
<Button size="sm" variant="border" color="light" on:click={() => jsonViewerDrawer.toggleDrawer()}>
<Button size="xs" variant="border" color="light" on:click={() => jsonViewerDrawer.toggleDrawer()}>
<Icon data={faFileExport} scale={0.6} class="inline mr-2" />
JSON
</Button>
@@ -1,7 +1,7 @@
<script lang="ts">
import { classNames } from '$lib/utils'
import type { AppComponent } from '../types'
import { Anchor, Move, X } from 'lucide-svelte'
import { Anchor, Move } from 'lucide-svelte'
import { createEventDispatcher } from 'svelte'
export let component: AppComponent
@@ -16,7 +16,7 @@
<span
title={`Id: ${component.id}`}
class={classNames(
'px-2 text-2xs font-bold rounded-t-sm w-fit absolute shadow -top-1 -left-2 border z-50',
'px-2 text-2xs font-bold w-fit absolute shadow -top-1 -left-2 border z-50',
selected
? 'bg-indigo-500/90 border-blue-500 text-white'
: 'bg-gray-200/90 border-gray-300 text-gray-500'
@@ -30,13 +30,11 @@
<button
title="Position locking"
class={classNames(
'text-gray-800 px-1 text-2xs py-0.5 font-bold rounded-t-sm w-fit shadow border border-gray-300 absolute -top-1 right-[2.5rem] z-50 cursor-pointer',
'text-gray-800 px-1 text-2xs py-0.5 font-bold w-fit shadow border border-gray-300 absolute -top-1 right-[2.5rem] z-50 cursor-pointer',
' hover:bg-gray-300',
selected ? 'bg-gray-200/80' : 'bg-gray-200/80'
)}
on:click={() => {
dispatch('lock')
}}
on:click={() => dispatch('lock')}
>
{#if locked}
<Anchor aria-label="Unlock position" size={14} class="text-orange-500" />
@@ -53,6 +51,8 @@
class={classNames(
'text-gray-600 px-1 text-2xs py-0.5 font-bold rounded-t-sm w-fit absolute border border-gray-300 -top-1 shadow right-[4.5rem] z-50 cursor-move',
'bg-gray-200/80'
)}><Move size={14} /></span
)}
>
<Move size={14} />
</span>
{/if}
@@ -1,5 +1,5 @@
<script lang="ts">
import { getContext, onDestroy, onMount } from 'svelte'
import { getContext } from 'svelte'
import type { AppEditorContext } from '../types'
import Grid from 'svelte-grid'
import ComponentEditor from './ComponentEditor.svelte'
@@ -146,6 +146,7 @@
>{dataItem.data.id}</div
>
{/if}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
on:pointerdown={() => {
selectComponent(dataItem.data.id)
@@ -121,7 +121,7 @@
class="border w-24 shadow-sm h-16 p-2 flex flex-col gap-2 items-center
justify-center bg-white rounded-md hover:bg-gray-100 duration-200"
>
<svelte:component this={displayData[item.type].icon} class="text-blue-800" />
<svelte:component this={displayData[item.type].icon} />
<div class="text-xs w-full text-center ellipsize">
{displayData[item.type].name}
</div>
@@ -23,7 +23,6 @@
const { runnableComponents } = getContext<AppEditorContext>('AppEditorContext')
let editor: Editor
let validCode = false
async function inferInlineScriptSchema(
@@ -38,7 +38,6 @@
>
<div class="flex justify-center flex-col items-center gap-2">
<MousePointerClick size={18} />
<span class="text-xs">Pick</span>
</div>
</Button>
@@ -1,77 +0,0 @@
<script lang="ts">
import { Alert } from '$lib/components/common'
import Badge from '$lib/components/common/badge/Badge.svelte'
import Button from '$lib/components/common/button/Button.svelte'
import ConfirmationModal from '$lib/components/common/confirmationModal/ConfirmationModal.svelte'
import { faTrashAlt } from '@fortawesome/free-solid-svg-icons'
import { createEventDispatcher } from 'svelte'
import type { AppSection } from '../../types'
import PanelSection from './common/PanelSection.svelte'
export let section: AppSection | undefined
const dispatch = createEventDispatcher()
let deleteConfirmedCallback: (() => void) | undefined = undefined
$: open = Boolean(deleteConfirmedCallback)
function deleteSection(event: CustomEvent<PointerEvent>) {
if (
section &&
Array.isArray(section.components) &&
section?.components.length > 0 &&
!event.detail.shiftKey
) {
deleteConfirmedCallback = () => {
dispatch('remove')
deleteConfirmedCallback = undefined
}
} else {
dispatch('remove')
}
}
</script>
{#if section}
<div class="flex flex-col w-full divide-y">
<span class="text-sm border-y w-full py-1 px-2 bg-gray-800 text-white">Section editor</span>
<PanelSection title="Danger zone">
<Button
size="xs"
variant="border"
color="red"
startIcon={{ icon: faTrashAlt }}
on:click={deleteSection}
>
Delete section
</Button>
</PanelSection>
</div>
{/if}
<ConfirmationModal
{open}
title="Remove section"
confirmationText="Remove"
on:canceled={() => {
deleteConfirmedCallback = undefined
}}
on:confirmed={() => {
if (deleteConfirmedCallback) {
deleteConfirmedCallback()
}
deleteConfirmedCallback = undefined
}}
>
<div class="flex flex-col w-full space-y-4">
<span>Are you sure you want to remove this section?</span>
<Alert type="info" title="Bypass confirmation">
<div>
You can press
<Badge color="dark-gray">SHIFT</Badge>
while removing a resource to bypass confirmation.
</div>
</Alert>
</div>
</ConfirmationModal>
@@ -54,11 +54,12 @@
<Button size="xs" color="light" variant="border" startIcon={{ icon: faEdit }} on:click={edit}>
Edit
</Button>
<Button size="xs" color="light" variant="border" on:click={detach}
>Detach <Tooltip
>Detaching an inline script keep it for later to be reused by another component</Tooltip
></Button
>
<Button size="xs" color="light" variant="border" on:click={detach}>
Detach
<Tooltip>
Detaching an inline script keep it for later to be reused by another component
</Tooltip>
</Button>
{/if}
<Button size="xs" color="red" variant="border" startIcon={{ icon: faClose }} on:click={clear}>
Clear
@@ -1,2 +0,0 @@
<script lang="ts">
</script>
@@ -3,9 +3,9 @@
import { Badge, Button } from '$lib/components/common'
import { faArrowRight, faClose } from '@fortawesome/free-solid-svg-icons'
import { getContext } from 'svelte'
import type { AppInput } from '../../../inputType'
import type { ConnectedAppInput } from '../../../inputType'
export let componentInput: AppInput
export let componentInput: ConnectedAppInput
const { connectingInput } = getContext<AppEditorContext>('AppEditorContext')
@@ -13,7 +13,6 @@
if (
!$connectingInput.opened &&
$connectingInput.input !== undefined &&
componentInput.type === 'connected' &&
!componentInput.connection
) {
componentInput.connection = $connectingInput.input.connection
@@ -25,56 +24,62 @@
}
}
function startConnecting() {
$connectingInput = {
opened: true,
input: undefined,
hoveredComponent: undefined
}
}
$: $connectingInput && applyConnection()
</script>
{#if componentInput.type === 'connected'}
{#if componentInput.connection}
<div class="flex justify-between w-full gap-1">
<span class="text-xs">Status</span>
<Badge color="green">Connected</Badge>
</div>
<div class="flex justify-between w-full">
<span class="text-xs">Component</span>
<Badge color="indigo">{componentInput.connection.componentId}</Badge>
</div>
<div class="flex justify-between w-full">
<span class="text-xs">Path</span>
<Badge color="indigo">{componentInput.connection.path}</Badge>
</div>
<Button
size="xs"
startIcon={{ icon: faClose }}
color="red"
variant="border"
on:click={() => {
if (componentInput.type === 'connected') {
componentInput.connection = undefined
{#if componentInput.connection}
<div class="flex justify-between w-full gap-1">
<span class="text-xs">Status</span>
<Badge color="green">Connected</Badge>
</div>
<div class="flex justify-between w-full">
<span class="text-xs">Component</span>
<Badge color="indigo">{componentInput.connection.componentId}</Badge>
</div>
<div class="flex justify-between w-full">
<span class="text-xs">Path</span>
<Badge color="indigo">{componentInput.connection.path}</Badge>
</div>
<Button
size="xs"
startIcon={{ icon: faClose }}
color="red"
variant="border"
on:click={() => {
if (componentInput.type === 'connected') {
componentInput.connection = undefined
}
}}
>
Disconnect
</Button>
{:else}
<div class="flex justify-between w-full gap-1">
<span class="text-xs">Status</span>
<Badge color="yellow">Not connected</Badge>
</div>
<Button
size="xs"
endIcon={{ icon: faArrowRight }}
color="blue"
on:click={() => {
if (componentInput.type === 'connected') {
$connectingInput = {
opened: true,
input: undefined,
hoveredComponent: undefined
}
}}
>
Disconnect
</Button>
{:else}
<div class="flex justify-between w-full gap-1">
<span class="text-xs">Status</span>
<Badge color="yellow">Not connected</Badge>
</div>
<Button
size="xs"
endIcon={{ icon: faArrowRight }}
color="blue"
on:click={() => {
if (componentInput.type === 'connected') {
$connectingInput = {
opened: true,
input: undefined,
hoveredComponent: undefined
}
}
}}
>
Connect
</Button>
{/if}
}
}}
>
Connect
</Button>
{/if}
@@ -46,10 +46,12 @@
: undefined}
/>
{:else}
<JsonEditor
bind:value={componentInput.value}
code={JSON.stringify(componentInput.value, null, 2)}
/>
<div class="flex w-full flex-col">
<JsonEditor
bind:value={componentInput.value}
code={JSON.stringify(componentInput.value, null, 2)}
/>
</div>
{/if}
{:else if componentInput.fieldType === 'array'}
<ArrayStaticInputEditor bind:componentInput />
@@ -10,16 +10,16 @@
import { writable, type Writable } from 'svelte/store'
export let selected: any
const dispatch = createEventDispatcher()
export let col = false
$: setSelected(selected)
const dispatch = createEventDispatcher()
const selectedContent = writable(selected)
function setSelected(selected: any) {
selectedContent.set(selected)
}
const selectedContent = writable(selected)
$: setSelected(selected)
$: $selectedContent && dispatch('selected', $selectedContent)
setContext<ToggleButtonContext>('ToggleButtonGroup', {