fix(frontend): Minor app editor updates (#1458)
* fix(frontend): Update clearable inputs * update icon picker popup * update
This commit is contained in:
@@ -311,7 +311,7 @@
|
||||
<TabContent value="insert">
|
||||
<ComponentList />
|
||||
</TabContent>
|
||||
<TabContent value="css">
|
||||
<TabContent value="css" class="h-full">
|
||||
<CssSettings />
|
||||
</TabContent>
|
||||
</div>
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
} from '../component'
|
||||
import ListItem from './ListItem.svelte'
|
||||
import { appComponentFromType, insertNewGridItem } from '../appUtils'
|
||||
import { X } from 'lucide-svelte'
|
||||
import { push } from '$lib/history'
|
||||
import { flip } from 'svelte/animate'
|
||||
import { ClearableInput } from '../../../common'
|
||||
|
||||
const { app, selectedComponent, focusedGrid } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
@@ -47,22 +47,7 @@
|
||||
</script>
|
||||
|
||||
<section class="p-2 sticky bg-white w-full z-10 top-0">
|
||||
<div class="relative">
|
||||
<input
|
||||
bind:value={search}
|
||||
class="px-2 py-1 border border-gray-300 rounded-sm !pr-6"
|
||||
placeholder="Search components..."
|
||||
/>
|
||||
|
||||
{#if search !== ''}
|
||||
<button
|
||||
class="absolute right-2 top-1/2 transform -translate-y-1/2 hover:bg-gray-500 bg-gray-300 transition-all rounded-full p-0.5"
|
||||
on:click|stopPropagation|preventDefault={() => (search = '')}
|
||||
>
|
||||
<X size="10" color="white" />
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
<ClearableInput bind:value={search} placeholder="Search components..." />
|
||||
</section>
|
||||
|
||||
<div class="relative">
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import { LayoutDashboardIcon, MousePointer2, CurlyBraces, X } from 'lucide-svelte'
|
||||
import { LayoutDashboardIcon, MousePointer2, CurlyBraces } from 'lucide-svelte'
|
||||
import SimpleEditor from '$lib/components/SimpleEditor.svelte'
|
||||
import { emptyString } from '$lib/utils'
|
||||
import { Tab, TabContent, Tabs } from '../../../common'
|
||||
import { ClearableInput, Tab, TabContent, Tabs } from '../../../common'
|
||||
import type { AppViewerContext } from '../../types'
|
||||
import ListItem from './ListItem.svelte'
|
||||
import CssProperty from './CssProperty.svelte'
|
||||
import { ccomponents, components } from '../component'
|
||||
import { slide } from 'svelte/transition'
|
||||
|
||||
const STATIC_ELEMENTS = ['app'] as const
|
||||
const TITLE_PREFIX = 'Css.' as const
|
||||
@@ -27,6 +28,7 @@
|
||||
|
||||
$: rawCode && parseJson()
|
||||
let jsonError = ''
|
||||
let jsonErrorHeight: number
|
||||
|
||||
function parseJson() {
|
||||
try {
|
||||
@@ -67,8 +69,8 @@
|
||||
let search = ''
|
||||
</script>
|
||||
|
||||
<div class="w-full text-lg font-semibold text-center text-gray-600 p-2">Global Styling</div>
|
||||
<Tabs selected="ui" on:selected={(e) => switchTab(e.detail === 'json')} class="relative">
|
||||
<!-- <div class="w-full text-lg font-semibold text-center text-gray-600 p-2">Global Styling</div> -->
|
||||
<Tabs selected="ui" on:selected={(e) => switchTab(e.detail === 'json')} class="h-full">
|
||||
<Tab value="ui" size="xs" class="w-1/2">
|
||||
<div class="m-1 center-center">
|
||||
<MousePointer2 size={16} />
|
||||
@@ -81,80 +83,64 @@
|
||||
<span class="pl-1">JSON</span>
|
||||
</div>
|
||||
</Tab>
|
||||
<div slot="content" class="h-full overflow-y-auto">
|
||||
<TabContent value="ui">
|
||||
<div class="sticky top-0 left-0 w-full bg-white p-2">
|
||||
<div class="relative">
|
||||
<input
|
||||
bind:value={search}
|
||||
class="px-2 pb-1 border border-gray-300 rounded-sm {search ? 'pr-8' : ''}"
|
||||
placeholder="Search..."
|
||||
/>
|
||||
{#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>
|
||||
<div slot="content" class="h-[calc(100%-35px)] overflow-auto">
|
||||
<TabContent value="ui" class="h-full">
|
||||
<div class="p-2">
|
||||
<ClearableInput bind:value={search} placeholder="Search..." />
|
||||
</div>
|
||||
{#each search != '' ? entries.filter((x) => x.name
|
||||
.toLowerCase()
|
||||
.includes(search.toLowerCase())) : entries as { type, name, icon, ids } (name)}
|
||||
{#if ids.length > 0}
|
||||
<ListItem
|
||||
title={name}
|
||||
prefix={TITLE_PREFIX}
|
||||
on:open={(e) => {
|
||||
if ($app.css != undefined) {
|
||||
if (e.detail && $app.css[type] == undefined) {
|
||||
$app.css[type] = Object.fromEntries(ids.map(({ id }) => [id, {}]))
|
||||
<div class="h-[calc(100%-50px)] overflow-auto relative">
|
||||
{#each search != '' ? entries.filter((x) => x.name
|
||||
.toLowerCase()
|
||||
.includes(search.toLowerCase())) : entries as { type, name, icon, ids } (name)}
|
||||
{#if ids.length > 0}
|
||||
<ListItem
|
||||
title={name}
|
||||
prefix={TITLE_PREFIX}
|
||||
on:open={(e) => {
|
||||
if ($app.css != undefined) {
|
||||
if (e.detail && $app.css[type] == undefined) {
|
||||
$app.css[type] = Object.fromEntries(ids.map(({ id }) => [id, {}]))
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div slot="title" class="flex items-center">
|
||||
<svelte:component this={icon} size={18} />
|
||||
<span class="ml-1">
|
||||
{name}
|
||||
</span>
|
||||
</div>
|
||||
<div class="pb-2">
|
||||
{#each ids as { id, forceStyle, forceClass }}
|
||||
<div class="mb-3">
|
||||
{#if $app?.css?.[type]}
|
||||
<CssProperty
|
||||
{forceClass}
|
||||
{forceStyle}
|
||||
name={id}
|
||||
bind:value={$app.css[type][id]}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</ListItem>
|
||||
{/if}
|
||||
{/each}
|
||||
}}
|
||||
>
|
||||
<div slot="title" class="flex items-center">
|
||||
<svelte:component this={icon} size={18} />
|
||||
<span class="ml-1">
|
||||
{name}
|
||||
</span>
|
||||
</div>
|
||||
<div class="pb-2">
|
||||
{#each ids as { id, forceStyle, forceClass }}
|
||||
<div class="mb-3">
|
||||
{#if $app?.css?.[type]}
|
||||
<CssProperty
|
||||
{forceClass}
|
||||
{forceStyle}
|
||||
name={id}
|
||||
bind:value={$app.css[type][id]}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</ListItem>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
</TabContent>
|
||||
<TabContent value="json">
|
||||
<TabContent value="json" class="h-full">
|
||||
{#if !emptyString(jsonError)}
|
||||
<span class="text-red-400 text-xs mb-1 flex flex-row-reverse">
|
||||
<div
|
||||
transition:slide={{ duration: 200 }}
|
||||
bind:clientHeight={jsonErrorHeight}
|
||||
class="text-red-500 text-xs p-1"
|
||||
>
|
||||
{jsonError}
|
||||
</span>
|
||||
{:else}
|
||||
<div class="py-2" />
|
||||
</div>
|
||||
{/if}
|
||||
<div class="h-full w-full py-1">
|
||||
<SimpleEditor
|
||||
autoHeight
|
||||
class="editor"
|
||||
lang="json"
|
||||
bind:code={rawCode}
|
||||
fixedOverflowWidgets={false}
|
||||
/>
|
||||
<div style="height: calc(100% - {jsonErrorHeight || 0}px);">
|
||||
<SimpleEditor class="h-full" lang="json" bind:code={rawCode} fixedOverflowWidgets={false} />
|
||||
</div>
|
||||
</TabContent>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { classNames } from '$lib/utils'
|
||||
import { X } from 'lucide-svelte'
|
||||
import { getContext } from 'svelte'
|
||||
|
||||
import type { AppViewerContext, ContextPanelContext } from '../../types'
|
||||
@@ -10,6 +9,7 @@
|
||||
import ComponentOutputViewer from './ComponentOutputViewer.svelte'
|
||||
import BackgroundScriptsOutput from './components/BackgroundScriptsOutput.svelte'
|
||||
import OutputHeader from './components/OutputHeader.svelte'
|
||||
import { ClearableInput } from '../../../common'
|
||||
|
||||
const { connectingInput, app } = getContext<AppViewerContext>('AppViewerContext')
|
||||
const { search } = getContext<ContextPanelContext>('ContextPanel')
|
||||
@@ -26,21 +26,7 @@
|
||||
>
|
||||
<div class="min-w-[150px]">
|
||||
<div class="sticky z-10 top-0 left-0 w-full bg-white p-1.5">
|
||||
<div class="relative w-full">
|
||||
<input
|
||||
bind:value={$search}
|
||||
class="!border-gray-200 !rounded-md !text-xs !pr-6"
|
||||
placeholder="Search outputs..."
|
||||
/>
|
||||
{#if $search !== ''}
|
||||
<button
|
||||
class="absolute right-2 top-1/2 transform -translate-y-1/2 hover:bg-gray-500 bg-gray-300 transition-all rounded-full p-0.5"
|
||||
on:click|stopPropagation|preventDefault={() => ($search = '')}
|
||||
>
|
||||
<X size="10" color="white" />
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
<ClearableInput bind:value={$search} placeholder="Search outputs..." />
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-4">
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
}
|
||||
|
||||
.color-picker-input span > div.wrapper {
|
||||
box-shadow: 0 5px 30px -5px rgba(0, 0, 0, 0.25) !important;
|
||||
box-shadow: 0 10px 40px -5px rgba(0, 0, 0, 0.25) !important;
|
||||
border-style: none !important;
|
||||
border-radius: 0.375rem !important;
|
||||
top: 6px !important;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type { StaticInput } from '../../../inputType'
|
||||
import { Loader2, X } from 'lucide-svelte'
|
||||
import { Popup } from '../../../../common'
|
||||
import { Loader2 } from 'lucide-svelte'
|
||||
import { ClearableInput, Popup } from '../../../../common'
|
||||
import { fade } from 'svelte/transition'
|
||||
|
||||
export let componentInput: StaticInput<string>
|
||||
@@ -52,11 +52,11 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="relative">
|
||||
<input
|
||||
<div bind:this={anchor} class="relative">
|
||||
<ClearableInput
|
||||
readonly
|
||||
value={formatName(componentInput.value)}
|
||||
bind:this={anchor}
|
||||
on:change={({ detail }) => (componentInput.value = detail)}
|
||||
on:focus={getData}
|
||||
class="!pr-6"
|
||||
/>
|
||||
@@ -64,28 +64,24 @@
|
||||
<div class="center-center absolute right-2 top-1/2 transform -translate-y-1/2 p-0.5">
|
||||
<Loader2 class="animate-spin" size={16} />
|
||||
</div>
|
||||
{:else if componentInput.value}
|
||||
<button
|
||||
class="absolute right-2 top-1/2 transform -translate-y-1/2 hover:bg-gray-500 bg-gray-300 transition-all rounded-full p-0.5"
|
||||
on:click|stopPropagation|preventDefault={() => (componentInput.value = undefined)}
|
||||
title="Clear"
|
||||
aria-label="Clear"
|
||||
>
|
||||
<X size="10" color="white" />
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
{#if anchor}
|
||||
<Popup
|
||||
ref={anchor}
|
||||
closeOn={[]}
|
||||
options={{ placement: 'bottom' }}
|
||||
options={{
|
||||
placement: 'bottom-start',
|
||||
strategy: 'fixed',
|
||||
modifiers: [{ name: 'offset', options: { offset: [0, 6] } }]
|
||||
}}
|
||||
bind:open={openPopup}
|
||||
let:close
|
||||
transition={fade}
|
||||
wrapperClasses=" z-[1002]"
|
||||
>
|
||||
{#if !loading}
|
||||
<div class="max-w-xs shadow-[0_10px_40px_-5px_rgba(0,0,0,0.25)] bg-white rounded-md p-2">
|
||||
<div class="w-72 shadow-[0_10px_40px_-5px_rgba(0,0,0,0.25)] bg-white rounded-md p-2">
|
||||
{#if filteredItems}
|
||||
<input
|
||||
on:keydown={(event) => {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
export let wrapperClass = ''
|
||||
export let buttonClass = ''
|
||||
const dispatch = createEventDispatcher()
|
||||
let isHovered = false
|
||||
|
||||
$: isNumeric = ['number', 'range'].includes(type)
|
||||
$: dispatch('change', value)
|
||||
@@ -23,7 +24,11 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="relative grow {wrapperClass}">
|
||||
<div
|
||||
class="relative grow {wrapperClass}"
|
||||
on:mouseenter={() => (isHovered = true)}
|
||||
on:mouseleave={() => (isHovered = false)}
|
||||
>
|
||||
{#if type === 'textarea'}
|
||||
<textarea
|
||||
{value}
|
||||
@@ -47,14 +52,15 @@
|
||||
on:blur
|
||||
/>
|
||||
{/if}
|
||||
{#if value}
|
||||
{#if value && isHovered}
|
||||
<button
|
||||
transition:fade|local={{ duration: 100 }}
|
||||
class="absolute z-10 top-1.5 right-1 rounded-full p-1 bg-white/60 duration-200 hover:bg-gray-200 {buttonClass}"
|
||||
transition:fade|local={{ duration: 80 }}
|
||||
class="absolute z-10 top-[9.5px] right-2 rounded-full p-0.5 text-white bg-gray-300
|
||||
duration-200 hover:bg-gray-600 focus:bg-gray-600 {buttonClass}"
|
||||
aria-label="Clear"
|
||||
on:click|preventDefault|stopPropagation={clear}
|
||||
>
|
||||
<X size={14} />
|
||||
<X size={11} class="" />
|
||||
</button>
|
||||
{/if}
|
||||
<slot />
|
||||
|
||||
Reference in New Issue
Block a user