App small fixes (#1258)

* fix(frontend): Fix runnable editor

* fix(frontend): remove isopenstore

* fix(frontend): add output searchbar

* fix(frontend): fix build

* fix(frontend): add missing clear button
This commit is contained in:
Faton Ramadani
2023-03-03 15:15:29 +01:00
committed by GitHub
parent dcfb29fb80
commit 70dd6f759c
12 changed files with 197 additions and 91 deletions
+1 -1
View File
@@ -13,7 +13,7 @@
<Popover notClickable {placement} class={wrapperClass}>
<Icon
class="{light
? 'text-gray-300'
? 'text-gray-400'
: ' text-gray-500'} font-thin inline-block align-middle w-4 {$$props.class}"
data={faInfoCircle}
{scale}
@@ -201,20 +201,20 @@
</Pane>
<Pane size={21} minSize={5} maxSize={33}>
<div class="relative flex flex-col h-full">
<Tabs bind:selected={selectedTab} class="!border-b-2 !border-gray-200">
<Tab value="insert" size="xs" class="grow">
<Tabs bind:selected={selectedTab} class="!border-b-2 !border-gray-200 !h-full">
<Tab value="insert" size="xs">
<div class="m-1 center-center gap-2">
<Icon data={faPlus} />
<span>Insert</span>
</div>
</Tab>
<Tab value="settings" size="xs" class="grow">
<Tab value="settings" size="xs">
<div class="m-1 center-center gap-2">
<Icon data={faSliders} />
<span>Settings</span>
</div>
</Tab>
<Tab value="css" size="xs" class="grow">
<Tab value="css" size="xs">
<div class="m-1 center-center gap-2">
<Icon data={faCode} />
<span>CSS</span>
@@ -1,13 +1,12 @@
<script lang="ts">
import type { AppEditorContext } from '../../types'
import { getContext, onMount } from 'svelte'
import { isOpenStore } from './store'
import { getContext } from 'svelte'
import { dirtyStore } from '$lib/components/common/confirmationModal/dirtyStore'
import { components as componentsRecord, COMPONENT_SETS, type AppComponent } from '../component'
import ListItem from './ListItem.svelte'
import { insertNewGridItem } from '../appUtils'
import { X } from 'lucide-svelte'
const TITLE_PREFIX = 'Component.' as const
const { app, selectedComponent, focusedGrid } = getContext<AppEditorContext>('AppEditorContext')
function addComponent(appComponentType: AppComponent['type']): void {
@@ -19,31 +18,62 @@
$selectedComponent = id
}
onMount(() => {
isOpenStore.addItems(COMPONENT_SETS.map((set) => ({ [TITLE_PREFIX + set.title]: true })))
})
let search = ''
// Filter COMPONENT_SETS by search
$: componentsFiltered = COMPONENT_SETS.map((set) => ({
...set,
components: set.components.filter((component) => {
const name = componentsRecord[component].name.toLowerCase()
return name.includes(search.toLowerCase())
})
}))
</script>
{#each COMPONENT_SETS as { title, components }, index (index)}
<ListItem {title} prefix={TITLE_PREFIX}>
{#if components.length}
<div class="flex flex-wrap gap-2 py-2">
{#each components as item}
<button
on:click={() => addComponent(item)}
title={componentsRecord[item].name}
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={componentsRecord[item].icon} />
<div class="text-xs w-full text-center ellipsize">
{componentsRecord[item].name}
</div>
</button>
{/each}
</div>
{:else}
<div class="text-xs text-gray-500 py-1 px-2"> There are no components in this group yet </div>
<section class="p-2 sticky bg-white border-b w-full h-12 z-20 top-0">
<div class="relative">
<input
bind:value={search}
class="px-2 py-1 border border-gray-300 rounded-sm {search ? 'pr-8' : ''}"
placeholder="Search components..."
/>
{#if search}
<button
class="absolute right-2 top-1/2 transform -translate-y-1/2 hover:bg-gray-200 rounded-full p-0.5"
on:click|stopPropagation|preventDefault={() => (search = '')}
>
<X size="14" />
</button>
{/if}
</ListItem>
{/each}
</div>
</section>
{#if componentsFiltered.reduce((acc, { components }) => acc + components.length, 0) === 0}
<div class="text-xs text-gray-500 py-1 px-2"> No components found </div>
{:else}
{#each componentsFiltered as { title, components }, index (index)}
<ListItem title={`${title} (${components.length})`}>
{#if components.length}
<div class="flex flex-wrap gap-2 py-2">
{#each components as item}
<button
on:click={() => addComponent(item)}
title={componentsRecord[item].name}
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={componentsRecord[item].icon} />
<div class="text-xs w-full text-center ellipsize">
{componentsRecord[item].name}
</div>
</button>
{/each}
</div>
{:else}
<div class="text-xs text-gray-500 py-1 px-2">
There are no components in this group yet
</div>
{/if}
</ListItem>
{/each}
{/if}
@@ -4,27 +4,38 @@
import { isOpenStore } from './store'
export let title: string
export let prefix: string = ''
export let prefix: string | undefined = undefined
$: storeTitle = prefix + title
$: isOpen = $isOpenStore[storeTitle]
$: isOpen = prefix ? $isOpenStore[storeTitle] : true
</script>
<section class="pt-1 pb-2 px-1">
<button
on:click|preventDefault={() => isOpenStore.toggle(storeTitle)}
class="w-full flex justify-between items-center text-gray-700 px-2 py-1
{#if prefix !== undefined}
<button
on:click|preventDefault={() => isOpenStore.toggle(storeTitle)}
class="w-full flex justify-between items-center text-gray-700 px-2 py-1
rounded-sm duration-200 hover:bg-gray-100"
>
<h1 class="text-sm font-semibold text-left">
>
<h1 class="text-sm font-semibold text-left">
<slot name="title">
{title}
</slot>
</h1>
<ChevronDown class="rotate-0 duration-300 {isOpen ? '!rotate-180' : ''}" />
</button>
{#if isOpen}
<div transition:slide|local={{ duration: 300 }} class="px-2">
<slot />
</div>
{/if}
{:else}
<h1 class="text-sm font-semibold text-left px-2 py-1">
<slot name="title">
{title}
</slot>
</h1>
<ChevronDown class="rotate-0 duration-300 {isOpen ? '!rotate-180' : ''}" />
</button>
{#if isOpen}
<div transition:slide|local={{ duration: 300 }} class="px-2">
<div class="px-2">
<slot />
</div>
{/if}
@@ -1,5 +1,6 @@
<script lang="ts">
import { classNames } from '$lib/utils'
import { X } from 'lucide-svelte'
import { getContext } from 'svelte'
import type { AppEditorContext } from '../../types'
import { findGridItem } from '../appUtils'
@@ -42,13 +43,40 @@
$: panels = [['ctx', ['email', 'username', 'query']] as [string, string[]]].concat(
Object.entries($staticOutputs)
)
let search = ''
// filter out outputs that don't match the search by name (computed by getComponentNameById) and id
// The output should be [string, string[]][]
$: filteredPanels = panels.filter(([componentId, outputs]) => {
const name = getComponentNameById(componentId)
return (
name.toLowerCase().includes(search.toLowerCase()) ||
componentId.toLowerCase().includes(search.toLowerCase())
)
})
</script>
<PanelSection noPadding titlePadding="px-4 pt-2 pb-0.5" title="Outputs">
<div
class="overflow-auto min-w-[150px] border-t w-full relative flex flex-col gap-4 px-2 pt-4 pb-2"
>
{#each panels as [componentId, outputs] (componentId)}
<div class="relative">
<input
bind:value={search}
class="px-2 py-1 border border-gray-300 rounded-sm {search ? 'pr-8' : ''}"
placeholder="Search outputs..."
/>
{#if search}
<button
class="absolute right-2 top-1/2 transform -translate-y-1/2 hover:bg-gray-200 rounded-full p-0.5"
on:click|stopPropagation|preventDefault={() => (search = '')}
>
<X size="14" />
</button>
{/if}
</div>
{#each filteredPanels as [componentId, outputs] (componentId)}
{#if outputs.length > 0 && $worldStore?.outputsById[componentId]}
{@const name = getComponentNameById(componentId)}
<div>
@@ -7,7 +7,7 @@
import { inferArgs } from '$lib/infer'
import { initialCode } from '$lib/script_helpers'
import { capitalize, emptySchema, getScriptByPath } from '$lib/utils'
import { faCodeBranch } from '@fortawesome/free-solid-svg-icons'
import { faCodeBranch, faTrash } from '@fortawesome/free-solid-svg-icons'
import { Building, Globe2 } from 'lucide-svelte'
import { createEventDispatcher, getContext } from 'svelte'
import { fly } from 'svelte/transition'
@@ -176,12 +176,22 @@
<div class="mt-4">
<Button
on:click={() => picker?.openDrawer()}
size="sm"
size="xs"
color="blue"
startIcon={{ icon: faCodeBranch }}
btnClasses="truncate"
>
or fork a detached/Workspace/Hub script
</Button>
<Button
on:click={() => dispatch('delete')}
size="xs"
color="red"
variant="border"
startIcon={{ icon: faTrash }}
btnClasses="truncate"
>
Delete
</Button>
</div>
</div>
@@ -3,7 +3,7 @@
import type { Preview } from '$lib/gen'
import { createEventDispatcher, getContext, onMount } from 'svelte'
import type { AppEditorContext, InlineScript } from '../../types'
import { CheckCircle, Maximize2, Trash2, X } from 'lucide-svelte'
import { CornerDownLeft, Maximize2, Plus, Trash2, X } from 'lucide-svelte'
import InlineScriptEditorDrawer from './InlineScriptEditorDrawer.svelte'
import { inferArgs } from '$lib/infer'
import type { Schema } from '$lib/common'
@@ -11,11 +11,11 @@
import { fly } from 'svelte/transition'
import Editor from '$lib/components/Editor.svelte'
import { emptySchema, scriptLangToEditorLang } from '$lib/utils'
import Tooltip from '$lib/components/Tooltip.svelte'
import Popover from '../../../Popover.svelte'
import { computeFields } from './utils'
import { deepEqual } from 'fast-equals'
import type { AppInput } from '../../inputType'
import Kbd from '$lib/components/common/kbd/Kbd.svelte'
let inlineScriptEditorDrawer: InlineScriptEditorDrawer
@@ -73,6 +73,8 @@
}
}
}
let isMac = navigator.userAgent.indexOf('Mac OS X') !== -1
</script>
<InlineScriptEditorDrawer {editor} bind:this={inlineScriptEditorDrawer} bind:inlineScript />
@@ -80,30 +82,25 @@
<div class="h-full flex flex-col gap-1" transition:fly|local={{ duration: 50 }}>
<div class="flex justify-between w-full gap-2 px-2 pt-1 flex-row items-center">
{#if name !== undefined}
<input bind:value={name} placeholder="Inline script name" />
<input bind:value={name} placeholder="Inline script name" class="!text-xs !rounded-xs" />
{/if}
<div class="flex w-full flex-row gap-2 items-center justify-end">
{#if validCode}
<Badge color="green" baseClass="!p-0 !h-[30px] aspect-square center-center">
<CheckCircle size={16} />
</Badge>
<Badge color="green" baseClass="!text-2xs">Valid</Badge>
{:else}
<Badge color="red" baseClass="!p-0 !h-[30px] aspect-square center-center">
<X size={16} />
</Badge>
<Badge color="red" baseClass="!text-2xs">Invalid</Badge>
{/if}
{#if id.startsWith('unused-') || id.startsWith('bg_')}
<Popover notClickable placement="bottom">
<Button
size="xs"
color="red"
variant="border"
btnClasses="!px-1.5"
color="light"
btnClasses="!px-2 !bg-red-100 hover:!bg-red-200"
aria-label="Delete"
on:click={() => dispatch('delete')}
>
<Trash2 size={16} />
<Trash2 size={14} class="text-red-800" />
</Button>
<svelte:fragment slot="text">Delete</svelte:fragment>
</Popover>
@@ -111,41 +108,62 @@
<Popover notClickable placement="bottom">
<Button
size="xs"
color="blue"
variant="border"
btnClasses="!px-1.5"
color="light"
btnClasses="!px-2 !bg-gray-100 hover:!bg-gray-200"
aria-label="Open full editor"
on:click={() => {
inlineScriptEditorDrawer?.openDrawer()
}}
>
<Maximize2 size={16} />
<Maximize2 size={14} />
</Button>
<svelte:fragment slot="text">Open full editor</svelte:fragment>
</Popover>
<Button
variant="border"
size="xs"
color="blue"
color="light"
btnClasses="!px-2 !py-1"
on:click={async () => {
editor.format()
}}
>
Format&nbsp;<Tooltip placement="bottom">Ctrl+S</Tooltip>
<div class="flex flex-row gap-1 items-center">
Format
<div class="flex flex-row items-center">
<Kbd>{isMac ? '⌘' : 'CTRL'}</Kbd>
<Plus size={12} />
<Kbd>S</Kbd>
</div>
</div>
</Button>
{#if $runnableComponents[id] != undefined}
<Button
loading={runLoading}
size="xs"
color="blue"
color="dark"
variant="border"
btnClasses="!px-2 !py-1 !bg-gray-700 !text-white hover:!bg-gray-900"
on:click={async () => {
runLoading = true
await $runnableComponents[id]?.()
runLoading = false
}}
>
Run&nbsp;
<Tooltip light placement="bottom">Ctrl+Enter</Tooltip>
<div class="flex flex-row gap-1 items-center">
Run
<div class="flex flex-row items-center">
<Kbd>{isMac ? '⌘' : 'CTRL'}</Kbd>
<Plus size={12} />
<Kbd>
<div class="h-4 flex items-center justify-center">
<CornerDownLeft size={10} />
</div>
</Kbd>
</div>
</div>
</Button>
{/if}
</div>
@@ -62,6 +62,12 @@
) {
refreshScript(componentInput.runnable)
}
function deleteInlineScript() {
if (componentInput && componentInput.type == 'runnable') {
componentInput = clearResultAppInput(componentInput)
}
}
</script>
<Drawer bind:this={drawerFlowViewer} size="1200px">
@@ -80,16 +86,13 @@
bind:name={componentInput.runnable.name}
bind:fields={componentInput.fields}
syncFields
on:delete={() => {
if (componentInput && componentInput.type == 'runnable') {
componentInput = clearResultAppInput(componentInput)
}
}}
on:delete={deleteInlineScript}
/>
{:else}
<EmptyInlineScript
{id}
name={componentInput.runnable.name}
on:delete={deleteInlineScript}
on:new={(e) => {
if (
componentInput &&
@@ -12,6 +12,16 @@
getContext<AppEditorContext>('AppEditorContext')
let selectedScriptComponentId: string | undefined = undefined
function deleteBackgroundScript(index: number) {
// remove the script from the array at the index
$app.hiddenInlineScripts.splice(index, 1)
$app.hiddenInlineScripts = [...$app.hiddenInlineScripts]
delete $staticOutputs[`bg_${index}`]
delete $runnableComponents[`bg_${index}`]
$staticOutputs = $staticOutputs
}
</script>
<SplitPanesWrapper>
@@ -97,20 +107,13 @@
bind:name={hiddenInlineScript.name}
bind:fields={hiddenInlineScript.fields}
syncFields
on:delete={() => {
// remove the script from the array at the index
$app.hiddenInlineScripts.splice(index, 1)
$app.hiddenInlineScripts = [...$app.hiddenInlineScripts]
delete $staticOutputs[`bg_${index}`]
delete $runnableComponents[`bg_${index}`]
$staticOutputs = $staticOutputs
}}
on:delete={() => deleteBackgroundScript(index)}
/>
{:else}
<EmptyInlineScript
id={`b_${index}`}
name={hiddenInlineScript.name}
on:delete={() => deleteBackgroundScript(index)}
on:new={(e) => {
hiddenInlineScript.inlineScript = e.detail
}}
@@ -54,9 +54,9 @@
</script>
<div
class="sticky top-0 py-0.5 px-2 text-center bg-gray-200 text-gray-500 text-2xs font-bold tracking-wide border-b border-gray-300"
class="sticky top-0 py-0.5 px-2 text-left bg-gray-50 text-gray-800 text-xs font-semibold tracking-wide border-b border-gray-300"
>
RUNNABLES
Runnables
</div>
<div bind:this={list} class="grow flex flex-col gap-4">
<PanelSection title="Inline scripts" smallPadding>
@@ -129,9 +129,12 @@
smallPadding
>
<svelte:fragment slot="action">
<Button size="xs" color="dark" variant="border" on:click={createBackgroundScript}
><Plus size={14} />
</Button>
<button
class="rounded-full bg-gray-100 hover:bg-gray-200 p-1 border border-gray-200"
on:click={createBackgroundScript}
>
<Plus size={14} class="text-gray-500" />
</button>
</svelte:fragment>
<div class="flex flex-col gap-1 w-full">
{#if $app.hiddenInlineScripts?.length > 0}
@@ -2,7 +2,7 @@
import Button from '$lib/components/common/button/Button.svelte'
import { faChevronDown, faChevronUp, faCopy, faTrashAlt } from '@fortawesome/free-solid-svg-icons'
import { getContext } from 'svelte'
import type { AppEditorContext, GridItem } from '../../types'
import type { AppEditorContext } from '../../types'
import PanelSection from './common/PanelSection.svelte'
import InputsSpecsEditor from './InputsSpecsEditor.svelte'
import TableActions from './TableActions.svelte'
@@ -17,12 +17,12 @@
)}
>
<div class="flex justify-between flex-wrap items-center w-full gap-1">
<div class="text-sm inline-flex items-center font-extrabold {titlePadding}">
<div class="text-sm inline-flex items-center font-extrabold {titlePadding} gap-1">
<span class="truncate">
{title}
</span>
{#if tooltip}
<Tooltip>
<Tooltip light>
{tooltip}
</Tooltip>
{/if}