mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-12 16:05:43 +00:00
fix(frontend): Clean up app editor (#1267)
* fix(frontend): Clean up app editor * fix(frontend): Add outputs search empty state * fix(frontend): Add remove button to icon input * label * fix(frontend): Iconed app button
This commit is contained in:
@@ -166,11 +166,11 @@
|
||||
{loading}
|
||||
>
|
||||
<span class="truncate inline-flex gap-2 items-center">
|
||||
{#if beforeIconComponent}
|
||||
{#if beforeIcon && beforeIconComponent}
|
||||
<svelte:component this={beforeIconComponent} size={14} />
|
||||
{/if}
|
||||
<div>{labelValue}</div>
|
||||
{#if afterIconComponent}
|
||||
{#if afterIcon && afterIconComponent}
|
||||
<svelte:component this={afterIconComponent} size={14} />
|
||||
{/if}
|
||||
</span>
|
||||
|
||||
@@ -22,12 +22,10 @@
|
||||
let strokeWidth: number
|
||||
let iconComponent: any
|
||||
|
||||
$: icon && handleIcon()
|
||||
$: handleIcon(icon)
|
||||
|
||||
async function handleIcon() {
|
||||
if (icon) {
|
||||
iconComponent = await loadIcon(icon)
|
||||
}
|
||||
async function handleIcon(i?: string) {
|
||||
iconComponent = i ? await loadIcon(i) : undefined
|
||||
}
|
||||
|
||||
$: css = concatCustomCss($app.css?.iconcomponent, customCss)
|
||||
@@ -45,7 +43,7 @@
|
||||
class={css?.container?.class ?? ''}
|
||||
style={css?.container?.style ?? ''}
|
||||
>
|
||||
{#if iconComponent}
|
||||
{#if icon && iconComponent}
|
||||
<svelte:component
|
||||
this={iconComponent}
|
||||
size={size || 24}
|
||||
|
||||
@@ -204,7 +204,7 @@
|
||||
</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 !h-full">
|
||||
<Tabs bind:selected={selectedTab} wrapperClass="!h-[40px]" class="!h-full">
|
||||
<Tab value="insert" size="xs">
|
||||
<div class="m-1 center-center gap-2">
|
||||
<Icon data={faPlus} />
|
||||
|
||||
@@ -471,13 +471,14 @@
|
||||
<div class="min-w-64 w-64">
|
||||
<input type="text" placeholder="App summary" class="text-sm w-full" bind:value={$summary} />
|
||||
</div>
|
||||
<div class="flex gap-1">
|
||||
<div class="flex">
|
||||
<Button
|
||||
title="Undo"
|
||||
disabled={$history.index == 0}
|
||||
variant="border"
|
||||
color="dark"
|
||||
color="light"
|
||||
size="xs"
|
||||
btnClasses="!min-h-[30px] !rounded-r-none"
|
||||
on:click={async () => {
|
||||
$app = undo(history, $app)
|
||||
}}
|
||||
@@ -488,8 +489,9 @@
|
||||
title="Redo"
|
||||
disabled={$history.index == $history.history.length - 1}
|
||||
variant="border"
|
||||
color="dark"
|
||||
color="light"
|
||||
size="xs"
|
||||
btnClasses="!min-h-[30px] !rounded-l-none !border-r-0"
|
||||
on:click={async () => {
|
||||
$app = redo(history)
|
||||
}}
|
||||
@@ -529,7 +531,7 @@
|
||||
<ToggleButton position="left" value={false} size="xs">
|
||||
<div class="flex gap-1 justify-start items-center">
|
||||
<AlignHorizontalSpaceAround size={14} />
|
||||
<Tooltip light>
|
||||
<Tooltip light class="mb-0.5">
|
||||
The max width is 1168px and the content stay centered instead of taking the full page
|
||||
width
|
||||
</Tooltip>
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
<script lang="ts">
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import { getContext } from 'svelte'
|
||||
import { fade, slide } from 'svelte/transition'
|
||||
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'
|
||||
import { push } from '$lib/history'
|
||||
import { flip } from 'svelte/animate'
|
||||
|
||||
const { app, selectedComponent, focusedGrid, history } =
|
||||
getContext<AppEditorContext>('AppEditorContext')
|
||||
@@ -34,7 +36,7 @@
|
||||
}))
|
||||
</script>
|
||||
|
||||
<section class="p-2 sticky bg-white border-b w-full h-12 z-20 top-0">
|
||||
<section class="p-2 sticky bg-white w-full z-20 top-0">
|
||||
<div class="relative">
|
||||
<input
|
||||
bind:value={search}
|
||||
@@ -52,32 +54,41 @@
|
||||
</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 class="relative">
|
||||
{#if componentsFiltered.reduce((acc, { components }) => acc + components.length, 0) === 0}
|
||||
<div
|
||||
in:fade|local={{ duration: 50, delay: 50 }}
|
||||
out:fade|local={{ duration: 50 }}
|
||||
class="absolute left-0 top-0 w-full text-sm text-gray-500 text-center py-6 px-2"
|
||||
>
|
||||
No components found
|
||||
</div>
|
||||
{:else}
|
||||
<div in:fade|local={{ duration: 50, delay: 50 }} out:fade|local={{ duration: 50 }}>
|
||||
{#each componentsFiltered as { title, components }, index (index)}
|
||||
{#if components.length}
|
||||
<div transition:slide|local={{ duration: 300 }}>
|
||||
<ListItem title={`${title} (${components.length})`}>
|
||||
<div class="flex flex-wrap gap-2 py-2">
|
||||
{#each components as item (item)}
|
||||
<button
|
||||
animate:flip={{ duration: 300 }}
|
||||
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>
|
||||
</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}
|
||||
</ListItem>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
import { classNames } from '$lib/utils'
|
||||
import { X } from 'lucide-svelte'
|
||||
import { getContext } from 'svelte'
|
||||
import { flip } from 'svelte/animate'
|
||||
import { fade } from 'svelte/transition'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import { findGridItem } from '../appUtils'
|
||||
import { components } from '../component'
|
||||
@@ -58,9 +60,7 @@
|
||||
</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"
|
||||
>
|
||||
<div class="overflow-auto h-full min-w-[150px] w-full relative flex flex-col gap-4 p-2">
|
||||
<div class="relative {$connectingInput?.opened ? 'bg-white z-50' : ''}">
|
||||
<input
|
||||
bind:value={search}
|
||||
@@ -76,65 +76,81 @@
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
{#each filteredPanels as [componentId, outputs] (componentId)}
|
||||
{#if outputs.length > 0 && $worldStore?.outputsById[componentId]}
|
||||
{@const name = getComponentNameById(componentId)}
|
||||
<div>
|
||||
<div
|
||||
class="flex {$connectingInput?.opened
|
||||
? 'bg-white z-50'
|
||||
: ''} flex-row justify-between w-full"
|
||||
>
|
||||
<button
|
||||
on:click|stopPropagation|preventDefault={$connectingInput.opened
|
||||
? undefined
|
||||
: () => ($selectedComponent = componentId)}
|
||||
class={classNames(
|
||||
'px-2 text-2xs py-0.5 border-t border-x font-bold rounded-t-sm w-fit',
|
||||
$selectedComponent === componentId
|
||||
? ' bg-blue-500 text-white border-blue-500'
|
||||
: 'bg-gray-100 text-gray-500 border-gray-200'
|
||||
)}
|
||||
>
|
||||
{componentId}
|
||||
</button>
|
||||
<span
|
||||
class={classNames(
|
||||
'px-1 text-2xs py-0.5 font-semibold rounded-t-sm w-fit',
|
||||
'bg-gray-700 text-white'
|
||||
)}
|
||||
>
|
||||
{name}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
class={classNames(
|
||||
$connectingInput?.opened ? 'bg-white z-50' : '',
|
||||
`w-full py-2 grow border relative break-all `,
|
||||
$selectedComponent === componentId ? 'border border-blue-500 ' : '',
|
||||
$connectingInput.hoveredComponent === componentId ? 'outline outline-blue-500' : ''
|
||||
)}
|
||||
>
|
||||
{#key $selectedComponent}
|
||||
{#key $connectingInput?.opened}
|
||||
<ComponentOutputViewer
|
||||
outputs={$connectingInput?.opened && $selectedComponent === componentId
|
||||
? name == 'Table'
|
||||
? ['search']
|
||||
: []
|
||||
: outputs}
|
||||
<div class="relative">
|
||||
{#each filteredPanels as [componentId, outputs] (componentId)}
|
||||
<div
|
||||
animate:flip={{ duration: 300 }}
|
||||
in:fade|local={{ duration: 100, delay: 50 }}
|
||||
out:fade|local={{ duration: 100 }}
|
||||
>
|
||||
{#if outputs.length > 0 && $worldStore?.outputsById[componentId]}
|
||||
{@const name = getComponentNameById(componentId)}
|
||||
<div>
|
||||
<div
|
||||
class="flex {$connectingInput?.opened
|
||||
? 'bg-white z-50'
|
||||
: ''} flex-row justify-between w-full"
|
||||
>
|
||||
<button
|
||||
on:click|stopPropagation|preventDefault={$connectingInput.opened
|
||||
? undefined
|
||||
: () => ($selectedComponent = componentId)}
|
||||
class={classNames(
|
||||
'px-2 text-2xs py-0.5 border-t border-x font-bold rounded-t-sm w-fit',
|
||||
$selectedComponent === componentId
|
||||
? ' bg-indigo-500/90 text-white border-indigo-500/90'
|
||||
: 'bg-gray-100 text-gray-500 border-gray-200'
|
||||
)}
|
||||
>
|
||||
{componentId}
|
||||
on:select={({ detail }) => {
|
||||
connectInput(componentId, detail)
|
||||
}}
|
||||
/>
|
||||
{/key}
|
||||
{/key}
|
||||
</div>
|
||||
</button>
|
||||
<span
|
||||
class={classNames(
|
||||
'px-1 text-2xs py-0.5 font-semibold rounded-t-sm w-fit',
|
||||
'bg-gray-700 text-white'
|
||||
)}
|
||||
>
|
||||
{name}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class={classNames(
|
||||
$connectingInput?.opened ? 'bg-white z-50' : '',
|
||||
`w-full py-2 grow border relative break-all `,
|
||||
$selectedComponent === componentId ? 'border border-indigo-500/90 ' : '',
|
||||
$connectingInput.hoveredComponent === componentId
|
||||
? 'outline outline-indigo-500/90'
|
||||
: ''
|
||||
)}
|
||||
>
|
||||
{#key $selectedComponent}
|
||||
{#key $connectingInput?.opened}
|
||||
<ComponentOutputViewer
|
||||
outputs={$connectingInput?.opened && $selectedComponent === componentId
|
||||
? name == 'Table'
|
||||
? ['search']
|
||||
: []
|
||||
: outputs}
|
||||
{componentId}
|
||||
on:select={({ detail }) => {
|
||||
connectInput(componentId, detail)
|
||||
}}
|
||||
/>
|
||||
{/key}
|
||||
{/key}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
{:else}
|
||||
<div
|
||||
in:fade|local={{ duration: 50, delay: 100 }}
|
||||
out:fade|local={{ duration: 50 }}
|
||||
class="absolute left-0 top-0 w-full text-sm text-gray-500 text-center py-4 px-2"
|
||||
>
|
||||
No outputs found
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</PanelSection>
|
||||
|
||||
+93
-90
@@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
import { Badge, Button } from '$lib/components/common'
|
||||
import { faPlus } from '@fortawesome/free-solid-svg-icons'
|
||||
import { Plus } from 'lucide-svelte'
|
||||
import { getContext } from 'svelte'
|
||||
import Tooltip from '../../../Tooltip.svelte'
|
||||
import type { AppEditorContext } from '../../types'
|
||||
import { getAllScriptNames } from '../../utils'
|
||||
import PanelSection from '../settingsPanel/common/PanelSection.svelte'
|
||||
@@ -12,7 +12,6 @@
|
||||
|
||||
export let selectedScriptComponentId: string | undefined = undefined
|
||||
const { app, selectedComponent } = getContext<AppEditorContext>('AppEditorContext')
|
||||
let list: HTMLElement
|
||||
|
||||
function selectScript(id: string) {
|
||||
selectedScriptComponentId = id
|
||||
@@ -53,106 +52,110 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
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
|
||||
</div>
|
||||
<div bind:this={list} class="grow flex flex-col gap-4">
|
||||
<PanelSection title="Inline scripts" smallPadding>
|
||||
<div class="flex flex-col gap-2 w-full">
|
||||
{#if runnables.inline.length > 0}
|
||||
<div class="flex gap-1 flex-col ">
|
||||
{#each runnables.inline as { name, id }, index (index)}
|
||||
<PanelSection title="Runnables" smallPadding>
|
||||
<div class="w-full flex flex-col gap-6 py-1">
|
||||
<div>
|
||||
<div class="text-sm font-semibold truncate mb-1"> Inline scripts </div>
|
||||
<div class="flex flex-col gap-2 w-full">
|
||||
{#if runnables.inline.length > 0}
|
||||
<div class="flex gap-1 flex-col ">
|
||||
{#each runnables.inline as { name, id }, index (index)}
|
||||
<button
|
||||
id={PREFIX + id}
|
||||
class="border flex gap-1 truncate font-normal justify-between w-full items-center py-1 px-2 rounded-sm duration-200
|
||||
{selectedScriptComponentId === id ? 'border-blue-500 bg-blue-100' : 'hover:bg-blue-50'}"
|
||||
on:click={() => selectScript(id)}
|
||||
>
|
||||
<span class="text-2xs truncate">{name}</span>
|
||||
<div>
|
||||
<Badge color="dark-indigo">{id}</Badge>
|
||||
</div>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{#if $app.unusedInlineScripts?.length > 0}
|
||||
<div class="flex gap-1 flex-col ">
|
||||
{#each $app.unusedInlineScripts as unusedInlineScript, index (index)}
|
||||
{@const id = `unused-${index}`}
|
||||
<button
|
||||
id={PREFIX + id}
|
||||
class="border flex gap-1 truncate font-normal justify-between w-full items-center py-1 px-2 rounded-sm duration-200
|
||||
{selectedScriptComponentId === id ? 'border-blue-500 bg-blue-100' : 'hover:bg-blue-50'}"
|
||||
on:click={() => selectScript(id)}
|
||||
>
|
||||
<span class="text-2xs truncate">{unusedInlineScript.name}</span>
|
||||
<Badge color="red">Detached</Badge>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{#if runnables.inline.length == 0 && $app.unusedInlineScripts?.length == 0}
|
||||
<div class="text-xs text-gray-500">No inline scripts</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="text-sm font-semibold truncate mb-1"> Workspace/Hub </div>
|
||||
<div class="flex flex-col gap-1 w-full">
|
||||
{#if runnables.imported.length > 0}
|
||||
{#each runnables.imported as { name, id }, index (index)}
|
||||
<button
|
||||
id={PREFIX + id}
|
||||
class="border flex gap-1 truncate font-normal justify-between w-full items-center py-1 px-2 rounded-sm duration-200
|
||||
{selectedScriptComponentId === id ? 'border-blue-500 bg-blue-100' : 'hover:bg-blue-50'}"
|
||||
class="border flex gap-1 truncate font-normal justify-between w-full items-center py-1 px-2 rounded-sm duration-200
|
||||
{selectedScriptComponentId === id ? 'border-blue-500 bg-blue-100' : 'hover:bg-blue-50'}"
|
||||
on:click={() => selectScript(id)}
|
||||
>
|
||||
<span class="text-2xs truncate">{name}</span>
|
||||
<div>
|
||||
<Badge color="dark-indigo">{id}</Badge>
|
||||
</div>
|
||||
<Badge color="dark-indigo">{id}</Badge>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="text-xs text-gray-500">No imported scripts/flows</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if $app.unusedInlineScripts?.length > 0}
|
||||
<div class="flex gap-1 flex-col ">
|
||||
{#each $app.unusedInlineScripts as unusedInlineScript, index (index)}
|
||||
{@const id = `unused-${index}`}
|
||||
<div>
|
||||
<div class="w-full flex justify-between items-center">
|
||||
<div class="text-sm font-semibold truncate mb-1">
|
||||
Background scripts
|
||||
<Tooltip class="mb-0.5">
|
||||
Background scripts are triggered upon global refresh or when their input changes. The
|
||||
result of a background script can be shared among many components.
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Button
|
||||
size="xs"
|
||||
color="light"
|
||||
variant="border"
|
||||
btnClasses="!rounded-full !p-1"
|
||||
title="Create a new background script"
|
||||
aria-label="Create a new background script"
|
||||
on:click={createBackgroundScript}
|
||||
>
|
||||
<Plus size={14} class="text-gray-500" />
|
||||
</Button>
|
||||
</div>
|
||||
<div class="flex flex-col gap-1 w-full">
|
||||
{#if $app.hiddenInlineScripts?.length > 0}
|
||||
{#each $app.hiddenInlineScripts as { name }, index (index)}
|
||||
{@const id = `bg_${index}`}
|
||||
<button
|
||||
id={PREFIX + id}
|
||||
class="border flex gap-1 truncate font-normal justify-between w-full items-center py-1 px-2 rounded-sm duration-200
|
||||
{selectedScriptComponentId === id ? 'border-blue-500 bg-blue-100' : 'hover:bg-blue-50'}"
|
||||
class="border flex gap-1 truncate font-normal justify-between w-full items-center py-1 px-2 rounded-sm duration-200
|
||||
{selectedScriptComponentId === id ? 'border-blue-500 bg-blue-100' : 'hover:bg-blue-50'}"
|
||||
on:click={() => selectScript(id)}
|
||||
>
|
||||
<span class="text-2xs truncate">{unusedInlineScript.name}</span>
|
||||
<Badge color="red">Detached</Badge>
|
||||
<span class="text-2xs truncate">{name}</span>
|
||||
<Badge color="dark-indigo">{id}</Badge>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if runnables.inline.length == 0 && $app.unusedInlineScripts?.length == 0}
|
||||
<div class="text-sm text-gray-500">No inline scripts</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="text-xs text-gray-500">No background scripts</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</PanelSection>
|
||||
|
||||
<PanelSection title="Workspace/Hub" smallPadding>
|
||||
<div class="flex flex-col gap-1 w-full">
|
||||
{#if runnables.imported.length > 0}
|
||||
{#each runnables.imported as { name, id }, index (index)}
|
||||
<button
|
||||
id={PREFIX + id}
|
||||
class="border flex gap-1 truncate font-normal justify-between w-full items-center py-1 px-2 rounded-sm duration-200
|
||||
{selectedScriptComponentId === id ? 'border-blue-500 bg-blue-100' : 'hover:bg-blue-50'}"
|
||||
on:click={() => selectScript(id)}
|
||||
>
|
||||
<span class="text-2xs truncate">{name}</span>
|
||||
<Badge color="dark-indigo">{id}</Badge>
|
||||
</button>
|
||||
{/each}
|
||||
{:else}
|
||||
<div class="text-sm text-gray-500">No imported scripts/flows</div>
|
||||
{/if}
|
||||
</div>
|
||||
</PanelSection>
|
||||
|
||||
<PanelSection
|
||||
title="Background scripts"
|
||||
tooltip="Background scripts are triggered upon global refresh or when their input changes. The result
|
||||
of a background script can be shared among many components."
|
||||
smallPadding
|
||||
>
|
||||
<svelte:fragment slot="action">
|
||||
<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}
|
||||
{#each $app.hiddenInlineScripts as { name }, index (index)}
|
||||
{@const id = `bg_${index}`}
|
||||
<button
|
||||
id={PREFIX + id}
|
||||
class="border flex gap-1 truncate font-normal justify-between w-full items-center py-1 px-2 rounded-sm duration-200
|
||||
{selectedScriptComponentId === id ? 'border-blue-500 bg-blue-100' : 'hover:bg-blue-50'}"
|
||||
on:click={() => selectScript(id)}
|
||||
>
|
||||
<span class="text-2xs truncate">{name}</span>
|
||||
<Badge color="dark-indigo">{id}</Badge>
|
||||
</button>
|
||||
{/each}
|
||||
{:else}
|
||||
<div class="text-sm text-gray-500">No items</div>
|
||||
{/if}
|
||||
</div>
|
||||
</PanelSection>
|
||||
</div>
|
||||
</div>
|
||||
</PanelSection>
|
||||
|
||||
@@ -53,9 +53,9 @@
|
||||
{#if panes.length == 0}
|
||||
<span class="text-xs text-gray-500">No panes</span>
|
||||
{/if}
|
||||
<div class="flex gap-2 flex-col mt-2">
|
||||
<div class="w-full flex gap-2 flex-col mt-2">
|
||||
{#each panes as value, index (index)}
|
||||
<div class="flex flex-row gap-2 items-center relative">
|
||||
<div class="w-full flex flex-row gap-2 items-center relative">
|
||||
<input type="number" bind:value />
|
||||
|
||||
<div class="absolute top-1 right-1">
|
||||
|
||||
@@ -51,9 +51,9 @@
|
||||
{#if tabs.length == 0}
|
||||
<span class="text-xs text-gray-500">No Tabs</span>
|
||||
{/if}
|
||||
<div class="flex gap-2 flex-col mt-2">
|
||||
<div class="w-full flex gap-2 flex-col mt-2">
|
||||
{#each tabs as value, index (index)}
|
||||
<div class="flex flex-row gap-2 items-center relative">
|
||||
<div class="w-full flex flex-row gap-2 items-center relative">
|
||||
<input type="text" bind:value />
|
||||
|
||||
<div class="absolute top-1 right-1">
|
||||
|
||||
+20
-2
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import type { AppInput, StaticInput } from '../../../inputType'
|
||||
import { Loader2 } from 'lucide-svelte'
|
||||
import { Loader2, X } from 'lucide-svelte'
|
||||
import { Popup } from '../../../../common'
|
||||
|
||||
export let componentInput: StaticInput<string> & Extract<AppInput, { fieldType: 'icon-select' }>
|
||||
@@ -46,7 +46,25 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<input readonly value={formatName(componentInput.value)} bind:this={anchor} on:focus={getData} />
|
||||
<div class="relative">
|
||||
<input
|
||||
readonly
|
||||
value={formatName(componentInput.value)}
|
||||
bind:this={anchor}
|
||||
on:focus={getData}
|
||||
class="pr-8"
|
||||
/>
|
||||
{#if componentInput.value}
|
||||
<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={() => (componentInput.value = undefined)}
|
||||
title="Clear"
|
||||
aria-label="Clear"
|
||||
>
|
||||
<X size="14" />
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
{#if anchor}
|
||||
<Popup ref={anchor} options={{ placement: 'bottom' }} let:close>
|
||||
<div class="max-w-xs shadow-[0_10px_40px_-5px_rgba(0,0,0,0.25)] bg-white rounded-md p-2">
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
export let selected: string
|
||||
let c = ''
|
||||
export { c as class }
|
||||
export let wrapperClass = ''
|
||||
export let style = ''
|
||||
|
||||
$: selected && updateSelected()
|
||||
@@ -37,7 +38,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<div class="overflow-x-auto {wrapperClass}">
|
||||
<div
|
||||
class={twMerge('border-b border-gray-200 flex flex-row whitespace-nowrap scrollbar-hidden', c)}
|
||||
{style}
|
||||
|
||||
Reference in New Issue
Block a user