add more apps components

This commit is contained in:
Ruben Fiszel
2023-01-04 15:54:23 +01:00
parent e1b28caf36
commit cffbf8fa8e
24 changed files with 430 additions and 55 deletions
@@ -16,7 +16,6 @@
} from 'chart.js'
import type { CompletedJob } from '$lib/gen'
import { createEventDispatcher } from 'svelte'
import { json } from 'svelte-highlight/languages'
export let jobs: CompletedJob[] | undefined = []
@@ -359,6 +359,17 @@
languages.setLanguageConfiguration('template', conf)
meditor.defineTheme('myTheme', {
base: 'vs',
inherit: true,
rules: [],
colors: {
'editorLineNumber.foreground': '#999',
'editorGutter.background': '#F9FAFB'
}
})
meditor.setTheme('myTheme')
let divEl: HTMLDivElement | null = null
let editor: meditor.IStandaloneCodeEditor
let model: meditor.ITextModel
@@ -428,7 +439,7 @@
lineNumbers: 'off',
fontSize,
suggestOnTriggerCharacters: true,
lineDecorationsWidth: 24
lineDecorationsWidth: 14
})
const stdLib = { content: libStdContent, filePath: 'es5.d.ts' }
@@ -1,5 +1,5 @@
<script lang="ts">
import { Bar } from 'svelte-chartjs'
import { Bar, Line } from 'svelte-chartjs'
import {
Chart as ChartJS,
@@ -13,7 +13,6 @@
BarElement
} from 'chart.js'
import type { ChartData } from 'chart.js'
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
import type { AppInput } from '../../inputType'
import InputValue from '../helpers/InputValue.svelte'
@@ -35,9 +34,9 @@
BarElement
)
let result: ChartData<'bar', number[], unknown> | undefined = undefined
let labels: string[] = []
let result: { data: number[]; labels?: string[] } | undefined = undefined
let theme: string = 'theme1'
let lineChart = false
$: backgroundColor = {
theme1: ['#FF6384', '#4BC0C0', '#FFCE56', '#E7E9ED', '#36A2EB'],
@@ -59,10 +58,10 @@
}
$: data = {
labels,
labels: result?.labels ?? [],
datasets: [
{
data: result,
data: result?.data ?? [],
backgroundColor
}
]
@@ -70,10 +69,14 @@
</script>
<InputValue {id} input={configuration.theme} bind:value={theme} />
<InputValue {id} input={configuration.labels} bind:value={labels} />
<InputValue {id} input={configuration.line} bind:value={lineChart} />
<RunnableWrapper autoRefresh bind:componentInput {id} bind:result>
{#if result}
<Bar {data} {options} />
{#if lineChart}
<Line {data} {options} />
{:else}
<Bar {data} {options} />
{/if}
{/if}
</RunnableWrapper>
@@ -0,0 +1,28 @@
<script lang="ts">
import type { AppInput } from '../../inputType'
import AlignWrapper from '../helpers/AlignWrapper.svelte'
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
export let id: string
export let componentInput: AppInput | undefined
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = 'left'
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
export let configuration: Record<string, AppInput>
export const staticOutputs: string[] = ['result', 'loading']
let result: string | undefined = undefined
</script>
<RunnableWrapper bind:componentInput {id} bind:result>
<AlignWrapper {horizontalAlignment} {verticalAlignment}>
<div
on:pointerdown={(e) => {
e?.preventDefault()
}}
class="h-full relative w-full overflow-hidden"
>
{@html result ?? 'No html'}
</div>
</AlignWrapper>
</RunnableWrapper>
@@ -1,5 +1,5 @@
<script lang="ts">
import { Pie } from 'svelte-chartjs'
import { Doughnut, Pie } from 'svelte-chartjs'
import {
Chart as ChartJS,
@@ -12,7 +12,6 @@
CategoryScale,
ArcElement
} from 'chart.js'
import type { ChartData } from 'chart.js'
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
import type { AppInput } from '../../inputType'
import InputValue from '../helpers/InputValue.svelte'
@@ -34,9 +33,9 @@
ArcElement
)
let result: ChartData<'bar', number[], unknown> | undefined = undefined
let labels: string[] = []
let result: { data: number[]; labels?: string[] } | undefined = undefined
let theme: string = 'theme1'
let doughnut = false
$: backgroundColor = {
theme1: ['#FF6384', '#4BC0C0', '#FFCE56', '#E7E9ED', '#36A2EB'],
@@ -53,10 +52,10 @@
}
$: data = {
labels,
labels: result?.labels ?? [],
datasets: [
{
data: result,
data: result?.data ?? [],
backgroundColor: backgroundColor
}
]
@@ -64,10 +63,14 @@
</script>
<InputValue {id} input={configuration.theme} bind:value={theme} />
<InputValue {id} input={configuration.labels} bind:value={labels} />
<InputValue {id} input={configuration.doughnutStyle} bind:value={doughnut} />
<RunnableWrapper autoRefresh bind:componentInput {id} bind:result>
{#if result}
<Pie {data} {options} />
{#if doughnut}
<Doughnut {data} {options} />
{:else}
<Pie {data} {options} />
{/if}
{/if}
</RunnableWrapper>
@@ -0,0 +1,72 @@
<script lang="ts">
import zoomPlugin from 'chartjs-plugin-zoom'
import {
Chart as ChartJS,
Title,
Tooltip,
Legend,
LineElement,
LinearScale,
PointElement,
CategoryScale,
BarElement
} from 'chart.js'
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
import type { AppInput } from '../../inputType'
import Scatter from 'svelte-chartjs/Scatter.svelte'
export let id: string
export let componentInput: AppInput | undefined
export let configuration: Record<string, AppInput>
export const staticOutputs: string[] = ['loading', 'result']
ChartJS.register(
Title,
Tooltip,
Legend,
LineElement,
LinearScale,
PointElement,
CategoryScale,
BarElement,
zoomPlugin
)
const zoomOptions = {
pan: {
enabled: true
},
zoom: {
drag: {
enabled: false
},
wheel: {
enabled: true
}
}
}
let result: { data: { x: any[]; y: string[] } } | undefined = undefined
const options = {
responsive: true,
animation: false,
maintainAspectRatio: false,
plugins: {
zoom: zoomOptions
}
}
$: data = {
datasets: result ?? []
}
</script>
<RunnableWrapper autoRefresh bind:componentInput {id} bind:result>
{#if result}
<Scatter {data} {options} />
{/if}
</RunnableWrapper>
@@ -0,0 +1,90 @@
<script lang="ts">
import zoomPlugin from 'chartjs-plugin-zoom'
import 'chartjs-adapter-date-fns'
import {
Chart as ChartJS,
Title,
Tooltip,
Legend,
LineElement,
LinearScale,
PointElement,
CategoryScale,
BarElement,
TimeScale,
LogarithmicScale
} from 'chart.js'
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
import type { AppInput } from '../../inputType'
import Scatter from 'svelte-chartjs/Scatter.svelte'
import InputValue from '../helpers/InputValue.svelte'
export let id: string
export let componentInput: AppInput | undefined
export let configuration: Record<string, AppInput>
export const staticOutputs: string[] = ['loading', 'result']
let logarithmicScale = false
ChartJS.register(
Title,
Tooltip,
Legend,
LineElement,
LinearScale,
PointElement,
CategoryScale,
BarElement,
zoomPlugin,
TimeScale,
LogarithmicScale
)
const zoomOptions = {
pan: {
enabled: true
},
zoom: {
drag: {
enabled: false
},
wheel: {
enabled: true
}
}
}
let result: { data: { x: any[]; y: string[] } } | undefined = undefined
$: options = {
responsive: true,
animation: false,
maintainAspectRatio: false,
plugins: {
zoom: zoomOptions
},
scales: {
x: {
type: 'time'
},
y: {
type: logarithmicScale ? 'logarithmic' : 'linear'
}
}
}
$: data = {
datasets: result ?? []
}
</script>
<InputValue {id} input={configuration.logarithmicScale} bind:value={logarithmicScale} />
<RunnableWrapper autoRefresh bind:componentInput {id} bind:result>
{#if result}
<Scatter {data} {options} />
{/if}
</RunnableWrapper>
@@ -156,7 +156,7 @@
</SplitPanesWrapper>
</Pane>
<Pane size={21} minSize={5} maxSize={33}>
<div class="relative">
<div class="relative flex flex-col h-full">
<Tabs bind:selected={selectedTab}>
<Tab value="insert" size="xs">
<div class="m-1 flex flex-row gap-2">
@@ -171,7 +171,7 @@
</div>
</Tab>
<svelte:fragment slot="content">
<TabContent value="settings">
<TabContent class="overflow-auto" value="settings">
{#if $selectedComponent !== undefined}
<SettingsPanel />
{:else}
@@ -15,6 +15,9 @@
import DateInputComponent from '../components/dateInputs/AppDateInput.svelte'
import ComponentHeader from './ComponentHeader.svelte'
import AppForm from '../components/form/AppForm.svelte'
import AppScatterChart from '../components/dataDisplay/AppScatterChart.svelte'
import AppTimeseries from '../components/dataDisplay/AppTimeseries.svelte'
import AppHtml from '../components/dataDisplay/AppHtml.svelte'
export let component: AppComponent
export let selected: boolean
@@ -35,12 +38,18 @@
{/if}
<div
on:pointerdown={(e) => {
if ($mode === 'preview') {
e?.stopPropagation()
}
}}
class={classNames(
'border cursor-pointer h-full bg-white',
'border h-full bg-white',
selected && $mode !== 'preview' ? 'border-blue-500' : 'border-white',
!selected && $mode !== 'preview' && !component.card ? 'border-gray-100' : '',
$mode !== 'preview' && !$connectingInput.opened ? 'hover:border-blue-500' : '',
component.softWrap ? '' : 'overflow-auto',
$mode != 'preview' ? 'cursor-pointer' : '',
'relative z-auto'
)}
>
@@ -56,6 +65,24 @@
bind:componentInput={component.componentInput}
bind:staticOutputs={$staticOutputs[component.id]}
/>
{:else if component.type === 'timeseriescomponent'}
<AppTimeseries
{...component}
bind:componentInput={component.componentInput}
bind:staticOutputs={$staticOutputs[component.id]}
/>
{:else if component.type === 'htmlcomponent'}
<AppHtml
{...component}
bind:componentInput={component.componentInput}
bind:staticOutputs={$staticOutputs[component.id]}
/>
{:else if component.type === 'scatterchartcomponent'}
<AppScatterChart
{...component}
bind:componentInput={component.componentInput}
bind:staticOutputs={$staticOutputs[component.id]}
/>
{:else if component.type === 'piechartcomponent'}
<PieChartComponent
{...component}
@@ -21,6 +21,7 @@
const dimensions: Record<`${number}:${number}-${number}:${number}`, AppComponent['type'][]> = {
'4:1-4:1': ['textcomponent'],
'2:1-2:1': ['buttoncomponent'],
'2:2-2:2': ['htmlcomponent'],
'4:2-4:2': [
'checkboxcomponent',
'textinputcomponent',
@@ -30,7 +31,13 @@
'dateinputcomponent'
],
'3:5-6:5': ['formcomponent'],
'4:12-4:12': ['barchartcomponent', 'piechartcomponent', 'displaycomponent'],
'4:12-4:12': [
'timeseriescomponent',
'barchartcomponent',
'piechartcomponent',
'displaycomponent',
'scatterchartcomponent'
],
'3:10-6:12': ['tablecomponent']
}
// Finds the key that is associated with the component type and extracts the dimensions from it
@@ -179,6 +179,24 @@ const buttons: ComponentSet = {
const display: ComponentSet = {
title: 'Display',
components: [
{
softWrap: false,
id: 'htmlcomponent',
type: 'htmlcomponent',
componentInput: {
type: 'static',
fieldType: 'template',
value: `<img
src="https://images.unsplash.com/photo-1554629947-334ff61d85dc?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&amp;ixlib=rb-1.2.1&amp;auto=format&amp;fit=crop&amp;w=1024&amp;h=1280&amp;q=80"
>
<div class="absolute top-4 left-2 text-white">
Hello \${ctx.username}
</div>
`,
},
configuration: {},
card: false
},
{
softWrap: true,
horizontalAlignment: 'left',
@@ -187,7 +205,7 @@ const display: ComponentSet = {
type: 'textcomponent',
componentInput: {
type: 'static',
fieldType: 'textarea',
fieldType: 'template',
value: 'Hello ${ctx.username}',
},
configuration: {
@@ -260,19 +278,17 @@ const display: ComponentSet = {
optionValuesKey: 'chartThemeOptions',
value: 'theme1'
},
labels: {
doughnutStyle: {
type: 'static',
value: ['First', 'Second', 'Third'],
fieldType: 'array',
subFieldType: 'text',
onlyStatic: true,
fieldType: 'boolean',
value: false
}
},
componentInput: {
type: 'static',
fieldType: 'array',
subFieldType: 'number',
value: [25, 50, 25],
fieldType: 'object',
value: { data: [25, 50, 25], labels: ['Pie', 'Charts', '<3'] }
},
card: true
},
@@ -287,20 +303,93 @@ const display: ComponentSet = {
optionValuesKey: 'chartThemeOptions',
value: 'theme1'
},
labels: {
line: {
type: 'static',
onlyStatic: true,
fieldType: 'boolean',
value: false
}
fieldType: 'array',
subFieldType: 'text',
value: ['Lorem ipsum', 'Lorem ipsum', 'Lorem ipsum']
},
componentInput: {
type: 'static',
fieldType: 'object',
value: { data: [25, 50, 25], labels: ['Bar', 'Charts', '<3'] }
},
card: true
},
{
id: 'scatterchartcomponent',
type: 'scatterchartcomponent',
configuration: {},
componentInput: {
type: 'static',
fieldType: 'array',
subFieldType: 'object',
value: [{
label: 'foo',
data: [
{ x: 25, y: 50 },
{ x: 23, y: 23 },
{ x: 12, y: 37 }
],
backgroundColor: 'rgb(255, 12, 137)'
},
{
label: 'foobar',
data: [
{ x: 32, y: 32 },
{ x: 25, y: 42 },
{ x: 3, y: 27 }
],
backgroundColor: 'orange'
}]
},
card: true
},
{
id: 'timeseriescomponent',
type: 'timeseriescomponent',
configuration: {
logarithmicScale: {
type: 'static',
onlyStatic: true,
fieldType: 'boolean',
value: false
}
},
componentInput: {
type: 'static',
fieldType: 'array',
subFieldType: 'number',
value: [25, 50, 25],
subFieldType: 'object',
value: [{
label: 'foo',
data: [{
x: '2021-11-06 23:39:30',
y: 50
}, {
x: '2021-11-07 01:00:28',
y: 60
}, {
x: '2021-11-07 09:00:28',
y: 20
}],
backgroundColor: 'rgb(255, 12, 137)'
},
{
label: 'foobar',
data: [{
x: '2021-11-06 23:39:30',
y: 20
}, {
x: '2021-11-07 01:00:28',
y: 13
}, {
x: '2021-11-07 09:00:28',
y: 45
}],
backgroundColor: 'orange'
}]
},
card: true
},
@@ -315,7 +404,8 @@ const display: ComponentSet = {
},
configuration: {},
card: false
}
},
]
}
@@ -26,7 +26,7 @@
}
function getComponentNameById(componentId: string) {
const component = $app.grid.find((c) => c.data.id === componentId)
const component = $app.grid.find((c) => c?.data?.id === componentId)
if (component?.data.type) {
return displayData[component?.data.type].name
@@ -7,11 +7,13 @@
import { emptySchema } from '$lib/utils'
import { createEventDispatcher, getContext } from 'svelte'
import { fly } from 'svelte/transition'
import { defaultCode } from '../../editorUtils'
import type { AppEditorContext } from '../../types'
export let name: string
export let id: string
const { appPath } = getContext<AppEditorContext>('AppEditorContext')
const { appPath, app } = getContext<AppEditorContext>('AppEditorContext')
const dispatch = createEventDispatcher()
async function inferInlineScriptSchema(
@@ -33,8 +35,12 @@
path: string,
subkind: 'pgsql' | 'mysql' | undefined = undefined
) {
const componentType = $app.grid.find((c) => c.data.id === id)?.data?.type
const fullPath = `${appPath}/inline-script/${path}`
const content = initialCode(language, Script.kind.SCRIPT, subkind ?? 'flow')
const content =
defaultCode(componentType, language) ??
initialCode(language, Script.kind.SCRIPT, subkind ?? 'flow')
let schema: Schema = emptySchema()
@@ -61,6 +61,7 @@
/>
{:else}
<EmptyInlineScript
{id}
name={componentInput.runnable.name}
on:new={(e) => {
if (
@@ -44,16 +44,17 @@
<div class="flex gap-2 flex-col mt-2">
{#if componentInput.value}
{#each componentInput.value as value, index (index)}
<div class="flex flex-row gap-2 items-center">
<div class="flex flex-row gap-2 items-center relative">
<SubTypeEditor bind:componentInput bind:value />
<div>
<div class="absolute top-4 right-4">
<Button
size="xs"
color="light"
variant="border"
on:click={() => deleteElementByType(index)}
iconOnly
btnClasses="!text-red-500"
startIcon={{ icon: faTrashAlt }}
/>
</div>
@@ -10,7 +10,7 @@
const { onchange } = getContext<AppEditorContext>('AppEditorContext')
$: if (componentInput.fieldType == 'textarea' && componentInput.type == 'static') {
$: if (componentInput.fieldType == 'template' && componentInput.type == 'static') {
//@ts-ignore
componentInput.type = 'template'
componentInput['eval'] = componentInput.value
@@ -24,7 +24,7 @@
{#if componentInput.fieldType !== 'any'}
<div class="w-full overflow-x-auto" bind:clientWidth>
<ToggleButtonGroup on:selected={() => onchange?.()} bind:selected={componentInput.type}>
{#if componentInput.fieldType === 'textarea'}
{#if componentInput.fieldType === 'template'}
<ToggleButton position="left" value="template" size="xs" disable={disableStatic}>
{brackets}&nbsp;<span class="hidden lg:block">Template</span>
</ToggleButton>
@@ -123,7 +123,7 @@ declare const ${k} = ${JSON.stringify(v)};
<StaticInputEditor bind:componentInput={component.componentInput} />
{:else if component.componentInput.type === 'template' && component.componentInput !== undefined}
<div class="py-1 min-h-[28px] rounded border border-1 border-gray-500">
<TemplateEditor bind:code={component.componentInput.eval} {extraLib} />
<TemplateEditor fontSize={12} bind:code={component.componentInput.eval} {extraLib} />
</div>
{:else if component.componentInput.type === 'connected' && component.componentInput !== undefined}
<ConnectedInputEditor bind:componentInput={component.componentInput} />
@@ -15,9 +15,11 @@
$: code && parseJson()
</script>
<div class="border rounded-sm w-full">
<SimpleEditor lang="json" bind:code class="few-lines-editor" />
<div class="flex flex-col w-full">
<div class="border border-gray-300 w-full">
<SimpleEditor autoHeight lang="json" bind:code />
</div>
{#if error != ''}
<span class="text-red-600 text-xs">{error}</span>
{/if}
</div>
{#if error != ''}
<span class="text-red-600 text-xs">{error}</span>
{/if}
@@ -103,6 +103,16 @@
acc.push(componentInput.runnable.name)
}
if (componentInput.type === 'tablecomponent') {
componentInput.actionButtons.forEach((actionButton) => {
if (actionButton.componentInput?.type === 'runnable') {
if (actionButton.componentInput.runnable?.type === 'runnableByName') {
acc.push(actionButton.componentInput.runnable.name)
}
}
})
}
return acc
}, [] as string[])
@@ -0,0 +1,3 @@
export function defaultCode(component: string, language: string): string | undefined {
return undefined
}
@@ -4,6 +4,7 @@ import type { InlineScript } from './types'
export type InputType =
| 'text'
| 'textarea'
| 'template'
| 'number'
| 'boolean'
| 'select'
@@ -91,6 +92,7 @@ type InputConfiguration<T extends InputType, U, V extends InputType> = {
export type AppInput =
| AppInputSpec<'text', string>
| AppInputSpec<'textarea', string>
| AppInputSpec<'template', string>
| AppInputSpec<'number', number>
| AppInputSpec<'boolean', boolean>
| AppInputSpec<'date', string>
@@ -20,6 +20,8 @@ export type TextInputComponent = BaseComponent<'textinputcomponent'>
export type PasswordInputComponent = BaseComponent<'passwordinputcomponent'>
export type DateInputComponent = BaseComponent<'dateinputcomponent'>
export type NumberInputComponent = BaseComponent<'numberinputcomponent'>
export type HtmlComponent = BaseComponent<'htmlcomponent'>
export type TimeseriesComponent = BaseComponent<'timeseriescomponent'>
export type ButtonComponent = BaseComponent<'buttoncomponent'> & {
recomputeIds: string[] | undefined
}
@@ -31,6 +33,7 @@ export type FormComponent = BaseComponent<'formcomponent'> & {
export type RunFormComponent = BaseComponent<'runformcomponent'>
export type BarChartComponent = BaseComponent<'barchartcomponent'>
export type PieChartComponent = BaseComponent<'piechartcomponent'>
export type ScatterChartComponent = BaseComponent<'scatterchartcomponent'>
export type TableComponent = BaseComponent<'tablecomponent'> & {
actionButtons: (BaseAppComponent & ButtonComponent)[]
}
@@ -72,11 +75,14 @@ export type AppComponent = BaseAppComponent &
| DateInputComponent
| NumberInputComponent
| BarChartComponent
| TimeseriesComponent
| HtmlComponent
| TableComponent
| TextComponent
| TableComponent
| ButtonComponent
| PieChartComponent
| ScatterChartComponent
| ImageComponent
| InputComponent
| SelectComponent
+16 -2
View File
@@ -17,7 +17,9 @@ import {
Type,
Lock,
Calendar,
ToggleLeft
ToggleLeft,
GripHorizontal,
Code2
} from 'lucide-svelte'
import type { AppInput, InputType, ResultAppInput, StaticAppInput } from './inputType'
import type { AppComponent } from './types'
@@ -93,9 +95,21 @@ export const displayData: Record<AppComponent['type'], { name: string; icon: any
icon: PieChart
},
barchartcomponent: {
name: 'Bar Chart',
name: 'Bar/Line Chart',
icon: BarChart4
},
htmlcomponent: {
name: 'Html',
icon: Code2
},
timeseriescomponent: {
name: 'Timeseries',
icon: GripHorizontal
},
scatterchartcomponent: {
name: 'Scatter Chart',
icon: GripHorizontal
},
tablecomponent: {
name: 'Table',
icon: Table2
@@ -36,7 +36,7 @@
<div
class={classNames(
'border-b border-gray-200 flex flex-row whitespace-nowrap overflow-y-auto scrollbar-hidden',
'border-b border-gray-200 flex flex-row whitespace-nowrap scrollbar-hidden',
$$props.class
)}
>