finish migrating app editor to svelte 5 (#6090)

* all

* all

* all

* all
This commit is contained in:
Ruben Fiszel
2025-06-29 21:01:06 +02:00
committed by GitHub
parent 56dd79c518
commit 7968d8cfc4
152 changed files with 3208 additions and 2168 deletions
@@ -3,10 +3,15 @@
import type { Writable } from 'svelte/store'
import type { GroupContext } from '../types'
export let context: Writable<Record<string, any>>
export let id: string
interface Props {
context: Writable<Record<string, any>>
id: string
children?: import('svelte').Snippet
}
let { context, id, children }: Props = $props()
setContext<GroupContext>('GroupContext', { id, context })
</script>
<slot />
{@render children?.()}
@@ -22,18 +22,28 @@
import ResolveConfig from '../helpers/ResolveConfig.svelte'
import { twMerge } from 'tailwind-merge'
import ResolveStyle from '../helpers/ResolveStyle.svelte'
export let id: string
export let componentInput: AppInput | undefined
export let configuration: RichConfigurations
export let initializing: boolean | undefined = undefined
export let customCss: ComponentCustomCSS<'barchartcomponent'> | undefined = undefined
export let render: boolean
interface Props {
id: string
componentInput: AppInput | undefined
configuration: RichConfigurations
initializing?: boolean | undefined
customCss?: ComponentCustomCSS<'barchartcomponent'> | undefined
render: boolean
}
let {
id,
componentInput,
configuration,
initializing = $bindable(undefined),
customCss = undefined,
render
}: Props = $props()
const { app, worldStore } = getContext<AppViewerContext>('AppViewerContext')
let resolvedConfig = initConfig(
components['barchartcomponent'].initialData.configuration,
configuration
let resolvedConfig = $state(
initConfig(components['barchartcomponent'].initialData.configuration, configuration)
)
let outputs = initOutput($worldStore, id, {
@@ -52,15 +62,17 @@
BarElement
)
let result: { data: number[]; labels?: string[] } | undefined = undefined
let result = $state(undefined) as { data: number[]; labels?: string[] } | undefined
$: backgroundColor = {
theme1: ['#FF6384', '#4BC0C0', '#FFCE56', '#E7E9ED', '#36A2EB'],
// blue theme
theme2: ['#4e73df', '#1cc88a', '#36b9cc', '#f6c23e', '#e74a3b'],
// red theme
theme3: ['#e74a3b', '#4e73df', '#1cc88a', '#36b9cc', '#f6c23e']
}[resolvedConfig.theme ?? 'theme1']
let backgroundColor = $derived(
{
theme1: ['#FF6384', '#4BC0C0', '#FFCE56', '#E7E9ED', '#36A2EB'],
// blue theme
theme2: ['#4e73df', '#1cc88a', '#36b9cc', '#f6c23e', '#e74a3b'],
// red theme
theme3: ['#e74a3b', '#4e73df', '#1cc88a', '#36b9cc', '#f6c23e']
}[resolvedConfig.theme ?? 'theme1']
)
const lineOptions: ChartOptions<'line'> = {
responsive: true,
@@ -84,7 +96,7 @@
}
}
$: data = {
let data = $derived({
labels: result?.labels ?? [],
datasets: [
@@ -105,9 +117,9 @@
}
}
}
}
})
let css = initCss($app.css?.barchartcomponent, customCss)
let css = $state(initCss($app.css?.barchartcomponent, customCss))
</script>
{#each Object.keys(components['barchartcomponent'].initialData.configuration) as key (key)}
@@ -12,12 +12,23 @@
import { twMerge } from 'tailwind-merge'
import ResolveStyle from '../helpers/ResolveStyle.svelte'
export let id: string
export let componentInput: AppInput | undefined
export let configuration: RichConfigurations
export let initializing: boolean | undefined = undefined
export let customCss: ComponentCustomCSS<'chartjscomponent'> | undefined = undefined
export let render: boolean
interface Props {
id: string
componentInput: AppInput | undefined
configuration: RichConfigurations
initializing?: boolean | undefined
customCss?: ComponentCustomCSS<'chartjscomponent'> | undefined
render: boolean
}
let {
id,
componentInput,
configuration,
initializing = $bindable(undefined),
customCss = undefined,
render
}: Props = $props()
const { app, worldStore } = getContext<AppViewerContext>('AppViewerContext')
@@ -28,20 +39,19 @@
ChartJS.register(...registerables)
let result: undefined = undefined
let result: undefined = $state(undefined)
const resolvedConfig = initConfig(
components['chartjscomponent'].initialData.configuration,
configuration
const resolvedConfig = $state(
initConfig(components['chartjscomponent'].initialData.configuration, configuration)
)
$: options = {
let options = $derived({
responsive: true,
animation: false,
maintainAspectRatio: false,
...(resolvedConfig.options ?? {})
} as ChartOptions
} as ChartOptions)
let css = initCss($app.css?.chartjscomponent, customCss)
let css = $state(initCss($app.css?.chartjscomponent, customCss))
</script>
{#each Object.keys(components['chartjscomponent'].initialData.configuration) as key (key)}
@@ -1,5 +1,5 @@
<script lang="ts">
import { getContext, onMount } from 'svelte'
import { getContext, onMount, untrack } from 'svelte'
import { initOutput } from '../../editor/appUtils'
import type { AppViewerContext } from '../../types'
@@ -22,10 +22,14 @@
import { Loader2 } from 'lucide-svelte'
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
export let id: string
export let render: boolean
export let componentInput: AppInput | undefined
export let customComponent: CustomComponentConfig
interface Props {
id: string
render: boolean
componentInput: AppInput | undefined
customComponent: CustomComponentConfig
}
let { id, render, componentInput, customComponent }: Props = $props()
let divId = `custom-component-${id}`
const { worldStore, workspace } = getContext<AppViewerContext>('AppViewerContext')
@@ -36,8 +40,8 @@
loading: false
})
let setInput
let setRender
let setInput = $state() as ((input: any) => void) | undefined
let setRender = $state() as ((render: boolean) => void) | undefined
let ccProps: CCProps<any> = {
render,
id: divId,
@@ -49,7 +53,7 @@
outputs.output.set(output)
}
}
let loaded = false
let loaded = $state(false)
let renderer: ((props: CCProps<number>) => void) | undefined = undefined
// $: renderer && divEl && renderer(ccProps)
onMount(async () => {
@@ -96,18 +100,22 @@
}
console.log('mounted', render, setRender)
})
let result
let result = $state()
$: render != undefined && setRender && handleRender()
function handleRender() {
setRender?.(render)
}
$: result != undefined && setInput && handleResult()
function handleResult() {
setInput?.(result)
}
// $: result && setInput && setInput(result)
$effect(() => {
render != undefined && setRender && untrack(() => handleRender())
})
$effect(() => {
result != undefined && setInput && untrack(() => handleResult())
})
</script>
<InitializeComponent {id} />
@@ -1,10 +1,10 @@
<script lang="ts" context="module">
<script lang="ts" module>
import { writable, type Writable } from 'svelte/store'
let selected: Writable<string | undefined> = writable(undefined)
</script>
<script lang="ts">
import { getContext, onMount } from 'svelte'
import { getContext, onMount, untrack } from 'svelte'
import type { AppViewerContext } from '../../types'
import { type NavbarItem } from '../../editor/component'
import Button from '$lib/components/common/button/Button.svelte'
@@ -14,20 +14,29 @@
import type { Output } from '../../rx'
import ResolveNavbarItemPath from './ResolveNavbarItemPath.svelte'
export let navbarItem: NavbarItem
export let id: string
export let borderColor: string | undefined = undefined
export let index: number
export let output: {
result: Output<{
currentPath: string
}>
interface Props {
navbarItem: NavbarItem
id: string
borderColor?: string | undefined
index: number
output: {
result: Output<{
currentPath: string
}>
}
orientation?: 'horizontal' | 'vertical' | undefined
}
export let orientation: 'horizontal' | 'vertical' | undefined = undefined
let icon: any
let {
navbarItem,
id,
borderColor = undefined,
index,
output = $bindable(),
orientation = undefined
}: Props = $props()
$: navbarItem.icon && icon && handleIcon()
let icon: any = $state()
async function handleIcon() {
if (navbarItem.icon) {
@@ -38,10 +47,10 @@
const { appPath, replaceStateFn, gotoFn, isEditor, worldStore } =
getContext<AppViewerContext>('AppViewerContext')
let resolvedPath: string | undefined = undefined
let resolvedLabel: string | undefined = undefined
let resolvedDisabled: boolean | undefined = undefined
let resolvedHidden: boolean | undefined = undefined
let resolvedPath: string | undefined = $state(undefined)
let resolvedLabel: string | undefined = $state(undefined)
let resolvedDisabled: boolean | undefined = $state(undefined)
let resolvedHidden: boolean | undefined = $state(undefined)
function extractPathDetails() {
const url = window.location.pathname + window.location.search + window.location.hash
@@ -53,7 +62,7 @@
$selected = resolvedPath === extractPathDetails() ? resolvedPath : undefined
})
let initialized: boolean = false
let initialized: boolean = $state(false)
function initSelection() {
initialized = true
@@ -63,8 +72,6 @@
$selected = resolvedPath === extractPathDetails() ? resolvedPath : undefined
}
$: !initialized && resolvedPath && initSelection()
function getButtonProps(resolvedPath: string | undefined) {
if ($appPath && resolvedPath?.includes($appPath)) {
return {
@@ -112,7 +119,13 @@
}
}
$: buttonProps = getButtonProps(resolvedPath)
$effect(() => {
navbarItem.icon && icon && untrack(() => handleIcon())
})
$effect(() => {
!initialized && resolvedPath && untrack(() => initSelection())
})
let buttonProps = $derived(getButtonProps(resolvedPath))
</script>
<ResolveNavbarItemPath {navbarItem} {id} {index} bind:resolvedPath />
@@ -21,12 +21,23 @@
import { twMerge } from 'tailwind-merge'
import ResolveStyle from '../helpers/ResolveStyle.svelte'
export let id: string
export let componentInput: AppInput | undefined
export let configuration: RichConfigurations
export let initializing: boolean | undefined = undefined
export let customCss: ComponentCustomCSS<'piechartcomponent'> | undefined = undefined
export let render: boolean
interface Props {
id: string
componentInput: AppInput | undefined
configuration: RichConfigurations
initializing?: boolean | undefined
customCss?: ComponentCustomCSS<'piechartcomponent'> | undefined
render: boolean
}
let {
id,
componentInput,
configuration,
initializing = $bindable(undefined),
customCss = undefined,
render
}: Props = $props()
const { app, worldStore } = getContext<AppViewerContext>('AppViewerContext')
@@ -46,17 +57,19 @@
ArcElement
)
let result: { data: number[]; labels?: string[] } | undefined = undefined
let theme: string = 'theme1'
let doughnut = false
let result = $state(undefined) as { data: number[]; labels?: string[] } | undefined
let theme: string = $state('theme1')
let doughnut = $state(false)
$: backgroundColor = {
theme1: ['#FF6384', '#4BC0C0', '#FFCE56', '#E7E9ED', '#36A2EB'],
// blue theme
theme2: ['#4e73df', '#1cc88a', '#36b9cc', '#f6c23e', '#e74a3b'],
// red theme
theme3: ['#e74a3b', '#4e73df', '#1cc88a', '#36b9cc', '#f6c23e']
}[theme]
let backgroundColor = $derived(
{
theme1: ['#FF6384', '#4BC0C0', '#FFCE56', '#E7E9ED', '#36A2EB'],
// blue theme
theme2: ['#4e73df', '#1cc88a', '#36b9cc', '#f6c23e', '#e74a3b'],
// red theme
theme3: ['#e74a3b', '#4e73df', '#1cc88a', '#36b9cc', '#f6c23e']
}[theme]
)
const options = {
responsive: true,
@@ -64,7 +77,7 @@
maintainAspectRatio: false
}
$: data = {
let data = $derived({
labels: result?.labels ?? [],
datasets: [
{
@@ -72,9 +85,9 @@
backgroundColor: backgroundColor
}
]
}
})
let css = initCss($app.css?.piechartcomponent, customCss)
let css = $state(initCss($app.css?.piechartcomponent, customCss))
</script>
{#each Object.keys(css ?? {}) as key (key)}
@@ -24,15 +24,26 @@
import ResolveStyle from '../helpers/ResolveStyle.svelte'
import { Scatter } from '$lib/components/chartjs-wrappers/chartJs'
export let id: string
export let componentInput: AppInput | undefined
export let configuration: RichConfigurations
export let initializing: boolean | undefined = undefined
export let customCss: ComponentCustomCSS<'scatterchartcomponent'> | undefined = undefined
export let render: boolean
interface Props {
id: string
componentInput: AppInput | undefined
configuration: RichConfigurations
initializing?: boolean | undefined
customCss?: ComponentCustomCSS<'scatterchartcomponent'> | undefined
render: boolean
}
let zoomable = false
let pannable = false
let {
id,
componentInput,
configuration,
initializing = $bindable(undefined),
customCss = undefined,
render
}: Props = $props()
let zoomable = $state(false)
let pannable = $state(false)
const { app, worldStore } = getContext<AppViewerContext>('AppViewerContext')
@@ -53,9 +64,9 @@
zoomPlugin
)
let result: { data: { x: any[]; y: string[] } } | undefined = undefined
let result: { data: { x: any[]; y: string[] } } | undefined = $state(undefined)
$: options = {
let options = $derived({
responsive: true,
animation: false,
maintainAspectRatio: false,
@@ -74,13 +85,13 @@
}
}
}
} as ChartOptions<'scatter'>
} as ChartOptions<'scatter'>)
$: data = {
let data = $derived({
datasets: result ?? []
} as ChartData<'scatter', (number | Point)[], unknown>
} as ChartData<'scatter', (number | Point)[], unknown>)
let css = initCss($app.css?.scatterchartcomponent, customCss)
let css = $state(initCss($app.css?.scatterchartcomponent, customCss))
</script>
{#each Object.keys(css ?? {}) as key (key)}
@@ -27,12 +27,23 @@
import { twMerge } from 'tailwind-merge'
import ResolveStyle from '../helpers/ResolveStyle.svelte'
export let id: string
export let componentInput: AppInput | undefined
export let configuration: RichConfigurations
export let initializing: boolean | undefined = undefined
export let customCss: ComponentCustomCSS<'timeseriescomponent'> | undefined = undefined
export let render: boolean
interface Props {
id: string
componentInput: AppInput | undefined
configuration: RichConfigurations
initializing?: boolean | undefined
customCss?: ComponentCustomCSS<'timeseriescomponent'> | undefined
render: boolean
}
let {
id,
componentInput,
configuration,
initializing = $bindable(undefined),
customCss = undefined,
render
}: Props = $props()
const { app, worldStore } = getContext<AppViewerContext>('AppViewerContext')
@@ -41,9 +52,9 @@
loading: false
})
let logarithmicScale = false
let zoomable = false
let pannable = false
let logarithmicScale = $state(false)
let zoomable = $state(false)
let pannable = $state(false)
ChartJS.register(
Title,
@@ -59,9 +70,9 @@
LogarithmicScale
)
let result: { data: { x: any[]; y: string[] } } | undefined = undefined
let result: { data: { x: any[]; y: string[] } } | undefined = $state(undefined)
$: options = {
let options = $derived({
responsive: true,
animation: false,
maintainAspectRatio: false,
@@ -88,13 +99,13 @@
type: logarithmicScale ? 'logarithmic' : 'linear'
}
}
} as ChartOptions<'scatter'>
} as ChartOptions<'scatter'>)
$: data = {
let data = $derived({
datasets: result ?? []
} as ChartData<'scatter', (number | Point)[], unknown>
} as ChartData<'scatter', (number | Point)[], unknown>)
let css = initCss($app.css?.timeseriescomponent, customCss)
let css = $state(initCss($app.css?.timeseriescomponent, customCss))
</script>
{#each Object.keys(css ?? {}) as key (key)}
@@ -1,6 +1,9 @@
<script lang="ts">
import { createBubbler } from 'svelte/legacy'
const bubble = createBubbler()
import { Alert } from '$lib/components/common'
import { getContext, onMount } from 'svelte'
import { getContext, onMount, untrack } from 'svelte'
import { initConfig, initOutput } from '../../editor/appUtils'
import { components } from '../../editor/component'
import type { AppInput } from '../../inputType'
@@ -8,18 +11,26 @@
import ResolveConfig from '../helpers/ResolveConfig.svelte'
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
export let id: string
export let componentInput: AppInput | undefined
export let configuration: RichConfigurations
interface Props {
id: string
componentInput: AppInput | undefined
configuration: RichConfigurations
initializing?: boolean | undefined
render: boolean
}
export let initializing: boolean | undefined = undefined
export let render: boolean
let {
id,
componentInput,
configuration,
initializing = $bindable(undefined),
render
}: Props = $props()
const { worldStore, darkMode } = getContext<AppViewerContext>('AppViewerContext')
let resolvedConfig = initConfig(
components['plotlycomponent'].initialData.configuration,
configuration
let resolvedConfig = $state(
initConfig(components['plotlycomponent'].initialData.configuration, configuration)
)
const outputs = initOutput($worldStore, id, {
@@ -27,10 +38,10 @@
loading: false
})
let result: object | undefined = undefined
let divEl: HTMLDivElement | null = null
let result: object | undefined = $state(undefined)
let divEl: HTMLDivElement | null = $state(null)
let Plotly
let Plotly = $state() as any
onMount(async () => {
//@ts-ignore
await import(
@@ -43,26 +54,16 @@
Plotly = window['Plotly']
})
let h: number | undefined = undefined
let w: number | undefined = undefined
let h: number | undefined = $state(undefined)
let w: number | undefined = $state(undefined)
let shouldeUpdate = 1
let shouldeUpdate = $state(1)
darkMode.subscribe(() => {
shouldeUpdate++
})
$: Plotly &&
render &&
result &&
resolvedConfig.layout &&
divEl &&
h &&
w &&
shouldeUpdate &&
plot()
let error = ''
let error = $state('')
function plot() {
try {
Plotly.newPlot(
@@ -92,6 +93,17 @@
console.error(e)
}
}
$effect.pre(() => {
Plotly &&
render &&
result &&
resolvedConfig.layout &&
divEl &&
h &&
w &&
shouldeUpdate &&
untrack(() => plot())
})
</script>
{#each Object.keys(components['plotlycomponent'].initialData.configuration) as key (key)}
@@ -113,7 +125,7 @@
</Alert>
</div>
{/if}
<div on:pointerdown bind:this={divEl}></div>
<div onpointerdown={bubble('pointerdown')} bind:this={divEl}></div>
</RunnableWrapper>
</div>
{:else}
@@ -1,6 +1,9 @@
<script lang="ts">
import { createBubbler } from 'svelte/legacy'
const bubble = createBubbler()
import { Alert } from '$lib/components/common'
import { getContext, onMount } from 'svelte'
import { getContext, onMount, untrack } from 'svelte'
import { initConfig, initOutput } from '../../editor/appUtils'
import { components } from '../../editor/component'
import type { AppInput } from '../../inputType'
@@ -8,20 +11,30 @@
import ResolveConfig from '../helpers/ResolveConfig.svelte'
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
export let id: string
export let componentInput: AppInput | undefined
export let configuration: RichConfigurations
export let datasets: RichConfiguration | undefined
export let xData: RichConfiguration | undefined
interface Props {
id: string
componentInput: AppInput | undefined
configuration: RichConfigurations
datasets: RichConfiguration | undefined
xData: RichConfiguration | undefined
initializing?: boolean | undefined
render: boolean
}
export let initializing: boolean | undefined = undefined
export let render: boolean
let {
id,
componentInput,
configuration,
datasets,
xData,
initializing = $bindable(undefined),
render
}: Props = $props()
const { worldStore, darkMode } = getContext<AppViewerContext>('AppViewerContext')
let resolvedConfig = initConfig(
components['plotlycomponentv2'].initialData.configuration,
configuration
let resolvedConfig = $state(
initConfig(components['plotlycomponentv2'].initialData.configuration, configuration)
)
const outputs = initOutput($worldStore, id, {
@@ -29,10 +42,10 @@
loading: false
})
let result: object | undefined = undefined
let divEl: HTMLDivElement | null = null
let result: object | undefined = $state(undefined)
let divEl: HTMLDivElement | null = $state(null)
let Plotly
let Plotly = $state() as any
onMount(async () => {
//@ts-ignore
await import(
@@ -44,53 +57,16 @@
Plotly = window['Plotly']
})
let h: number | undefined = undefined
let w: number | undefined = undefined
let h: number | undefined = $state(undefined)
let w: number | undefined = $state(undefined)
let shouldeUpdate = 1
let shouldeUpdate = $state(1)
darkMode.subscribe(() => {
shouldeUpdate++
})
$: Plotly && render && resolvedConfig.layout && divEl && h && w && shouldeUpdate && plot(data)
$: data =
datasets && xData && resolvedDatasets
? resolvedDatasets.map((d, index) => {
const fields =
d.type === 'pie'
? {
values: resolvedDatasetsValues[index]
}
: {
x: resolvedXData,
y: resolvedDatasetsValues[index]
}
return {
type: d.type,
color: d.color,
text: d.tooltip,
...fields,
marker: {
color: d.color
},
transforms: [
{
type: 'aggregate',
groups: resolvedXData,
aggregations: [{ target: 'y', func: d.aggregation_method, enabled: true }]
}
],
...(d?.extraOptions ?? {})
}
})
: Array.isArray(result)
? result
: [result]
let error = ''
let error = $state('')
function plot(data) {
try {
Plotly.newPlot(
@@ -110,6 +86,11 @@
yaxis: {
color: $darkMode ? '#f3f6f8' : '#000',
...(resolvedConfig?.layout?.['yaxis'] ?? {})
},
legend: {
font: {
color: $darkMode ? '#f3f6f8' : '#000'
}
}
},
{ responsive: true, displayModeBar: false }
@@ -131,9 +112,56 @@
extraOptions?: { mode: 'markers' | 'lines' | 'lines+markers' } | undefined
}
let resolvedDatasets: Dataset[]
let resolvedDatasetsValues: Array<number[]> = []
let resolvedXData: number[] = []
let resolvedDatasets: Dataset[] | undefined = $state()
let resolvedDatasetsValues: Array<number[]> = $state([])
let resolvedXData: number[] = $state([])
let data = $derived.by(() => {
return datasets && xData && resolvedDatasets
? resolvedDatasets.map((d, index) => {
const fields =
d.type === 'pie'
? {
values: resolvedDatasetsValues[index]
}
: {
x: resolvedXData,
y: resolvedDatasetsValues[index]
}
return {
type: d.type,
color: d.color,
text: d.tooltip,
name: d.name,
...fields,
marker: {
color: d.color
},
transforms: [
{
type: 'aggregate',
groups: resolvedXData,
aggregations: [{ target: 'y', func: d.aggregation_method, enabled: true }]
}
],
...(d?.extraOptions ?? {})
}
})
: Array.isArray(result)
? result
: [result]
})
$effect(() => {
Plotly &&
render &&
resolvedConfig.layout &&
divEl &&
h &&
w &&
shouldeUpdate &&
data &&
untrack(() => plot(data))
})
</script>
{#each Object.keys(components['plotlycomponentv2'].initialData.configuration) as key (key)}
@@ -179,7 +207,7 @@
</Alert>
</div>
{/if}
<div on:pointerdown bind:this={divEl}></div>
<div onpointerdown={bubble('pointerdown')} bind:this={divEl}></div>
</RunnableWrapper>
</div>
{:else}
@@ -3,20 +3,25 @@
import ResolveConfig from '../helpers/ResolveConfig.svelte'
import { initConfig } from '../../editor/appUtils'
export let navbarItem: NavbarItem
export let id: string
export let index: number
interface Props {
navbarItem: NavbarItem
id: string
index: number
resolvedPath?: string | undefined
}
export let resolvedPath: string | undefined = undefined
let { navbarItem, id, index, resolvedPath = $bindable(undefined) }: Props = $props()
let resolvedConfig = initConfig({ path: navbarItem.path }, { path: navbarItem.path })
let resolvedConfig = $state(initConfig({ path: navbarItem.path }, { path: navbarItem.path }))
$: resolvedPath = (
resolvedConfig?.path?.selected === 'href'
? resolvedConfig?.path?.configuration?.href?.href
: resolvedConfig?.path?.configuration?.app?.path +
(resolvedConfig?.path?.configuration?.app?.queryParamsOrHash ?? '')
) as string | undefined
$effect.pre(() => {
resolvedPath = (
resolvedConfig?.path?.selected === 'href'
? resolvedConfig?.path?.configuration?.href?.href
: resolvedConfig?.path?.configuration?.app?.path +
(resolvedConfig?.path?.configuration?.app?.queryParamsOrHash ?? '')
) as string | undefined
})
</script>
<ResolveConfig
@@ -1,4 +1,7 @@
<script lang="ts">
import { createBubbler } from 'svelte/legacy'
const bubble = createBubbler()
import { getContext, onMount } from 'svelte'
import { initOutput } from '../../editor/appUtils'
import type { AppInput } from '../../inputType'
@@ -6,14 +9,24 @@
import InputValue from '../helpers/InputValue.svelte'
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
export let id: string
export let componentInput: AppInput | undefined
export let configuration: RichConfigurations
export let initializing: boolean | undefined = undefined
export let render: boolean
interface Props {
id: string
componentInput: AppInput | undefined
configuration: RichConfigurations
initializing?: boolean | undefined
render: boolean
}
let result: object | undefined = undefined
let divEl: HTMLDivElement | null = null
let {
id,
componentInput,
configuration,
initializing = $bindable(undefined),
render
}: Props = $props()
let result: object | undefined = $state(undefined)
let divEl: HTMLDivElement | null = $state(null)
const { worldStore } = getContext<AppViewerContext>('AppViewerContext')
@@ -22,7 +35,7 @@
loading: false
})
let vegaEmbed
let vegaEmbed = $state() as any
onMount(async () => {
//@ts-ignore
await import(/* webpackIgnore: true */ 'https://cdn.jsdelivr.net/npm/vega@5.22.1')
@@ -34,35 +47,37 @@
vegaEmbed = window['vegaEmbed']
})
let h: number | undefined = undefined
let w: number | undefined = undefined
let canvas = false
let h: number | undefined = $state(undefined)
let w: number | undefined = $state(undefined)
let canvas = $state(false)
$: vegaEmbed &&
result &&
divEl &&
h &&
w &&
vegaEmbed(
divEl,
{
...result,
...{
width: w - 100,
config: {
legend: { orient: 'bottom', ...(result?.['config']?.['legend'] ?? {}) },
...(result?.['config'] ?? {})
$effect(() => {
vegaEmbed &&
result &&
divEl &&
h &&
w &&
vegaEmbed(
divEl,
{
...result,
...{
width: w - 100,
config: {
legend: { orient: 'bottom', ...(result?.['config']?.['legend'] ?? {}) },
...(result?.['config'] ?? {})
}
}
},
{
mode: 'vega-lite',
actions: false,
renderer: canvas ? 'canvas' : 'svg',
height: h - 75,
width: w - 100
}
},
{
mode: 'vega-lite',
actions: false,
renderer: canvas ? 'canvas' : 'svg',
height: h - 75,
width: w - 100
}
)
)
})
</script>
<InputValue key={'canvas'} {id} input={configuration.canvas} bind:value={canvas} />
@@ -70,7 +85,7 @@
{#if render}
<div class="w-full h-full" bind:clientHeight={h} bind:clientWidth={w}>
<RunnableWrapper {outputs} {render} {componentInput} {id} bind:initializing bind:result>
<div on:pointerdown bind:this={divEl}></div>
<div onpointerdown={bubble('pointerdown')} bind:this={divEl}></div>
</RunnableWrapper>
</div>
{:else}
@@ -1,12 +1,7 @@
<script lang="ts">
import RunnableWrapper from '../../helpers/RunnableWrapper.svelte'
import type { AppInput } from '../../../inputType'
import type {
AppViewerContext,
ComponentCustomCSS,
RichConfiguration,
RichConfigurations
} from '../../../types'
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../../types'
import { initCss } from '../../../utils'
import { getContext, tick, untrack } from 'svelte'
import { initConfig, initOutput } from '../../../editor/appUtils'
@@ -24,8 +19,7 @@
initializing?: boolean | undefined
customCss?: ComponentCustomCSS<'agchartscomponent'> | undefined
render: boolean
datasets: RichConfiguration | undefined
xData: RichConfiguration | undefined
license?: string | undefined
ee?: boolean
}
@@ -37,23 +31,13 @@
initializing = $bindable(undefined),
customCss = undefined,
render,
datasets,
xData,
license = undefined,
ee = false
}: Props = $props()
const { app, worldStore } = getContext<AppViewerContext>('AppViewerContext')
type Dataset = {
value: RichConfiguration
name: string
type: 'bar' | 'line' | 'scatter' | 'area' | 'range-bar' | 'range-area'
}
let resolvedDatasets = $state(undefined) as Dataset[] | undefined
let resolvedXData: number[] = $state([])
const outputs = initOutput($worldStore, id, {
result: undefined as
| {
@@ -115,80 +99,6 @@
}
}
function updateChart() {
if (!chartInstance) {
return
}
let data = [] as any[]
if (!resolvedDatasets) {
return
}
for (let i = 0; i < resolvedXData.length; i++) {
const o = {
x: resolvedXData[i]
}
if (!resolvedDatasetsValues) {
return
}
for (let j = 0; j < resolvedDatasets.length; j++) {
if (!resolvedDatasetsValues[j]) {
continue
}
if (
resolvedDatasetsValues[j].type === 'range-bar' ||
resolvedDatasetsValues[j].type === 'range-area'
) {
o[`y-${j}-low`] = resolvedDatasetsValues[j].value[i]?.[0]
o[`y-${j}-high`] = resolvedDatasetsValues[j].value[i]?.[1]
} else {
o[`y-${j}`] = resolvedDatasetsValues[j].value[i]
}
}
data.push(o)
}
const options = {
container: document.getElementById(`agchart-${id}`) as HTMLElement,
data: data,
series:
(resolvedDatasets?.map((d, index) => {
if (!resolvedDatasetsValues) {
return
}
const type = resolvedDatasetsValues?.[index]?.type
if (!type) {
return
}
if (type === 'range-bar' || type === 'range-area') {
return {
type: type,
xKey: 'x',
yLowKey: `y-${index}-low`,
yHighKey: `y-${index}-high`,
yName: d.name
}
} else {
return {
type: type,
xKey: 'x',
yKey: `y-${index}`,
yName: d.name
}
}
}) as any[]) ?? [],
...getChartStyleByTheme()
}
outputs.result.set({
data: options.data,
series: options.series
})
AgChartsInstance?.update(chartInstance, options)
}
function updateChartByResult() {
if (!result || !chartInstance) {
return
@@ -248,100 +158,7 @@
}
let darkMode = $state(false)
let resolvedDatasetsValues = $derived(
resolvedDatasets?.map((d) => {
const config = initConfig(
{
value: {
type: 'oneOf',
selected: 'bar',
labels: {
bar: 'Bar',
scatter: 'Scatter',
line: 'Line',
area: 'Area',
['range-bar']: 'Range Bar',
['range-area']: 'Range Area'
},
configuration: {
bar: {
value: {
type: 'static',
fieldType: 'array',
subFieldType: 'number',
value: [25, 25, 50]
}
},
scatter: {
value: {
type: 'static',
fieldType: 'array',
subFieldType: 'number',
value: [25, 25, 50]
}
},
line: {
value: {
type: 'static',
fieldType: 'array',
subFieldType: 'number',
value: [25, 25, 50]
}
},
area: {
value: {
type: 'static',
fieldType: 'array',
subFieldType: 'number',
value: [25, 25, 50]
}
},
['range-bar']: {
value: {
type: 'static',
fieldType: 'array',
subFieldType: 'number-tuple',
value: [
[10, 15],
[20, 25],
[18, 27]
]
}
},
['range-area']: {
value: {
type: 'static',
fieldType: 'array',
subFieldType: 'number-tuple',
value: [
[10, 15],
[20, 25],
[18, 27]
]
}
}
}
}
},
{
value: d.value
}
)
return {
type: d.value['selected'],
// @ts-ignore
value: config.value?.['configuration']?.[d.value['selected']].value
}
})
)
$effect(() => {
resolvedXData &&
resolvedDatasets &&
resolvedDatasetsValues &&
chartInstance &&
untrack(() => updateChart())
})
$effect(() => {
result && chartInstance && untrack(() => updateChartByResult())
})
@@ -364,38 +181,11 @@
bind:darkMode
on:change={(e) => {
tick().then(() => {
chartInstance && resolvedDatasetsValues && updateChart()
chartInstance && updateChartByResult()
})
}}
/>
{#if datasets}
<ResolveConfig
{id}
key={'datasets'}
bind:resolvedConfig={resolvedDatasets}
configuration={datasets}
/>
{/if}
{#if xData}
<ResolveConfig {id} key={'xData'} bind:resolvedConfig={resolvedXData} configuration={xData} />
{/if}
{#if resolvedDatasets}
{#each resolvedDatasets as resolvedDataset, index (resolvedDataset.name + index)}
{#if resolvedDatasetsValues}
<ResolveConfig
{id}
key={'datasets' + index}
extraKey={resolvedDataset.name}
bind:resolvedConfig={resolvedDatasetsValues[index]}
configuration={resolvedDataset.value}
/>
{/if}
{/each}
{/if}
{#each Object.keys(components['agchartscomponent'].initialData.configuration) as key (key)}
<ResolveConfig
{id}
@@ -1,5 +1,5 @@
<script lang="ts">
import { getContext, tick } from 'svelte'
import { getContext, tick, untrack } from 'svelte'
import type { AppInput } from '../../../inputType'
import type { AppViewerContext } from '../../../types'
import type RunnableComponent from '../../helpers/RunnableComponent.svelte'
@@ -8,14 +8,27 @@
import { type ColumnDef, type DbType } from './utils'
import { getCountInput } from './queries/count'
export let id: string
export let table: string | undefined
export let resource: string | undefined
export let renderCount: number
export let quicksearch: string
export let resourceType: string
export let columnDefs: ColumnDef[]
export let whereClause: string | undefined
interface Props {
id: string
table: string | undefined
resource: string | undefined
renderCount: number
quicksearch: string
resourceType: string
columnDefs: ColumnDef[]
whereClause: string | undefined
}
let {
id,
table,
resource,
renderCount,
quicksearch,
resourceType,
columnDefs,
whereClause
}: Props = $props()
const { worldStore } = getContext<AppViewerContext>('AppViewerContext')
@@ -25,17 +38,15 @@
jobId: undefined
})
let runnableComponent: RunnableComponent
let loading = false
let input: AppInput | undefined = undefined
let runnableComponent: RunnableComponent | undefined = $state()
let loading = $state(false)
let input: AppInput | undefined = $state(undefined)
let lastTableCount: string | undefined = undefined
let renderCountLast = -1
let quicksearchLast: string | undefined = undefined
let localColumnDefs = columnDefs
let lastTable = table
$: lastTable != undefined && table && onTableChange()
let lastTable = $state(table)
function onTableChange() {
if (table !== lastTable) {
@@ -44,8 +55,6 @@
}
}
$: table && renderCount != undefined && quicksearch != undefined && computeCount()
export async function computeCount(forceCompute?: boolean | undefined) {
if (!forceCompute) {
if (
@@ -76,6 +85,12 @@
})
}
}
$effect(() => {
lastTable != undefined && table && untrack(() => onTableChange())
})
$effect(() => {
table && renderCount != undefined && quicksearch != undefined && untrack(() => computeCount())
})
</script>
<RunnableWrapper
@@ -9,7 +9,11 @@
import { sendUserToast } from '$lib/toast'
import { getUpdateInput } from './queries/update'
export let id: string
interface Props {
id: string
}
let { id }: Props = $props()
const { worldStore } = getContext<AppViewerContext>('AppViewerContext')
@@ -19,9 +23,9 @@
jobId: undefined
})
let runnableComponent: RunnableComponent
let loading = false
let input: AppInput | undefined = undefined
let runnableComponent: RunnableComponent | undefined = $state(undefined)
let loading = $state(false)
let input: AppInput | undefined = $state(undefined)
export async function triggerUpdate(
resource: string,
@@ -1,4 +1,4 @@
<script context="module">
<script module>
</script>
<script lang="ts">
@@ -11,16 +11,29 @@
import type { TableAction } from '$lib/components/apps/editor/component'
import AppAggridInfiniteTable from './AppAggridInfiniteTable.svelte'
export let id: string
export let license: string
export let componentInput: AppInput | undefined
export let configuration: RichConfigurations
export let initializing: boolean | undefined = undefined
export let render: boolean
export let customCss: ComponentCustomCSS<'aggridinfinitecomponentee'> | undefined = undefined
export let actions: TableAction[] = []
interface Props {
id: string
license: string
componentInput: AppInput | undefined
configuration: RichConfigurations
initializing?: boolean | undefined
render: boolean
customCss?: ComponentCustomCSS<'aggridinfinitecomponentee'> | undefined
actions?: TableAction[]
}
let loaded = false
let {
id,
license,
componentInput,
configuration,
initializing = $bindable(undefined),
render,
customCss = undefined,
actions = []
}: Props = $props()
let loaded = $state(false)
async function load() {
await import('ag-grid-enterprise')
const { LicenseManager } = await import('ag-grid-enterprise')
@@ -1,4 +1,6 @@
<script lang="ts">
import { stopPropagation } from 'svelte/legacy'
import { createEventDispatcher, getContext } from 'svelte'
import type { AppViewerContext } from '../../../types'
import type { TableAction } from '$lib/components/apps/editor/component'
@@ -18,16 +20,31 @@
import type { ICellRendererParams } from 'ag-grid-community'
import Popover from '$lib/components/meltComponents/Popover.svelte'
export let p: ICellRendererParams<any>
export let id: string
export let render: boolean
export let actions: TableAction[] = []
export let rowIndex: number
export let row: { original: Record<string, any> }
export let onSet: (id: string, value: any, rowIndex: number) => void
export let onRemove: (id: string, rowIndex: number) => void
export let wrapActions: boolean | undefined = undefined
export let selectRow: (params: ICellRendererParams<any>) => void
interface Props {
p: ICellRendererParams<any>
id: string
render: boolean
actions?: TableAction[]
rowIndex: number
row: { original: Record<string, any> }
onSet: (id: string, value: any, rowIndex: number) => void
onRemove: (id: string, rowIndex: number) => void
wrapActions?: boolean | undefined
selectRow: (params: ICellRendererParams<any>) => void
}
let {
p,
id,
render,
actions = [],
rowIndex,
row,
onSet,
onRemove,
wrapActions = undefined,
selectRow
}: Props = $props()
const dispatch = createEventDispatcher()
const { selectedComponent, hoverStore, mode, connectingInput } =
@@ -47,27 +64,26 @@
)}
>
{#each actions as action, actionIndex}
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
<!-- svelte-ignore missing-declaration -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y_mouse_events_have_key_events -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
on:mouseover|stopPropagation={() => {
onmouseover={stopPropagation(() => {
if (action.id !== $hoverStore) {
$hoverStore = action.id
}
}}
on:mouseout|stopPropagation={() => {
})}
onmouseout={stopPropagation(() => {
if ($hoverStore !== undefined) {
$hoverStore = undefined
}
}}
on:pointerdown|stopPropagation={(e) => {
})}
onpointerdown={stopPropagation((e) => {
selectRow(p)
if (!$connectingInput.opened) {
$selectedComponent = [action.id]
}
}}
})}
class={twMerge(
($selectedComponent?.includes(action.id) || $hoverStore === action.id) &&
$mode !== 'preview'
@@ -80,8 +96,8 @@
)}
>
{#if $mode !== 'preview'}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y_click_events_have_key_events -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
title={`Id: ${action.id}`}
class={twMerge(
@@ -94,9 +110,9 @@
? 'opacity-100'
: 'opacity-0'
)}
on:click|stopPropagation={() => {
onclick={stopPropagation(() => {
$selectedComponent = [action.id]
}}
})}
>
{action.id}
</div>
@@ -111,14 +127,14 @@
closeOnOtherPopoverOpen
contentClasses="p-4"
>
<svelte:fragment slot="trigger">
{#snippet trigger()}
<button
class="bg-red-500/70 border border-red-600 px-1 py-0.5"
title="Outputs"
aria-label="Open output"><Plug2 size={12} /></button
>
</svelte:fragment>
<svelte:fragment slot="content">
{/snippet}
{#snippet content()}
<ComponentOutputViewer
suffix="table"
on:select={({ detail }) => {
@@ -133,7 +149,7 @@
}}
componentId={action.id}
/>
</svelte:fragment>
{/snippet}
</Popover>
</div>
{/if}
@@ -1,4 +1,4 @@
<script context="module">
<script module>
</script>
<script lang="ts">
@@ -15,17 +15,31 @@
import { Loader2 } from 'lucide-svelte'
import type { TableAction } from '$lib/components/apps/editor/component'
export let id: string
export let license: string
export let componentInput: AppInput | undefined
export let configuration: RichConfigurations
export let initializing: boolean | undefined = undefined
export let render: boolean
export let customCss: ComponentCustomCSS<'aggridcomponent'> | undefined = undefined
export let actions: TableAction[] = []
export let actionsOrder: RichConfiguration | undefined = undefined
interface Props {
id: string
license: string
componentInput: AppInput | undefined
configuration: RichConfigurations
initializing?: boolean | undefined
render: boolean
customCss?: ComponentCustomCSS<'aggridcomponent'> | undefined
actions?: TableAction[]
actionsOrder?: RichConfiguration | undefined
}
let loaded = false
let {
id,
license,
componentInput,
configuration,
initializing = $bindable(undefined),
render,
customCss = undefined,
actions = [],
actionsOrder = undefined
}: Props = $props()
let loaded = $state(false)
async function load() {
await import('ag-grid-enterprise')
const { LicenseManager } = await import('ag-grid-enterprise')
@@ -1,16 +1,23 @@
<script lang="ts">
import { createBubbler } from 'svelte/legacy'
const bubble = createBubbler()
import Badge from '$lib/components/common/badge/Badge.svelte'
import { createEventDispatcher, tick } from 'svelte'
import { writable } from 'svelte/store'
import { twMerge } from 'tailwind-merge'
import { isLinkObject } from './utils'
export let type: 'text' | 'badge' | 'link' = 'text'
export let value: any
export let width: number
interface Props {
type?: 'text' | 'badge' | 'link'
value: any
width: number
}
let { type = 'text', value = $bindable(), width }: Props = $props()
let isEditable = writable(false)
let tempValue = value
let tempValue = $state(value)
const dispatch = createEventDispatcher()
@@ -42,8 +49,8 @@
</script>
<td
on:keydown
on:click
onkeydown={bubble('keydown')}
onclick={bubble('click')}
class={twMerge(
'p-4 whitespace-pre-wrap truncate text-xs text-primary',
$isEditable && 'bg-gray-100'
@@ -66,20 +73,20 @@
<input
type="text"
value={tempValue}
on:input={handleInput}
on:blur={saveEdit}
oninput={handleInput}
onblur={saveEdit}
id="cell"
class="!appearance-none !bg-transparent !border-none !p-0 !m-0 leading-normal !text-xs"
style="outline: none; box-shadow: none; height: auto; resize: none;"
on:keypress={(e) => {
onkeypress={(e) => {
if (e.key === 'Enter') {
saveEdit()
}
}}
/>
{:else}
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div on:dblclick={toggleEdit}>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div ondblclick={toggleEdit}>
{#if typeof value == 'object'}
{JSON.stringify(value)}
{:else}
@@ -1,18 +1,31 @@
<script lang="ts">
import { createBubbler, stopPropagation } from 'svelte/legacy'
const bubble = createBubbler()
import { onMount } from 'svelte'
import { twMerge } from 'tailwind-merge'
export let placeholder: string = 'Search...'
// Using 'any' so 'type="number"' can be passed to the input
// which should return a number
export let value: any
export let debounceDelay: number = 100
let parentClass: string | undefined = undefined
export { parentClass as class }
interface Props {
placeholder?: string
// which should return a number
value: any
debounceDelay?: number
class?: string | undefined
[key: string]: any
}
let {
placeholder = 'Search...',
value = $bindable(),
debounceDelay = 100,
class: parentClass = undefined,
...rest
}: Props = $props()
let timer: NodeJS.Timeout
let inputElement: HTMLInputElement | null = null
let inputElement: HTMLInputElement | null = $state(null)
function debounce(event: KeyboardEvent): void {
clearTimeout(timer)
@@ -33,9 +46,9 @@
<input
bind:this={inputElement}
{placeholder}
on:pointerdown|stopPropagation
on:keyup={debounce}
on:keydown|stopPropagation
onpointerdown={stopPropagation(bubble('pointerdown'))}
onkeyup={debounce}
onkeydown={stopPropagation(bubble('keydown'))}
class={twMerge(parentClass, 'mb-1 h-8 !rounded-md !shadow-none')}
{...$$restProps}
{...rest}
/>
@@ -6,13 +6,18 @@
import RunnableComponent from './RunnableComponent.svelte'
import InitializeComponent from './InitializeComponent.svelte'
export let id: string
export let runnable: HiddenRunnable
interface Props {
id: string
runnable: HiddenRunnable
children?: import('svelte').Snippet
}
let { id, runnable, children }: Props = $props()
const { worldStore, staticExporter, noBackend, runnableComponents } =
getContext<AppViewerContext>('AppViewerContext')
let result: any = noBackend ? runnable.noBackendValue : undefined
let result: any = $state(noBackend ? runnable.noBackendValue : undefined)
export function onSuccess() {
if (runnable.recomputeIds) {
@@ -49,7 +54,7 @@
on:success={onSuccess}
{outputs}
>
<slot />
{@render children?.()}
</RunnableComponent>
{:else}
<InitializeComponent {id} />
@@ -2,7 +2,11 @@
import { getContext, onDestroy, onMount } from 'svelte'
import type { AppViewerContext } from '../../types'
export let id: string
interface Props {
id: string
}
let { id }: Props = $props()
const { initialized } = getContext<AppViewerContext>('AppViewerContext')
onMount(() => {
@@ -1,7 +1,13 @@
<script lang="ts">
import { untrack } from 'svelte'
type InputType = string | number | boolean
export let input: HTMLInputElement | undefined = undefined
export let defaultValue: InputType | undefined = undefined
interface Props {
input?: HTMLInputElement | undefined
defaultValue?: InputType | undefined
}
let { input = $bindable(undefined), defaultValue = undefined }: Props = $props()
function setInputValueToDefaultValue() {
if (defaultValue !== undefined && input) {
@@ -15,8 +21,12 @@
}
}
$: input && defaultValue && setInputValueToDefaultValue()
$: input &&
(defaultValue === '' || defaultValue === undefined || defaultValue === null) &&
clearInputValue()
$effect.pre(() => {
input && defaultValue && untrack(() => setInputValueToDefaultValue())
})
$effect.pre(() => {
input &&
(defaultValue === '' || defaultValue === undefined || defaultValue === null) &&
untrack(() => clearInputValue())
})
</script>
@@ -347,6 +347,10 @@
) &&
untrack(() => debounceTemplate())
})
// $effect(() => {
// console.log('handleConnection4', input)
// })
$effect(() => {
input?.type == 'static' && input.value
input && $worldStore && untrack(() => debounce(handleConnection))
@@ -1,11 +1,16 @@
<script lang="ts">
import { Loader2 } from 'lucide-svelte'
export let loading: boolean
interface Props {
loading: boolean
children?: import('svelte').Snippet
}
let { loading, children }: Props = $props()
</script>
{#if !loading}
<slot />
{@render children?.()}
{:else}
<div class="w-full h-full">
<div class="absolute inset-0 center-center flex-col bg-surface text-tertiary border">
@@ -1,7 +1,11 @@
<script lang="ts">
import type { AppInput } from '../../inputType'
export let input: AppInput
interface Props {
input: AppInput
}
let { input }: Props = $props()
</script>
{#if input.type === 'connected'}
@@ -10,6 +10,7 @@
resolvedConfig: any | { type: 'oneOf'; configuration: any; selected: string }
configuration: RichConfiguration
initialConfig?: RichConfiguration | undefined
debug?: boolean
}
let {
@@ -18,15 +19,17 @@
key,
resolvedConfig = $bindable(),
configuration,
initialConfig = undefined
initialConfig = undefined,
debug = false
}: Props = $props()
function handleSelected(selected: string) {
if (resolvedConfig?.selected != undefined && resolvedConfig?.selected != selected) {
resolvedConfig.selected = selected
}
// console.log('handleSelected', JSON.stringify({ resolvedConfig, configuration }))
}
$effect(() => {
$effect.pre(() => {
configuration?.type == 'oneOf' &&
configuration.selected &&
untrack(() => handleSelected(configuration.selected))
@@ -50,6 +53,13 @@
{/if}
{/each}
{:else}
{#if debug}
<pre class="text-2xs">
key: {key}
{JSON.stringify({ r: resolvedConfig })}
{JSON.stringify(configuration)}</pre
>
{/if}
<InputValue
field={key}
key={key + extraKey}
@@ -2,17 +2,28 @@
import { deepEqual } from 'fast-equals'
import type { ComponentCssProperty } from '../../types'
import InputValue from './InputValue.svelte'
import { untrack } from 'svelte'
export let css: ComponentCssProperty
export let id: string
export let key: string
export let extraKey: string = ''
interface Props {
css: ComponentCssProperty
id: string
key: string
extraKey?: string
customCss?: Record<string, ComponentCssProperty> | undefined
componentStyle?: Record<string, ComponentCssProperty> | undefined
}
export let customCss: Record<string, ComponentCssProperty> | undefined = undefined
export let componentStyle: Record<string, ComponentCssProperty> | undefined = undefined
let {
css = $bindable(),
id,
key,
extraKey = '',
customCss = undefined,
componentStyle = undefined
}: Props = $props()
let evalClassValue: string | undefined = undefined
let evalClassValueGlobal: string | undefined = undefined
let evalClassValue: string | undefined = $state(undefined)
let evalClassValueGlobal: string | undefined = $state(undefined)
function updateCss(
componentStyle: Record<string, ComponentCssProperty> | undefined,
@@ -36,17 +47,24 @@
}
// When any of the values change, update the css
$: updateCss(componentStyle, customCss, evalClassValue, evalClassValueGlobal)
$effect(() => {
;[componentStyle, customCss, evalClassValue, evalClassValueGlobal]
untrack(() => updateCss(componentStyle, customCss, evalClassValue, evalClassValueGlobal))
})
// We need to clear the evalClassValue if the user has disabled the evalClass
$: if (customCss?.[key]?.evalClass === undefined && evalClassValue !== undefined) {
evalClassValue = undefined
}
$effect(() => {
if (customCss?.[key]?.evalClass === undefined && evalClassValue !== undefined) {
evalClassValue = undefined
}
})
// We need to clear the evalClassValue if the user has disabled the evalClass
$: if (componentStyle?.[key]?.evalClass === undefined && evalClassValueGlobal !== undefined) {
evalClassValueGlobal = undefined
}
$effect(() => {
if (componentStyle?.[key]?.evalClass === undefined && evalClassValueGlobal !== undefined) {
evalClassValueGlobal = undefined
}
})
</script>
{#if customCss}
@@ -1,5 +1,8 @@
<script lang="ts">
import { getContext } from 'svelte'
import { createBubbler, stopPropagation } from 'svelte/legacy'
const bubble = createBubbler()
import { getContext, untrack } from 'svelte'
import { twMerge } from 'tailwind-merge'
import { initConfig, initOutput } from '../../editor/appUtils'
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types'
@@ -11,23 +14,34 @@
import ResolveConfig from '../helpers/ResolveConfig.svelte'
import ResolveStyle from '../helpers/ResolveStyle.svelte'
export let id: string
export let configuration: RichConfigurations
export let inputType: 'date'
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
export let customCss: ComponentCustomCSS<'dateinputcomponent'> | undefined = undefined
export let render: boolean
export let onChange: string[] | undefined = undefined
interface Props {
id: string
configuration: RichConfigurations
inputType: 'date'
verticalAlignment?: 'top' | 'center' | 'bottom' | undefined
customCss?: ComponentCustomCSS<'dateinputcomponent'> | undefined
render: boolean
onChange?: string[] | undefined
}
let {
id,
configuration,
inputType,
verticalAlignment = undefined,
customCss = undefined,
render,
onChange = undefined
}: Props = $props()
const { app, worldStore, selectedComponent, componentControl, runnableComponents } =
getContext<AppViewerContext>('AppViewerContext')
let resolvedConfig = initConfig(
components['dateinputcomponent'].initialData.configuration,
configuration
let resolvedConfig = $state(
initConfig(components['dateinputcomponent'].initialData.configuration, configuration)
)
let value: string | undefined = undefined
let value: string | undefined = $state(undefined)
$componentControl[id] = {
setValue(nvalue: string) {
@@ -44,8 +58,6 @@
result: undefined as string | undefined
})
$: handleDefault(resolvedConfig.defaultValue)
function formatDate(dateString: string, formatString: string = 'dd.MM.yyyy') {
if (formatString === '') {
formatString = 'dd.MM.yyyy'
@@ -59,15 +71,6 @@
}
}
$: {
if (value) {
outputs?.result.set(formatDate(value, resolvedConfig.outputFormat))
} else {
outputs?.result.set(undefined)
}
fireOnChange()
}
function fireOnChange() {
if (onChange) {
onChange.forEach((id) => $runnableComponents?.[id]?.cb?.forEach((cb) => cb()))
@@ -77,7 +80,19 @@
function handleDefault(defaultValue: string | undefined) {
value = defaultValue
}
let css = initCss($app.css?.dateinputcomponent, customCss)
let css = $state(initCss($app.css?.dateinputcomponent, customCss))
$effect.pre(() => {
resolvedConfig.defaultValue
untrack(() => handleDefault(resolvedConfig.defaultValue))
})
$effect.pre(() => {
if (value) {
outputs?.result.set(formatDate(value, resolvedConfig.outputFormat))
} else {
outputs?.result.set(undefined)
}
untrack(() => fireOnChange())
})
</script>
{#each Object.keys(components['dateinputcomponent'].initialData.configuration) as key (key)}
@@ -104,11 +119,11 @@
<AlignWrapper {render} {verticalAlignment}>
{#if inputType === 'date'}
<input
on:keydown={(e) => {
onkeydown={(e) => {
e.stopPropagation()
}}
on:focus={() => ($selectedComponent = [id])}
on:pointerdown|stopPropagation
onfocus={() => ($selectedComponent = [id])}
onpointerdown={stopPropagation(bubble('pointerdown'))}
type="date"
bind:value
disabled={resolvedConfig.disabled}
@@ -1,5 +1,5 @@
<script lang="ts">
import { getContext } from 'svelte'
import { getContext, untrack } from 'svelte'
import { initConfig, initOutput } from '../../editor/appUtils'
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types'
import { initCss } from '../../utils'
@@ -14,20 +14,29 @@
import Select from '$lib/components/select/Select.svelte'
import { safeSelectItems } from '$lib/components/select/utils.svelte'
export let id: string
export let configuration: RichConfigurations
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
export let customCss: ComponentCustomCSS<'dateselectcomponent'> | undefined = undefined
export let render: boolean
interface Props {
id: string
configuration: RichConfigurations
verticalAlignment?: 'top' | 'center' | 'bottom' | undefined
customCss?: ComponentCustomCSS<'dateselectcomponent'> | undefined
render: boolean
}
let {
id,
configuration,
verticalAlignment = undefined,
customCss = undefined,
render
}: Props = $props()
const { app, worldStore, componentControl } = getContext<AppViewerContext>('AppViewerContext')
let resolvedConfig = initConfig(
components['dateselectcomponent'].initialData.configuration,
configuration
let resolvedConfig = $state(
initConfig(components['dateselectcomponent'].initialData.configuration, configuration)
)
let value: string | undefined = undefined
let value: string | undefined = $state(undefined)
$componentControl[id] = {
setValue(nvalue: string) {
@@ -41,8 +50,6 @@
year: undefined as number | undefined
})
$: !value && handleDefault(resolvedConfig.defaultValue)
function getLocale(locale: string = 'en-US') {
const localeMapping: { [key: string]: Locale } = {
'en-US': enUS,
@@ -83,13 +90,11 @@
}
}
let css = initCss($app.css?.dateinputcomponent, customCss)
let css = $state(initCss($app.css?.dateinputcomponent, customCss))
let selectedDay: string | undefined = undefined
let selectedMonth: string | undefined = undefined
let selectedYear: string | undefined = undefined
$: monthItems = computeMonthItems(resolvedConfig?.locale)
let selectedDay: string | undefined = $state(undefined)
let selectedMonth: string | undefined = $state(undefined)
let selectedYear: string | undefined = $state(undefined)
function updateOutputs(enableDay?: boolean, enableMonth?: boolean, enableYear?: boolean) {
if (enableDay) {
@@ -113,8 +118,6 @@
}
}
$: updateOutputs(resolvedConfig.enableDay, resolvedConfig.enableMonth, resolvedConfig.enableYear)
function computeMonthItems(locale: string = 'en-US') {
return [
{
@@ -193,6 +196,17 @@
return daysInMonth
}
$effect.pre(() => {
resolvedConfig.defaultValue
untrack(() => !value && handleDefault(resolvedConfig.defaultValue))
})
let monthItems = $derived(computeMonthItems(resolvedConfig?.locale))
$effect.pre(() => {
;[resolvedConfig.enableDay, resolvedConfig.enableMonth, resolvedConfig.enableYear]
untrack(() =>
updateOutputs(resolvedConfig.enableDay, resolvedConfig.enableMonth, resolvedConfig.enableYear)
)
})
</script>
{#each Object.keys(components['dateselectcomponent'].initialData.configuration) as key (key)}
@@ -1,6 +1,8 @@
<script lang="ts">
import { stopPropagation } from 'svelte/legacy'
import RangeSlider from 'svelte-range-slider-pips'
import { getContext, onDestroy } from 'svelte'
import { getContext, onDestroy, untrack } from 'svelte'
import type {
ListInputs,
AppViewerContext,
@@ -18,11 +20,21 @@
import ResolveStyle from '../helpers/ResolveStyle.svelte'
import { parseISO, format as formatDateFns } from 'date-fns'
export let id: string
export let configuration: RichConfigurations
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
export let customCss: ComponentCustomCSS<'dateslidercomponent'> | undefined = undefined
export let render: boolean
interface Props {
id: string
configuration: RichConfigurations
verticalAlignment?: 'top' | 'center' | 'bottom' | undefined
customCss?: ComponentCustomCSS<'dateslidercomponent'> | undefined
render: boolean
}
let {
id,
configuration,
verticalAlignment = undefined,
customCss = undefined,
render
}: Props = $props()
const iterContext = getContext<ListContext>('ListWrapperContext')
const listInputs: ListInputs | undefined = getContext<ListInputs>('ListInputs')
@@ -30,22 +42,19 @@
const { app, worldStore, selectedComponent, componentControl } =
getContext<AppViewerContext>('AppViewerContext')
let resolvedConfig = initConfig(
components['dateslidercomponent'].initialData.configuration,
configuration
let resolvedConfig = $state(
initConfig(components['dateslidercomponent'].initialData.configuration, configuration)
)
onDestroy(() => {
listInputs?.remove(id)
})
let values: [number] = [
let values: [number] = $state([
resolvedConfig.defaultValue
? new Date(resolvedConfig.defaultValue).getTime()
: new Date().getTime()
]
$: resolvedConfig.defaultValue != undefined && handleDefault()
])
function handleDefault() {
values = [
@@ -55,7 +64,7 @@
]
}
let slider: HTMLElement
let slider: HTMLElement | undefined = $state()
const outputs = initOutput($worldStore, id, {
result: null as string | null
@@ -67,8 +76,6 @@
}
}
$: values && handleValues()
function formatDate(dateString: string, formatString: string = 'dd.MM.yyyy') {
if (formatString === '') {
formatString = 'dd.MM.yyyy'
@@ -92,18 +99,11 @@
}
}
let css = initCss($app.css?.dateslidercomponent, customCss)
let css = $state(initCss($app.css?.dateslidercomponent, customCss))
let lastStyle: string | undefined = undefined
$: if (css && slider && lastStyle !== css?.handle?.style) {
const handle = slider.querySelector<HTMLSpanElement>('.rangeNub')
if (handle) {
lastStyle = css?.handle?.style
handle.style.cssText = css?.handle?.style ?? ''
}
}
let lastStyle: string | undefined = $state(undefined)
let width = 0
let width = $state(0)
const spanClass =
'text-center text-sm font-medium bg-blue-100 text-blue-800 rounded px-2.5 py-0.5'
@@ -123,9 +123,26 @@
}
}
$: if (resolvedConfig.max != undefined && resolvedConfig.step && render) {
computeWidth()
}
$effect.pre(() => {
resolvedConfig.defaultValue != undefined && untrack(() => handleDefault())
})
$effect.pre(() => {
values?.[0] && untrack(() => handleValues())
})
$effect.pre(() => {
if (css && slider && lastStyle !== css?.handle?.style) {
const handle = slider.querySelector<HTMLSpanElement>('.rangeNub')
if (handle) {
lastStyle = css?.handle?.style
handle.style.cssText = css?.handle?.style ?? ''
}
}
})
$effect.pre(() => {
if (resolvedConfig.max != undefined && resolvedConfig.step && render) {
untrack(() => computeWidth())
}
})
</script>
{#each Object.keys(components['dateslidercomponent'].initialData.configuration) as key (key)}
@@ -169,7 +186,7 @@
resolvedConfig?.vertical ? 'h-full' : 'w-full'
)}
style={css?.bar?.style}
on:pointerdown|stopPropagation={() => ($selectedComponent = [id])}
onpointerdown={stopPropagation(() => ($selectedComponent = [id]))}
>
<RangeSlider
springValues={{ stiffness: 1, damping: 1 }}
@@ -1,5 +1,5 @@
<script lang="ts">
import { getContext, onDestroy } from 'svelte'
import { getContext, onDestroy, untrack } from 'svelte'
import { initConfig, initOutput } from '../../editor/appUtils'
import type {
AppViewerContext,
@@ -19,22 +19,33 @@
import { twMerge } from 'tailwind-merge'
import { parseISO, format as formatDateFns } from 'date-fns'
export let id: string
export let configuration: RichConfigurations
export let inputType: 'date'
export let verticalAlignment: VerticalAlignment | undefined = undefined
export let customCss: ComponentCustomCSS<'datetimeinputcomponent'> | undefined = undefined
export let render: boolean
export let onChange: string[] | undefined = undefined
interface Props {
id: string
configuration: RichConfigurations
inputType: 'date'
verticalAlignment?: VerticalAlignment | undefined
customCss?: ComponentCustomCSS<'datetimeinputcomponent'> | undefined
render: boolean
onChange?: string[] | undefined
}
let {
id,
configuration,
inputType,
verticalAlignment = undefined,
customCss = undefined,
render,
onChange = undefined
}: Props = $props()
const { app, worldStore, componentControl, selectedComponent, runnableComponents } =
getContext<AppViewerContext>('AppViewerContext')
const iterContext = getContext<ListContext>('ListWrapperContext')
const listInputs: ListInputs | undefined = getContext<ListInputs>('ListInputs')
let resolvedConfig = initConfig(
components['datetimeinputcomponent'].initialData.configuration,
configuration
let resolvedConfig = $state(
initConfig(components['datetimeinputcomponent'].initialData.configuration, configuration)
)
let outputs = initOutput($worldStore, id, {
@@ -43,8 +54,9 @@
})
let initValue = outputs?.result.peak()
let value: string | undefined =
let value: string | undefined = $state(
!iterContext && initValue != undefined ? initValue : resolvedConfig.defaultValue
)
$componentControl[id] = {
setValue(nvalue: string) {
@@ -59,8 +71,6 @@
let initialHandleDefault = true
$: handleDefault(resolvedConfig.defaultValue)
function formatDate(dateString: string, formatString: string = 'dd.MM.yyyy HH:mm') {
if (formatString === '') {
formatString = 'dd.MM.yyyy HH:mm'
@@ -74,7 +84,28 @@
}
}
$: {
function fireOnChange() {
if (onChange) {
onChange.forEach((id) => $runnableComponents?.[id]?.cb?.forEach((cb) => cb()))
}
}
function handleDefault(defaultValue: string | undefined) {
if (initialHandleDefault) {
initialHandleDefault = false
if (value != undefined) {
return
}
}
value = defaultValue
}
let css = $state(initCss($app.css?.datetimeinputcomponent, customCss))
$effect.pre(() => {
;[resolvedConfig.defaultValue]
untrack(() => handleDefault(resolvedConfig.defaultValue))
})
$effect.pre(() => {
if (value) {
const r = formatDate(value, resolvedConfig.outputFormat)
outputs?.result.set(r)
@@ -120,25 +151,7 @@
}
}
fireOnChange()
}
function fireOnChange() {
if (onChange) {
onChange.forEach((id) => $runnableComponents?.[id]?.cb?.forEach((cb) => cb()))
}
}
function handleDefault(defaultValue: string | undefined) {
if (initialHandleDefault) {
initialHandleDefault = false
if (value != undefined) {
return
}
}
value = defaultValue
}
let css = initCss($app.css?.datetimeinputcomponent, customCss)
})
</script>
{#each Object.keys(components['datetimeinputcomponent'].initialData.configuration) as key (key)}
@@ -1,5 +1,5 @@
<script lang="ts">
import { getContext } from 'svelte'
import { getContext, untrack } from 'svelte'
import { twMerge } from 'tailwind-merge'
import { FileInput } from '../../../common'
import { initConfig, initOutput } from '../../editor/appUtils'
@@ -10,12 +10,23 @@
import { components } from '../../editor/component'
import ResolveConfig from '../helpers/ResolveConfig.svelte'
export let id: string
export let configuration: RichConfigurations
export let customCss: ComponentCustomCSS<'fileinputcomponent'> | undefined = undefined
export let render: boolean
export let onFileChange: string[] | undefined = undefined
export let extraKey: string | undefined = undefined
interface Props {
id: string
configuration: RichConfigurations
customCss?: ComponentCustomCSS<'fileinputcomponent'> | undefined
render: boolean
onFileChange?: string[] | undefined
extraKey?: string | undefined
}
let {
id,
configuration,
customCss = undefined,
render,
onFileChange = undefined,
extraKey = undefined
}: Props = $props()
const { app, worldStore, componentControl, mode, runnableComponents } =
getContext<AppViewerContext>('AppViewerContext')
@@ -24,9 +35,8 @@
result: [] as { name: string; data: string }[] | undefined
})
let resolvedConfig = initConfig(
components['fileinputcomponent'].initialData.configuration,
configuration
let resolvedConfig = $state(
initConfig(components['fileinputcomponent'].initialData.configuration, configuration)
)
// Receives Base64 encoded strings from the input component
@@ -41,9 +51,9 @@
onFileChange?.forEach((id) => $runnableComponents?.[id]?.cb?.forEach((cb) => cb?.()))
}
let css = initCss($app.css?.fileinputcomponent, customCss)
let css = $state(initCss($app.css?.fileinputcomponent, customCss))
let fileInput: FileInput | undefined = undefined
let fileInput: FileInput | undefined = $state(undefined)
$componentControl[id] = {
clearFiles: () => {
@@ -51,7 +61,7 @@
}
}
let files: File[] | undefined = undefined
let files: File[] | undefined = $state(undefined)
function preFillFiles() {
const data = outputs?.result?.peak()
@@ -61,7 +71,9 @@
}
}
$: outputs.result && files === undefined && $mode === 'dnd' && preFillFiles()
$effect.pre(() => {
outputs.result && files === undefined && $mode === 'dnd' && untrack(() => preFillFiles())
})
</script>
{#each Object.keys(css ?? {}) as key (key)}
@@ -2,9 +2,13 @@
import AlignWrapper from '../helpers/AlignWrapper.svelte'
import InitializeComponent from '../helpers/InitializeComponent.svelte'
export let id: string
export let render: boolean
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
interface Props {
id: string
render: boolean
verticalAlignment?: 'top' | 'center' | 'bottom' | undefined
}
let { id, render, verticalAlignment = undefined }: Props = $props()
</script>
<InitializeComponent {id} />
@@ -1,5 +1,7 @@
<script lang="ts">
import { getContext, onDestroy } from 'svelte'
import { stopPropagation } from 'svelte/legacy'
import { getContext, onDestroy, untrack } from 'svelte'
import { twMerge } from 'tailwind-merge'
import { initConfig, initOutput } from '../../editor/appUtils'
import type {
@@ -16,20 +18,30 @@
import { components } from '../../editor/component'
import ResolveStyle from '../helpers/ResolveStyle.svelte'
export let id: string
export let configuration: RichConfigurations
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
export let customCss: ComponentCustomCSS<'numberinputcomponent'> | undefined = undefined
export let render: boolean
export let onChange: string[] | undefined = undefined
interface Props {
id: string
configuration: RichConfigurations
verticalAlignment?: 'top' | 'center' | 'bottom' | undefined
customCss?: ComponentCustomCSS<'numberinputcomponent'> | undefined
render: boolean
onChange?: string[] | undefined
}
let {
id,
configuration,
verticalAlignment = undefined,
customCss = undefined,
render,
onChange = undefined
}: Props = $props()
const { app, worldStore, selectedComponent, componentControl, runnableComponents } =
getContext<AppViewerContext>('AppViewerContext')
const iterContext = getContext<ListContext>('ListWrapperContext')
const listInputs: ListInputs | undefined = getContext<ListInputs>('ListInputs')
let resolvedConfig = initConfig(
components['numberinputcomponent'].initialData.configuration,
configuration
let resolvedConfig = $state(
initConfig(components['numberinputcomponent'].initialData.configuration, configuration)
)
onDestroy(() => {
@@ -41,8 +53,9 @@
})
let initValue = outputs?.result.peak()
let value: number | undefined =
let value: number | undefined = $state(
!iterContext && initValue != undefined ? initValue : resolvedConfig.defaultValue
)
$componentControl[id] = {
setValue(nvalue: number | undefined) {
@@ -52,10 +65,6 @@
}
let initialHandleDefault = true
$: handleDefault(resolvedConfig.defaultValue)
$: value, onChangeValue()
function onChangeValue() {
outputs?.result.set(value ?? undefined)
if (iterContext && listInputs) {
@@ -80,7 +89,15 @@
value = defaultValue
}
let css = initCss($app.css?.numberinputcomponent, customCss)
let css = $state(initCss($app.css?.numberinputcomponent, customCss))
$effect.pre(() => {
resolvedConfig.defaultValue
untrack(() => handleDefault(resolvedConfig.defaultValue))
})
$effect.pre(() => {
value
untrack(() => onChangeValue())
})
</script>
{#each Object.keys(components['numberinputcomponent'].initialData.configuration) as key (key)}
@@ -106,8 +123,8 @@
<AlignWrapper {render} {verticalAlignment}>
<input
on:pointerdown|stopPropagation={() => ($selectedComponent = [id])}
on:focus={() => ($selectedComponent = [id])}
onpointerdown={stopPropagation(() => ($selectedComponent = [id]))}
onfocus={() => ($selectedComponent = [id])}
class={twMerge(
'windmillapp w-full py-1.5 px-2 text-sm app-editor-input',
css?.input?.class ?? '',
@@ -1,43 +1,45 @@
<script lang="ts">
import { getContext } from 'svelte'
import { stopPropagation } from 'svelte/legacy'
import { getContext, untrack } from 'svelte'
import { initOutput } from '../../editor/appUtils'
import type { AppViewerContext, RichConfigurations } from '../../types'
import 'quill/dist/quill.snow.css'
import InputValue from '../helpers/InputValue.svelte'
import InitializeComponent from '../helpers/InitializeComponent.svelte'
export let id: string
export let configuration: RichConfigurations
export let render: boolean
let editor = $state()
let quill: any = $state()
interface Props {
id: string
configuration: RichConfigurations
render: boolean
toolbarOptions?: any
}
let editor
let quill: any
export let toolbarOptions = [
[{ header: 1 }, { header: 2 }, 'blockquote', 'link', 'image', 'video'],
['bold', 'italic', 'underline', 'strike'],
[{ list: 'ordered' }, { list: 'ordered' }],
[{ align: [] }],
['clean']
]
let {
id,
configuration,
render,
toolbarOptions = [
[{ header: 1 }, { header: 2 }, 'blockquote', 'link', 'image', 'video'],
['bold', 'italic', 'underline', 'strike'],
[{ list: 'ordered' }, { list: 'ordered' }],
[{ align: [] }],
['clean']
]
}: Props = $props()
const { worldStore, componentControl, selectedComponent, connectingInput } =
getContext<AppViewerContext>('AppViewerContext')
let placeholder: string | undefined = undefined
let defaultValue: string | undefined = undefined
let placeholder: string | undefined = $state(undefined)
let defaultValue: string | undefined = $state(undefined)
let outputs = initOutput($worldStore, id, {
result: ''
})
$: if (!render) {
quill = undefined
}
$: if (!quill && render) {
loadQuill()
}
async function loadQuill() {
const { default: Quill } = await import('quill')
@@ -73,14 +75,26 @@
}
}
$: handleDefault(defaultValue)
function handleDefault(defaultValue: string | undefined) {
if (quill) {
quill.root.innerHTML = defaultValue
setOutput()
}
}
$effect.pre(() => {
if (!render) {
quill = undefined
}
})
$effect.pre(() => {
if (!quill && render) {
untrack(() => loadQuill())
}
})
$effect.pre(() => {
;[defaultValue]
untrack(() => handleDefault(defaultValue))
})
</script>
<InputValue key="placeholder" {id} input={configuration.placeholder} bind:value={placeholder} />
@@ -90,11 +104,11 @@
{#if render}
<div
class="editor-wrapper h-full flex-col flex max-h-full overflow-hidden"
on:pointerdown|stopPropagation={() => {
onpointerdown={stopPropagation(() => {
if (!$connectingInput.opened) {
$selectedComponent = [id]
}
}}
})}
>
<div bind:this={editor}></div>
</div>
@@ -1,5 +1,8 @@
<script lang="ts">
import { getContext, onDestroy } from 'svelte'
import { createBubbler, stopPropagation } from 'svelte/legacy'
const bubble = createBubbler()
import { getContext, onDestroy, untrack } from 'svelte'
import RangeSlider from 'svelte-range-slider-pips'
import { twMerge } from 'tailwind-merge'
import { initConfig, initOutput } from '../../editor/appUtils'
@@ -17,31 +20,37 @@
import { components } from '../../editor/component'
import ResolveStyle from '../helpers/ResolveStyle.svelte'
export let id: string
export let configuration: RichConfigurations
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
export let customCss: ComponentCustomCSS<'rangecomponent'> | undefined = undefined
export let render: boolean
interface Props {
id: string
configuration: RichConfigurations
verticalAlignment?: 'top' | 'center' | 'bottom' | undefined
customCss?: ComponentCustomCSS<'rangecomponent'> | undefined
render: boolean
}
let {
id,
configuration,
verticalAlignment = undefined,
customCss = undefined,
render
}: Props = $props()
const { app, worldStore, componentControl } = getContext<AppViewerContext>('AppViewerContext')
const iterContext = getContext<ListContext>('ListWrapperContext')
const listInputs: ListInputs | undefined = getContext<ListInputs>('ListInputs')
let resolvedConfig = initConfig(
components['rangecomponent'].initialData.configuration,
configuration
let resolvedConfig = $state(
initConfig(components['rangecomponent'].initialData.configuration, configuration)
)
let values: [number, number] = [0, 1]
$: (resolvedConfig.defaultLow != undefined || resolvedConfig.defaultHigh != undefined) &&
handleDefault()
let values: [number, number] = $state([0, 1])
function handleDefault() {
values = [resolvedConfig?.defaultLow ?? 0, resolvedConfig?.defaultHigh ?? 1]
}
let slider: HTMLElement
let slider: HTMLElement | undefined = $state()
let outputs = initOutput($worldStore, id, {
result: null as [number, number] | null
@@ -57,27 +66,33 @@
listInputs?.remove(id)
})
$: {
outputs?.result.set(values)
if (iterContext && listInputs) {
listInputs.set(id, values)
}
}
let css = $state(initCss($app.css?.rangecomponent, customCss))
let css = initCss($app.css?.rangecomponent, customCss)
let lastStyle: string | undefined = undefined
$: if (css && slider && lastStyle !== css?.handles?.style) {
const handles = slider.querySelectorAll<HTMLSpanElement>('.rangeNub')
if (handles) {
lastStyle = css?.handles?.style
handles.forEach((handle) => (handle.style.cssText = css?.handles?.style ?? ''))
}
}
let lastStyle: string | undefined = $state(undefined)
const format = (v, i, p) => {
return `${v}`
}
$effect.pre(() => {
resolvedConfig.defaultLow != undefined || resolvedConfig.defaultHigh != undefined
untrack(() => handleDefault())
})
$effect.pre(() => {
;[values[0], values[1]]
outputs?.result.set(values)
if (iterContext && listInputs) {
listInputs.set(id, values)
}
})
$effect.pre(() => {
if (css && slider && lastStyle !== css?.handles?.style) {
const handles = slider.querySelectorAll<HTMLSpanElement>('.rangeNub')
if (handles) {
lastStyle = css?.handles?.style
handles.forEach((handle) => (handle.style.cssText = css?.handles?.style ?? ''))
}
}
})
</script>
{#each Object.keys(components['rangecomponent'].initialData.configuration) as key (key)}
@@ -108,7 +123,7 @@
class={twMerge('grow', 'wm-slider-bar')}
style="--range-handle-focus: {'#7e9abd'}; --range-handle: {'#7e9abd'}; {css?.bar?.style ??
''}"
on:pointerdown|stopPropagation
onpointerdown={stopPropagation(bubble('pointerdown'))}
>
<RangeSlider
id="range-slider"
@@ -15,16 +15,26 @@
import { defaultIfEmptyString } from '$lib/utils'
import { userStore } from '$lib/stores'
export let id: string
export let configuration: RichConfigurations
export let customCss: ComponentCustomCSS<'s3fileinputcomponent'> | undefined = undefined
export let render: boolean
export let extraKey: string | undefined = undefined
export let onFileChange: string[] | undefined = undefined
interface Props {
id: string
configuration: RichConfigurations
customCss?: ComponentCustomCSS<'s3fileinputcomponent'> | undefined
render: boolean
extraKey?: string | undefined
onFileChange?: string[] | undefined
}
let resolvedConfig = initConfig(
components['s3fileinputcomponent'].initialData.configuration,
configuration
let {
id,
configuration,
customCss = undefined,
render,
extraKey = undefined,
onFileChange = undefined
}: Props = $props()
let resolvedConfig = $state(
initConfig(components['s3fileinputcomponent'].initialData.configuration, configuration)
)
let fileUploads: Writable<FileUploadData[]> = writable([])
@@ -38,16 +48,16 @@
}
}
let value: { path: string; filename: string }[] | undefined = undefined
let value: { path: string; filename: string }[] | undefined = $state(undefined)
const outputs = initOutput($worldStore, id, {
result: value ?? ([] as { path: string; filename: string }[] | undefined),
loading: false,
jobId: undefined
})
$: resolvedConfigS3 = resolvedConfig.type.configuration.s3
let resolvedConfigS3 = $derived(resolvedConfig.type.configuration.s3)
let css = initCss($app.css?.fileinputcomponent, customCss)
let css = $state(initCss($app.css?.fileinputcomponent, customCss))
/*
{#if resolvedConfig.displayDirectLink && fileUpload.progress === 100}
@@ -1,6 +1,8 @@
<script lang="ts">
import { stopPropagation } from 'svelte/legacy'
import RangeSlider from 'svelte-range-slider-pips'
import { getContext, onDestroy } from 'svelte'
import { getContext, onDestroy, untrack } from 'svelte'
import type {
ListInputs,
AppViewerContext,
@@ -17,11 +19,21 @@
import ResolveConfig from '../helpers/ResolveConfig.svelte'
import ResolveStyle from '../helpers/ResolveStyle.svelte'
export let id: string
export let configuration: RichConfigurations
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
export let customCss: ComponentCustomCSS<'slidercomponent'> | undefined = undefined
export let render: boolean
interface Props {
id: string
configuration: RichConfigurations
verticalAlignment?: 'top' | 'center' | 'bottom' | undefined
customCss?: ComponentCustomCSS<'slidercomponent'> | undefined
render: boolean
}
let {
id,
configuration,
verticalAlignment = undefined,
customCss = undefined,
render
}: Props = $props()
const iterContext = getContext<ListContext>('ListWrapperContext')
const listInputs: ListInputs | undefined = getContext<ListInputs>('ListInputs')
@@ -29,24 +41,21 @@
const { app, worldStore, selectedComponent, componentControl } =
getContext<AppViewerContext>('AppViewerContext')
let resolvedConfig = initConfig(
components['slidercomponent'].initialData.configuration,
configuration
let resolvedConfig = $state(
initConfig(components['slidercomponent'].initialData.configuration, configuration)
)
onDestroy(() => {
listInputs?.remove(id)
})
let values: [number] = [resolvedConfig.defaultValue ?? 0]
$: resolvedConfig.defaultValue != undefined && handleDefault()
let values: [number] = $state([resolvedConfig.defaultValue ?? 0])
function handleDefault() {
values = [resolvedConfig?.defaultValue ?? 0]
}
let slider: HTMLElement
let slider: HTMLElement | undefined = $state()
const outputs = initOutput($worldStore, id, {
result: (0 as number) || null
@@ -58,8 +67,6 @@
}
}
$: values && handleValues()
function handleValues() {
// Disallow 'e' character in numbers
// if(value && value.toString().includes('e')) {
@@ -73,16 +80,9 @@
}
}
let css = initCss($app.css?.slidercomponent, customCss)
let css = $state(initCss($app.css?.slidercomponent, customCss))
let lastStyle: string | undefined = undefined
$: if (css && slider && lastStyle !== css?.handle?.style) {
const handle = slider.querySelector<HTMLSpanElement>('.rangeNub')
if (handle) {
lastStyle = css?.handle?.style
handle.style.cssText = css?.handle?.style ?? ''
}
}
let lastStyle: string | undefined = $state(undefined)
const spanClass =
'text-center text-sm font-medium bg-blue-100 text-blue-800 rounded px-2.5 py-0.5'
@@ -101,9 +101,27 @@
}
}
$: if (resolvedConfig.max != undefined && resolvedConfig.step && render) {
computeWidth()
}
$effect.pre(() => {
resolvedConfig.defaultValue != undefined && untrack(() => handleDefault())
})
$effect.pre(() => {
values?.[0]
values && handleValues()
})
$effect.pre(() => {
if (css && slider && lastStyle !== css?.handle?.style) {
const handle = slider.querySelector<HTMLSpanElement>('.rangeNub')
if (handle) {
lastStyle = css?.handle?.style
handle.style.cssText = css?.handle?.style ?? ''
}
}
})
$effect.pre(() => {
if (resolvedConfig.max != undefined && resolvedConfig.step && render) {
untrack(() => computeWidth())
}
})
</script>
{#each Object.keys(components['slidercomponent'].initialData.configuration) as key (key)}
@@ -138,7 +156,7 @@
)}
style="--range-handle-focus: {'#7e9abd'}; --range-handle: {'#7e9abd'}; {css?.bar?.style ??
''}"
on:pointerdown|stopPropagation={() => ($selectedComponent = [id])}
onpointerdown={stopPropagation(() => ($selectedComponent = [id]))}
>
<RangeSlider
id="slider-input"
@@ -1,5 +1,8 @@
<script lang="ts">
import { getContext, onDestroy } from 'svelte'
import { stopPropagation, createBubbler } from 'svelte/legacy'
const bubble = createBubbler()
import { getContext, onDestroy, untrack } from 'svelte'
import { twMerge } from 'tailwind-merge'
import { initConfig, initOutput, selectId } from '../../editor/appUtils'
import type {
@@ -17,18 +20,31 @@
import ResolveStyle from '../helpers/ResolveStyle.svelte'
import { loadIcon } from '../icon'
export let id: string
export let configuration: RichConfigurations
export let inputType = 'text'
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
export let customCss: ComponentCustomCSS<'textinputcomponent'> | undefined = undefined
export let appCssKey:
| 'textinputcomponent'
| 'passwordinputcomponent'
| 'emailinputcomponent'
| 'textareainputcomponent' = 'textinputcomponent'
export let render: boolean
export let onChange: string[] | undefined = undefined
interface Props {
id: string
configuration: RichConfigurations
inputType?: string
verticalAlignment?: 'top' | 'center' | 'bottom' | undefined
customCss?: ComponentCustomCSS<'textinputcomponent'> | undefined
appCssKey?:
| 'textinputcomponent'
| 'passwordinputcomponent'
| 'emailinputcomponent'
| 'textareainputcomponent'
render: boolean
onChange?: string[] | undefined
}
let {
id,
configuration,
inputType = 'text',
verticalAlignment = undefined,
customCss = undefined,
appCssKey = 'textinputcomponent',
render,
onChange = undefined
}: Props = $props()
const {
app,
@@ -39,9 +55,8 @@
runnableComponents
} = getContext<AppViewerContext>('AppViewerContext')
let resolvedConfig = initConfig(
components['textinputcomponent'].initialData.configuration,
configuration
let resolvedConfig = $state(
initConfig(components['textinputcomponent'].initialData.configuration, configuration)
)
const iterContext = getContext<ListContext>('ListWrapperContext')
@@ -52,8 +67,9 @@
})
let initValue = outputs?.result.peak()
let value: string | undefined =
let value: string | undefined = $state(
!iterContext && initValue && initValue != '' ? initValue : resolvedConfig.defaultValue
)
onDestroy(() => {
listInputs?.remove(id)
@@ -67,9 +83,6 @@
}
let initialHandleDefault = true
$: handleDefault(resolvedConfig.defaultValue)
$: value, onValueChange()
function onValueChange() {
let val = value ?? ''
@@ -96,21 +109,10 @@
value = defaultValue
}
let css = initCss($app.css?.[appCssKey], customCss)
let css = $state(initCss($app.css?.[appCssKey], customCss))
$: classInput = twMerge(
'windmillapp w-full py-1.5 px-2 text-sm',
'app-editor-input',
css?.input?.class ?? '',
'wm-input',
'wm-text-input'
)
let beforeIconComponent: any
let afterIconComponent: any
$: resolvedConfig.beforeIcon && beforeIconComponent && handleBeforeIcon()
$: resolvedConfig.afterIcon && afterIconComponent && handleAfterIcon()
let beforeIconComponent: any = $state()
let afterIconComponent: any = $state()
async function handleBeforeIcon() {
if (resolvedConfig.beforeIcon) {
@@ -135,6 +137,29 @@
)
}
}
$effect.pre(() => {
resolvedConfig.defaultValue
untrack(() => handleDefault(resolvedConfig.defaultValue))
})
$effect.pre(() => {
value
untrack(() => onValueChange())
})
let classInput = $derived(
twMerge(
'windmillapp w-full py-1.5 px-2 text-sm',
'app-editor-input',
css?.input?.class ?? '',
'wm-input',
'wm-text-input'
)
)
$effect.pre(() => {
resolvedConfig.beforeIcon && beforeIconComponent && untrack(() => handleBeforeIcon())
})
$effect.pre(() => {
resolvedConfig.afterIcon && afterIconComponent && untrack(() => handleAfterIcon())
})
</script>
{#each Object.keys(components['textinputcomponent'].initialData.configuration) as key (key)}
@@ -169,9 +194,11 @@
'h-full'
)}
style="resize:none; {css?.input?.style ?? ''}"
on:pointerdown|stopPropagation={(e) =>
!$connectingInput.opened && selectId(e, id, selectedComponent, $app)}
on:keydown|stopPropagation
onpointerdown={(e) => {
e.stopPropagation()
!$connectingInput.opened && selectId(e, id, selectedComponent, $app)
}}
onkeydown={stopPropagation(bubble('keydown'))}
bind:value
placeholder={resolvedConfig.placeholder}
disabled={resolvedConfig.disabled}
@@ -195,9 +222,11 @@
resolvedConfig.afterIcon ? '!pr-8' : ''
)}
style={css?.input?.style ?? ''}
on:pointerdown|stopPropagation={(e) =>
!$connectingInput.opened && selectId(e, id, selectedComponent, $app)}
on:keydown|stopPropagation
onpointerdown={(e) => {
e.stopPropagation()
!$connectingInput.opened && selectId(e, id, selectedComponent, $app)
}}
onkeydown={stopPropagation(bubble('keydown'))}
type="password"
autocomplete="new-password"
bind:value
@@ -212,9 +241,11 @@
resolvedConfig.afterIcon ? '!pr-8' : ''
)}
style={css?.input?.style ?? ''}
on:pointerdown|stopPropagation={(e) =>
!$connectingInput.opened && selectId(e, id, selectedComponent, $app)}
on:keydown|stopPropagation
onpointerdown={(e) => {
e.stopPropagation()
!$connectingInput.opened && selectId(e, id, selectedComponent, $app)
}}
onkeydown={stopPropagation(bubble('keydown'))}
type="text"
bind:value
placeholder={resolvedConfig.placeholder}
@@ -228,9 +259,11 @@
resolvedConfig.afterIcon ? '!pr-8' : ''
)}
style={css?.input?.style ?? ''}
on:pointerdown|stopPropagation={(e) =>
!$connectingInput.opened && selectId(e, id, selectedComponent, $app)}
on:keydown|stopPropagation
onpointerdown={(e) => {
e.stopPropagation()
!$connectingInput.opened && selectId(e, id, selectedComponent, $app)
}}
onkeydown={stopPropagation(bubble('keydown'))}
type="email"
bind:value
placeholder={resolvedConfig.placeholder}
@@ -1,5 +1,7 @@
<script lang="ts">
import { getContext } from 'svelte'
import { stopPropagation } from 'svelte/legacy'
import { getContext, untrack } from 'svelte'
import { twMerge } from 'tailwind-merge'
import { initConfig, initOutput } from '../../editor/appUtils'
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types'
@@ -10,22 +12,32 @@
import ResolveConfig from '../helpers/ResolveConfig.svelte'
import ResolveStyle from '../helpers/ResolveStyle.svelte'
export let id: string
export let configuration: RichConfigurations
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
export let customCss: ComponentCustomCSS<'timeinputcomponent'> | undefined = undefined
export let render: boolean
export let onChange: string[] | undefined = undefined
interface Props {
id: string
configuration: RichConfigurations
verticalAlignment?: 'top' | 'center' | 'bottom' | undefined
customCss?: ComponentCustomCSS<'timeinputcomponent'> | undefined
render: boolean
onChange?: string[] | undefined
}
let {
id,
configuration,
verticalAlignment = undefined,
customCss = undefined,
render,
onChange = undefined
}: Props = $props()
const { app, worldStore, selectedComponent, componentControl, runnableComponents } =
getContext<AppViewerContext>('AppViewerContext')
let resolvedConfig = initConfig(
components['timeinputcomponent'].initialData.configuration,
configuration
let resolvedConfig = $state(
initConfig(components['timeinputcomponent'].initialData.configuration, configuration)
)
let value: string | undefined = undefined
let value: string | undefined = $state(undefined)
$componentControl[id] = {
setValue(nvalue: string) {
@@ -38,14 +50,26 @@
validity: true as boolean
})
$: !value && handleDefault(resolvedConfig.defaultValue)
function convertToMinutes(time: string) {
const [hours, minutes] = time.split(':').map(Number)
return hours * 60 + minutes
}
$: {
function fireOnChange() {
if (onChange) {
onChange.forEach((id) => $runnableComponents?.[id]?.cb?.forEach((cb) => cb()))
}
}
function handleDefault(defaultValue: string | undefined) {
value = defaultValue
}
let css = $state(initCss($app.css?.timeinputcomponent, customCss))
$effect.pre(() => {
resolvedConfig.defaultValue
!value && untrack(() => handleDefault(resolvedConfig.defaultValue))
})
$effect.pre(() => {
if (value) {
if (!resolvedConfig['24hFormat']) {
let time = value.split(':')
@@ -80,18 +104,7 @@
outputs?.validity.set(isValid)
}
fireOnChange()
}
function fireOnChange() {
if (onChange) {
onChange.forEach((id) => $runnableComponents?.[id]?.cb?.forEach((cb) => cb()))
}
}
function handleDefault(defaultValue: string | undefined) {
value = defaultValue
}
let css = initCss($app.css?.timeinputcomponent, customCss)
})
</script>
{#each Object.keys(components['timeinputcomponent'].initialData.configuration) as key (key)}
@@ -117,8 +130,8 @@
<AlignWrapper {render} {verticalAlignment}>
<input
on:focus={() => ($selectedComponent = [id])}
on:pointerdown|stopPropagation={() => ($selectedComponent = [id])}
onfocus={() => ($selectedComponent = [id])}
onpointerdown={stopPropagation(() => ($selectedComponent = [id]))}
type="time"
bind:value
min={resolvedConfig.minTime}
@@ -1,6 +1,8 @@
<script lang="ts">
import { stopPropagation } from 'svelte/legacy'
import { initConfig, initOutput } from '$lib/components/apps/editor/appUtils'
import { getContext, onDestroy } from 'svelte'
import { getContext, onDestroy, untrack } from 'svelte'
import { twMerge } from 'tailwind-merge'
import type {
AppViewerContext,
@@ -17,11 +19,21 @@
import { components } from '$lib/components/apps/editor/component'
import ResolveStyle from '../../helpers/ResolveStyle.svelte'
export let id: string
export let configuration: RichConfigurations
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
export let customCss: ComponentCustomCSS<'currencycomponent'> | undefined = undefined
export let render: boolean
interface Props {
id: string
configuration: RichConfigurations
verticalAlignment?: 'top' | 'center' | 'bottom' | undefined
customCss?: ComponentCustomCSS<'currencycomponent'> | undefined
render: boolean
}
let {
id,
configuration,
verticalAlignment = undefined,
customCss = undefined,
render
}: Props = $props()
const { app, worldStore, selectedComponent, componentControl } =
getContext<AppViewerContext>('AppViewerContext')
@@ -36,14 +48,14 @@
listInputs?.remove(id)
})
let resolvedConfig = initConfig(
components['currencycomponent'].initialData.configuration,
configuration
let resolvedConfig = $state(
initConfig(components['currencycomponent'].initialData.configuration, configuration)
)
let initValue = outputs?.result.peak()
let value: number | undefined =
let value: number | undefined = $state(
!iterContext && initValue != undefined ? initValue : resolvedConfig.defaultValue
)
$componentControl[id] = {
setValue(nvalue: number) {
@@ -61,8 +73,6 @@
let initialHandleDefault = true
$: handleDefault(resolvedConfig.defaultValue)
function handleDefault(dflt: number | undefined) {
if (initialHandleDefault) {
initialHandleDefault = false
@@ -74,9 +84,14 @@
handleInput()
}
$: value != undefined && handleInput()
let css = initCss($app.css?.currencycomponent, customCss)
let css = $state(initCss($app.css?.currencycomponent, customCss))
$effect(() => {
resolvedConfig.defaultValue
untrack(() => handleDefault(resolvedConfig.defaultValue))
})
$effect(() => {
value != undefined && untrack(() => handleInput())
})
</script>
{#each Object.keys(components['currencycomponent'].initialData.configuration) as key (key)}
@@ -104,7 +119,7 @@
{#key resolvedConfig.isNegativeAllowed}
{#key resolvedConfig.locale}
{#key resolvedConfig.currency}
<div class="w-full" on:pointerdown|stopPropagation={() => ($selectedComponent = [id])}>
<div class="w-full" onpointerdown={stopPropagation(() => ($selectedComponent = [id]))}>
<CurrencyInput
inputClasses={{
formatted: twMerge(
@@ -1,5 +1,6 @@
<script lang="ts">
import DarkModeObserver from '$lib/components/DarkModeObserver.svelte'
import { untrack } from 'svelte'
/* Forked from MIT LICENSE
https://raw.githubusercontent.com/Canutin/svelte-currency-input/main/src/lib/CurrencyInput.svelte
@@ -27,18 +28,35 @@
formattedZero?: string
}
export let value: number = DEFAULT_VALUE
export let locale: string = DEFAULT_LOCALE
export let currency: string = DEFAULT_CURRENCY
export let name: string = DEFAULT_NAME
export let required: boolean = false
export let disabled: boolean = false
export let placeholder: number | null = DEFAULT_VALUE
export let isNegativeAllowed: boolean = true
export let fractionDigits: number = DEFAULT_FRACTION_DIGITS
export let inputClasses: InputClasses | null = null
export let noColor: boolean = false
export let style: string = ''
interface Props {
value?: number
locale?: string
currency?: string
name?: string
required?: boolean
disabled?: boolean
placeholder?: number | null
isNegativeAllowed?: boolean
fractionDigits?: number
inputClasses?: InputClasses | null
noColor?: boolean
style?: string
}
let {
value = $bindable(DEFAULT_VALUE),
locale = DEFAULT_LOCALE,
currency = DEFAULT_CURRENCY,
name = DEFAULT_NAME,
required = false,
disabled = false,
placeholder = DEFAULT_VALUE,
isNegativeAllowed = true,
fractionDigits = DEFAULT_FRACTION_DIGITS,
inputClasses = null,
noColor = false,
style = ''
}: Props = $props()
// Formats value as: e.g. $1,523.00 | -$1,523.00
const formatCurrency = (
@@ -158,14 +176,19 @@
}, 0.1)
}
let formattedValue = ''
let formattedValue = $state('')
let formattedPlaceholder =
placeholder !== null ? formatCurrency(placeholder, fractionDigits, fractionDigits) : ''
$: isZero = value === 0
$: isNegative = value < 0
$: value, setFormattedValue()
let isZero = $derived(value === 0)
let isNegative = $derived(value < 0)
$effect(() => {
value
untrack(() => {
setFormattedValue()
})
})
let darkMode: boolean = false
let darkMode: boolean = $state(false)
</script>
<DarkModeObserver bind:darkMode />
@@ -182,11 +205,11 @@
class="
{inputClasses?.formatted ?? DEFAULT_CLASS_FORMATTED}
{isNegativeAllowed && !isZero && !isNegative
? inputClasses?.formattedPositive ?? DEFAULT_CLASS_FORMATTED_POSITIVE
? (inputClasses?.formattedPositive ?? DEFAULT_CLASS_FORMATTED_POSITIVE)
: ''}
{isZero ? inputClasses?.formattedZero ?? DEFAULT_CLASS_FORMATTED_ZERO : ''}
{isZero ? (inputClasses?.formattedZero ?? DEFAULT_CLASS_FORMATTED_ZERO) : ''}
{isNegativeAllowed && isNegative
? inputClasses?.formattedNegative ?? DEFAULT_CLASS_FORMATTED_NEGATIVE
? (inputClasses?.formattedNegative ?? DEFAULT_CLASS_FORMATTED_NEGATIVE)
: ''}
"
style={style ? style : noColor ? (darkMode ? 'color: white;' : 'color: black;') : ''}
@@ -197,8 +220,8 @@
placeholder={formattedPlaceholder}
{disabled}
bind:value={formattedValue}
on:keydown={handleKeyDown}
on:keyup={setUnformattedValue}
onkeydown={handleKeyDown}
onkeyup={setUnformattedValue}
/>
</div>
@@ -15,21 +15,33 @@
import InitializeComponent from '../helpers/InitializeComponent.svelte'
import ResolveStyle from '../helpers/ResolveStyle.svelte'
export let id: string
export let configuration: RichConfigurations
export let horizontalAlignment: HorizontalAlignment | undefined = undefined
export let verticalAlignment: VerticalAlignment | undefined = undefined
export let customCss:
| ComponentCustomCSS<'verticaldividercomponent' | 'horizontaldividercomponent'>
| undefined = undefined
export let position: 'horizontal' | 'vertical'
export let render: boolean
interface Props {
id: string
configuration: RichConfigurations
horizontalAlignment?: HorizontalAlignment | undefined
verticalAlignment?: VerticalAlignment | undefined
customCss?:
| ComponentCustomCSS<'verticaldividercomponent' | 'horizontaldividercomponent'>
| undefined
position: 'horizontal' | 'vertical'
render: boolean
}
let {
id,
configuration,
horizontalAlignment = undefined,
verticalAlignment = undefined,
customCss = undefined,
position,
render
}: Props = $props()
const { app, worldStore } = getContext<AppViewerContext>('AppViewerContext')
let size = 2
let color = '#00000060'
let size = $state(2)
let color = $state('#00000060')
let css = initCss($app.css?.[position + 'dividercomponent'], customCss)
let css = $state(initCss($app.css?.[position + 'dividercomponent'], customCss))
//used so that we can count number of outputs setup for first refresh
initOutput($worldStore, id, {})
@@ -3,14 +3,28 @@
import type { ListInputs, ListContext } from '../../types'
import { writable } from 'svelte/store'
export let index: number
export let value: any
export let disabled = false
export let onSet: ((id: string, value: any) => void) | undefined = undefined
export let onRemove: ((id: string) => void) | undefined = undefined
interface Props {
index: number
value: any
disabled?: boolean
onSet?: ((id: string, value: any) => void) | undefined
onRemove?: ((id: string) => void) | undefined
children?: import('svelte').Snippet
}
let {
index,
value,
disabled = false,
onSet = undefined,
onRemove = undefined,
children
}: Props = $props()
const ctx = writable({ index, value, disabled })
$: $ctx = { index, value, disabled }
$effect(() => {
$ctx = { index, value, disabled }
})
setContext<ListContext>('ListWrapperContext', ctx)
setContext<ListInputs>('ListInputs', {
set: (id: string, value: any) => {
@@ -22,4 +36,4 @@
})
</script>
<slot />
{@render children?.()}
@@ -3,15 +3,22 @@
import type { ListInputs, ListContext } from '../../types'
import { writable } from 'svelte/store'
export let index: number
export let value: any
export let disabled = false
export let onSet: (id: string, value: any) => void
export let onRemove: (id: string) => void
interface Props {
index: number
value: any
disabled?: boolean
onSet: (id: string, value: any) => void
onRemove: (id: string) => void
children?: import('svelte').Snippet
}
let { index, value, disabled = false, onSet, onRemove, children }: Props = $props()
const ctx = writable({ index, value, disabled })
$: $ctx = { index, value, disabled }
$effect(() => {
$ctx = { index, value, disabled }
})
setContext<ListContext>('RowWrapperContext', ctx)
setContext<ListInputs>('RowInputs', {
@@ -24,4 +31,4 @@
})
</script>
<slot />
{@render children?.()}
@@ -2,8 +2,12 @@
import { components, type AppComponent } from './component'
import InputsSpecsEditor from './settingsPanel/InputsSpecsEditor.svelte'
export let component: AppComponent
export let resourceOnly = false
interface Props {
component: AppComponent
resourceOnly?: boolean
}
let { component = $bindable(), resourceOnly = false }: Props = $props()
</script>
{#if component?.componentInput?.type === 'runnable' && Object.keys(component?.componentInput?.fields ?? {}).length > 0}
@@ -5,8 +5,12 @@
import { sendUserToast } from '$lib/toast'
import DeploymentHistory from './DeploymentHistory.svelte'
export let appPath: string | undefined = undefined
let historyBrowserDrawerOpen = false
interface Props {
appPath?: string | undefined
}
let { appPath = undefined }: Props = $props()
let historyBrowserDrawerOpen = $state(false)
export function open() {
historyBrowserDrawerOpen = true
@@ -1,15 +1,26 @@
<script lang="ts">
import { createBubbler } from 'svelte/legacy'
const bubble = createBubbler()
import InlineScriptsPanel from './inlineScriptsPanel/InlineScriptsPanel.svelte'
import RunnableJobPanel from './RunnableJobPanel.svelte'
export let rightPanelSize: number = 0
export let centerPanelWidth: number = 0
export let runnablePanelSize: number = 0
interface Props {
rightPanelSize?: number
centerPanelWidth?: number
runnablePanelSize?: number
}
let { rightPanelSize = 0, centerPanelWidth = 0, runnablePanelSize = 0 }: Props = $props()
</script>
{#if rightPanelSize !== 0}
<!-- svelte-ignore a11y-no-static-element-interactions -->
<div class="relative h-full w-full overflow-x-visible" on:mouseenter on:mouseleave>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
class="relative h-full w-full overflow-x-visible"
onmouseenter={bubble('mouseenter')}
onmouseleave={bubble('mouseleave')}
>
<InlineScriptsPanel on:hidePanel />
<RunnableJobPanel hidden={runnablePanelSize === 0} />
</div>
@@ -20,30 +20,47 @@
import TestJobLoader from '$lib/components/TestJobLoader.svelte'
import type { Job } from '$lib/gen'
import type { JobById } from '../types'
import { createEventDispatcher } from 'svelte'
import { createEventDispatcher, untrack } from 'svelte'
export let open = false
export let jobs: string[]
export let jobsById: Record<string, JobById>
interface Props {
open?: boolean
jobs: string[]
jobsById: Record<string, JobById>
hasErrors?: boolean
selectedJobId?: string | undefined
refreshComponents?: (() => void) | undefined
errorByComponent?: Record<string, { id?: string; error: string }>
}
export let hasErrors: boolean = false
export let selectedJobId: string | undefined = undefined
export let refreshComponents: (() => void) | undefined = undefined
export let errorByComponent: Record<string, { id?: string; error: string }> = {}
let {
open = $bindable(false),
jobs,
jobsById,
hasErrors = false,
selectedJobId = $bindable(undefined),
refreshComponents = undefined,
errorByComponent = {}
}: Props = $props()
const dispatch = createEventDispatcher()
let testJobLoader: TestJobLoader
let job: Job | undefined = undefined
let testIsLoading = false
let testJobLoader: TestJobLoader | undefined = $state()
let job: Job | undefined = $state(undefined)
let testIsLoading = $state(false)
let rightColumnSelect: 'timeline' | 'detail' = 'timeline'
let rightColumnSelect: 'timeline' | 'detail' = $state('timeline')
$: selectedJobId && !selectedJobId?.includes('Frontend') && testJobLoader?.watchJob(selectedJobId)
$effect(() => {
selectedJobId &&
!selectedJobId?.includes('Frontend') &&
untrack(() => selectedJobId && testJobLoader?.watchJob(selectedJobId))
})
$: if (selectedJobId?.includes('Frontend') && selectedJobId) {
job = undefined
}
$effect(() => {
if (selectedJobId?.includes('Frontend') && selectedJobId) {
job = undefined
}
})
</script>
<TestJobLoader bind:this={testJobLoader} bind:isLoading={testIsLoading} bind:job />
@@ -67,8 +84,8 @@
{#each jobs ?? [] as id}
{@const selectedJob = jobsById[id]}
{#if selectedJob}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y_click_events_have_key_events -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
class={classNames(
'border flex gap-1 truncate justify-between flex-row w-full items-center p-2 rounded-md cursor-pointer hover:bg-surface-secondary hover:text-blue-400',
@@ -81,7 +98,7 @@
? 'text-blue-600'
: ''
)}
on:click={() => {
onclick={() => {
selectedJobId = id
rightColumnSelect = 'detail'
}}
@@ -2,8 +2,12 @@
import { Drawer } from '$lib/components/common'
import AppReportsDrawerInner from './AppReportsDrawerInner.svelte'
export let appPath: string
export let open = false
interface Props {
appPath: string
open?: boolean
}
let { appPath, open = $bindable(false) }: Props = $props()
</script>
<Drawer bind:open size="800px">
@@ -1,24 +1,30 @@
<script lang="ts">
import { debounce } from '$lib/utils'
import { onDestroy } from 'svelte'
import { onDestroy, untrack } from 'svelte'
import TimelineBar from '$lib/components/TimelineBar.svelte'
import type { JobById } from '../types'
export let jobs: string[]
export let jobsById: Record<string, JobById>
interface Props {
jobs: string[]
jobsById: Record<string, JobById>
}
let min: undefined | number = undefined
let { jobs, jobsById }: Props = $props()
let min: undefined | number = $state(undefined)
let max: undefined | number = undefined
let total: number | undefined = undefined
let total: number | undefined = $state(undefined)
let { debounced, clearDebounce } = debounce(() => computeItems(jobs), 30)
$: jobs && jobsById && debounced()
$effect(() => {
jobs && jobsById && untrack(() => debounced())
})
let items: Record<
string,
{ created_at?: number; started_at?: number; duration_ms?: number; id: string }[]
> = {}
> = $state({})
export function reset() {
min = undefined
@@ -88,7 +94,7 @@
items = nitems
}
let now = Date.now()
let now = $state(Date.now())
let interval = setInterval((x) => {
if (!max) {
@@ -1,4 +1,7 @@
<script lang="ts">
import { stopPropagation, createBubbler } from 'svelte/legacy'
const bubble = createBubbler()
import { classNames } from '$lib/utils'
import type { AppViewerContext } from '../types'
import { Anchor, ArrowDownFromLine, Bug, Expand, Network, Pen, Plug } from 'lucide-svelte'
@@ -14,29 +17,45 @@
import DecisionTreeDebug from './DecisionTreeDebug.svelte'
import AnimatedButton from '$lib/components/common/button/AnimatedButton.svelte'
export let component: AppComponent
export let selected: boolean
export let locked: boolean = false
export let hover: boolean = false
export let connecting: boolean = false
export let hasInlineEditor: boolean = false
export let inlineEditorOpened: boolean = false
export let errorHandledByComponent: boolean = false
export let fullHeight: boolean = false
export let componentContainerWidth: number
//export let willNotDisplay: boolean = false
interface Props {
component: AppComponent
selected: boolean
locked?: boolean
hover?: boolean
connecting?: boolean
hasInlineEditor?: boolean
inlineEditorOpened?: boolean
errorHandledByComponent?: boolean
fullHeight?: boolean
componentContainerWidth: number
}
let {
component,
selected,
locked = false,
hover = false,
connecting = false,
hasInlineEditor = false,
inlineEditorOpened = false,
errorHandledByComponent = false,
fullHeight = false,
componentContainerWidth
}: Props = $props()
const DECISION_TREE_THRESHOLD = 300
const STEPPER_THRESHOLD = 180
const CONDITIONAL_WRAPPER_THRESHOLD = 200
const MINIMUM_WIDTH = 20
let maxWidth = 10
let isManuallySelected = false
let componentIsDebugging = false
let id_width = 0
let maxWidth = $state(10)
let isManuallySelected = $state(false)
let componentIsDebugging = $state(false)
let id_width = $state(0)
$: maxWidth = Math.max(Math.round(0.2 * componentContainerWidth), MINIMUM_WIDTH)
$effect(() => {
maxWidth = Math.max(Math.round(0.2 * componentContainerWidth), MINIMUM_WIDTH)
})
const dispatch = createEventDispatcher()
@@ -59,12 +78,12 @@
return Object.values(componentOptions).some((value) => value)
}
let hoverHeader = false
let hoverHeader = $state(false)
</script>
{#if connecting}
<!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<!-- svelte-ignore a11y_mouse_events_have_key_events -->
<div
class="absolute z-50 overflow-auto -top-[18px]"
style="left: {id_width}px;"
@@ -75,7 +94,7 @@
closeOnOtherPopoverOpen
contentClasses="p-4"
>
<svelte:fragment slot="trigger">
{#snippet trigger()}
<AnimatedButton
animate={true}
baseRadius="9999px"
@@ -90,32 +109,32 @@
aria-label="Open output"><Plug size={12} /></div
>
</AnimatedButton>
</svelte:fragment>
<svelte:fragment slot="content">
{/snippet}
{#snippet content()}
<ComponentOutputViewer
suffix="connect"
on:select={({ detail }) =>
connectOutput(connectingInput, component.type, component.id, detail)}
componentId={component.id}
/>
</svelte:fragment>
{/snippet}
</Popover>
</div>
{/if}
{#if selected || hover}
<!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<!-- svelte-ignore a11y_mouse_events_have_key_events -->
<div class="-top-[18px] -left-[8px] flex flex-row flex-nowrap w-fit h-fit absolute gap-0.5">
<div
on:mouseover|stopPropagation={() => {
onmouseover={stopPropagation(() => {
hoverHeader = true
dispatch('mouseover')
}}
on:mouseleave|stopPropagation={() => {
})}
onmouseleave={stopPropagation(() => {
hoverHeader = false
}}
on:mousedown|stopPropagation|capture
})}
onmousedowncapture={stopPropagation(bubble('mousedown'))}
draggable="false"
title={`Id: ${component.id}`}
class={twMerge(
@@ -144,8 +163,8 @@
? 'bg-blue-300 text-blue-800'
: 'text-white hover:bg-blue-400 hover:text-white'
)}
on:click={() => dispatch('fillHeight')}
on:pointerdown|stopPropagation
onclick={() => dispatch('fillHeight')}
onpointerdown={stopPropagation(bubble('pointerdown'))}
>
<ArrowDownFromLine aria-label="Full height" size={11} />
</button>
@@ -156,8 +175,8 @@
'px-1 py-0.5 text-2xs font-bold rounded cursor-pointer w-fit h-full',
locked ? 'bg-blue-300 text-blue-800' : 'text-white hover:bg-blue-400 hover:text-white'
)}
on:click={() => dispatch('lock')}
on:pointerdown|stopPropagation
onclick={() => dispatch('lock')}
onpointerdown={stopPropagation(bubble('pointerdown'))}
>
{#if locked}
<Anchor aria-label="Unlock position" size={11} />
@@ -171,8 +190,8 @@
class={twMerge(
'px-1 py-0.5 text-2xs font-bold rounded cursor-pointer w-fit h-full text-white hover:bg-blue-400 hover:text-white'
)}
on:click={() => dispatch('expand')}
on:pointerdown|stopPropagation
onclick={() => dispatch('expand')}
onpointerdown={stopPropagation(bubble('pointerdown'))}
>
<Expand aria-label="Expand" size={11} />
</button>
@@ -198,8 +217,8 @@
? 'bg-blue-300 text-blue-800'
: 'text-blue-600 hover:bg-blue-300 hover:text-blue-800'
)}
on:click={() => dispatch('triggerInlineEditor')}
on:pointerdown|stopPropagation
onclick={() => dispatch('triggerInlineEditor')}
onpointerdown={stopPropagation(bubble('pointerdown'))}
>
<Pen aria-label="Edit" size={11} />
</button>
@@ -228,13 +247,13 @@
? 'text-red-600 hover:bg-red-300 hover:text-red-800'
: 'text-blue-600 hover:bg-blue-300 hover:text-blue-800'
)}
on:click={() => {
onclick={() => {
const element = document.getElementById(`decision-tree-graph-editor`)
if (element) {
element.click()
}
}}
on:pointerdown|stopPropagation
onpointerdown={stopPropagation(bubble('pointerdown'))}
>
<Network size={11} />
</button>
@@ -264,7 +283,7 @@
</Popover>
{/if} -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
</div>
{/if}
</div>
@@ -279,14 +298,14 @@
)}
>
<Popover notClickable placement="bottom" contentClasses="!bg-surface border w-96 p-4">
<svelte:fragment slot="trigger">
{#snippet trigger()}
<Bug size={14} />
</svelte:fragment>
<svelte:fragment slot="content">
{/snippet}
{#snippet content()}
<div class="bg-surface">
<pre class="whitespace-pre-wrap text-red-600 bg-surface border w-full p-4 text-xs mb-2"
>{error ?? ''}
</pre>
</pre>
</div>
<Button
color="red"
@@ -294,7 +313,7 @@
on:click={() => $openDebugRun?.($errorByComponent[component.id]?.id ?? '')}
>Open Debug Runs</Button
>
</svelte:fragment>
{/snippet}
</Popover>
</span>
{/if}
@@ -1,4 +1,7 @@
<script lang="ts">
import { stopPropagation, createBubbler } from 'svelte/legacy'
const bubble = createBubbler()
import { Pane, Splitpanes } from 'svelte-splitpanes'
import PanelSection from './settingsPanel/common/PanelSection.svelte'
import { classNames, displayDate, emptyString } from '$lib/utils'
@@ -6,21 +9,23 @@
import { workspaceStore } from '$lib/stores'
import { Skeleton } from '$lib/components/common'
import Button from '$lib/components/common/button/Button.svelte'
import { createEventDispatcher } from 'svelte'
import { createEventDispatcher, untrack } from 'svelte'
import { Pencil, ArrowRight, X, Loader2 } from 'lucide-svelte'
export let appPath: string | undefined
let loading: boolean = false
interface Props {
appPath: string | undefined
}
let versions: AppHistory[] = []
let { appPath }: Props = $props()
let loading: boolean = $state(false)
let selectedVersion: AppHistory | undefined = undefined
let selected: (AppWithLastVersion & { value: any }) | undefined = undefined
let versions: AppHistory[] = $state([])
let deploymentMsgUpdateMode = false
let deploymentMsgUpdate: string | undefined = undefined
let selectedVersion: AppHistory | undefined = $state(undefined)
let selected: (AppWithLastVersion & { value: any }) | undefined = $state(undefined)
$: selectedVersion !== undefined && loadValue(selectedVersion.version)
let deploymentMsgUpdateMode = $state(false)
let deploymentMsgUpdate: string | undefined = $state(undefined)
async function loadVersions() {
if (appPath === undefined) {
@@ -65,6 +70,12 @@
const dispatch = createEventDispatcher()
loadVersions()
$effect(() => {
selectedVersion?.version !== undefined &&
untrack(() => {
selectedVersion && loadValue(selectedVersion.version!)
})
})
</script>
<Splitpanes class="!overflow-visible">
@@ -75,14 +86,14 @@
{#if versions.length > 0}
<div class="flex gap-2 flex-col">
{#each versions ?? [] as version}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y_click_events_have_key_events -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
class={classNames(
'border flex gap-1 truncate justify-between flex-row w-full items-center p-2 rounded-md cursor-pointer hover:bg-blue-50 hover:text-blue-400',
selectedVersion?.version == version.version ? 'bg-blue-100 text-blue-600' : ''
)}
on:click={() => {
onclick={() => {
selectedVersion = version
deploymentMsgUpdateMode = false
deploymentMsgUpdate = undefined
@@ -115,10 +126,11 @@
type="text"
bind:value={deploymentMsgUpdate}
class="!w-auto grow"
on:click|stopPropagation={() => {}}
on:keydown|stopPropagation
on:keypress|stopPropagation={({ key }) => {
if (key === 'Enter')
onclick={stopPropagation(() => {})}
onkeydown={stopPropagation(bubble('keydown'))}
onkeypress={(e) => {
e.stopPropagation()
if (e.key === 'Enter')
updateDeploymentMsg(selected?.id, selectedVersion?.version)
}}
/>
@@ -155,7 +167,7 @@
Deployed {displayDate(selected.created_at)} by {selected.created_by}
{/if}
<button
on:click={() => {
onclick={() => {
deploymentMsgUpdate = selectedVersion?.deployment_msg
deploymentMsgUpdateMode = true
}}
@@ -231,7 +231,7 @@
}}
disableMove={!!$connectingInput.opened}
>
{#snippet children({ dataItem, overlapped, moveMode, componentDraggedId })}
{#snippet children({ dataItem, overlapped, componentDraggedId })}
<ComponentWrapper
id={dataItem.id}
type={dataItem.data.type}
@@ -276,7 +276,6 @@
$app = $app
}}
{overlapped}
{moveMode}
{componentDraggedId}
/>
</GridEditorMenu>
@@ -18,34 +18,49 @@
const { app } = getContext<AppViewerContext>('AppViewerContext')
export let items: FilledItem<T>[]
export let rowHeight: number = ROW_HEIGHT
export let gap = [ROW_GAP_X, ROW_GAP_Y]
export let throttleUpdate = 100
export let maxRow: number
export let breakpoint: EditorBreakpoint
interface Props {
items: FilledItem<T>[]
rowHeight?: number
gap?: any
throttleUpdate?: number
maxRow: number
breakpoint: EditorBreakpoint
allIdsInPath?: string[] | undefined
containerWidth?: number | undefined
parentWidth?: number | undefined
children?: import('svelte').Snippet<[any]>
}
export let allIdsInPath: string[] | undefined = undefined
export let containerWidth: number | undefined = undefined
export let parentWidth: number | undefined = undefined
let {
items = $bindable(),
rowHeight = ROW_HEIGHT,
gap = [ROW_GAP_X, ROW_GAP_Y],
throttleUpdate = 100,
maxRow,
breakpoint,
allIdsInPath = undefined,
containerWidth = $bindable(undefined),
parentWidth = undefined,
children
}: Props = $props()
const cols = columnConfiguration
let showSkeleton = false
let showSkeleton = $state(false)
let getComputedCols: 3 | 12 | undefined =
let getComputedCols: 3 | 12 | undefined = $state(
$app.mobileViewOnSmallerScreens == false ? WIDE_GRID_COLUMNS : undefined
)
let container
let container = $state()
$: [gapX, gapY] = gap
let [gapX, gapY] = $derived(gap)
let xPerPx = 0
let xPerPx = $state(0)
let yPerPx = rowHeight
$: containerHeight = getContainerHeight(items, yPerPx, getComputedCols)
let containerHeight = $derived(getContainerHeight(items, yPerPx, getComputedCols))
const onResize = throttle(() => {
if (!getComputedCols) return
@@ -92,7 +107,7 @@
})
})
sizeObserver.observe(container)
sizeObserver.observe(container as Element)
return () => sizeObserver.disconnect()
})
@@ -121,7 +136,7 @@
: ''} top: {top}px; left: {left}px;"
>
{#if item[getComputedCols]}
<slot dataItem={item} item={item[getComputedCols]} />
{@render children?.({ dataItem: item, item: item[getComputedCols] })}
{/if}
</div>
{/each}
@@ -1,26 +1,31 @@
<script lang="ts">
import { getContext } from 'svelte'
import { getContext, untrack } from 'svelte'
import type { AppEditorContext } from '../types'
import TestJobLoader from '$lib/components/TestJobLoader.svelte'
import type { Job } from '$lib/gen'
import RunnableJobPanelInner from './RunnableJobPanelInner.svelte'
export let float: boolean = true
export let hidden: boolean = false
export let testJob: Job | undefined = undefined
export let jobToWatch: { componentId: string; job: string } | undefined = undefined
export let width: number | undefined = undefined
interface Props {
float?: boolean
hidden?: boolean
testJob?: Job | undefined
jobToWatch?: { componentId: string; job: string } | undefined
width?: number | undefined
}
let {
float = true,
hidden = false,
testJob = $bindable(undefined),
jobToWatch = $bindable(undefined),
width = undefined
}: Props = $props()
const { runnableJobEditorPanel, selectedComponentInEditor } =
getContext<AppEditorContext>('AppEditorContext')
let testIsLoading = false
let testIsLoading = $state(false)
let testJobLoader: TestJobLoader
$: ($runnableJobEditorPanel.focused || !float) &&
$selectedComponentInEditor &&
$runnableJobEditorPanel.jobs &&
updateSelectedJob()
let testJobLoader: TestJobLoader | undefined = $state()
function updateSelectedJob() {
const selectedComponent = $selectedComponentInEditor
@@ -45,13 +50,21 @@
let logDrawerOpen = false
let resultDrawerOpen = false
$effect(() => {
;($runnableJobEditorPanel.focused || !float) &&
$selectedComponentInEditor &&
$runnableJobEditorPanel.jobs &&
untrack(() => {
updateSelectedJob()
})
})
</script>
<TestJobLoader bind:this={testJobLoader} bind:isLoading={testIsLoading} bind:job={testJob} />
{#if ($runnableJobEditorPanel.focused && $selectedComponentInEditor) || logDrawerOpen || resultDrawerOpen || !float}
{@const frontendJob = $runnableJobEditorPanel?.frontendJobs[$selectedComponentInEditor ?? '']}
<!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
{#if float}
<div
class="absolute z-[100] top-0 right-0 border-t h-full"
@@ -5,12 +5,16 @@
import type { Job } from '$lib/gen'
import { Loader2 } from 'lucide-svelte'
export let frontendJob: boolean | any = false
export let testJob: Job | any = undefined
export let testIsLoading = false
interface Props {
frontendJob?: boolean | any
testJob?: Job | any
testIsLoading?: boolean
}
let logDrawerOpen = false
let resultDrawerOpen = false
let { frontendJob = false, testJob = undefined, testIsLoading = false }: Props = $props()
let logDrawerOpen = $state(false)
let resultDrawerOpen = $state(false)
</script>
<Splitpanes horizontal>
@@ -109,58 +109,37 @@
</script>
{#if gridItemWithLocation}
{#key gridItemWithLocation.item.id}
<ComponentPanel
bind:componentSettings={
() => gridItemWithLocation,
(cs) => {
if (gridItemWithLocation?.location.type === 'grid') {
$app.grid[gridItemWithLocation.location.gridItemIndex] = cs.item
} else if (
gridItemWithLocation?.location.type === 'subgrid' &&
Array.isArray($app.subgrids?.[gridItemWithLocation.location.subgridKey])
) {
if (
$app.subgrids[gridItemWithLocation.location.subgridKey][
gridItemWithLocation.location.subgridItemIndex
]
) {
$app.subgrids[gridItemWithLocation.location.subgridKey][
gridItemWithLocation.location.subgridItemIndex
] = cs.item
}
}
}
}
onDelete={() => {
dispatch('delete')
}}
/>
{/key}
{#if gridItemWithLocation.location.type === 'grid'}
{#each $app.grid as gridItem, gridItemIndex}
{#if gridItem.data.id === gridItemWithLocation.item.id}
<ComponentPanel
bind:item={$app.grid[gridItemIndex]}
parent={gridItemWithLocation.parent}
onDelete={() => {
dispatch('delete')
}}
/>
{/if}
{/each}
{:else if gridItemWithLocation.location.type === 'subgrid' && $app.subgrids}
{#each $app.subgrids[gridItemWithLocation.location.subgridKey] as subgridItem, subgridItemIndex}
{#if subgridItem.data.id === gridItemWithLocation.item.id}
<ComponentPanel
bind:item={$app.subgrids[gridItemWithLocation.location.subgridKey][subgridItemIndex]}
parent={gridItemWithLocation.parent}
onDelete={() => {
dispatch('delete')
}}
/>
{/if}
{/each}
{/if}
{:else if tableActionSettings}
{#key tableActionSettings?.item?.data?.id}
<ComponentPanel
noGrid
bind:componentSettings={
() => tableActionSettings,
(cs) => {
if (tableActionSettings) {
if (tableActionSettings.gridItemLocation.type === 'grid') {
const { gridItemIndex } = tableActionSettings.gridItemLocation
const { key, index } = tableActionSettings.location
if ($app.grid[gridItemIndex]?.data?.[key]) {
$app.grid[gridItemIndex].data[key][index] = cs.item.data
}
} else if (tableActionSettings.gridItemLocation.type === 'subgrid') {
const { subgridKey, subgridItemIndex } = tableActionSettings.gridItemLocation
const { key, index } = tableActionSettings.location
if ($app.subgrids?.[subgridKey]?.[subgridItemIndex]?.data?.[key]) {
$app.subgrids[subgridKey][subgridItemIndex].data[key][index] = cs.item.data
}
}
}
}
}
bind:item={tableActionSettings.item}
parent={tableActionSettings.parent}
duplicateMoveAllowed={false}
onDelete={() => {
if (tableActionSettings) {
@@ -209,24 +188,8 @@
{#key menuItemsSettings?.item?.id}
<ComponentPanel
noGrid
bind:componentSettings={
() => menuItemsSettings,
(cs) => {
if (menuItemsSettings) {
if (menuItemsSettings.gridItemLocation.type === 'grid') {
const { gridItemIndex } = menuItemsSettings.gridItemLocation
if ($app.grid[gridItemIndex]?.data?.type === 'menucomponent') {
$app.grid[gridItemIndex].data.menuItems[cs.index] = cs.item.data
}
} else if (menuItemsSettings.gridItemLocation.type === 'subgrid') {
const { subgridKey, subgridItemIndex } = menuItemsSettings.gridItemLocation
if ($app.subgrids?.[subgridKey]?.[subgridItemIndex]?.data?.type === 'menucomponent') {
$app.subgrids[subgridKey][subgridItemIndex].data.menuItems[cs.index] = cs.item.data
}
}
}
}
}
bind:item={menuItemsSettings.item}
parent={menuItemsSettings.parent}
onDelete={() => {
if (menuItemsSettings) {
const item = findGridItemWithLocation($app, menuItemsSettings.parent)
@@ -268,7 +268,7 @@
}}
disableMove={!!$connectingInput.opened}
>
{#snippet children({ dataItem, overlapped, moveMode, componentDraggedId })}
{#snippet children({ dataItem, overlapped, componentDraggedId })}
<ComponentWrapper
id={dataItem.id}
type={dataItem.data.type}
@@ -314,7 +314,6 @@
}
$app = $app
}}
{moveMode}
{componentDraggedId}
/>
</GridEditorMenu>
@@ -6,7 +6,8 @@ import type {
EditorBreakpoint,
FocusedGrid,
GeneralAppInput,
GridItem
GridItem,
RichConfiguration
} from '../types'
import {
ccomponents,
@@ -38,14 +39,14 @@ import gridHelp from '../svelte-grid/utils/helper'
type GridItemLocation =
| {
type: 'grid'
gridItemIndex: number
}
type: 'grid'
gridItemIndex: number
}
| {
type: 'subgrid'
subgridItemIndex: number
subgridKey: string
}
type: 'subgrid'
subgridItemIndex: number
subgridKey: string
}
interface GridItemWithLocation {
location: GridItemLocation
item: GridItem
@@ -186,7 +187,7 @@ export function selectId(
selectedComponent: Writable<string[] | undefined>,
app: App
) {
;(document?.activeElement as HTMLElement)?.blur()
; (document?.activeElement as HTMLElement)?.blur()
if (e.shiftKey) {
selectedComponent.update((old) => {
if (old && old?.[0]) {
@@ -482,10 +483,67 @@ export function appComponentFromType<T extends keyof typeof components>(
horizontalAlignment: override?.horizontalAlignment ?? init.horizontalAlignment,
verticalAlignment: override?.verticalAlignment ?? init.verticalAlignment,
id,
datasets:
type === 'plotlycomponentv2'
? createPlotlyComponentDataset()
: type === 'chartjscomponentv2'
? createChartjsComponentDataset()
: undefined,
xData:
type === 'plotlycomponentv2' || type === 'chartjscomponentv2'
? {
type: 'evalv2',
fieldType: 'array',
expr: '[1, 2, 3, 4]',
connections: []
}
: undefined,
...(extra ?? {})
}
}
}
export function createChartjsComponentDataset(): RichConfiguration {
return {
type: 'static',
fieldType: 'array',
subFieldType: 'chartjs',
value: [
{
value: {
type: 'static',
fieldType: 'array',
subFieldType: 'number',
value: [25, 25, 50]
},
name: 'Dataset 1'
}
]
}
}
export function createPlotlyComponentDataset(): RichConfiguration {
return {
type: 'static',
fieldType: 'array',
subFieldType: 'plotly',
value: [
{
value: {
type: 'static',
fieldType: 'array',
subFieldType: 'number',
value: [1, 2, 3, 4]
},
name: 'Dataset 1',
aggregation_method: 'sum',
type: 'bar',
tooltip: '',
color: '#C8A2C8'
}
]
}
}
export function insertNewGridItem(
app: App,
builddata: (id: string) => AppComponent,
@@ -845,33 +903,33 @@ export type InitConfig<
| EvalAppInput
| EvalV2AppInput
| {
type: 'oneOf'
selected: string
configuration: Record<
string,
Record<string, StaticAppInput | EvalAppInput | EvalV2AppInput>
>
}
type: 'oneOf'
selected: string
configuration: Record<
string,
Record<string, StaticAppInput | EvalAppInput | EvalV2AppInput>
>
}
>
> = {
[Property in keyof T]: T[Property] extends StaticAppInput
[Property in keyof T]: T[Property] extends StaticAppInput
? T[Property]['value'] | undefined
: T[Property] extends { type: 'oneOf' }
? {
type: 'oneOf'
selected: keyof T[Property]['configuration']
configuration: {
[Choice in keyof T[Property]['configuration']]: {
[IT in keyof T[Property]['configuration'][Choice]]: T[Property]['configuration'][Choice][IT] extends StaticAppInput
? T[Property]['configuration'][Choice][IT] extends StaticAppInputOnDemand
? () => Promise<T[Property]['configuration'][Choice][IT]['value'] | undefined>
: T[Property]['configuration'][Choice][IT]['value'] | undefined
: undefined
}
}
? {
type: 'oneOf'
selected: keyof T[Property]['configuration']
configuration: {
[Choice in keyof T[Property]['configuration']]: {
[IT in keyof T[Property]['configuration'][Choice]]: T[Property]['configuration'][Choice][IT] extends StaticAppInput
? T[Property]['configuration'][Choice][IT] extends StaticAppInputOnDemand
? () => Promise<T[Property]['configuration'][Choice][IT]['value'] | undefined>
: T[Property]['configuration'][Choice][IT]['value'] | undefined
: undefined
}
: undefined
}
}
}
: undefined
}
export function initConfig<
T extends Record<
@@ -880,13 +938,13 @@ export function initConfig<
| EvalAppInput
| EvalV2AppInput
| {
type: 'oneOf'
selected: string
configuration: Record<
string,
Record<string, StaticAppInput | EvalAppInput | EvalV2AppInput>
>
}
type: 'oneOf'
selected: string
configuration: Record<
string,
Record<string, StaticAppInput | EvalAppInput | EvalV2AppInput>
>
}
>
>(
r: T,
@@ -894,13 +952,13 @@ export function initConfig<
string,
| StaticAppInput
| {
type: 'oneOf'
selected: string
configuration: Record<
string,
Record<string, StaticAppInput | EvalAppInput | EvalV2AppInput | boolean>
>
}
type: 'oneOf'
selected: string
configuration: Record<
string,
Record<string, StaticAppInput | EvalAppInput | EvalV2AppInput | boolean>
>
}
| any
>
): InitConfig<T> {
@@ -910,31 +968,31 @@ export function initConfig<
Object.entries(r).map(([key, value]) =>
value.type == 'static'
? [
key,
configuration?.[key]?.type == 'static' ? configuration?.[key]?.['value'] : undefined
]
key,
configuration?.[key]?.type == 'static' ? configuration?.[key]?.['value'] : undefined
]
: value.type == 'oneOf'
? [
key,
{
selected: value.selected,
type: 'oneOf',
configuration: Object.fromEntries(
Object.entries(value.configuration).map(([choice, config]) => {
const conf = initConfig(
config,
configuration?.[key]?.configuration?.[choice]
)
Object.entries(config).forEach(([innerKey, innerValue]) => {
if (innerValue.type === 'static' && !(innerKey in conf)) {
conf[innerKey] = innerValue.value
}
})
return [choice, conf]
key,
{
selected: value.selected,
type: 'oneOf',
configuration: Object.fromEntries(
Object.entries(value.configuration).map(([choice, config]) => {
const conf = initConfig(
config,
configuration?.[key]?.configuration?.[choice]
)
Object.entries(config).forEach(([innerKey, innerValue]) => {
if (innerValue.type === 'static' && !(innerKey in conf)) {
conf[innerKey] = innerValue.value
}
})
)
}
]
return [choice, conf]
})
)
}
]
: [key, undefined]
)
) as any
@@ -5,19 +5,31 @@
import type { AppComponent } from './components'
import type { AppViewerContext } from '../../types'
export let component: AppComponent
export let selected: boolean
export let locked: boolean = false
export let render: boolean
export let fullHeight: boolean
export let overlapped: string | undefined = undefined
export let componentDraggedId: string | undefined = undefined
const { app } = getContext<AppViewerContext>('AppViewerContext')
export let moveMode: string | undefined = undefined
let everRender = render
interface Props {
component: AppComponent
selected: boolean
locked?: boolean
render: boolean
fullHeight: boolean
overlapped?: string | undefined
componentDraggedId?: string | undefined
}
$: render && !everRender && (everRender = true)
let {
component,
selected,
locked = false,
render,
fullHeight,
overlapped = undefined,
componentDraggedId = undefined
}: Props = $props()
let everRender = $state(render)
$effect(() => {
render && !everRender && (everRender = true)
})
</script>
{#if everRender || $app.eagerRendering}
@@ -25,7 +37,6 @@
on:expand
on:lock
on:fillHeight
{moveMode}
{componentDraggedId}
{overlapped}
{render}
@@ -94,7 +94,11 @@
}: Props = $props()
</script>
<svelte:boundary>
<svelte:boundary
onerror={(e) => {
console.error(e)
}}
>
{#if component.type === 'displaycomponent'}
<AppDisplayComponent
id={component.id}
@@ -217,8 +221,6 @@
customCss={component.customCss}
bind:initializing
componentInput={component.componentInput}
datasets={component.datasets}
xData={component.xData}
{render}
/>
{:else if component.type === 'agchartscomponentee'}
@@ -228,8 +230,6 @@
customCss={component.customCss}
bind:initializing
componentInput={component.componentInput}
datasets={component.datasets}
xData={component.xData}
license={component.license}
ee={true}
{render}
@@ -1,9 +1,11 @@
<script lang="ts" context="module">
<script lang="ts" module>
let outTimeout: NodeJS.Timeout | undefined = undefined
</script>
<script lang="ts">
import { getContext, onMount } from 'svelte'
import { stopPropagation } from 'svelte/legacy'
import { getContext, onMount, untrack } from 'svelte'
import { twMerge } from 'tailwind-merge'
import type { AppEditorContext, AppViewerContext } from '../../types'
import ComponentHeader from '../ComponentHeader.svelte'
@@ -12,17 +14,29 @@
import { Anchor } from 'lucide-svelte'
import { findGridItemParentGrid, isContainer } from '../appUtils'
import ComponentInner from './ComponentInner.svelte'
import { moveMode } from '../../gridUtils'
export let component: AppComponent
export let selected: boolean
export let locked: boolean = false
export let fullHeight: boolean
export let overlapped: string | undefined = undefined
export let moveMode: string | undefined = undefined
export let componentDraggedId: string | undefined = undefined
export let render: boolean = false
interface Props {
component: AppComponent
selected: boolean
locked?: boolean
fullHeight: boolean
overlapped?: string | undefined
componentDraggedId?: string | undefined
render?: boolean
}
let initializing: boolean | undefined
let {
component,
selected,
locked = false,
fullHeight,
overlapped = undefined,
componentDraggedId = undefined,
render = false
}: Props = $props()
let initializing: boolean | undefined = $state()
const { mode, app, hoverStore, connectingInput } =
getContext<AppViewerContext>('AppViewerContext')
@@ -31,15 +45,16 @@
const componentActive = editorContext?.componentActive
const movingcomponents = editorContext?.movingcomponents
$: ismoving =
let ismoving = $derived(
movingcomponents != undefined && $mode == 'dnd' && $movingcomponents?.includes(component.id)
)
let errorHandledByComponent: boolean = false
let componentContainerHeight: number = 0
let componentContainerWidth: number = 0
let errorHandledByComponent: boolean = $state(false)
let componentContainerHeight: number = $state(0)
let componentContainerWidth: number = $state(0)
let inlineEditorOpened: boolean = false
let showSkeleton = false
let inlineEditorOpened: boolean = $state(false)
let showSkeleton = $state(false)
onMount(() => {
setTimeout(() => {
@@ -74,8 +89,8 @@
)
}
let cachedComponentDraggedIsNotChild: boolean | undefined
let cachedAreOnTheSameSubgrid: boolean | undefined
let cachedComponentDraggedIsNotChild: boolean | undefined = $state()
let cachedAreOnTheSameSubgrid: boolean | undefined = $state()
function updateCache(componentDraggedId: string | undefined) {
if (componentDraggedId) {
@@ -90,19 +105,24 @@
}
}
$: updateCache(componentDraggedId)
$effect(() => {
componentDraggedId
untrack(() => {
updateCache(componentDraggedId)
})
})
</script>
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y_mouse_events_have_key_events -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
on:mouseover|stopPropagation={() => {
onmouseover={stopPropagation(() => {
outTimeout && clearTimeout(outTimeout)
if (component.id !== $hoverStore) {
$hoverStore = component.id
}
}}
on:mouseout|stopPropagation={mouseOut}
})}
onmouseout={stopPropagation(mouseOut)}
class={twMerge(
'h-full flex flex-col w-full component relative',
initializing ? 'overflow-hidden h-0' : ''
@@ -110,7 +130,7 @@
data-connection-button
>
{#if render}
{#if locked && componentActive && $componentActive && moveMode === 'move' && componentDraggedId && componentDraggedId !== component.id && cachedAreOnTheSameSubgrid}
{#if locked && componentActive && $componentActive && $moveMode === 'move' && componentDraggedId && componentDraggedId !== component.id && cachedAreOnTheSameSubgrid}
<div
class={twMerge('absolute inset-0 bg-locked center-center flex-col z-50', 'bg-locked-hover')}
>
@@ -119,7 +139,7 @@
<div class="text-xs"> Anchored: The component cannot be moved. </div>
</div>
</div>
{:else if moveMode === 'insert' && isContainer(component.type) && componentDraggedId && componentDraggedId !== component.id && cachedComponentDraggedIsNotChild}
{:else if $moveMode === 'insert' && isContainer(component.type) && componentDraggedId && componentDraggedId !== component.id && cachedComponentDraggedIsNotChild}
<div
class={twMerge(
'absolute inset-0 flex-col rounded-md bg-blue-100 dark:bg-gray-800 bg-opacity-50',
@@ -162,7 +182,7 @@
<div class="absolute -top-8 w-40">
<button
class="border p-0.5 text-xs"
on:click={() => {
onclick={() => {
$movingcomponents = undefined
}}
>
@@ -202,19 +222,19 @@
</div>
</div>
{#if initializing && render && showSkeleton}
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y_mouse_events_have_key_events -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
on:mouseover|stopPropagation={() => {
onmouseover={stopPropagation(() => {
if (component.id !== $hoverStore) {
$hoverStore = component.id
}
}}
on:mouseout|stopPropagation={() => {
})}
onmouseout={stopPropagation(() => {
if ($hoverStore !== undefined) {
$hoverStore = undefined
}
}}
})}
class="absolute inset-0 center-center flex-col border animate-skeleton dark:bg-frost-900/50 [animation-delay:1000ms]"
></div>
{/if}
@@ -3,8 +3,14 @@
import type { AppViewerContext, ContextPanelContext } from '../../types'
import { dfs, selectId } from '../appUtils'
export let id: string
export let type: string
interface Props {
id: string
type: string
class: string
children?: import('svelte').Snippet
}
let { id, type, class: clazz, children }: Props = $props()
const { app, connectingInput, selectedComponent } =
getContext<AppViewerContext>('AppViewerContext')
@@ -40,34 +46,34 @@
}
</script>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y_click_events_have_key_events -->
<!-- svelte-ignore a11y_mouse_events_have_key_events -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
class={$$props.class}
on:pointerover={(e) => {
class={clazz}
onpointerover={(e) => {
if ($connectingInput.opened && $connectingInput.hoveredComponent !== id) {
$connectingInput.hoveredComponent = id
}
e.stopPropagation()
}}
on:pointerleave={(e) => {
onpointerleave={(e) => {
if ($connectingInput.opened) {
$connectingInput.hoveredComponent = undefined
e.stopPropagation()
}
}}
on:focus={(e) => {
onfocus={(e) => {
if ($connectingInput.opened) {
e.stopPropagation()
}
}}
on:pointerdown={onPointerDown}
on:click|capture={(event) =>
onpointerdown={onPointerDown}
onclickcapture={(event) =>
preventInteraction(event, type === 'tabscomponent' || type === 'steppercomponent')}
on:drag|capture={preventInteraction}
on:pointerup|capture={(event) =>
ondragcapture={preventInteraction}
onpointerupcapture={(event) =>
preventInteraction(event, type === 'tabscomponent' || type === 'steppercomponent')}
>
<slot />
{@render children?.()}
</div>
@@ -972,7 +972,27 @@ const agchartscomponentconst = {
},
initialData: {
configuration: {},
componentInput: undefined
componentInput: {
type: 'evalv2',
noStatic: true,
fieldType: 'object',
expr: `({
data: [
{ x: 1, y: 5 },
{ x: 2, y: 10 },
{ x: 3, y: 2 },
{ x: 4, y: 8 }
],
series: [{
type: 'bar',
xKey: 'x',
yKey: 'y',
fill: '#C8A2C8',
strokeWidth: 2.5
}]
})`,
connections: [] as InputConnectionEval[]
}
}
} as const
@@ -1628,8 +1648,8 @@ export const components = {
backgroundColor: ['#FF6384', '#4BC0C0', '#FFCE56']
}
]
}
}
},
},
}
},
chartjscomponentv2: {
@@ -1656,7 +1676,20 @@ export const components = {
tooltip: 'ChartJs options object https://www.chartjs.org/docs/latest/general/options.html'
}
},
componentInput: undefined
componentInput: {
type: 'static',
fieldType: 'object',
value: {
labels: ['Pie', 'Charts', '<3'],
datasets: [
{
data: [25, 50, 25],
backgroundColor: ['#FF6384', '#4BC0C0', '#FFCE56']
}
]
},
},
}
},
barchartcomponent: {
@@ -1826,7 +1859,8 @@ This is a paragraph.
width: 2.5
}
}
}
},
},
configuration: {
layout: {
@@ -1846,7 +1880,21 @@ This is a paragraph.
dims: '2:8-6:8' as AppComponentDimensions,
customCss: {},
initialData: {
componentInput: undefined,
componentInput: {
type: 'static',
fieldType: 'object',
value: {
type: 'bar',
x: [1, 2, 3, 4],
y: [5, 10, 2, 8],
marker: {
color: '#C8A2C8',
line: {
width: 2.5
}
}
},
},
configuration: {
layout: {
type: 'static',
@@ -1856,7 +1904,8 @@ This is a paragraph.
'Layout options for the plot. See https://plotly.com/javascript/reference/layout/ for more information'
}
}
}
},
},
timeseriescomponent: {
name: 'Timeseries',
@@ -3,12 +3,16 @@
import type { AppViewerContext, RichConfiguration } from '../../types'
import InputsSpecEditor from '../settingsPanel/InputsSpecEditor.svelte'
export let evalClass: RichConfiguration
export let key: string
interface Props {
evalClass: RichConfiguration
key: string
}
let { evalClass = $bindable(), key }: Props = $props()
const { selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
$: id = Array.isArray($selectedComponent) ? $selectedComponent[0] : $selectedComponent
let id = $derived(Array.isArray($selectedComponent) ? $selectedComponent[0] : $selectedComponent)
</script>
{#if evalClass && id}
@@ -98,7 +98,7 @@
}
}}
>
<div slot="title" class="flex items-center">
<div slot="titleSlot" class="flex items-center">
<svelte:component this={icon} size={18} />
<span class="ml-1">
{name}
@@ -14,15 +14,19 @@
import type { AppViewerContext, GridItem } from '../../types'
import { getAllSubgridsAndComponentIds } from '../appUtils'
export let item: GridItem
interface Props {
item: GridItem
}
let { item }: Props = $props()
const { app } = getContext<AppViewerContext>('AppViewerContext')
let groups: Array<{
name: string
path: string
}> = []
}> = $state([])
let loading: boolean = false
let loading: boolean = $state(false)
async function getGroups() {
loading = true
@@ -79,7 +83,7 @@
nameField = ''
}
let nameField: string = ''
let nameField: string = $state('')
getGroups()
</script>
@@ -3,17 +3,21 @@
import GroupList from './GroupList.svelte'
import type { GridItem } from '../../types'
export let item: GridItem
interface Props {
item: GridItem
}
let codeDrawer: Drawer
let { item }: Props = $props()
let codeDrawer: Drawer | undefined = $state()
export async function openDrawer() {
codeDrawer.openDrawer()
codeDrawer?.openDrawer()
}
</script>
<Drawer bind:this={codeDrawer}>
<DrawerContent title="Component Groups" on:close={codeDrawer.closeDrawer}>
<DrawerContent title="Component Groups" on:close={() => codeDrawer?.closeDrawer()}>
<GroupList {item} />
</DrawerContent>
</Drawer>
@@ -1,29 +1,52 @@
<script lang="ts">
import { preventDefault } from 'svelte/legacy'
import { slide } from 'svelte/transition'
import { ChevronDown } from 'lucide-svelte'
import { isOpenStore } from './store'
import { createEventDispatcher, onMount } from 'svelte'
import Tooltip from '$lib/components/Tooltip.svelte'
import { createDispatcherIfMounted } from '$lib/createDispatcherIfMounted'
export let title: string
export let prefix: string | undefined = undefined
export let openByDefault: boolean = false
export let wrapperClasses = ''
export let toggleClasses = ''
export let contentWrapperClasses = ''
export let isOpen = false
export let tooltip: string | undefined = undefined
export let documentationLink: string | undefined = undefined
export let subtitle: string | undefined = undefined
interface Props {
title: string
prefix?: string | undefined
openByDefault?: boolean
wrapperClasses?: string
toggleClasses?: string
contentWrapperClasses?: string
isOpen?: boolean
tooltip?: string | undefined
documentationLink?: string | undefined
subtitle?: string | undefined
titleSlot?: import('svelte').Snippet
children?: import('svelte').Snippet
}
let {
title,
prefix = undefined,
openByDefault = false,
wrapperClasses = '',
toggleClasses = '',
contentWrapperClasses = '',
isOpen = $bindable(false),
tooltip = undefined,
documentationLink = undefined,
subtitle = undefined,
titleSlot,
children
}: Props = $props()
const dispatch = createEventDispatcher()
const dispatchIfMounted = createDispatcherIfMounted(dispatch)
$: storeTitle = prefix + title
$: isOpen = prefix ? $isOpenStore[storeTitle] : true
let storeTitle = $derived(prefix + title)
$effect(() => {
isOpen = prefix ? $isOpenStore[storeTitle] : true
})
$: dispatchIfMounted('open', isOpen)
$effect(() => {
dispatch('open', isOpen)
})
onMount(() => {
if (prefix !== undefined && !(prefix + title in $isOpenStore)) {
@@ -35,19 +58,19 @@
<section class="pt-1 pb-2 px-1 {wrapperClasses}">
{#if prefix !== undefined}
<button
on:click|preventDefault={() => isOpenStore.toggle(storeTitle)}
onclick={preventDefault(() => isOpenStore.toggle(storeTitle))}
class="w-full flex justify-between items-center text-secondary px-2 py-1
rounded-sm duration-200 hover:bg-surface-hover {toggleClasses}"
>
<h1 class="text-sm font-semibold text-left">
<slot name="title">
{#if titleSlot}{@render titleSlot()}{:else}
{title}
{#if subtitle}
<span class="text-2xs text-tertiary ml-1">
{subtitle}
</span>
{/if}
</slot>
{/if}
{#if tooltip}
<Tooltip class="ml-1" {documentationLink}>{tooltip}</Tooltip>
{/if}
@@ -56,25 +79,25 @@
</button>
{#if isOpen}
<div transition:slide|local={{ duration: 300 }} class="px-2 {contentWrapperClasses}">
<slot />
{@render children?.()}
</div>
{/if}
{:else}
<h1 class="text-base font-semibold text-left px-2 py-1 text-secondary">
<slot name="title">
{#if titleSlot}{@render titleSlot()}{:else}
{title}
{#if subtitle}
<span class="text-2xs text-tertiary ml-1">
{subtitle}
</span>
{/if}
</slot>
{/if}
{#if tooltip}
<Tooltip class="ml-1" {documentationLink}>{tooltip}</Tooltip>
{/if}
</h1>
<div class="px-2">
<slot />
{@render children?.()}
</div>
{/if}
</section>
@@ -4,13 +4,17 @@
import { Pen } from 'lucide-svelte'
import { createEventDispatcher } from 'svelte'
export let kind: string
export let row: {
name: string
path: string
interface Props {
kind: string
row: {
name: string
path: string
}
}
let editedName = row.name
let { kind, row }: Props = $props()
let editedName = $state(row.name)
const dispatch = createEventDispatcher()
function onkeydown(e) {
@@ -21,18 +25,18 @@
</script>
<Popover floatingConfig={{ strategy: 'absolute', placement: 'bottom-end' }}>
<svelte:fragment slot="trigger">
{#snippet trigger()}
<Button color="light" size="xs2" nonCaptureEvent={true}>
<div class="flex flex-row gap-1 items-center">
<Pen size={16} />
</div>
</Button>
</svelte:fragment>
<svelte:fragment slot="content" let:close>
{/snippet}
{#snippet content({ close })}
<div class="flex flex-col w-80 gap-2 p-4">
<div class="leading-6 font-semibold text-xs">Edit {kind} name</div>
<div class="flex flex-row gap-2">
<input on:keydown={onkeydown} bind:value={editedName} />
<input {onkeydown} bind:value={editedName} />
<Button
color="dark"
size="xs"
@@ -45,5 +49,5 @@
</Button>
</div>
</div>
</svelte:fragment>
{/snippet}
</Popover>
@@ -211,7 +211,7 @@
{isOpen[prefix] ? '!bg-surface-secondary hover:!bg-surface-hover' : ''}"
>
<!-- @migration-task: migrate this slot by hand, `title` would shadow a prop on the parent component -->
<svelte:fragment slot="title">
<svelte:fragment slot="titleSlot">
<span class="font-normal text-xs">
{group}
</span>
@@ -5,10 +5,15 @@
import { onMount } from 'svelte'
import SimpleEditor from '$lib/components/SimpleEditor.svelte'
export let theme: AppTheme | undefined = undefined
interface Props {
theme?: AppTheme | undefined
children?: import('svelte').Snippet
}
let code: string | undefined = undefined
let cssEditor: SimpleEditor | undefined = undefined
let { theme = undefined, children }: Props = $props()
let code: string | undefined = $state(undefined)
let cssEditor: SimpleEditor | undefined = $state(undefined)
onMount(async () => {
code = await resolveTheme(theme, $workspaceStore)
@@ -18,7 +23,7 @@
<div class="relative h-full">
<div class="absolute z-[100] left-[50%] top-[50%] translate-x-[-50%] translate-y-[-50%]">
<slot />
{@render children?.()}
</div>
<div class="absolute top-0 left-0 right-0 bottom-0 bg-gray-100 bg-opacity-50 z-50"></div>
<SimpleEditor
@@ -7,13 +7,17 @@
import { workspaceStore } from '$lib/stores'
import HighlightTheme from '$lib/components/HighlightTheme.svelte'
export let theme: AppTheme
interface Props {
theme: AppTheme
}
let code: string | undefined = undefined
let codeDrawer: Drawer
let { theme }: Props = $props()
let code: string | undefined = $state(undefined)
let codeDrawer: Drawer | undefined = $state()
export async function openDrawer() {
codeDrawer.openDrawer()
codeDrawer?.openDrawer()
code = await resolveTheme(theme, $workspaceStore)
}
</script>
@@ -7,12 +7,16 @@
import { createEventDispatcher } from 'svelte'
import { sendUserToast } from '$lib/toast'
export let row: {
name: string
path: string
interface Props {
row: {
name: string
path: string
}
}
let editedName = row.name
let { row }: Props = $props()
let editedName = $state(row.name)
const dispatch = createEventDispatcher()
</script>
@@ -22,14 +26,14 @@
closeOnOtherPopoverOpen
contentClasses="flex flex-col w-80 gap-2 p-4"
>
<svelte:fragment slot="trigger">
{#snippet trigger()}
<Button color="light" size="xs2" nonCaptureEvent={true}>
<div class="flex flex-row gap-1 items-center">
<Pen size={16} />
</div>
</Button>
</svelte:fragment>
<svelte:fragment slot="content" let:close>
{/snippet}
{#snippet content({ close })}
<div class="leading-6 font-semibold text-xs">Edit theme name</div>
<div class="flex flex-row gap-2">
<input bind:value={editedName} />
@@ -52,5 +56,5 @@
Update
</Button>
</div>
</svelte:fragment>
{/snippet}
</Popover>
@@ -11,18 +11,29 @@
// @ts-ignore
import MenuItemsOutput from './components/MenuItemsOutput.svelte'
export let gridItem: GridItem
export let first: boolean = false
export let nested: boolean = false
export let expanded: boolean = false
export let renderRec: boolean = true
interface Props {
gridItem: GridItem
first?: boolean
nested?: boolean
expanded?: boolean
renderRec?: boolean
}
let {
gridItem,
first = false,
nested = false,
expanded = false,
renderRec = true
}: Props = $props()
const { connectingInput } = getContext<AppViewerContext>('AppViewerContext')
const name = getComponentNameById(gridItem.id)
$: nameOverrides =
let nameOverrides = $derived(
gridItem?.data?.type === 'decisiontreecomponent'
? gridItem.data.nodes.map((n, i) => `${n.label} (Tab index ${i})`)
: undefined
)
function getComponentNameById(componentId: string) {
if (gridItem?.data?.type) {
@@ -36,27 +47,30 @@
}
}
$: subGrids = Array.from({ length: gridItem.data?.numberOfSubgrids ?? 0 }).map(
(_, i) => `${gridItem.id}-${i}`
let subGrids = $derived(
Array.from({ length: gridItem.data?.numberOfSubgrids ?? 0 }).map(
(_, i) => `${gridItem.id}-${i}`
)
)
</script>
<OutputHeader
render={renderRec}
let:render
id={gridItem.id}
name={getComponentNameById(gridItem.id)}
{first}
{nested}
>
<ComponentOutputViewer
{render}
componentId={gridItem.id}
on:select={({ detail }) => {
connectOutput(connectingInput, gridItem?.data?.type, gridItem.data.id, detail)
}}
/>
<SubGridOutput {render} {name} {nameOverrides} {expanded} {subGrids} parentId={gridItem.id} />
<TableActionsOutput {render} {gridItem} />
<MenuItemsOutput {render} {gridItem} />
{#snippet children({ render })}
<ComponentOutputViewer
{render}
componentId={gridItem.id}
on:select={({ detail }) => {
connectOutput(connectingInput, gridItem?.data?.type, gridItem.data.id, detail)
}}
/>
<SubGridOutput {render} {name} {nameOverrides} {expanded} {subGrids} parentId={gridItem.id} />
<TableActionsOutput {render} {gridItem} />
<MenuItemsOutput {render} {gridItem} />
{/snippet}
</OutputHeader>
@@ -1,18 +1,22 @@
<script lang="ts">
import ObjectViewer from '$lib/components/propertyPicker/ObjectViewer.svelte'
import { getContext } from 'svelte'
import { getContext, untrack } from 'svelte'
import type { Output } from '../../rx'
import type { AppViewerContext, ContextPanelContext } from '../../types'
import { recursivelyFilterKeyInJSON as recursivelyFilterInJSON } from '../appUtils'
export let componentId: string
export let hasContent: boolean = false
export let suffix: string = ''
export let render: boolean = true
interface Props {
componentId: string
hasContent?: boolean
suffix?: string
render?: boolean
}
let { componentId, hasContent = $bindable(false), suffix = '', render = true }: Props = $props()
const { worldStore, connectingInput } = getContext<AppViewerContext>('AppViewerContext')
const { search, hasResult } = getContext<ContextPanelContext>('ContextPanel')
let object = {}
let object = $state({})
function subscribeToAllOutputs(observableOutputs: Record<string, Output<any>> | undefined) {
if (observableOutputs) {
@@ -34,9 +38,16 @@
}
}
$: subscribeToAllOutputs($worldStore?.outputsById?.[componentId])
$: filtered = recursivelyFilterInJSON(object, $search, componentId)
$: $hasResult[componentId] = Object.keys(filtered).length > 0
$effect(() => {
$worldStore?.outputsById?.[componentId]
untrack(() => {
subscribeToAllOutputs($worldStore?.outputsById?.[componentId])
})
})
let filtered = $derived(recursivelyFilterInJSON(object, $search, componentId))
$effect(() => {
$hasResult[componentId] = Object.keys(filtered).length > 0
})
</script>
{#if render && object != undefined && Object.keys(object).length > 0}
@@ -19,11 +19,11 @@
const dispatch = createEventDispatcher()
let hasState: boolean = false
let hasState: boolean = $state(false)
</script>
<PanelSection noPadding titlePadding="px-2 pt-2" title="Outputs">
<svelte:fragment slot="action">
{#snippet action()}
<div class="p-0.5">
<HideButton
on:click={() => {
@@ -33,7 +33,7 @@
/>
<DocLink docLink="https://www.windmill.dev/docs/apps/outputs" />
</div>
</svelte:fragment>
{/snippet}
<AnimatedButton
animate={$connectingInput.opened}
baseRadius="0px"
@@ -57,39 +57,35 @@
<div>
<span class="text-xs font-semibold text-secondary p-2">State & Context</span>
<OutputHeader
let:render
selectable={false}
id={'ctx'}
name={'App Context'}
first
color="blue"
>
<ComponentOutputViewer
{render}
componentId={'ctx'}
on:select={({ detail }) => {
$connectingInput = connectInput($connectingInput, 'ctx', detail)
}}
/>
<OutputHeader selectable={false} id={'ctx'} name={'App Context'} first color="blue">
{#snippet children({ render })}
<ComponentOutputViewer
{render}
componentId={'ctx'}
on:select={({ detail }) => {
$connectingInput = connectInput($connectingInput, 'ctx', detail)
}}
/>
{/snippet}
</OutputHeader>
<OutputHeader
let:render
selectable={false}
id={'state'}
name={'State'}
color="blue"
disabled={!hasState}
>
<ComponentOutputViewer
{render}
bind:hasContent={hasState}
componentId={'state'}
on:select={({ detail }) => {
$connectingInput = connectInput($connectingInput, 'state', detail)
}}
/>
{#snippet children({ render })}
<ComponentOutputViewer
{render}
bind:hasContent={hasState}
componentId={'state'}
on:select={({ detail }) => {
$connectingInput = connectInput($connectingInput, 'state', detail)
}}
/>
{/snippet}
</OutputHeader>
</div>
@@ -3,31 +3,28 @@
import { getContext } from 'svelte'
import type { Output } from '../../rx'
import type { AppViewerContext } from '../../types'
import { connectInput } from '../appUtils'
import ComponentOutput from './ComponentOutput.svelte'
export let name: string | undefined = undefined
export let parentId: string
export let expanded: boolean = false
export let subGrids: string[]
export let nameOverrides: string[] | undefined = undefined
export let render: boolean
const { app, connectingInput, worldStore } = getContext<AppViewerContext>('AppViewerContext')
let selected = 0
$: outputs = $worldStore?.outputsById[parentId] as {
selectedTabIndex: Output<number>
interface Props {
name?: string | undefined
parentId: string
expanded?: boolean
subGrids: string[]
nameOverrides?: string[] | undefined
render: boolean
}
$: subgridItems = subGrids.map((k) => ({
k,
items: $app.subgrids?.[k] ?? []
}))
let {
name = undefined,
parentId,
expanded = false,
subGrids,
nameOverrides = undefined,
render
}: Props = $props()
const { app, worldStore } = getContext<AppViewerContext>('AppViewerContext')
$: if (outputs?.selectedTabIndex) {
subscribeToOutput()
}
let selected = $state(0)
function subscribeToOutput() {
outputs.selectedTabIndex.subscribe(
@@ -40,19 +37,35 @@
selected
)
}
let outputs = $derived(
$worldStore?.outputsById[parentId] as {
selectedTabIndex: Output<number>
}
)
let subgridItems = $derived(
subGrids.map((k) => ({
k,
items: $app.subgrids?.[k] ?? []
}))
)
$effect(() => {
if (outputs?.selectedTabIndex) {
subscribeToOutput()
}
})
</script>
{#each subgridItems as { k, items }, index (k)}
<div class="ml-2 my-2">
{#if subGrids.length > 1 && render}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y_click_events_have_key_events -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
class={classNames(
'px-1 py-0.5 flex justify-between items-center font-semibold text-xs border-l border-y w-full cursor-pointer',
selected === index ? 'bg-surface-selected' : 'bg-surface'
)}
on:click={() => {
onclick={() => {
selected = index
}}
>
@@ -78,9 +91,6 @@
gridItem={subGridItem}
first={index === 0}
{expanded}
on:select={({ detail }) => {
$connectingInput = connectInput($connectingInput, subGridItem.id, detail)
}}
/>
{/each}
{:else}
@@ -7,17 +7,23 @@
const { connectingInput } = getContext<AppViewerContext>('AppViewerContext')
export let id: string
export let name: string
export let first: boolean = false
interface Props {
id: string;
name: string;
first?: boolean;
}
let { id, name, first = false }: Props = $props();
</script>
<OutputHeader let:render renamable={false} selectable={true} {id} {name} color="blue" {first}>
<ComponentOutputViewer
{render}
componentId={id}
on:select={({ detail }) => {
$connectingInput = connectInput($connectingInput, id, detail)
}}
/>
<OutputHeader renamable={false} selectable={true} {id} {name} color="blue" {first}>
{#snippet children({ render })}
<ComponentOutputViewer
{render}
componentId={id}
on:select={({ detail }) => {
$connectingInput = connectInput($connectingInput, id, detail)
}}
/>
{/snippet}
</OutputHeader>
@@ -8,11 +8,15 @@
const { app, selectedComponent } = getContext<AppViewerContext>('AppViewerContext')
export let id: string
interface Props {
id: string
}
let { id }: Props = $props()
const dispatch = createEventDispatcher()
$: reservedIds = allItems($app.grid, $app.subgrids).map((item) => item.id)
let reservedIds = $derived(allItems($app.grid, $app.subgrids).map((item) => item.id))
</script>
<Popover
@@ -20,9 +24,9 @@
closeOnOtherPopoverOpen
contentClasses="p-4"
>
<svelte:fragment slot="trigger">
{#snippet trigger()}
<button
on:click={() => {
onclick={() => {
$selectedComponent = [id]
}}
title="Edit ID"
@@ -31,8 +35,8 @@
>
<Pencil size={14} />
</button>
</svelte:fragment>
<svelte:fragment slot="content" let:close>
{/snippet}
{#snippet content({ close })}
<IdEditorInput
initialId={id}
on:close={() => close()}
@@ -42,5 +46,5 @@
}}
{reservedIds}
/>
</svelte:fragment>
{/snippet}
</Popover>
@@ -2,8 +2,12 @@
import type { GridItem } from '$lib/components/apps/types'
import Output from './Output.svelte'
export let gridItem: GridItem
export let render: boolean
interface Props {
gridItem: GridItem
render: boolean
}
let { gridItem, render }: Props = $props()
</script>
<div class="my-1">
@@ -7,18 +7,24 @@
const { connectingInput } = getContext<AppViewerContext>('AppViewerContext')
export let id: string
export let first: boolean = false
export let label: string
export let renderRec: boolean
interface Props {
id: string
first?: boolean
label: string
renderRec: boolean
}
let { id, first = false, label, renderRec }: Props = $props()
</script>
<OutputHeader render={renderRec} let:render renamable={false} {id} name={label} {first}>
<ComponentOutputViewer
{render}
componentId={id}
on:select={({ detail }) => {
$connectingInput = connectInput($connectingInput, id, detail)
}}
/>
<OutputHeader render={renderRec} renamable={false} {id} name={label} {first}>
{#snippet children({ render })}
<ComponentOutputViewer
{render}
componentId={id}
on:select={({ detail }) => {
$connectingInput = connectInput($connectingInput, id, detail)
}}
/>
{/snippet}
</OutputHeader>
@@ -1,4 +1,6 @@
<script lang="ts">
import { stopPropagation } from 'svelte/legacy'
import type { AppViewerContext, ContextPanelContext } from '$lib/components/apps/types'
import { allItems } from '$lib/components/apps/utils'
import { classNames } from '$lib/utils'
@@ -10,28 +12,46 @@
import type { Runnable } from '$lib/components/apps/inputType'
import DocLink from '../../settingsPanel/DocLink.svelte'
export let id: string
export let name: string
export let first: boolean = false
export let nested: boolean = false
export let color: 'blue' | 'indigo' = 'indigo'
export let selectable: boolean = true
export let renamable: boolean = true
export let disabled: boolean = false
export let render: boolean = true
interface Props {
id: string
name: string
first?: boolean
nested?: boolean
color?: 'blue' | 'indigo'
selectable?: boolean
renamable?: boolean
disabled?: boolean
render?: boolean
children?: import('svelte').Snippet<[any]>
}
let {
id,
name,
first = false,
nested = false,
color = 'indigo',
selectable = true,
renamable = true,
disabled = false,
render = true,
children
}: Props = $props()
const { manuallyOpened, search, hasResult } = getContext<ContextPanelContext>('ContextPanel')
const { selectedComponent, app, hoverStore, allIdsInPath, connectingInput, worldStore } =
getContext<AppViewerContext>('AppViewerContext')
$: subids = $search != '' ? allsubIds($app, id) : []
$: inSearch =
let subids = $derived($search != '' ? allsubIds($app, id) : [])
let inSearch = $derived(
$search != '' &&
($hasResult[id] ||
Object.entries($hasResult).some(([key, value]) => value && subids.includes(key)))
$: open =
($hasResult[id] ||
Object.entries($hasResult).some(([key, value]) => value && subids.includes(key)))
)
let open = $derived(
$allIdsInPath.includes(id) || id == $selectedComponent?.[0] || $manuallyOpened[id] || inSearch
)
const hoverColor = {
blue: 'hover:bg-blue-100 hover:text-blue-500 dark:hover:bg-frost-900 dark:hover:text-frost-100',
@@ -199,35 +219,35 @@
}
</script>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<!-- svelte-ignore a11y_click_events_have_key_events -->
<div class={render && ($search == '' || inSearch) ? '' : 'invisible h-0 overflow-hidden'}>
{#if render && ($search == '' || inSearch)}
<!-- svelte-ignore a11y-mouse-events-have-key-events -->
<!-- svelte-ignore a11y-no-static-element-interactions -->
<!-- svelte-ignore a11y_mouse_events_have_key_events -->
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
on:mouseenter|stopPropagation={() => {
onmouseenter={stopPropagation(() => {
if (id !== $hoverStore) {
$hoverStore = id
}
}}
on:mouseleave|stopPropagation={() => {
})}
onmouseleave={stopPropagation(() => {
if ($hoverStore !== undefined) {
$hoverStore = undefined
}
}}
})}
class={classNames(
'flex items-center justify-between p-1 cursor-pointer gap-1 truncate',
hoverColor[color],
$selectedComponent?.includes(id)
? openBackground[color]
: $connectingInput.hoveredComponent === id
? 'bg-[#fab157]'
: 'bg-surface-secondary',
? 'bg-[#fab157]'
: 'bg-surface-secondary',
first ? 'border-t' : '',
nested ? 'border-l' : '',
'transition-all'
)}
on:click={() => {
onclick={() => {
if (!disabled) {
$manuallyOpened[id] = $manuallyOpened[id] != undefined ? !$manuallyOpened[id] : true
}
@@ -238,7 +258,7 @@
<button
disabled={!(selectable && !$selectedComponent?.includes(id)) || $connectingInput?.opened}
title="Select component"
on:click|stopPropagation={() => ($selectedComponent = [id])}
onclick={stopPropagation(() => ($selectedComponent = [id]))}
class="flex items-center ml-0.5 rounded-sm bg-surface-selected hover:text-primary text-tertiary"
>
<div
@@ -271,8 +291,8 @@
docLink={id === 'state'
? 'https://www.windmill.dev/docs/apps/outputs#state'
: id === 'ctx'
? 'https://www.windmill.dev/docs/apps/outputs#app-context'
: ''}
? 'https://www.windmill.dev/docs/apps/outputs#app-context'
: ''}
size="xs2"
/>
{/if}
@@ -299,7 +319,7 @@
: ''}"
>
<div class={classNames(nested ? 'border-l ml-2' : '', open ? 'border-t' : '')}>
<slot render={open && render} />
{@render children?.({ render: open && render })}
</div>
</div>
</div>
@@ -2,8 +2,12 @@
import type { GridItem } from '$lib/components/apps/types'
import Output from './Output.svelte'
export let gridItem: GridItem
export let render: boolean
interface Props {
gridItem: GridItem
render: boolean
}
let { gridItem, render }: Props = $props()
</script>
<div class="my-1">
@@ -9,10 +9,19 @@
import RunButtonInner from '$lib/components/RunButton.svelte'
export let id: string
export let inlineScript: InlineScript | undefined = undefined
export let runLoading = false
export let hideShortcut = false
interface Props {
id: string
inlineScript?: InlineScript | undefined
runLoading?: boolean
hideShortcut?: boolean
}
let {
id,
inlineScript = undefined,
runLoading = $bindable(false),
hideShortcut = false
}: Props = $props()
const { runnableComponents } = getContext<AppViewerContext>('AppViewerContext')
const { runnableJobEditorPanel } = getContext<AppEditorContext>('AppEditorContext')
@@ -5,7 +5,11 @@
import Popover from '$lib/components/meltComponents/Popover.svelte'
import { autoPlacement } from '@floating-ui/core'
export let cache_ttl: number | undefined
interface Props {
cache_ttl: number | undefined
}
let { cache_ttl = $bindable() }: Props = $props()
</script>
<Popover
@@ -19,7 +23,7 @@
closeButton
contentClasses="block text-primary p-4"
>
<svelte:fragment slot="trigger">
{#snippet trigger()}
<Button
nonCaptureEvent={true}
btnClasses={Boolean(cache_ttl)
@@ -32,8 +36,8 @@
startIcon={{ icon: Database }}
title="Cache settings"
/>
</svelte:fragment>
<svelte:fragment slot="content">
{/snippet}
{#snippet content()}
<Toggle
checked={Boolean(cache_ttl)}
on:change={() => {
@@ -56,5 +60,5 @@
<SecondsInput disabled />
{/if}
</div>
</svelte:fragment>
{/snippet}
</Popover>
@@ -20,14 +20,23 @@
import type { Preview } from '$lib/gen'
import type { InlineScript } from '../../types'
export let componentType: string | undefined = undefined
export let showScriptPicker = false
export let rawApps = false
export let unusedInlineScripts: { name: string; inlineScript: InlineScript }[]
interface Props {
componentType?: string | undefined
showScriptPicker?: boolean
rawApps?: boolean
unusedInlineScripts: { name: string; inlineScript: InlineScript }[]
}
let tab = 'workspacescripts'
let filter: string = ''
let picker: Drawer
let {
componentType = undefined,
showScriptPicker = false,
rawApps = false,
unusedInlineScripts
}: Props = $props()
let tab = $state('workspacescripts')
let filter: string = $state('')
let picker: Drawer | undefined = $state(undefined)
const dispatch = createEventDispatcher()
@@ -78,13 +87,15 @@
newInlineScript(script.content, script.language)
}
$: langs = processLangs(undefined, $defaultScripts?.order ?? Object.keys(defaultScriptLanguages))
.map((l) => [defaultScriptLanguages[l], l])
.filter(
(x) =>
x[1] != 'docker' &&
($defaultScripts?.hidden == undefined || !$defaultScripts.hidden.includes(x[1]))
) as [string, Preview['language']][]
let langs = $derived(
processLangs(undefined, $defaultScripts?.order ?? Object.keys(defaultScriptLanguages))
.map((l) => [defaultScriptLanguages[l], l])
.filter(
(x) =>
x[1] != 'docker' &&
($defaultScripts?.hidden == undefined || !$defaultScripts.hidden.includes(x[1]))
) as [string, Preview['language']][]
)
</script>
<Drawer bind:this={picker} size="1000px">
@@ -8,11 +8,21 @@
import type { AppViewerContext } from '../../types'
import InlineScriptRunnableByPath from './InlineScriptRunnableByPath.svelte'
export let componentInput: AppInput | undefined
export let defaultUserInput = false
export let componentType: string
export let id: string
export let transformer: boolean
interface Props {
componentInput: AppInput | undefined
defaultUserInput?: boolean
componentType: string
id: string
transformer: boolean
}
let {
componentInput = $bindable(),
defaultUserInput = false,
componentType,
id,
transformer
}: Props = $props()
const { app } = getContext<AppViewerContext>('AppViewerContext')
@@ -6,9 +6,13 @@
import type { Runnable, StaticAppInput } from '../../inputType'
import { createEventDispatcher, getContext } from 'svelte'
export let runnable: HiddenRunnable
export let id: string
export let transformer: boolean
interface Props {
runnable: HiddenRunnable
id: string
transformer: boolean
}
let { runnable = $bindable(), id, transformer }: Props = $props()
const { runnableComponents, app } = getContext<AppViewerContext>('AppViewerContext')
async function fork(nrunnable: Runnable) {
@@ -1,10 +1,13 @@
<script lang="ts">
import { createBubbler, stopPropagation } from 'svelte/legacy'
const bubble = createBubbler()
import { Button, Drawer, DrawerContent } from '$lib/components/common'
import { base } from '$lib/base'
import FlowModuleScript from '$lib/components/flows/content/FlowModuleScript.svelte'
import FlowPathViewer from '$lib/components/flows/content/FlowPathViewer.svelte'
import { emptySchema } from '$lib/utils'
import { getContext, tick } from 'svelte'
import { getContext, tick, untrack } from 'svelte'
import type {
ConnectedAppInput,
RowAppInput,
@@ -27,21 +30,33 @@
import RunButton from '$lib/components/RunButton.svelte'
import Popover from '$lib/components/meltComponents/Popover.svelte'
export let runnable: RunnableByPath
export let fields:
| Record<string, StaticAppInput | ConnectedAppInput | RowAppInput | UserAppInput>
| undefined
export let id: string
export let rawApps = false
export let isLoading = false
export let onRun = async () => {}
export let onCancel = async () => {}
interface Props {
runnable: RunnableByPath
fields:
| Record<string, StaticAppInput | ConnectedAppInput | RowAppInput | UserAppInput>
| undefined
id: string
rawApps?: boolean
isLoading?: boolean
onRun?: any
onCancel?: any
}
let {
runnable = $bindable(),
fields = $bindable(),
id,
rawApps = false,
isLoading = false,
onRun = async () => {},
onCancel = async () => {}
}: Props = $props()
const viewerContext = getContext<AppViewerContext>('AppViewerContext')
let drawerFlowViewer: Drawer
let flowPath: string = ''
let notFound = false
let drawerFlowViewer: Drawer | undefined = $state(undefined)
let flowPath: string = $state('')
let notFound = $state(false)
const dispatch = createEventDispatcher()
@@ -110,7 +125,12 @@
}
lastRunnable = runnable
}
$: refresh(runnable)
$effect(() => {
runnable.path
untrack(() => {
refresh(runnable)
})
})
</script>
<Drawer bind:this={drawerFlowViewer} size="1200px">
@@ -160,7 +180,7 @@
startIcon={{ icon: Eye }}
on:click={() => {
flowPath = runnable.path
drawerFlowViewer.openDrawer()
drawerFlowViewer?.openDrawer()
}}
>
Expand
@@ -215,7 +235,7 @@
closeButton
contentClasses="block text-primary text-xs p-4 w-[20vh]"
>
<svelte:fragment slot="trigger">
{#snippet trigger()}
<Button
nonCaptureEvent={true}
btnClasses={'bg-surface text-primay hover:bg-hover'}
@@ -223,16 +243,16 @@
variant="border"
size="xs">Cache</Button
>
</svelte:fragment>
<svelte:fragment slot="content">
{/snippet}
{#snippet content()}
Since this is a reference to a workspace {runnable.runType}, set the cache in the {runnable.runType}
settings directly by editing it. The cache will be shared by any app or flow that uses this
{runnable.runType}.
</svelte:fragment>
{/snippet}
</Popover>
<input
on:keydown|stopPropagation
onkeydown={stopPropagation(bubble('keydown'))}
bind:value={runnable.name}
placeholder="Background runnable name"
class="!text-xs !rounded-xs"
@@ -40,24 +40,33 @@
}
}
$: gridItem =
let gridItem = $derived(
$selectedComponentInEditor && !$selectedComponentInEditor.startsWith(BG_PREFIX)
? findGridItem($app, $selectedComponentInEditor?.split('_')?.[0])
: undefined
$: hiddenInlineScript = $app?.hiddenInlineScripts?.findIndex((k_, index) => {
const [prefix, id] = $selectedComponentInEditor?.split('_') || []
if (prefix !== 'bg') return false
return Number(id) === index
})
$: unusedInlineScript = $app?.unusedInlineScripts?.findIndex(
(k_, index) => `unused-${index}` === $selectedComponentInEditor
)
export let width: number | undefined = undefined
let hiddenInlineScript = $derived(
$app?.hiddenInlineScripts?.findIndex((k_, index) => {
const [prefix, id] = $selectedComponentInEditor?.split('_') || []
if (prefix !== 'bg') return false
return Number(id) === index
})
)
let unusedInlineScript = $derived(
$app?.unusedInlineScripts?.findIndex(
(k_, index) => `unused-${index}` === $selectedComponentInEditor
)
)
interface Props {
width?: number | undefined
}
let { width = undefined }: Props = $props()
</script>
<Splitpanes
@@ -1,7 +1,7 @@
<script lang="ts">
import { Badge, Button } from '$lib/components/common'
import { Plus } from 'lucide-svelte'
import { createEventDispatcher, getContext } from 'svelte'
import { createEventDispatcher, getContext, untrack } from 'svelte'
import Tooltip from '../../../Tooltip.svelte'
import type { AppEditorContext, AppViewerContext } from '../../types'
import { BG_PREFIX, getAllScriptNames } from '../../utils'
@@ -26,11 +26,6 @@
}
}
$: runnables = getAppScripts($app.grid, $app.subgrids)
// When selected component changes, update selectedScriptComponentId
$: selectedComponent && handleSelectedComponent($selectedComponent)
function handleSelectedComponent(selectedComponent: string[] | undefined) {
if (
selectedComponent != $selectedComponentInEditor &&
@@ -79,12 +74,17 @@
selectScript(`${BG_PREFIX}${$app.hiddenInlineScripts.length - 1}`)
}
let appTutorials: AppTutorials | undefined = undefined
let appTutorials: AppTutorials | undefined = $state(undefined)
const dispatch = createEventDispatcher()
let runnables = $derived(getAppScripts($app.grid, $app.subgrids))
// When selected component changes, update selectedScriptComponentId
$effect(() => {
$selectedComponent && untrack(() => handleSelectedComponent($selectedComponent))
})
</script>
<PanelSection title="Runnables" id="app-editor-runnable-panel">
<svelte:fragment slot="action">
{#snippet action()}
<div class="flex flex-row gap-1">
<HideButton
direction="bottom"
@@ -96,7 +96,7 @@
docLink="https://www.windmill.dev/docs/apps/app-runnable-panel#creating-a-runnable"
/>
</div>
</svelte:fragment>
{/snippet}
<div class="w-full flex flex-col gap-6 py-1">
<div>
<div class="flex flex-col gap-2 w-full">
@@ -109,7 +109,7 @@
{$selectedComponentInEditor === id
? 'border-blue-500 bg-blue-100 dark:bg-frost-900/50'
: 'hover:bg-blue-50 dark:hover:bg-frost-900/50'}"
on:click={() => selectScript(id)}
onclick={() => selectScript(id)}
>
<span class="text-2xs truncate">{name}</span>
<div>
@@ -124,7 +124,7 @@
{$selectedComponentInEditor === id + '_transformer'
? 'border-blue-500 bg-blue-100 dark:bg-frost-900/50'
: 'hover:bg-blue-50 dark:hover:bg-frost-900/50'}"
on:click={() => selectScript(id + '_transformer')}
onclick={() => selectScript(id + '_transformer')}
>
<span class="text-2xs truncate">Transformer</span>
</button>
@@ -140,7 +140,7 @@
{$selectedComponentInEditor === id
? 'border-blue-500 bg-blue-100 dark:bg-frost-900/50'
: 'hover:bg-blue-50'}"
on:click={() => selectScript(id)}
onclick={() => selectScript(id)}
>
<span class="text-2xs truncate">{name}</span>
<Badge color="indigo">{id}</Badge>
@@ -153,7 +153,7 @@
{$selectedComponentInEditor === id + '_transformer'
? 'border-blue-500 bg-blue-100 dark:bg-frost-900/50'
: 'hover:bg-blue-50 dark:hover:bg-frost-900/50'}"
on:click={() => selectScript(id + '_transformer')}
onclick={() => selectScript(id + '_transformer')}
>
<span class="text-2xs truncate">Transformer</span>
</button>
@@ -171,7 +171,7 @@
{$selectedComponentInEditor === id
? 'border-blue-500 bg-blue-100 dark:bg-frost-900/50'
: 'hover:bg-blue-50 dark:hover:bg-frost-900/50'}"
on:click={() => selectScript(id)}
onclick={() => selectScript(id)}
>
<span class="text-2xs truncate">{unusedInlineScript.name}</span>
<Badge color="red">Detached</Badge>
@@ -220,7 +220,7 @@
{$selectedComponentInEditor === id
? 'border-blue-500 bg-blue-100 dark:bg-frost-900/50'
: 'hover:bg-blue-50 dark:hover:bg-frost-900/50'}"
on:click={() => selectScript(id)}
onclick={() => selectScript(id)}
>
<span class="text-2xs truncate">{name}</span>
<Badge color="indigo">{id}</Badge>
@@ -233,7 +233,7 @@
{$selectedComponentInEditor === id + '_transformer'
? 'border-blue-500 bg-blue-100 dark:bg-frost-900/50'
: 'hover:bg-blue-50 dark:hover:bg-frost-900/50'}"
on:click={() => selectScript(id + '_transformer')}
onclick={() => selectScript(id + '_transformer')}
>
<span class="text-2xs truncate">Transformer</span>
</button>
@@ -5,7 +5,11 @@
const { selectedComponentInEditor } = getContext<AppEditorContext>('AppEditorContext')
export let gridItem: GridItem
interface Props {
gridItem: GridItem
}
let { gridItem = $bindable() }: Props = $props()
</script>
{#if gridItem?.data?.id === $selectedComponentInEditor || gridItem?.data?.id + '_transformer' === $selectedComponentInEditor}
@@ -9,14 +9,23 @@
import { Button } from '$lib/components/common'
import { CornerDownLeft, Loader2 } from 'lucide-svelte'
export let id: string
export let inlineScript: InlineScript | undefined = undefined
export let runLoading = false
export let hideShortcut = false
interface Props {
id: string
inlineScript?: InlineScript | undefined
runLoading?: boolean
hideShortcut?: boolean
}
let {
id,
inlineScript = undefined,
runLoading = $bindable(false),
hideShortcut = false
}: Props = $props()
const { runnableComponents } = getContext<AppViewerContext>('AppViewerContext')
const { runnableJobEditorPanel } = getContext<AppEditorContext>('AppEditorContext')
let cancelable: CancelablePromise<void>[] | undefined = undefined
let cancelable: CancelablePromise<void>[] | undefined = $state(undefined)
</script>
{#if $runnableComponents[id] != undefined}
@@ -3,9 +3,13 @@
import InputsSpecEditor from './InputsSpecEditor.svelte'
import PanelSection from './common/PanelSection.svelte'
export let datasets: RichConfiguration | undefined = undefined
export let xData: RichConfiguration | undefined = undefined
export let id: string
interface Props {
datasets?: RichConfiguration | undefined
xData?: RichConfiguration | undefined
id: string
}
let { datasets = $bindable(undefined), xData = $bindable(undefined), id }: Props = $props()
</script>
<PanelSection
@@ -180,6 +180,7 @@
})
}
}
componentInput.value = $state.snapshot(componentInput.value)
componentInput = componentInput
items = getItems(componentInput)
}
@@ -16,14 +16,18 @@
import CloseButton from '$lib/components/common/CloseButton.svelte'
import HighlightTheme from '$lib/components/HighlightTheme.svelte'
import Portal from '$lib/components/Portal.svelte'
export let component: AppComponent | undefined
interface Props {
component: AppComponent | undefined
}
let { component = $bindable() }: Props = $props()
const { app } = getContext<AppViewerContext>('AppViewerContext')
function fadeFast(node: HTMLElement) {
return fade(node, { duration: 100 })
}
let migrationModalOpen: boolean = false
let migrationModalOpen: boolean = $state(false)
export function open() {
migrationModalOpen = true
@@ -38,9 +42,6 @@
return code
}
$: generatedCode = generateCodeFromMigrations(migrations)
$: migrations = new Map<string, string[]>()
function getSelector(key: string) {
return customisationByComponent
.find((c) => c.components.includes(component?.type ?? ''))
@@ -126,6 +127,8 @@
}
let type: string | undefined = component?.type
let migrations = $derived(new Map<string, string[]>())
let generatedCode = $derived(generateCodeFromMigrations(migrations))
</script>
<HighlightTheme />
@@ -3,9 +3,13 @@
import InputsSpecEditor from './InputsSpecEditor.svelte'
import PanelSection from './common/PanelSection.svelte'
export let datasets: RichConfiguration | undefined = undefined
export let xData: RichConfiguration | undefined = undefined
export let id: string
interface Props {
datasets?: RichConfiguration | undefined
xData?: RichConfiguration | undefined
id: string
}
let { datasets = $bindable(undefined), xData = $bindable(undefined), id }: Props = $props()
</script>
<PanelSection

Some files were not shown because too many files have changed in this diff Show More