Compare commits

...

12 Commits

Author SHA1 Message Date
Faton Ramadani
4140117b6a Merge branch 'main' into app-preview-scroll 2023-03-07 16:50:59 +01:00
Faton Ramadani
f20ea37eb0 feat(frontend): Fix app preview error display 2023-03-07 16:48:23 +01:00
Faton Ramadani
87d31ee63b Merge branch 'main' into app-preview-scroll 2023-03-07 15:29:24 +01:00
Faton Ramadani
fb2d52f99b feat(frontend): Fix recomputeIds + app table 2023-03-07 15:28:58 +01:00
Faton Ramadani
2e3271257f Merge branch 'main' into app-preview-scroll 2023-03-07 11:42:10 +01:00
Faton Ramadani
b013996585 Merge branch 'main' into app-preview-scroll 2023-03-06 18:49:55 +01:00
Faton Ramadani
0a76e3533c feat(frontend): remove useless softWrap 2023-03-06 15:46:00 +01:00
Faton Ramadani
7648b382ff feat(frontend): remvove useless softWrap 2023-03-06 15:14:23 +01:00
Faton Ramadani
e2747e824f feat(frontend): merge main 2023-03-06 13:59:08 +01:00
Faton Ramadani
2060445d20 feat(frontend): fix text resize 2023-03-06 13:40:07 +01:00
Faton Ramadani
94e30052bf Merge branch 'main' into app-preview-scroll 2023-03-06 08:23:05 +01:00
Faton Ramadani
68add7932e feat(frontend): add a way to automatically resize (wip) + add automatic resizable component 2023-03-06 08:21:54 +01:00
12 changed files with 151 additions and 91 deletions

View File

@@ -86,6 +86,33 @@
$: errorsMessage = Object.values(errors)
.filter((x) => x != '')
.join('\n')
async function handleClick(event: CustomEvent) {
event?.stopPropagation()
event?.preventDefault()
if (preclickAction) {
await preclickAction()
}
ownClick = true
if (!runnableComponent) {
if (gotoUrl) {
if (gotoNewTab) {
window.open(gotoUrl, '_blank')
} else {
goto(gotoUrl)
}
}
} else {
await runnableComponent?.runComponent()
}
if (recomputeIds) {
await Promise.all(recomputeIds.map((id) => $runnableComponents?.[id]?.()))
}
}
</script>
<InputValue {id} input={configuration.label} bind:value={labelValue} />
@@ -131,32 +158,7 @@
e?.stopPropagation()
window.dispatchEvent(new Event('pointerup'))
}}
on:click={async (e) => {
if (preclickAction) {
await preclickAction()
}
e?.stopPropagation()
e?.preventDefault()
ownClick = true
if (!runnableComponent) {
if (gotoUrl) {
if (gotoNewTab) {
window.open(gotoUrl, '_blank')
} else {
goto(gotoUrl)
}
}
} else {
await runnableComponent?.runComponent()
}
if (recomputeIds) {
recomputeIds.forEach((id) => {
$runnableComponents[id]?.()
})
}
}}
on:click={handleClick}
{size}
{color}
{loading}

View File

@@ -164,7 +164,6 @@
function handleSyncRegion() {
const gridItem = findGridItem($app, id)
if (!map || !gridItem) {
return
}

View File

@@ -118,7 +118,6 @@
function scrollToPage(page: number) {
page = pageNumber = minMax(page, 1, pages.length)
const offset = (wrapper?.children.item(page - 1) as HTMLCanvasElement | null)?.offsetTop
// debugger
if (!offset) {
return
}

View File

@@ -10,6 +10,7 @@
import { twMerge } from 'tailwind-merge'
import type { AppEditorContext, ComponentCustomCSS } from '../../types'
import { getContext } from 'svelte'
import ResizeWrapper from '../helpers/ResizeWrapper.svelte'
export let id: string
export let componentInput: AppInput | undefined
@@ -26,6 +27,7 @@
let result: string | undefined = undefined
let style: 'Title' | 'Subtitle' | 'Body' | 'Caption' | 'Label' | undefined = undefined
let copyButton: boolean
let fitContent = false
function getComponent() {
switch (style) {
@@ -63,36 +65,39 @@
<InputValue {id} input={configuration.style} bind:value={style} />
<InputValue {id} input={configuration.copyButton} bind:value={copyButton} />
<InputValue {id} input={configuration.fitContent} bind:value={fitContent} />
<RunnableWrapper flexWrap {componentInput} {id} bind:initializing bind:result>
<AlignWrapper {horizontalAlignment} {verticalAlignment}>
{#if !result || result === ''}
<div class="text-gray-400 bg-gray-100 flex justify-center items-center h-full w-full">
No text
</div>
{:else}
<div class="flex flex-wrap gap-2 pb-0.5 overflow-x-auto">
<svelte:element
this={component}
class={twMerge(
'whitespace-pre-wrap',
$app.css?.['textcomponent']?.['text']?.class,
customCss?.text?.class,
classes
)}
style={[$app.css?.['textcomponent']?.['text']?.style, customCss?.text?.style].join(';')}
>
{String(result)}
</svelte:element>
{#if copyButton && result}
<Popover notClickable>
<Button size="xs" btnClasses="!px-2" on:click={() => copyToClipboard(result)}>
<Clipboard size={14} strokeWidth={2} />
</Button>
<svelte:fragment slot="text">Copy to clipboard</svelte:fragment>
</Popover>
{/if}
</div>
{/if}
</AlignWrapper>
<ResizeWrapper {id} shouldWrap={fitContent}>
<AlignWrapper {horizontalAlignment} {verticalAlignment}>
{#if !result || result === ''}
<div class="text-gray-400 bg-gray-100 flex justify-center items-center h-full w-full">
No text
</div>
{:else}
<div class="flex flex-wrap gap-2 pb-0.5 overflow-x-auto">
<svelte:element
this={component}
class={twMerge(
'whitespace-pre-wrap',
$app.css?.['textcomponent']?.['text']?.class,
customCss?.text?.class,
classes
)}
style={[$app.css?.['textcomponent']?.['text']?.style, customCss?.text?.style].join(';')}
>
{String(result)}
</svelte:element>
{#if copyButton && result}
<Popover notClickable>
<Button size="xs" btnClasses="!px-2" on:click={() => copyToClipboard(result)}>
<Clipboard size={14} strokeWidth={2} />
</Button>
<svelte:fragment slot="text">Copy to clipboard</svelte:fragment>
</Popover>
{/if}
</div>
{/if}
</AlignWrapper>
</ResizeWrapper>
</RunnableWrapper>

View File

@@ -64,11 +64,11 @@
let selectedRowIndex = -1
function toggleRow(row: Record<string, any>, rowIndex: number) {
if (selectedRowIndex !== rowIndex) {
function toggleRow(row: Record<string, any>, rowIndex: number, force: boolean = false) {
if (selectedRowIndex !== rowIndex || force) {
selectedRowIndex = rowIndex
outputs?.selectedRow.set(row.original)
outputs?.selectedRowIndex.set(rowIndex)
outputs?.selectedRow.set(row.original, force)
outputs?.selectedRowIndex.set(rowIndex, force)
}
}
@@ -138,6 +138,9 @@
function rerender() {
table = createSvelteTable(options)
if (result) {
toggleRow({ original: result[0] }, 0, true)
}
}
$: result && rerender()

View File

@@ -0,0 +1,30 @@
<script lang="ts">
import { getContext } from 'svelte'
import { findGridItem } from '../../editor/appUtils'
import type { AppEditorContext } from '../../types'
export let id: string
export let shouldWrap: boolean = false
const { app, breakpoint } = getContext<AppEditorContext>('AppEditorContext')
$: gridItem = findGridItem($app, id)
let wrapper: HTMLElement
$: {
if (wrapper && gridItem && shouldWrap) {
const wrapperHeight = wrapper.getBoundingClientRect().height
const width = $breakpoint === 'sm' ? 3 : 12
gridItem[width].h = Math.ceil(wrapperHeight / 36)
gridItem = gridItem
}
}
</script>
{#if shouldWrap}
<div bind:this={wrapper}>
<slot />
</div>
{:else}
<slot />
{/if}

View File

@@ -2,10 +2,12 @@
import { goto } from '$app/navigation'
import type { Schema } from '$lib/common'
import Alert from '$lib/components/common/alert/Alert.svelte'
import Popover from '$lib/components/Popover.svelte'
import SchemaForm from '$lib/components/SchemaForm.svelte'
import TestJobLoader from '$lib/components/TestJobLoader.svelte'
import { AppService, type CompletedJob } from '$lib/gen'
import { defaultIfEmptyString, emptySchema, sendUserToast } from '$lib/utils'
import { classNames, defaultIfEmptyString, emptySchema, sendUserToast } from '$lib/utils'
import { Bug } from 'lucide-svelte'
import { getContext, onMount } from 'svelte'
import type { AppInputs, Runnable } from '../../inputType'
import type { Output } from '../../rx'
@@ -104,7 +106,6 @@
}
$: outputs?.loading?.set(testIsLoading)
$: schemaStripped = stripSchema(fields, $stateId)
function stripSchema(inputs: AppInputs, s: any): Schema {
@@ -276,16 +277,28 @@
Please select a runnable
</Alert>
{:else if result?.error && $mode === 'preview'}
<div class="p-2">
<Alert type="error" title="Error during execution">
<div class="flex flex-col gap-2">
An error occured, please contact the app author.
<span class="font-semibold">Job id: {testJob?.id}</span>
<pre class=" whitespace-pre-wrap text-gray-900 bg-white border w-full p-4 text-xs"
>{JSON.stringify(result.error, null, 4)}
</pre>
</div>
</Alert>
<div
title="Error"
class={classNames(
'text-red-500 px-1 text-2xs py-0.5 font-bold w-fit absolute border border-red-500 -bottom-2 shadow left-1/2 transform -translate-x-1/2 z-50 cursor-pointer',
'bg-red-100/80'
)}
>
<Popover notClickable placement="bottom" popupClass="!bg-white border w-96">
<Bug size={14} />
<span slot="text">
<div class="bg-white">
<Alert type="error" title="Error during execution">
<div class="flex flex-col gap-2">
An error occured, please contact the app author.
<span class="font-semibold">Job id: {testJob?.id}</span>
</div>
</Alert>
</div>
</span>
</Popover>
</div>
<div class="block grow w-full max-h-full border border-red-300 bg-red-50 relative">
<slot />
</div>
{:else}

View File

@@ -15,8 +15,7 @@
const dispatch = createEventDispatcher()
const { errorByComponent, openDebugRun, focusedGrid, worldStore } =
getContext<AppEditorContext>('AppEditorContext')
const { errorByComponent, openDebugRun } = getContext<AppEditorContext>('AppEditorContext')
$: error = Object.values($errorByComponent).find((e) => e.componentId === component.id)

View File

@@ -47,7 +47,7 @@
export let locked: boolean = false
export let pointerdown: boolean = false
const { staticOutputs, mode, connectingInput, app } =
const { staticOutputs, mode, connectingInput, app, errorByComponent } =
getContext<AppEditorContext>('AppEditorContext')
let hover = false
let initializing: boolean | undefined = undefined
@@ -57,6 +57,9 @@
// $: if (!deepEqual(component, pComponent)) {
// component = JSON.parse(JSON.stringify(pComponent))
// }
$: componentWithErrors = Object.values($errorByComponent).map((e) => e.componentId)
$: hasError = componentWithErrors.includes(component.id)
</script>
<div
@@ -82,7 +85,7 @@
selected && $mode !== 'preview' ? 'border border-blue-500' : '',
!selected && $mode !== 'preview' && !component.card ? 'border-gray-100' : '',
$mode !== 'preview' && !$connectingInput.opened ? 'hover:border-blue-500' : '',
component.softWrap ? '' : 'overflow-auto',
component.softWrap || hasError ? '' : 'overflow-auto',
$mode != 'preview' ? 'cursor-pointer' : '',
'relative z-auto',
$app.css?.['app']?.['component']?.class
@@ -195,6 +198,7 @@
customCss={component.customCss}
componentInput={component.componentInput}
bind:staticOutputs={$staticOutputs[component.id]}
recomputeIds={component.recomputeIds}
/>
{:else if component.type === 'selectcomponent'}
<AppSelect
@@ -222,6 +226,7 @@
customCss={component.customCss}
componentInput={component.componentInput}
bind:staticOutputs={$staticOutputs[component.id]}
recomputeIds={component.recomputeIds}
/>
{:else if component.type === 'formbuttoncomponent'}
<AppFormButton
@@ -232,6 +237,7 @@
customCss={component.customCss}
componentInput={component.componentInput}
bind:staticOutputs={$staticOutputs[component.id]}
recomputeIds={component.recomputeIds}
/>
{:else if component.type === 'checkboxcomponent'}
<AppCheckbox
@@ -405,7 +411,7 @@
</div>
{#if initializing}
<div
out:fade={{ duration: 200 }}
out:fade|local={{ duration: 200 }}
class="absolute inset-0 center-center flex-col bg-white text-gray-600 border"
>
<Loader2 class="animate-spin" size={16} />

View File

@@ -231,6 +231,12 @@ export const components: Record<AppComponent['type'], AppComponentConfig> = {
value: false,
fieldType: 'boolean',
onlyStatic: true
},
fitContent: {
type: 'static',
value: false,
fieldType: 'boolean',
onlyStatic: true
}
},
customCss: {

View File

@@ -131,9 +131,8 @@
<div class="flex flex-row gap-1 items-center">
Format
<div class="flex flex-row items-center">
<div class="flex flex-row items-center gap-1">
<Kbd>{isMac ? '⌘' : 'CTRL'}</Kbd>
<Plus size={12} />
<Kbd>S</Kbd>
</div>
</div>
@@ -154,9 +153,8 @@
<div class="flex flex-row gap-1 items-center">
Run
<div class="flex flex-row items-center">
<div class="flex flex-row items-center gap-1">
<Kbd>{isMac ? '⌘' : 'CTRL'}</Kbd>
<Plus size={12} />
<Kbd>
<div class="h-4 flex items-center justify-center">
<CornerDownLeft size={10} />

View File

@@ -43,14 +43,14 @@ export interface BaseAppComponent extends Partial<Aligned> {
configuration: Record<
string,
GeneralAppInput &
(
| StaticAppInput
| ConnectedAppInput
| UserAppInput
| RowAppInput
| EvalAppInput
| UploadAppInput
)
(
| StaticAppInput
| ConnectedAppInput
| UserAppInput
| RowAppInput
| EvalAppInput
| UploadAppInput
)
>
card: boolean | undefined
customCss?: ComponentCustomCSS