mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-24 16:01:42 +00:00
apps rich configuration v0 + resource picker clear fix for apps
This commit is contained in:
@@ -75,6 +75,8 @@
|
||||
}}
|
||||
/>
|
||||
|
||||
{initialValue}
|
||||
{value}
|
||||
<div class="flex flex-row gap-x-1 w-full">
|
||||
<Select
|
||||
value={valueSelect}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import type RunnableComponent from '../helpers/RunnableComponent.svelte'
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let configuration: RichConfigurations
|
||||
export let recomputeIds: string[] | undefined = undefined
|
||||
export let extraQueryParams: Record<string, any> = {}
|
||||
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = undefined
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import { Icon } from 'svelte-awesome'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let configuration: RichConfigurations
|
||||
export let recomputeIds: string[] | undefined = undefined
|
||||
export let extraQueryParams: Record<string, any> = {}
|
||||
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = undefined
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import { Icon } from 'svelte-awesome'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import type RunnableComponent from '../helpers/RunnableComponent.svelte'
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let configuration: RichConfigurations
|
||||
export let recomputeIds: string[] | undefined = undefined
|
||||
export let extraQueryParams: Record<string, any> = {}
|
||||
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = undefined
|
||||
|
||||
@@ -13,16 +13,21 @@
|
||||
} from 'chart.js'
|
||||
import type { ChartOptions } from 'chart.js'
|
||||
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { AppInput, RichAppInput } from '../../inputType'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import type {
|
||||
AppViewerContext,
|
||||
ComponentCustomCSS,
|
||||
RichConfiguration,
|
||||
RichConfigurations
|
||||
} from '../../types'
|
||||
import { initOutput } from '../../editor/appUtils'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let configuration: RichConfigurations
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import type {
|
||||
AppViewerContext,
|
||||
ComponentCustomCSS,
|
||||
RichConfiguration,
|
||||
RichConfigurations
|
||||
} from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import { AlignWrapper, InputValue } from '../helpers'
|
||||
import { loadIcon } from '../icon'
|
||||
@@ -9,7 +13,7 @@
|
||||
export let id: string
|
||||
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = 'left'
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let configuration: RichConfigurations
|
||||
export let customCss: ComponentCustomCSS<'container' | 'icon'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
|
||||
@@ -2,15 +2,14 @@
|
||||
import { getContext } from 'svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import type { staticValues } from '../../editor/componentsPanel/componentStaticValues'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
|
||||
type FitOption = (typeof staticValues)['objectFitOptions'][number]
|
||||
|
||||
export let id: string
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let configuration: RichConfigurations
|
||||
export let customCss: ComponentCustomCSS<'image'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import type {
|
||||
AppViewerContext,
|
||||
ComponentCustomCSS,
|
||||
RichConfiguration,
|
||||
RichConfigurations
|
||||
} from '../../types'
|
||||
import { InputValue } from '../helpers'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { Map, View, Feature } from 'ol'
|
||||
@@ -28,7 +32,7 @@
|
||||
} as const
|
||||
|
||||
export let id: string
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let configuration: RichConfigurations
|
||||
export let customCss: ComponentCustomCSS<'map'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { getDocument, type PDFDocumentProxy, type PDFPageProxy } from 'pdfjs-dist'
|
||||
import 'pdfjs-dist/build/pdf.worker.entry'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS, RichConfiguration, RichConfigurations } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import { throttle } from '../../../../utils'
|
||||
@@ -14,7 +13,7 @@
|
||||
import { findGridItem, initOutput } from '../../editor/appUtils'
|
||||
|
||||
export let id: string
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let configuration: RichConfigurations
|
||||
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
|
||||
@@ -14,14 +14,19 @@
|
||||
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import type {
|
||||
AppViewerContext,
|
||||
ComponentCustomCSS,
|
||||
RichConfiguration,
|
||||
RichConfigurations
|
||||
} from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import { getContext } from 'svelte'
|
||||
import { initOutput } from '../../editor/appUtils'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let configuration: RichConfigurations
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
@@ -19,12 +19,17 @@
|
||||
import type { ChartOptions, ChartData } from 'chart.js'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import type {
|
||||
AppViewerContext,
|
||||
ComponentCustomCSS,
|
||||
RichConfiguration,
|
||||
RichConfigurations
|
||||
} from '../../types'
|
||||
import { initOutput } from '../../editor/appUtils'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let configuration: RichConfigurations
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
@@ -8,7 +8,13 @@
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import type { AppEditorContext, AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import type {
|
||||
AppEditorContext,
|
||||
AppViewerContext,
|
||||
ComponentCustomCSS,
|
||||
RichConfiguration,
|
||||
RichConfigurations
|
||||
} from '../../types'
|
||||
import { getContext } from 'svelte'
|
||||
import { initOutput } from '../../editor/appUtils'
|
||||
import Tooltip from '$lib/components/Tooltip.svelte'
|
||||
@@ -17,7 +23,7 @@
|
||||
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 let configuration: RichConfigurations
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'text'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
@@ -20,14 +20,19 @@
|
||||
import { Scatter } from 'svelte-chartjs'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import type { ChartOptions, ChartData } from 'chart.js'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import type {
|
||||
AppViewerContext,
|
||||
ComponentCustomCSS,
|
||||
RichConfiguration,
|
||||
RichConfigurations
|
||||
} from '../../types'
|
||||
import { getContext } from 'svelte'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import { initOutput } from '../../editor/appUtils'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let configuration: RichConfigurations
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
import { getContext, onMount } from 'svelte'
|
||||
import { initOutput } from '../../editor/appUtils'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { AppViewerContext } from '../../types'
|
||||
import type { AppViewerContext, RichConfiguration, RichConfigurations } from '../../types'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let configuration: RichConfigurations
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { getContext, onMount } from 'svelte'
|
||||
import type { Output } from '../../../rx'
|
||||
import type { AppViewerContext } from '../../../types'
|
||||
import type { AppViewerContext, RichConfiguration, RichConfigurations } from '../../../types'
|
||||
import InputValue from '../../helpers/InputValue.svelte'
|
||||
import type { AppInput } from '../../../inputType'
|
||||
import RunnableWrapper from '../../helpers/RunnableWrapper.svelte'
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let configuration: RichConfigurations
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { getContext, onMount } from 'svelte'
|
||||
import type { Output } from '../../../rx'
|
||||
import type { AppViewerContext, BaseAppComponent, ComponentCustomCSS } from '../../../types'
|
||||
import type {
|
||||
AppViewerContext,
|
||||
BaseAppComponent,
|
||||
ComponentCustomCSS,
|
||||
RichConfiguration,
|
||||
RichConfigurations
|
||||
} from '../../../types'
|
||||
import InputValue from '../../helpers/InputValue.svelte'
|
||||
import type { AppInput } from '../../../inputType'
|
||||
import RunnableWrapper from '../../helpers/RunnableWrapper.svelte'
|
||||
@@ -20,7 +25,7 @@
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let configuration: RichConfigurations
|
||||
export let actionButtons: (BaseAppComponent & ButtonComponent)[]
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let customCss:
|
||||
@@ -237,6 +242,7 @@
|
||||
{#if context}
|
||||
{@const component = renderCell(cell.column.columnDef.cell, context)}
|
||||
<td
|
||||
on:keydown={() => toggleRow(row, rowIndex)}
|
||||
on:click={() => toggleRow(row, rowIndex)}
|
||||
class="p-4 whitespace-pre-wrap truncate text-xs text-gray-900"
|
||||
>
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
import { isCodeInjection } from '$lib/components/flows/utils'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
import { createEventDispatcher, getContext } from 'svelte'
|
||||
import type { AppInput, EvalAppInput, UploadAppInput } from '../../inputType'
|
||||
import type { AppViewerContext } from '../../types'
|
||||
import type { AppInput, EvalAppInput, RichAppInput, UploadAppInput } from '../../inputType'
|
||||
import type { AppViewerContext, RichConfiguration } from '../../types'
|
||||
import { accessPropertyByPath } from '../../utils'
|
||||
import { computeGlobalContext, eval_like } from './eval'
|
||||
|
||||
type T = string | number | boolean | Record<string | number, any> | undefined
|
||||
|
||||
export let input: AppInput | undefined
|
||||
export let input: AppInput | RichConfiguration
|
||||
export let value: T
|
||||
export let id: string | undefined = undefined
|
||||
export let error: string = ''
|
||||
@@ -64,8 +64,7 @@
|
||||
|
||||
async function handleConnection() {
|
||||
if (lastInput.type === 'connected') {
|
||||
console.log('connected', lastInput, `${id}-${key}`)
|
||||
$worldStore?.connect<any>(lastInput, onValueChange, id)
|
||||
$worldStore?.connect<any>(lastInput, onValueChange, `${id}-${key}`)
|
||||
} else if (lastInput.type === 'static' || lastInput.type == 'template') {
|
||||
value = await getValue(lastInput)
|
||||
} else if (lastInput.type == 'eval') {
|
||||
|
||||
@@ -4,13 +4,18 @@
|
||||
import { initOutput } from '../../editor/appUtils'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import type {
|
||||
AppViewerContext,
|
||||
ComponentCustomCSS,
|
||||
RichConfiguration,
|
||||
RichConfigurations
|
||||
} from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
|
||||
export let id: string
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let configuration: RichConfigurations
|
||||
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = undefined
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'text'> | undefined = undefined
|
||||
|
||||
@@ -2,15 +2,13 @@
|
||||
import { getContext } from 'svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { initOutput } from '../../editor/appUtils'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
|
||||
export let id: string
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let configuration: RichConfigurations
|
||||
export let inputType: 'date'
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'input'> | undefined = undefined
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
import { initOutput } from '../../editor/appUtils'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
|
||||
export let id: string
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let configuration: RichConfigurations
|
||||
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import Select from 'svelte-select'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
@@ -11,7 +11,7 @@
|
||||
import { initOutput } from '../../editor/appUtils'
|
||||
|
||||
export let id: string
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let configuration: RichConfigurations
|
||||
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = undefined
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'input'> | undefined = undefined
|
||||
|
||||
@@ -2,15 +2,13 @@
|
||||
import { getContext } from 'svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { initOutput } from '../../editor/appUtils'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
|
||||
export let id: string
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let configuration: RichConfigurations
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'input'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
@@ -11,7 +11,7 @@
|
||||
import { initOutput } from '../../editor/appUtils'
|
||||
|
||||
export let id: string
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let configuration: RichConfigurations
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'handles' | 'bar' | 'limits' | 'values'> | undefined =
|
||||
undefined
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import Select from 'svelte-select'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
@@ -11,7 +11,7 @@
|
||||
import { initOutput } from '../../editor/appUtils'
|
||||
|
||||
export let id: string
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let configuration: RichConfigurations
|
||||
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = undefined
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'input'> | undefined = undefined
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
@@ -11,7 +11,7 @@
|
||||
import { initOutput } from '../../editor/appUtils'
|
||||
|
||||
export let id: string
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let configuration: RichConfigurations
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'handle' | 'limits' | 'value' | 'bar'> | undefined =
|
||||
undefined
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
import { initOutput } from '../../editor/appUtils'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { Output } from '../../rx'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
|
||||
export let id: string
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let configuration: RichConfigurations
|
||||
export let inputType = 'text'
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'input'> | undefined = undefined
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import type { AppInput } from '../../../inputType'
|
||||
import type { Output } from '../../../rx'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../../types'
|
||||
import { concatCustomCss } from '../../../utils'
|
||||
import AlignWrapper from '../../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../../helpers/InputValue.svelte'
|
||||
import CurrencyInput from './CurrencyInput.svelte'
|
||||
|
||||
export let id: string
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let configuration: RichConfigurations
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'input'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
import { concatCustomCss } from '../../utils'
|
||||
|
||||
export let id: string
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let componentContainerHeight: number
|
||||
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
|
||||
export let render: boolean
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
AppViewerContext,
|
||||
ComponentCustomCSS,
|
||||
HorizontalAlignment,
|
||||
RichConfigurations,
|
||||
VerticalAlignment
|
||||
} from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
@@ -13,7 +14,7 @@
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
|
||||
export let id: string
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let configuration: RichConfigurations
|
||||
export let horizontalAlignment: HorizontalAlignment | undefined = undefined
|
||||
export let verticalAlignment: VerticalAlignment | undefined = undefined
|
||||
export let customCss: ComponentCustomCSS<'container' | 'divider'> | undefined = undefined
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { getContext } from 'svelte'
|
||||
import SubGridEditor from '../../editor/SubGridEditor.svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import Portal from 'svelte-portal'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
|
||||
export let id: string
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let configuration: RichConfigurations
|
||||
export let horizontalAlignment: 'left' | 'center' | 'right' | undefined = undefined
|
||||
export let verticalAlignment: 'top' | 'center' | 'bottom' | undefined = undefined
|
||||
export let noWFull = false
|
||||
|
||||
@@ -2,14 +2,18 @@
|
||||
import { getContext } from 'svelte'
|
||||
import SubGridEditor from '../../editor/SubGridEditor.svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { AppEditorContext, AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import type {
|
||||
AppEditorContext,
|
||||
AppViewerContext,
|
||||
ComponentCustomCSS,
|
||||
RichConfigurations
|
||||
} from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
import { Pane, Splitpanes } from 'svelte-splitpanes'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
|
||||
export let id: string
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let componentContainerHeight: number
|
||||
export let customCss: ComponentCustomCSS<'container'> | undefined = undefined
|
||||
export let horizontal: boolean = false
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
import { initOutput } from '../../editor/appUtils'
|
||||
import SubGridEditor from '../../editor/SubGridEditor.svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { AppViewerContext, ComponentCustomCSS } from '../../types'
|
||||
import type { AppViewerContext, ComponentCustomCSS, RichConfigurations } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
|
||||
export let id: string
|
||||
export let configuration: Record<string, AppInput>
|
||||
export let configuration: RichConfigurations
|
||||
export let componentContainerHeight: number
|
||||
export let tabs: string[]
|
||||
export let customCss:
|
||||
|
||||
@@ -353,7 +353,6 @@
|
||||
/>
|
||||
{:else if component.type === 'containercomponent'}
|
||||
<AppContainer
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
{componentContainerHeight}
|
||||
@@ -361,7 +360,6 @@
|
||||
/>
|
||||
{:else if component.type === 'verticalsplitpanescomponent'}
|
||||
<AppSplitpanes
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
panes={component.panes}
|
||||
@@ -370,7 +368,6 @@
|
||||
/>
|
||||
{:else if component.type === 'horizontalsplitpanescomponent'}
|
||||
<AppSplitpanes
|
||||
configuration={component.configuration}
|
||||
id={component.id}
|
||||
customCss={component.customCss}
|
||||
panes={component.panes}
|
||||
|
||||
+4
-2
@@ -36,7 +36,8 @@
|
||||
{#each $app.grid as gridItem (gridItem?.data?.id)}
|
||||
{#if gridItem?.data?.id && gridItem.data.id === selectedScriptComponentId}
|
||||
<InlineScriptEditorPanel
|
||||
defaultUserInput={gridItem?.data?.type == 'formcomponent'}
|
||||
defaultUserInput={gridItem?.data?.type == 'formcomponent' ||
|
||||
gridItem?.data?.type == 'formbuttoncomponent'}
|
||||
id={gridItem.data.id}
|
||||
bind:componentInput={gridItem.data.componentInput}
|
||||
/>
|
||||
@@ -59,7 +60,8 @@
|
||||
{#each $app.subgrids[key] as gridItem (gridItem?.data?.id)}
|
||||
{#if gridItem?.data?.id && gridItem.data.id === selectedScriptComponentId}
|
||||
<InlineScriptEditorPanel
|
||||
defaultUserInput={gridItem.data?.type == 'formcomponent'}
|
||||
defaultUserInput={gridItem.data?.type == 'formcomponent' ||
|
||||
gridItem?.data?.type == 'formbuttoncomponent'}
|
||||
id={gridItem.data.id}
|
||||
bind:componentInput={gridItem.data.componentInput}
|
||||
/>
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
<script lang="ts">
|
||||
import VariableEditor from '$lib/components/VariableEditor.svelte'
|
||||
import { Badge, ToggleButton, ToggleButtonGroup } from '$lib/components/common'
|
||||
import { addWhitespaceBeforeCapitals, capitalize } from '$lib/utils'
|
||||
import { faArrowRight, faPen, faUpload, faUser } from '@fortawesome/free-solid-svg-icons'
|
||||
import { fieldTypeToTsType } from '../../utils'
|
||||
import Tooltip from '$lib/components/Tooltip.svelte'
|
||||
import Popover from '$lib/components/Popover.svelte'
|
||||
|
||||
import type {
|
||||
ConnectedAppInput,
|
||||
EvalAppInput,
|
||||
@@ -13,28 +19,120 @@
|
||||
import RowInputEditor from './inputEditor/RowInputEditor.svelte'
|
||||
import StaticInputEditor from './inputEditor/StaticInputEditor.svelte'
|
||||
import UploadInputEditor from './inputEditor/UploadInputEditor.svelte'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppViewerContext } from '../../types'
|
||||
|
||||
export let id: string
|
||||
export let componentInput:
|
||||
export let componentInput: (
|
||||
| StaticAppInput
|
||||
| ConnectedAppInput
|
||||
| UserAppInput
|
||||
| RowAppInput
|
||||
| EvalAppInput
|
||||
| UploadAppInput
|
||||
) & { tooltip?: string; onlyStatic?: boolean }
|
||||
export let key: string
|
||||
export let hasRows: boolean = false
|
||||
export let userInputEnabled: boolean = false
|
||||
export let shouldCapitalize: boolean = true
|
||||
export let resourceOnly = false
|
||||
|
||||
const { connectingInput } = getContext<AppViewerContext>('AppViewerContext')
|
||||
</script>
|
||||
|
||||
{#if componentInput.type === 'connected'}
|
||||
<ConnectedInputEditor bind:componentInput />
|
||||
{:else if componentInput.type === 'row'}
|
||||
<RowInputEditor bind:componentInput />
|
||||
{:else if componentInput.type === 'static'}
|
||||
<StaticInputEditor bind:componentInput />
|
||||
{:else if componentInput.type === 'eval'}
|
||||
<EvalInputEditor {hasRows} {id} bind:componentInput />
|
||||
{:else if componentInput.type === 'upload'}
|
||||
<UploadInputEditor bind:componentInput />
|
||||
{:else if componentInput.type === 'user'}
|
||||
<span class="text-2xs italic text-gray-6f00">Field's value is set by the user</span>
|
||||
{#if !(resourceOnly && (componentInput.fieldType !== 'object' || !componentInput.format?.startsWith('resource-')))}
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="flex justify-between items-end gap-1">
|
||||
<span class="text-sm font-semibold truncate">
|
||||
{shouldCapitalize ? capitalize(addWhitespaceBeforeCapitals(key)) : key}
|
||||
{#if componentInput.tooltip}
|
||||
<Tooltip>
|
||||
{componentInput.tooltip}
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</span>
|
||||
|
||||
<div class="flex gap-x-2 gap-y-1 flex-wrap justify-end items-center">
|
||||
<Badge color="blue">
|
||||
{componentInput.fieldType === 'array' && componentInput.subFieldType
|
||||
? `${capitalize(fieldTypeToTsType(componentInput.subFieldType))}[]`
|
||||
: capitalize(fieldTypeToTsType(componentInput.fieldType))}
|
||||
</Badge>
|
||||
|
||||
{#if !componentInput.onlyStatic && componentInput.type != 'eval'}
|
||||
<ToggleButtonGroup
|
||||
bind:selected={componentInput.type}
|
||||
on:selected={(e) => {
|
||||
if (e.detail == 'connected' && !componentInput['connection']) {
|
||||
$connectingInput = {
|
||||
opened: true,
|
||||
input: undefined,
|
||||
hoveredComponent: undefined
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Popover placement="bottom" notClickable disapperTimoout={0}>
|
||||
<ToggleButton
|
||||
position="left"
|
||||
value="static"
|
||||
startIcon={{ icon: faPen }}
|
||||
size="xs"
|
||||
iconOnly
|
||||
/>
|
||||
<svelte:fragment slot="text">Static</svelte:fragment>
|
||||
</Popover>
|
||||
{#if userInputEnabled && !componentInput.format?.startsWith('resource-')}
|
||||
<Popover placement="bottom" notClickable disapperTimoout={0}>
|
||||
<ToggleButton
|
||||
position="center"
|
||||
value="user"
|
||||
startIcon={{ icon: faUser }}
|
||||
size="xs"
|
||||
iconOnly
|
||||
/>
|
||||
<svelte:fragment slot="text">User Input</svelte:fragment>
|
||||
</Popover>
|
||||
{/if}
|
||||
{#if 'fileUpload' in componentInput}
|
||||
<Popover placement="bottom" notClickable disapperTimoout={0}>
|
||||
<ToggleButton
|
||||
position="center"
|
||||
value="upload"
|
||||
startIcon={{ icon: faUpload }}
|
||||
size="xs"
|
||||
iconOnly
|
||||
/>
|
||||
<svelte:fragment slot="text">Upload</svelte:fragment>
|
||||
</Popover>
|
||||
{/if}
|
||||
<Popover placement="bottom" notClickable disapperTimoout={0}>
|
||||
<ToggleButton
|
||||
position="right"
|
||||
value="connected"
|
||||
startIcon={{ icon: faArrowRight }}
|
||||
size="xs"
|
||||
iconOnly
|
||||
/>
|
||||
<svelte:fragment slot="text">Connect</svelte:fragment>
|
||||
</Popover>
|
||||
</ToggleButtonGroup>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if componentInput.type === 'connected'}
|
||||
<ConnectedInputEditor bind:componentInput />
|
||||
{:else if componentInput.type === 'row'}
|
||||
<RowInputEditor bind:componentInput />
|
||||
{:else if componentInput.type === 'static'}
|
||||
<StaticInputEditor bind:componentInput />
|
||||
{:else if componentInput.type === 'eval'}
|
||||
<EvalInputEditor {hasRows} {id} bind:componentInput />
|
||||
{:else if componentInput.type === 'upload'}
|
||||
<UploadInputEditor bind:componentInput />
|
||||
{:else if componentInput.type === 'user'}
|
||||
<span class="text-2xs italic text-gray-6f00">Field's value is set by the user</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -1,117 +1,36 @@
|
||||
<script lang="ts">
|
||||
import { Badge, ToggleButton, ToggleButtonGroup } from '$lib/components/common'
|
||||
import { addWhitespaceBeforeCapitals, capitalize } from '$lib/utils'
|
||||
import { faArrowRight, faPen, faUpload, faUser } from '@fortawesome/free-solid-svg-icons'
|
||||
import { fieldTypeToTsType } from '../../utils'
|
||||
import type {
|
||||
ConnectedAppInput,
|
||||
RowAppInput,
|
||||
StaticAppInput,
|
||||
UserAppInput
|
||||
} from '../../inputType'
|
||||
import type { RichConfigurations } from '../../types'
|
||||
import InputsSpecEditor from './InputsSpecEditor.svelte'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppViewerContext, BaseAppComponent } from '../../types'
|
||||
import Tooltip from '$lib/components/Tooltip.svelte'
|
||||
import Popover from '$lib/components/Popover.svelte'
|
||||
|
||||
export let id: string
|
||||
export let inputSpecs: BaseAppComponent['configuration']
|
||||
export let inputSpecs:
|
||||
| RichConfigurations
|
||||
| Record<string, StaticAppInput | ConnectedAppInput | RowAppInput | UserAppInput>
|
||||
export let userInputEnabled: boolean = false
|
||||
export let shouldCapitalize: boolean = true
|
||||
export let rowColumns = false
|
||||
export let resourceOnly = false
|
||||
|
||||
const { connectingInput } = getContext<AppViewerContext>('AppViewerContext')
|
||||
</script>
|
||||
|
||||
{#if inputSpecs}
|
||||
<div class="w-full flex flex-col gap-4">
|
||||
{#each Object.keys(inputSpecs) as inputSpecKey (inputSpecKey)}
|
||||
{@const input = inputSpecs[inputSpecKey]}
|
||||
{#if !(resourceOnly && (input.fieldType !== 'object' || !input.format?.startsWith('resource-')))}
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="flex justify-between items-end gap-1">
|
||||
<span class="text-sm font-semibold truncate">
|
||||
{shouldCapitalize
|
||||
? capitalize(addWhitespaceBeforeCapitals(inputSpecKey))
|
||||
: inputSpecKey}
|
||||
{#if input.tooltip}
|
||||
<Tooltip>
|
||||
{input.tooltip}
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</span>
|
||||
|
||||
<div class="flex gap-x-2 gap-y-1 flex-wrap justify-end items-center">
|
||||
<Badge color="blue">
|
||||
{input.fieldType === 'array' && input.subFieldType
|
||||
? `${capitalize(fieldTypeToTsType(input.subFieldType))}[]`
|
||||
: capitalize(fieldTypeToTsType(input.fieldType))}
|
||||
</Badge>
|
||||
|
||||
{#if !inputSpecs[inputSpecKey].onlyStatic && inputSpecs[inputSpecKey].type != 'eval'}
|
||||
<ToggleButtonGroup
|
||||
bind:selected={inputSpecs[inputSpecKey].type}
|
||||
on:selected={(e) => {
|
||||
if (e.detail == 'connected' && !inputSpecs[inputSpecKey]['connection']) {
|
||||
$connectingInput = {
|
||||
opened: true,
|
||||
input: undefined,
|
||||
hoveredComponent: undefined
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Popover placement="bottom" notClickable disapperTimoout={0}>
|
||||
<ToggleButton
|
||||
position="left"
|
||||
value="static"
|
||||
startIcon={{ icon: faPen }}
|
||||
size="xs"
|
||||
iconOnly
|
||||
/>
|
||||
<svelte:fragment slot="text">Static</svelte:fragment>
|
||||
</Popover>
|
||||
{#if userInputEnabled && !input.format?.startsWith('resource-')}
|
||||
<Popover placement="bottom" notClickable disapperTimoout={0}>
|
||||
<ToggleButton
|
||||
position="center"
|
||||
value="user"
|
||||
startIcon={{ icon: faUser }}
|
||||
size="xs"
|
||||
iconOnly
|
||||
/>
|
||||
<svelte:fragment slot="text">User Input</svelte:fragment>
|
||||
</Popover>
|
||||
{/if}
|
||||
{#if 'fileUpload' in inputSpecs[inputSpecKey]}
|
||||
<Popover placement="bottom" notClickable disapperTimoout={0}>
|
||||
<ToggleButton
|
||||
position="center"
|
||||
value="upload"
|
||||
startIcon={{ icon: faUpload }}
|
||||
size="xs"
|
||||
iconOnly
|
||||
/>
|
||||
<svelte:fragment slot="text">Upload</svelte:fragment>
|
||||
</Popover>
|
||||
{/if}
|
||||
<Popover placement="bottom" notClickable disapperTimoout={0}>
|
||||
<ToggleButton
|
||||
position="right"
|
||||
value="connected"
|
||||
startIcon={{ icon: faArrowRight }}
|
||||
size="xs"
|
||||
iconOnly
|
||||
/>
|
||||
<svelte:fragment slot="text">Connect</svelte:fragment>
|
||||
</Popover>
|
||||
</ToggleButtonGroup>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<InputsSpecEditor
|
||||
hasRows={rowColumns}
|
||||
{id}
|
||||
bind:componentInput={inputSpecs[inputSpecKey]}
|
||||
/>
|
||||
</div>
|
||||
{#each Object.entries(inputSpecs) as [k, v] (k)}
|
||||
{#if v.ctype == undefined}
|
||||
<InputsSpecEditor
|
||||
key={k}
|
||||
bind:componentInput={v}
|
||||
{id}
|
||||
{userInputEnabled}
|
||||
{shouldCapitalize}
|
||||
{resourceOnly}
|
||||
hasRows={rowColumns}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script lang="ts">
|
||||
import InputValue from '../../components/helpers/InputValue.svelte'
|
||||
import type { AppInput } from '../../inputType'
|
||||
import type { RichConfiguration } from '../../types'
|
||||
|
||||
export let componentInput: AppInput | undefined
|
||||
export let componentInput: RichConfiguration | undefined
|
||||
let label: string = ''
|
||||
</script>
|
||||
|
||||
|
||||
+12
-6
@@ -52,9 +52,12 @@
|
||||
initialValue={componentInput.value?.['value']?.split('$res:')[1] || ''}
|
||||
on:change={(e) => {
|
||||
let path = e.detail
|
||||
|
||||
if (componentInput && path) {
|
||||
componentInput.value['value'] = `$res:${path}`
|
||||
if (componentInput) {
|
||||
if (path) {
|
||||
componentInput.value['value'] = `$res:${path}`
|
||||
} else {
|
||||
componentInput.value['value'] = undefined
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
@@ -70,9 +73,12 @@
|
||||
initialValue={componentInput.value?.split('$res:')[1] || ''}
|
||||
on:change={(e) => {
|
||||
let path = e.detail
|
||||
|
||||
if (componentInput && path) {
|
||||
componentInput.value = `$res:${path}`
|
||||
if (componentInput) {
|
||||
if (path) {
|
||||
componentInput.value = `$res:${path}`
|
||||
} else {
|
||||
componentInput.value = undefined
|
||||
}
|
||||
}
|
||||
}}
|
||||
resourceType={componentInput.format.split('-').length > 1
|
||||
|
||||
@@ -98,9 +98,9 @@ type AppInputSpec<T extends InputType, U, V extends InputType = never> = (
|
||||
| ResultInput
|
||||
| TemplateInput
|
||||
) &
|
||||
InputConfiguration<T, U, V>
|
||||
InputConfiguration<T, V>
|
||||
|
||||
type InputConfiguration<T extends InputType, U, V extends InputType> = {
|
||||
type InputConfiguration<T extends InputType, V extends InputType> = {
|
||||
fieldType: T
|
||||
subFieldType?: V
|
||||
format?: string | undefined
|
||||
@@ -140,11 +140,11 @@ export type AppInput =
|
||||
| AppInputSpec<'object', Record<string | number, any>>
|
||||
| AppInputSpec<'object', string>
|
||||
| (AppInputSpec<'select', string> & {
|
||||
/**
|
||||
* One of the keys of `staticValues` from `lib/components/apps/editor/componentsPanel/componentStaticValues`
|
||||
*/
|
||||
optionValuesKey: keyof typeof staticValues
|
||||
})
|
||||
/**
|
||||
* One of the keys of `staticValues` from `lib/components/apps/editor/componentsPanel/componentStaticValues`
|
||||
*/
|
||||
optionValuesKey: keyof typeof staticValues
|
||||
})
|
||||
| (AppInputSpec<'select', string> & StaticOptions)
|
||||
| AppInputSpec<'icon-select', string>
|
||||
| AppInputSpec<'color', string>
|
||||
@@ -157,13 +157,12 @@ export type AppInput =
|
||||
| AppInputSpec<'array', string[], 'datetime'>
|
||||
| AppInputSpec<'array', object[], 'object'>
|
||||
| (AppInputSpec<'array', string[], 'select'> & {
|
||||
optionValuesKey: keyof typeof staticValues
|
||||
})
|
||||
optionValuesKey: keyof typeof staticValues
|
||||
})
|
||||
| (AppInputSpec<'array', string[], 'select'> & StaticOptions)
|
||||
| AppInputSpec<'array', object[], 'labeledresource'>
|
||||
| AppInputSpec<'labeledresource', object>
|
||||
|
||||
|
||||
export type RowAppInput = Extract<AppInput, { type: 'row' }>
|
||||
export type StaticAppInput = Extract<AppInput, { type: 'static' }>
|
||||
export type ConnectedAppInput = Extract<AppInput, { type: 'connected' }>
|
||||
@@ -172,4 +171,9 @@ export type ResultAppInput = Extract<AppInput, { type: 'runnable' }>
|
||||
export type EvalAppInput = Extract<AppInput, { type: 'eval' }>
|
||||
export type UploadAppInput = Extract<AppInput, { type: 'upload' }>
|
||||
|
||||
export type RichAppInput =
|
||||
| AppInput
|
||||
| { type: 'oneOf'; oneOf: string[]; configuration: Record<string, AppInput> }
|
||||
| { type: 'group'; title: string; configuration: Record<string, AppInput> }
|
||||
|
||||
export type AppInputs = Record<string, AppInput>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { AppInput } from './inputType'
|
||||
import { get, writable, type Writable } from 'svelte/store'
|
||||
import { writable, type Writable } from 'svelte/store'
|
||||
import { deepEqual } from 'fast-equals'
|
||||
|
||||
export interface Subscriber<T> {
|
||||
|
||||
@@ -38,21 +38,20 @@ export type ComponentCssProperty = {
|
||||
|
||||
export type ComponentCustomCSS<T extends string = string> = Record<T, ComponentCssProperty>
|
||||
|
||||
export type Configuration = GeneralAppInput &
|
||||
(StaticAppInput | ConnectedAppInput | UserAppInput | RowAppInput | EvalAppInput | UploadAppInput)
|
||||
|
||||
export type RichConfiguration =
|
||||
| (Configuration & { ctype?: undefined })
|
||||
| { ctype: 'oneOf'; selected: string; configuration: Record<string, Configuration> }
|
||||
| { ctype: 'group'; title: string; configuration: Record<string, Configuration> }
|
||||
|
||||
export type RichConfigurations = Record<string, RichConfiguration>
|
||||
|
||||
export interface BaseAppComponent extends Partial<Aligned> {
|
||||
id: ComponentID
|
||||
componentInput: AppInput | undefined
|
||||
configuration: Record<
|
||||
string,
|
||||
GeneralAppInput &
|
||||
(
|
||||
| StaticAppInput
|
||||
| ConnectedAppInput
|
||||
| UserAppInput
|
||||
| RowAppInput
|
||||
| EvalAppInput
|
||||
| UploadAppInput
|
||||
)
|
||||
>
|
||||
configuration: RichConfigurations
|
||||
customCss?: ComponentCustomCSS
|
||||
/**
|
||||
* If `true` then the wrapper will allow items to flow outside of it's borders.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Schema } from '$lib/common'
|
||||
import type { Flow, FlowModule, FlowStatusModule } from '$lib/gen'
|
||||
import type { Flow, FlowModule } from '$lib/gen'
|
||||
import type { Writable } from 'svelte/store'
|
||||
import { loadFlowModuleState } from './flowStateUtils'
|
||||
import { emptyFlowModuleState } from './utils'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Flow, FlowModule, ForloopFlow, InputTransform } from '$lib/gen'
|
||||
import { get, writable, type Writable } from 'svelte/store'
|
||||
import { writable, type Writable } from 'svelte/store'
|
||||
import { initFlowState, type FlowState } from './flowState'
|
||||
import { numberToChars } from './utils'
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { Schema } from '$lib/common'
|
||||
import type { Flow, FlowModule, Job } from '$lib/gen'
|
||||
import type { Flow, FlowModule } from '$lib/gen'
|
||||
import { schemaToObject } from '$lib/utils'
|
||||
import type { FlowState } from './flowState'
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
export let rebuildOnChange: any = undefined
|
||||
|
||||
export let selectedId: Writable<string | undefined> = writable<string | undefined>(undefined)
|
||||
export let initialZoom: number | undefined = 1.0
|
||||
|
||||
export let insertable = false
|
||||
export let moving: string | undefined = undefined
|
||||
@@ -650,7 +649,6 @@
|
||||
<Svelvet
|
||||
highlightEdges={false}
|
||||
locked
|
||||
{initialZoom}
|
||||
{nodes}
|
||||
{width}
|
||||
{edges}
|
||||
|
||||
@@ -20,10 +20,8 @@
|
||||
export let canvasId: string
|
||||
export let width: number
|
||||
export let height: number
|
||||
export let initialZoom = 3
|
||||
export let initialLocation
|
||||
|
||||
export let boundary = false
|
||||
export let minimap = false
|
||||
export let scroll = false
|
||||
// here we lookup the store using the unique key
|
||||
const store = findStore(canvasId)
|
||||
@@ -31,9 +29,6 @@
|
||||
edgesStore,
|
||||
nodesStore,
|
||||
anchorsStore,
|
||||
potentialAnchorsStore,
|
||||
temporaryEdgeStore,
|
||||
resizeNodesStore,
|
||||
nodeSelected,
|
||||
backgroundStore,
|
||||
movementStore,
|
||||
@@ -45,27 +40,6 @@
|
||||
$: nodes = Object.values($nodesStore)
|
||||
$: edges = Object.values($edgesStore)
|
||||
$: anchors = Object.values($anchorsStore)
|
||||
$: resizeNodes = Object.values($resizeNodesStore)
|
||||
$: potentialAnchors = Object.values($potentialAnchorsStore)
|
||||
$: tempEdges = $temporaryEdgeStore
|
||||
|
||||
/*
|
||||
This block of code is responsible for reactivity of the collapsible feature
|
||||
When collaspsibleStore changes, nodes/edges/resizeNodes/anchors are filtered so that
|
||||
only the visible ones are displayed
|
||||
*/
|
||||
let filteredNodes: NodeType[]
|
||||
let filteredEdges: EdgeType[]
|
||||
let filteredResizeNodes: ResizeNodeType[]
|
||||
let filteredAnchors: AnchorType[]
|
||||
$: {
|
||||
const tmp = $collapsibleStore // assignment is necessary for reactivity
|
||||
const obj = filterByCollapsible(store, nodes, resizeNodes, anchors, edges)
|
||||
filteredNodes = obj['filteredNodes']
|
||||
filteredEdges = obj['filteredEdges']
|
||||
filteredResizeNodes = obj['filteredResizeNodes']
|
||||
filteredAnchors = obj['filteredAnchors']
|
||||
}
|
||||
|
||||
// declaring the grid and dot size for d3's transformations and zoom
|
||||
const gridSize = 15
|
||||
@@ -188,7 +162,7 @@
|
||||
<div class={`Nodes Nodes-${canvasId}`} on:contextmenu|preventDefault>
|
||||
<!-- This container is transformed by d3zoom -->
|
||||
<div class={`Node Node-${canvasId}`}>
|
||||
{#each filteredNodes as node}
|
||||
{#each nodes as node}
|
||||
{#if node.data.html}
|
||||
<Node {node} {canvasId} nodeId={node.id}>{@html node.data.html}</Node>
|
||||
{:else if node.data.custom}
|
||||
@@ -239,7 +213,7 @@
|
||||
|
||||
<!-- <g> tag defines which edge type to render depending on properties of edge object -->
|
||||
<g>
|
||||
{#each filteredEdges as edge}
|
||||
{#each edges as edge}
|
||||
{#if edge.type === 'smoothstep'}
|
||||
<SmoothStepEdge {edge} {canvasId} />
|
||||
{:else if edge.type === 'step'}
|
||||
@@ -278,8 +252,6 @@
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
svg {
|
||||
}
|
||||
.Nodes {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
export let nodes: UserNodeType[] // TODO: update type to make possible user id being a number
|
||||
export let edges: UserEdgeType[] // TODO: update type to make possible user id being a number
|
||||
export let bgColor = '#ffffff' // this is used to set the background color of the the Svelvet canvas
|
||||
export let minimap = false
|
||||
export let width: number = 600
|
||||
export let height: number = 600
|
||||
export let background: boolean = true
|
||||
@@ -22,14 +21,10 @@
|
||||
export let snap: boolean = false
|
||||
export let snapTo: number = 30
|
||||
export let nodeCreate: boolean = false
|
||||
export let initialZoom = 3
|
||||
export let initialLocation = { x: 0, y: 0 }
|
||||
export let boundary = false
|
||||
export let collapsible = false
|
||||
export let shareable: boolean = false // used for "importExport" feature
|
||||
export let locked: boolean = false // if true, node movement is disabled
|
||||
export let editable: boolean = false
|
||||
export let resizable: boolean = true
|
||||
export let highlightEdges: boolean = true
|
||||
export let scroll: boolean = false
|
||||
|
||||
@@ -62,7 +57,6 @@
|
||||
store.collapsibleOption.set(collapsible)
|
||||
store.lockedOption.set(locked)
|
||||
store.editableOption.set(editable)
|
||||
store.resizableOption.set(resizable)
|
||||
store.highlightEdgesOption.set(highlightEdges)
|
||||
|
||||
// make sure that all canvas options are compatible
|
||||
@@ -97,7 +91,6 @@
|
||||
store.collapsibleOption.set(collapsible)
|
||||
store.lockedOption.set(locked)
|
||||
store.editableOption.set(editable)
|
||||
store.resizableOption.set(resizable)
|
||||
store.highlightEdgesOption.set(highlightEdges)
|
||||
|
||||
// make sure that all canvas options are compatible
|
||||
@@ -120,16 +113,7 @@
|
||||
{#if error != ''}
|
||||
<div class="error text-red-600 center-center p-4">{error}</div>
|
||||
{:else}
|
||||
<GraphView
|
||||
{scroll}
|
||||
{canvasId}
|
||||
{width}
|
||||
{height}
|
||||
{initialLocation}
|
||||
{initialZoom}
|
||||
{boundary}
|
||||
{minimap}
|
||||
/>
|
||||
<GraphView {scroll} {canvasId} {width} {height} {boundary} />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,62 +1,50 @@
|
||||
// Required for "custom css" feature
|
||||
|
||||
import type { NodeType, StoreType } from '../../store/types/types';
|
||||
import type { NodeType, StoreType } from '../../store/types/types'
|
||||
|
||||
// WHAT: For a given node with a user-defined classname, see if there are any css properties for height and width
|
||||
// Get that height and width and set properties in node store. This is necessary for
|
||||
export const forceCssHeightAndWidth = (store: StoreType, node: NodeType) => {
|
||||
let width, height, innerText;
|
||||
// Look through each CSS rule to find the one the user defined
|
||||
for (let i = 0; i < document.styleSheets.length; i++) {
|
||||
const styleSheet = document.styleSheets[i];
|
||||
const styleRules = styleSheet.cssRules;
|
||||
for (let j = 0; j < styleRules.length; j++) {
|
||||
const rule = styleRules[j];
|
||||
let width, height, innerText
|
||||
// Look through each CSS rule to find the one the user defined
|
||||
for (let i = 0; i < document.styleSheets.length; i++) {
|
||||
const styleSheet = document.styleSheets[i]
|
||||
const styleRules = styleSheet.cssRules
|
||||
for (let j = 0; j < styleRules.length; j++) {
|
||||
const rule = styleRules[j]
|
||||
|
||||
// this is necessary to get rid of typescript warning for rule.selectorText
|
||||
if (!(rule instanceof CSSStyleRule)) continue;
|
||||
// this is necessary to get rid of typescript warning for rule.selectorText
|
||||
if (!(rule instanceof CSSStyleRule)) continue
|
||||
|
||||
if (rule.selectorText === `.${node.className}`) {
|
||||
const initialText = rule.cssText; // getting the full text of the CSS rule
|
||||
const i = initialText.indexOf('{'); // finding index of first bracket
|
||||
innerText = initialText.substring(i + 1, initialText.length - 1); // extracting the CSS to insert into inline style
|
||||
// Adjusting the width and height if they are set via the custom class
|
||||
const arr = innerText.split(' ');
|
||||
arr.forEach((str, i) => {
|
||||
if (str === 'width:') {
|
||||
width = str.concat(arr[i + 1]); // go through the array and join width and the number
|
||||
const w = parseInt(arr[i + 1]); // getting the number for the width
|
||||
width = w;
|
||||
}
|
||||
if (str === 'height:') {
|
||||
height = str.concat(arr[i + 1]); // same as with the width
|
||||
const h = parseInt(arr[i + 1]);
|
||||
height = h;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rule.selectorText === `.${node.className}`) {
|
||||
const initialText = rule.cssText // getting the full text of the CSS rule
|
||||
const i = initialText.indexOf('{') // finding index of first bracket
|
||||
innerText = initialText.substring(i + 1, initialText.length - 1) // extracting the CSS to insert into inline style
|
||||
// Adjusting the width and height if they are set via the custom class
|
||||
const arr = innerText.split(' ')
|
||||
arr.forEach((str, i) => {
|
||||
if (str === 'width:') {
|
||||
width = str.concat(arr[i + 1]) // go through the array and join width and the number
|
||||
const w = parseInt(arr[i + 1]) // getting the number for the width
|
||||
width = w
|
||||
}
|
||||
if (str === 'height:') {
|
||||
height = str.concat(arr[i + 1]) // same as with the width
|
||||
const h = parseInt(arr[i + 1])
|
||||
height = h
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update the width/height of the node
|
||||
store.nodesStore.update((nodes) => {
|
||||
if (width !== undefined) nodes[node.id].width = width;
|
||||
if (height !== undefined) nodes[node.id].height = height;
|
||||
return { ...nodes };
|
||||
});
|
||||
// update the width/height of the node
|
||||
store.nodesStore.update((nodes) => {
|
||||
if (width !== undefined) nodes[node.id].width = width
|
||||
if (height !== undefined) nodes[node.id].height = height
|
||||
return { ...nodes }
|
||||
})
|
||||
|
||||
// move the node a bit to force update to anchors, potential anchors
|
||||
node.setPositionFromMovement(0, 0);
|
||||
|
||||
// update the position of the resize node. TODO: this should be refactored so that it cascades from movement
|
||||
store.resizeNodesStore.update((resizeNodes) => {
|
||||
for (let resizeNode of Object.values(resizeNodes)) {
|
||||
if (resizeNode.nodeId === node.id) {
|
||||
// set resizeNode position to bottom left corner of Node
|
||||
resizeNode.positionX = node.positionX + node.width;
|
||||
resizeNode.positionY = node.positionY + node.height;
|
||||
}
|
||||
}
|
||||
return { ...resizeNodes };
|
||||
});
|
||||
};
|
||||
// move the node a bit to force update to anchors, potential anchors
|
||||
node.setPositionFromMovement(0, 0)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { StoreType } from '../../store/types/types';
|
||||
import { writable, derived, get, readable } from 'svelte/store';
|
||||
import { get } from 'svelte/store';
|
||||
import type { AnchorType } from '../types/types';
|
||||
import { getNodeById } from '../../nodes/controllers/util';
|
||||
|
||||
/**
|
||||
* Finds all Anchors that matches the conditions specified in the filter parameter from a Svelvet store and returns these Anchors in an array.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { StoreType } from '../../store/types/types';
|
||||
import { writable, derived, get, readable } from 'svelte/store';
|
||||
import { get } from 'svelte/store';
|
||||
|
||||
/**
|
||||
* getNodeById will look for the targeted Node that has the same id provided in the Svelvet component store.
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
/** this is where we create our node store */
|
||||
import type { NodeType } from '../../store/types/types';
|
||||
import { writable, derived, get, readable } from 'svelte/store';
|
||||
import { stores } from '../../store/models/store';
|
||||
import { getAnchors, getEdgeById } from '../../edges/controllers/util';
|
||||
import { getResizeNodes } from '../../resizableNodes/controllers/util';
|
||||
import type { NodeType } from '../../store/types/types'
|
||||
import { get } from 'svelte/store'
|
||||
import { stores } from '../../store/models/store'
|
||||
import { getAnchors, getEdgeById } from '../../edges/controllers/util'
|
||||
/** A Node class that implements NodeType interface
|
||||
* @param {string} id The id of the Node
|
||||
* @param {number} positionX The X-axis position of the Node (left top corner of the Node)
|
||||
@@ -22,187 +21,137 @@ import { getResizeNodes } from '../../resizableNodes/controllers/util';
|
||||
* @param {string} className The custom class name if user specifies. This is for the custom className feature for Node.
|
||||
*/
|
||||
export class Node implements NodeType {
|
||||
constructor(
|
||||
public id: string,
|
||||
public positionX: number,
|
||||
public positionY: number,
|
||||
public width: number,
|
||||
public height: number,
|
||||
public bgColor: string,
|
||||
public data: object,
|
||||
public canvasId: string,
|
||||
public borderColor: string,
|
||||
public image: boolean,
|
||||
public src: string,
|
||||
public textColor: string,
|
||||
public borderRadius: number,
|
||||
public childNodes: string[],
|
||||
public className: string,
|
||||
public clickCallback: Function
|
||||
) { }
|
||||
constructor(
|
||||
public id: string,
|
||||
public positionX: number,
|
||||
public positionY: number,
|
||||
public width: number,
|
||||
public height: number,
|
||||
public bgColor: string,
|
||||
public data: object,
|
||||
public canvasId: string,
|
||||
public borderColor: string,
|
||||
public image: boolean,
|
||||
public src: string,
|
||||
public textColor: string,
|
||||
public borderRadius: number,
|
||||
public childNodes: string[],
|
||||
public className: string,
|
||||
public clickCallback: Function
|
||||
) {}
|
||||
|
||||
/**
|
||||
* setPositionFromMovement will update the positionX and positionY of the Node when user drags a Node around on the canvas, reflect the changes in real time in the nodesStore, and also cascade the changes to all relative elements like Anchors and Edges.
|
||||
* @param {number} movementX The mouse movement value on the X-axis
|
||||
* @param {number} movementY The mouse movement value on the Y-axis
|
||||
*/
|
||||
setPositionFromMovement(movementX: number, movementY: number) {
|
||||
const {
|
||||
nodesStore,
|
||||
anchorsStore,
|
||||
resizeNodesStore,
|
||||
potentialAnchorsStore,
|
||||
boundary,
|
||||
lockedOption,
|
||||
} = stores[this.canvasId];
|
||||
/**
|
||||
* setPositionFromMovement will update the positionX and positionY of the Node when user drags a Node around on the canvas, reflect the changes in real time in the nodesStore, and also cascade the changes to all relative elements like Anchors and Edges.
|
||||
* @param {number} movementX The mouse movement value on the X-axis
|
||||
* @param {number} movementY The mouse movement value on the Y-axis
|
||||
*/
|
||||
setPositionFromMovement(movementX: number, movementY: number) {
|
||||
const { nodesStore, anchorsStore, potentialAnchorsStore, boundary, lockedOption } =
|
||||
stores[this.canvasId]
|
||||
|
||||
if (get(lockedOption)) return; // don't do anything if locked is enabled
|
||||
if (get(lockedOption)) return // don't do anything if locked is enabled
|
||||
|
||||
// boundary sets the boundary of the canvas, or else it is false if their is no boundary
|
||||
// check if out of bounds, and if so return without doing anything
|
||||
const boundaryObj = get(boundary);
|
||||
if (
|
||||
typeof boundaryObj === 'object' &&
|
||||
(this.positionX + this.width + movementX >= boundaryObj.x ||
|
||||
this.positionY + this.height + movementY >= boundaryObj.y ||
|
||||
this.positionY + movementY <= 0 ||
|
||||
this.positionX + movementX <= 0)
|
||||
)
|
||||
return;
|
||||
//update all necessary data
|
||||
this.positionX += movementX;
|
||||
this.positionY += movementY;
|
||||
// boundary sets the boundary of the canvas, or else it is false if their is no boundary
|
||||
// check if out of bounds, and if so return without doing anything
|
||||
const boundaryObj = get(boundary)
|
||||
if (
|
||||
typeof boundaryObj === 'object' &&
|
||||
(this.positionX + this.width + movementX >= boundaryObj.x ||
|
||||
this.positionY + this.height + movementY >= boundaryObj.y ||
|
||||
this.positionY + movementY <= 0 ||
|
||||
this.positionX + movementX <= 0)
|
||||
)
|
||||
return
|
||||
//update all necessary data
|
||||
this.positionX += movementX
|
||||
this.positionY += movementY
|
||||
|
||||
// update children
|
||||
nodesStore.update((nodes) => {
|
||||
if (this.childNodes)
|
||||
for (const childNodeId of this.childNodes)
|
||||
nodes[childNodeId].setPositionFromMovement(movementX, movementY);
|
||||
return { ...nodes };
|
||||
});
|
||||
// update children
|
||||
nodesStore.update((nodes) => {
|
||||
if (this.childNodes)
|
||||
for (const childNodeId of this.childNodes)
|
||||
nodes[childNodeId].setPositionFromMovement(movementX, movementY)
|
||||
return { ...nodes }
|
||||
})
|
||||
|
||||
//update all the anchors on the node in the anchorsStore
|
||||
anchorsStore.update((anchors) => {
|
||||
for (const anchorId in anchors) {
|
||||
if (anchors[anchorId].nodeId === this.id) {
|
||||
anchors[anchorId].setPositionFromNode();
|
||||
}
|
||||
}
|
||||
return { ...anchors };
|
||||
});
|
||||
//update all the anchors on the node in the anchorsStore
|
||||
anchorsStore.update((anchors) => {
|
||||
for (const anchorId in anchors) {
|
||||
if (anchors[anchorId].nodeId === this.id) {
|
||||
anchors[anchorId].setPositionFromNode()
|
||||
}
|
||||
}
|
||||
return { ...anchors }
|
||||
})
|
||||
|
||||
//update all the anchors on the node in the anchorsStore
|
||||
potentialAnchorsStore.update((anchors) => {
|
||||
for (const anchorId in anchors) {
|
||||
if (anchors[anchorId].nodeId === this.id) {
|
||||
anchors[anchorId].callback(); // we don't have to worry about setting partner anchors/etc;
|
||||
}
|
||||
}
|
||||
return { ...anchors };
|
||||
});
|
||||
//update all the anchors on the node in the anchorsStore
|
||||
potentialAnchorsStore.update((anchors) => {
|
||||
for (const anchorId in anchors) {
|
||||
if (anchors[anchorId].nodeId === this.id) {
|
||||
anchors[anchorId].callback() // we don't have to worry about setting partner anchors/etc;
|
||||
}
|
||||
}
|
||||
return { ...anchors }
|
||||
})
|
||||
}
|
||||
|
||||
resizeNodesStore.update((resAnchors) => {
|
||||
for (const anchorId in resAnchors) {
|
||||
if (resAnchors[anchorId].nodeId === this.id) {
|
||||
resAnchors[anchorId].setPosition(movementX, movementY);
|
||||
//resAnchors[anchorId].setPosition(movementX, movementY);
|
||||
}
|
||||
}
|
||||
return { ...resAnchors };
|
||||
});
|
||||
}
|
||||
/**
|
||||
* setSizeFromMovement will update the width and height of the Node when user resizes the Node by dragging at the right bottom corner (where the ResizedNode attached), reflect the changes in real time in the nodesStore, and also cascade the changes to all relative elements like Anchors and potential Anchors.
|
||||
*
|
||||
* @param movementX The mouse movement value on the X-axis
|
||||
* @param movementY The mouse movement value on the Y-axis
|
||||
*/
|
||||
setSizeFromMovement(movementX: number, movementY: number) {
|
||||
this.width += movementX
|
||||
this.height += movementY
|
||||
|
||||
/**
|
||||
* setSizeFromMovement will update the width and height of the Node when user resizes the Node by dragging at the right bottom corner (where the ResizedNode attached), reflect the changes in real time in the nodesStore, and also cascade the changes to all relative elements like Anchors and potential Anchors.
|
||||
*
|
||||
* @param movementX The mouse movement value on the X-axis
|
||||
* @param movementY The mouse movement value on the Y-axis
|
||||
*/
|
||||
setSizeFromMovement(movementX: number, movementY: number) {
|
||||
this.width += movementX;
|
||||
this.height += movementY;
|
||||
const { anchorsStore, potentialAnchorsStore } = stores[this.canvasId]
|
||||
|
||||
const { anchorsStore, potentialAnchorsStore } = stores[this.canvasId];
|
||||
//Updates the anchor so it follows the node's position as the dimensions change
|
||||
anchorsStore.update((anchors) => {
|
||||
for (const anchorId in anchors) {
|
||||
if (anchors[anchorId].nodeId === this.id) {
|
||||
anchors[anchorId].setPositionFromNode()
|
||||
//anchors[anchorId].setPosition(movementX, movementY);
|
||||
}
|
||||
}
|
||||
return { ...anchors }
|
||||
})
|
||||
|
||||
//Updates the anchor so it follows the node's position as the dimensions change
|
||||
anchorsStore.update((anchors) => {
|
||||
for (const anchorId in anchors) {
|
||||
if (anchors[anchorId].nodeId === this.id) {
|
||||
anchors[anchorId].setPositionFromNode();
|
||||
//anchors[anchorId].setPosition(movementX, movementY);
|
||||
}
|
||||
}
|
||||
return { ...anchors };
|
||||
});
|
||||
//update all the anchors on the node in the anchorsStore
|
||||
potentialAnchorsStore.update((anchors) => {
|
||||
for (const anchorId in anchors) {
|
||||
if (anchors[anchorId].nodeId === this.id) {
|
||||
anchors[anchorId].callback() // we don't have to worry about setting partner anchors/etc;
|
||||
}
|
||||
}
|
||||
return { ...anchors }
|
||||
})
|
||||
}
|
||||
|
||||
//update all the anchors on the node in the anchorsStore
|
||||
potentialAnchorsStore.update((anchors) => {
|
||||
for (const anchorId in anchors) {
|
||||
if (anchors[anchorId].nodeId === this.id) {
|
||||
anchors[anchorId].callback(); // we don't have to worry about setting partner anchors/etc;
|
||||
}
|
||||
}
|
||||
return { ...anchors };
|
||||
});
|
||||
}
|
||||
/**
|
||||
* setExportableData is going to construct an object that holds all the node data that can be exported. This method is used for Exporting Diagrams feature.
|
||||
*
|
||||
* @returns An object with all the exportable data of the Node. The format of this object should align with the original format of node data user provided.
|
||||
*/
|
||||
setExportableData() {
|
||||
const exportableData = {
|
||||
id: this.id,
|
||||
// canvasId: this.canvasId,
|
||||
width: this.width,
|
||||
height: this.height,
|
||||
position: { x: this.positionX, y: this.positionY },
|
||||
data: this.data,
|
||||
bgColor: this.bgColor,
|
||||
borderColor: this.borderColor,
|
||||
textColor: this.textColor,
|
||||
borderRadius: this.borderRadius,
|
||||
image: this.image,
|
||||
src: this.src,
|
||||
childNodes: this.childNodes,
|
||||
customClassName: this.className
|
||||
}
|
||||
|
||||
/**
|
||||
* delete will handle the deletion of a Node (also waterfall down to delete anchors and edges)
|
||||
*/
|
||||
delete() {
|
||||
const store = stores[this.canvasId];
|
||||
const { nodesStore, anchorsStore, edgesStore } = stores[this.canvasId];
|
||||
|
||||
nodesStore.update((nodes) => {
|
||||
for (const nodeId in nodes) {
|
||||
if (nodes[nodeId].id === this.id) {
|
||||
delete nodes[nodeId];
|
||||
}
|
||||
}
|
||||
return { ...nodes };
|
||||
});
|
||||
|
||||
// variable `anchors` is an array of Anchor objects on the node
|
||||
const anchors = getAnchors(store, { nodeId: this.id });
|
||||
for (let anchorSelf of anchors) {
|
||||
const edgeId = anchorSelf.edgeId;
|
||||
const edge = getEdgeById(store, edgeId);
|
||||
edge.delete(); // this also deletes anchors. TODO: maybe this should be renamed to explicitly say
|
||||
}
|
||||
|
||||
// delete the resize nodes
|
||||
const resizeNodesArr = getResizeNodes(store, { nodeId: this.id });
|
||||
// there should be only 1 resize node if option is enabled, 0 if not enabled
|
||||
for (const resizeNode of resizeNodesArr) {
|
||||
resizeNode.delete();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* setExportableData is going to construct an object that holds all the node data that can be exported. This method is used for Exporting Diagrams feature.
|
||||
*
|
||||
* @returns An object with all the exportable data of the Node. The format of this object should align with the original format of node data user provided.
|
||||
*/
|
||||
setExportableData() {
|
||||
const exportableData = {
|
||||
id: this.id,
|
||||
// canvasId: this.canvasId,
|
||||
width: this.width,
|
||||
height: this.height,
|
||||
position: { x: this.positionX, y: this.positionY },
|
||||
data: this.data,
|
||||
bgColor: this.bgColor,
|
||||
borderColor: this.borderColor,
|
||||
textColor: this.textColor,
|
||||
borderRadius: this.borderRadius,
|
||||
image: this.image,
|
||||
src: this.src,
|
||||
childNodes: this.childNodes,
|
||||
customClassName: this.className,
|
||||
};
|
||||
|
||||
return exportableData;
|
||||
}
|
||||
return exportableData
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,146 +0,0 @@
|
||||
<script>
|
||||
import { findStore } from '../../store/controllers/storeApi';
|
||||
import { getNodeById } from '../../nodes/controllers/util';
|
||||
|
||||
export let nodeId;
|
||||
export let canvasId;
|
||||
export let isEditing;
|
||||
|
||||
let label;
|
||||
let width;
|
||||
let height;
|
||||
let customClass;
|
||||
let backgroundColor;
|
||||
|
||||
const store = findStore(canvasId);
|
||||
|
||||
const { nodesStore, nodeSelected } = store;
|
||||
let currentNode = $nodesStore[nodeId];
|
||||
|
||||
const editNode = (e) => {
|
||||
e.preventDefault();
|
||||
if (label) currentNode.data.label = label;
|
||||
if (width) currentNode.width = width;
|
||||
if (height) currentNode.height = height;
|
||||
if (backgroundColor) currentNode.bgColor = backgroundColor;
|
||||
width = '';
|
||||
height = '';
|
||||
customClass = '';
|
||||
label = '';
|
||||
|
||||
store.nodesStore.set($nodesStore);
|
||||
};
|
||||
</script>
|
||||
|
||||
{#if isEditing}
|
||||
<div
|
||||
on:mouseover={(e) => ($nodeSelected = true)}
|
||||
on:focus
|
||||
on:wheel|preventDefault
|
||||
class="EditNode"
|
||||
style="left:{currentNode.positionX +
|
||||
currentNode.width}px; top:{currentNode.positionY}px"
|
||||
>
|
||||
<form on:submit={editNode}>
|
||||
<label for="label-input">Label</label>
|
||||
<input
|
||||
type="text"
|
||||
id="label-input-{nodeId}"
|
||||
placeholder={currentNode.data.label}
|
||||
bind:value={label}
|
||||
/>
|
||||
|
||||
<label for="bg-color-input">Background Color</label>
|
||||
<input
|
||||
type="color"
|
||||
id="bg-color-input-{nodeId}"
|
||||
class="bgci"
|
||||
bind:value={backgroundColor}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder={currentNode.bgColor}
|
||||
bind:value={backgroundColor}
|
||||
/>
|
||||
</form>
|
||||
<div class="btn-container">
|
||||
<button
|
||||
on:click={(e) => {
|
||||
const store = findStore(canvasId);
|
||||
const node = getNodeById(store, nodeId);
|
||||
node.delete();
|
||||
isEditing = false;
|
||||
}}>Delete Node</button
|
||||
>
|
||||
<button
|
||||
on:click={(e) => {
|
||||
editNode(e);
|
||||
isEditing = false;
|
||||
}}>Submit</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.EditNode {
|
||||
position: absolute;
|
||||
padding: 0 1rem 1rem 1rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
border: 1px solid #333333;
|
||||
border-radius: 0.25rem;
|
||||
background-color: #ffffff;
|
||||
z-index: 10;
|
||||
user-select: text;
|
||||
box-shadow: 1px 1px 3px 1px rgba(0, 0, 0, 0.4);
|
||||
color: #333333;
|
||||
pointer-events: auto; /* this is needed for pointer events to work since we disable them in graphview */
|
||||
}
|
||||
|
||||
label {
|
||||
font-size: 0.8rem;
|
||||
font-weight: bold;
|
||||
margin-bottom: 0.15rem;
|
||||
}
|
||||
|
||||
.btn-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 0.25rem;
|
||||
background-color: white;
|
||||
box-shadow: 1px 1px 3px 1px rgba(0, 0, 0, 0.4);
|
||||
margin: 0.2rem;
|
||||
}
|
||||
|
||||
input {
|
||||
height: 1.6rem;
|
||||
border-color: #e45b56;
|
||||
}
|
||||
|
||||
.bgci {
|
||||
height: 2rem;
|
||||
width: 5rem;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background-color: none;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
cursor: pointer;
|
||||
background-color: #e45b56;
|
||||
color: white;
|
||||
}
|
||||
|
||||
form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
@@ -1,28 +0,0 @@
|
||||
import type { StoreType, ResizeNodeType } from '../../store/types/types';
|
||||
import { get } from 'svelte/store';
|
||||
/**
|
||||
* Finds all resizeNodes that matches the conditions specified in the filter parameter from a Svelvet store and returns these resizeNodes in an array
|
||||
*
|
||||
* @param store The Svelvet store containing the state of a Svelvet component
|
||||
* @param filter An object to specify conditions.
|
||||
* @returns An array of resizeNode objects that matches the conditions specified in filter parameter
|
||||
*/
|
||||
export function getResizeNodes(
|
||||
store: StoreType,
|
||||
filter?: { [key: string]: any }
|
||||
) {
|
||||
let resizeNodes = Object.values(get(store.resizeNodesStore));
|
||||
// filter the array for elements that match filter
|
||||
if (filter !== undefined) {
|
||||
resizeNodes = resizeNodes.filter((resizeNode) => {
|
||||
for (let filterKey in filter) {
|
||||
const filterValue = filter[filterKey];
|
||||
if (resizeNode[filterKey as keyof ResizeNodeType] !== filterValue)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
// return list of anchors
|
||||
return resizeNodes;
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
import type { ResizeNodeType } from '../../store/types/types';
|
||||
// import { writable, derived, get, readable } from 'svelte/store';
|
||||
import { stores } from '../../store/models/store';
|
||||
|
||||
/** A ResizeNode class that implements ResizeNodeType interface
|
||||
* @param {string} id The id of the ResizeNode
|
||||
* @param {string} canvasId The canvasId of the Svelvet component that the instantiated ResizeNode will be on.
|
||||
* @param {string} nodeId The id of the Node that the instantiated ResizedNode will be attached to.
|
||||
* @param {number} positionX The X-axis position of the ResizeNode (left top corner of the ResizeNode)
|
||||
* @param {number} positionY The Y-axis position of the ResizeNode (left top corner of the ResizeNode)
|
||||
*/
|
||||
export class ResizeNode implements ResizeNodeType {
|
||||
constructor(
|
||||
public id: string,
|
||||
public canvasId: string,
|
||||
public nodeId: string,
|
||||
public positionX: number,
|
||||
public positionY: number
|
||||
) {}
|
||||
|
||||
/**
|
||||
* setPosition will update the positionX and positionY of the ResizeNode. This will be invoked when the Node that this ResizeNode attached to is moving so that the ResizeNode will follow the Node.
|
||||
*
|
||||
* @param movementX The mouse movement value on the X-axis
|
||||
* @param movementY The mouse movement value on the Y-axis
|
||||
*/
|
||||
setPosition(movementX: number, movementY: number) {
|
||||
this.positionX += movementX;
|
||||
this.positionY += movementY;
|
||||
}
|
||||
|
||||
/**
|
||||
* setPositionAndCascade will update the positionX and positionY of the ResizeNode and also cascade changes to related Node. This will be invoked when the user drags the ResizeNode so the position of the ResizeNode and the width and height of the Node would change accordingly.
|
||||
* @param movementX The mouse movement value on the X-axis
|
||||
* @param movementY The mouse movement value on the Y-axis
|
||||
*/
|
||||
setPositionAndCascade(movementX: number, movementY: number) {
|
||||
//declare variables needed to interact with the corresponding node to this resize anchor
|
||||
const nodeId = this.nodeId;
|
||||
const { nodesStore } = stores[this.canvasId];
|
||||
|
||||
//Updates the width/height of the corresponding Node
|
||||
nodesStore.update((nodes) => {
|
||||
const node = nodes[nodeId];
|
||||
|
||||
//sets condition so node cannot be less than 20 px in width or height.
|
||||
if (node.width + movementX > 20 && node.height + movementY > 20) {
|
||||
//Updates position to this resizeNode anchor. Must be done within the update method so ensure the position doesn't change if the width goes below 20px
|
||||
this.positionX += movementX;
|
||||
this.positionY += movementY;
|
||||
//setSizeFromMovement will then update the anchors position as the width & height changes
|
||||
node.setSizeFromMovement(movementX, movementY);
|
||||
}
|
||||
return { ...nodes };
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* delete is going to delete the ResizeNode from the resizeNodesStore.
|
||||
*/
|
||||
delete() {
|
||||
const store = stores[this.canvasId];
|
||||
const { resizeNodesStore } = stores[this.canvasId];
|
||||
resizeNodesStore.update((resizeNodes) => {
|
||||
for (const resizeNodeId in resizeNodes) {
|
||||
if (resizeNodes[resizeNodeId].id === this.id) {
|
||||
delete resizeNodes[resizeNodeId];
|
||||
}
|
||||
}
|
||||
return { ...resizeNodes };
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { findStore } from '../../store/controllers/storeApi';
|
||||
import type { ResizeNodeType } from '../../store/types/types';
|
||||
import { writable, derived, get, readable } from 'svelte/store';
|
||||
|
||||
export let resizeId: string;
|
||||
export let canvasId: string;
|
||||
|
||||
const store = findStore(canvasId);
|
||||
const { resizeNodesStore, nodeSelected } = store;
|
||||
|
||||
let isSelected = false;
|
||||
let reactResizeNode: ResizeNodeType;
|
||||
$: reactResizeNode = $resizeNodesStore[resizeId];
|
||||
</script>
|
||||
|
||||
<svelte:window
|
||||
on:mousemove={(e) => {
|
||||
e.preventDefault();
|
||||
if (isSelected) {
|
||||
resizeNodesStore.update((resNode) => {
|
||||
const newResNode = resNode[resizeId];
|
||||
const d3Scale = get(store.d3Scale);
|
||||
newResNode.setPositionAndCascade(
|
||||
e.movementX / d3Scale,
|
||||
e.movementY / d3Scale,
|
||||
resizeId
|
||||
);
|
||||
return { ...resNode };
|
||||
});
|
||||
}
|
||||
}}
|
||||
on:mouseup={(e) => {
|
||||
e.preventDefault();
|
||||
// don't need to set $nodeSelected = false because Node component will do it for you
|
||||
isSelected = false;
|
||||
}}
|
||||
/>
|
||||
|
||||
<div
|
||||
on:mousedown={(e) => {
|
||||
e.preventDefault();
|
||||
$nodeSelected = true;
|
||||
isSelected = true;
|
||||
}}
|
||||
on:mouseover={(e) => ($nodeSelected = true)}
|
||||
on:focus
|
||||
on:mouseleave={(e) => ($nodeSelected = false)}
|
||||
on:mouseenter={(e) => ($nodeSelected = true)}
|
||||
on:wheel|preventDefault
|
||||
class="ResizeNode"
|
||||
style="
|
||||
left: {reactResizeNode.positionX - 10}px;
|
||||
top: {reactResizeNode.positionY - 10}px;
|
||||
width: {20}px;
|
||||
height: {20}px;
|
||||
background-color: purple;
|
||||
border-color: purple;
|
||||
border-radius: 50%;"
|
||||
id="svelvet-{resizeId}"
|
||||
/>
|
||||
|
||||
<style>
|
||||
.ResizeNode {
|
||||
position: absolute;
|
||||
display: grid;
|
||||
user-select: none;
|
||||
cursor: move;
|
||||
justify-content: center;
|
||||
overscroll-behavior: auto;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
border: solid 1px black;
|
||||
border-radius: 5px;
|
||||
box-shadow: 1px 1px 3px 1px rgba(0, 0, 0, 0.2);
|
||||
opacity: 0;
|
||||
pointer-events: auto; /* this is needed for pointer events to work since we disable them in graphview */
|
||||
}
|
||||
|
||||
.ResizeNode:hover {
|
||||
cursor: nwse-resize;
|
||||
opacity: 0.5;
|
||||
}
|
||||
</style>
|
||||
@@ -30,18 +30,13 @@ populateSvelvetStoreFromUserInput(canvasId, nodes, edges)
|
||||
- edges: same as nodes, this is an array of objects containing edge info THAT IS DIFFERENT FROM THE EDGE CLASS.
|
||||
- Returns: store
|
||||
*/
|
||||
import { stores } from '../models/store';
|
||||
import { writable, get } from 'svelte/store';
|
||||
import { stores } from '../models/store'
|
||||
import { writable, get } from 'svelte/store'
|
||||
|
||||
import type { StoreType } from '../types/types';
|
||||
import type { UserNodeType, UserEdgeType } from '../../types/types';
|
||||
import {
|
||||
populateAnchorsStore,
|
||||
populateNodesStore,
|
||||
populateEdgesStore,
|
||||
populateResizeNodeStore,
|
||||
} from './util';
|
||||
import { populateCollapsibleStore } from '../../collapsible/controllers/util';
|
||||
import type { StoreType } from '../types/types'
|
||||
import type { UserNodeType, UserEdgeType } from '../../types/types'
|
||||
import { populateAnchorsStore, populateNodesStore, populateEdgesStore } from './util'
|
||||
import { populateCollapsibleStore } from '../../collapsible/controllers/util'
|
||||
|
||||
/**
|
||||
* findStore is going to return the target Svelvet store with the canvasId provided as argument.
|
||||
@@ -50,7 +45,7 @@ import { populateCollapsibleStore } from '../../collapsible/controllers/util';
|
||||
* @returns The store of a Svelvet component that matches the canvasId
|
||||
*/
|
||||
export function findStore(canvasId: string): StoreType {
|
||||
return stores[canvasId];
|
||||
return stores[canvasId]
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,33 +63,31 @@ export function findStore(canvasId: string): StoreType {
|
||||
* @returns An empty store for the newly created Svelvet component.
|
||||
*/
|
||||
export function createStoreEmpty(canvasId: string): StoreType {
|
||||
stores[canvasId] = {
|
||||
nodesStore: writable({}),
|
||||
edgesStore: writable({}),
|
||||
anchorsStore: writable({}),
|
||||
resizeNodesStore: writable({}),
|
||||
potentialAnchorsStore: writable({}),
|
||||
widthStore: writable(600),
|
||||
heightStore: writable(600),
|
||||
backgroundStore: writable(false),
|
||||
movementStore: writable(true),
|
||||
nodeSelected: writable(false),
|
||||
nodeIdSelected: writable(-1),
|
||||
d3Scale: writable(1),
|
||||
options: writable({}),
|
||||
temporaryEdgeStore: writable([]),
|
||||
nodeCreate: writable(false), // this option sets whether the "nodeEdit" feature is enabled
|
||||
boundary: writable(false),
|
||||
edgeEditModal: writable(null), // this is used for edgeEditModal feature. When an edge is right clicked, store.edgeEditModal is set to the edgeId string. This causes a modal to be rendered
|
||||
collapsibleStore: writable([]), // this is used for the collaspsible node feature. If the feature is enabled, store.collapsible will be populated with Collapsible objects which will track whether the node should be displayed or not
|
||||
collapsibleOption: writable(false),
|
||||
lockedOption: writable(false),
|
||||
editableOption: writable(false), // true if you want nodes/edges to be editable. See feature editEdges
|
||||
d3ZoomParameters: writable({}), // this stores d3 parameters x, y, and zoom. This isn't used for anything other than giving users a way to access d3 zoom parameters if they want to build on top of Svelvet
|
||||
resizableOption: writable(true), // option to turn on/off resizable nodes.
|
||||
highlightEdgesOption: writable(true), // option to turn on/off highlightable edges
|
||||
};
|
||||
return stores[canvasId];
|
||||
stores[canvasId] = {
|
||||
nodesStore: writable({}),
|
||||
edgesStore: writable({}),
|
||||
anchorsStore: writable({}),
|
||||
potentialAnchorsStore: writable({}),
|
||||
widthStore: writable(600),
|
||||
heightStore: writable(600),
|
||||
backgroundStore: writable(false),
|
||||
movementStore: writable(true),
|
||||
nodeSelected: writable(false),
|
||||
nodeIdSelected: writable(-1),
|
||||
d3Scale: writable(1),
|
||||
options: writable({}),
|
||||
temporaryEdgeStore: writable([]),
|
||||
nodeCreate: writable(false), // this option sets whether the "nodeEdit" feature is enabled
|
||||
boundary: writable(false),
|
||||
edgeEditModal: writable(null), // this is used for edgeEditModal feature. When an edge is right clicked, store.edgeEditModal is set to the edgeId string. This causes a modal to be rendered
|
||||
collapsibleStore: writable([]), // this is used for the collaspsible node feature. If the feature is enabled, store.collapsible will be populated with Collapsible objects which will track whether the node should be displayed or not
|
||||
collapsibleOption: writable(false),
|
||||
lockedOption: writable(false),
|
||||
editableOption: writable(false), // true if you want nodes/edges to be editable. See feature editEdges
|
||||
d3ZoomParameters: writable({}), // this stores d3 parameters x, y, and zoom. This isn't used for anything other than giving users a way to access d3 zoom parameters if they want to build on top of Svelvet
|
||||
highlightEdgesOption: writable(true) // option to turn on/off highlightable edges
|
||||
}
|
||||
return stores[canvasId]
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,24 +98,20 @@ export function createStoreEmpty(canvasId: string): StoreType {
|
||||
* @param edges Same as nodes, this is an array of objects containing edge info THAT IS DIFFERENT FROM THE EDGE CLASS.
|
||||
*/
|
||||
export function populateSvelvetStoreFromUserInput(
|
||||
canvasId: string,
|
||||
nodes: UserNodeType[],
|
||||
edges: UserEdgeType[]
|
||||
canvasId: string,
|
||||
nodes: UserNodeType[],
|
||||
edges: UserEdgeType[]
|
||||
): void {
|
||||
// find the store
|
||||
const store = findStore(canvasId);
|
||||
// find the store
|
||||
const store = findStore(canvasId)
|
||||
|
||||
// populate store.nodesStore with user nodes
|
||||
populateNodesStore(store, nodes, canvasId);
|
||||
// populate store.anchorsStore with anchors. Note the userdoes not explictly define anchors; anchors are calculated from the edges
|
||||
populateAnchorsStore(store, nodes, edges, canvasId);
|
||||
// populate edges
|
||||
populateEdgesStore(store, edges, canvasId);
|
||||
//populate resize Store
|
||||
if (get(store.resizableOption))
|
||||
populateResizeNodeStore(store, nodes, canvasId);
|
||||
// populate store.nodesStore with user nodes
|
||||
populateNodesStore(store, nodes, canvasId)
|
||||
// populate store.anchorsStore with anchors. Note the userdoes not explictly define anchors; anchors are calculated from the edges
|
||||
populateAnchorsStore(store, nodes, edges, canvasId)
|
||||
// populate edges
|
||||
populateEdgesStore(store, edges, canvasId)
|
||||
|
||||
// populatate collapsible objects if "collapsible" feature is turned on
|
||||
if (get(store.collapsibleOption))
|
||||
populateCollapsibleStore(store, nodes, edges, canvasId);
|
||||
// populatate collapsible objects if "collapsible" feature is turned on
|
||||
if (get(store.collapsibleOption)) populateCollapsibleStore(store, nodes, edges, canvasId)
|
||||
}
|
||||
|
||||
@@ -1,51 +1,15 @@
|
||||
const pkStringGenerator = () => (Math.random() + 1).toString(36).substring(7);
|
||||
import {
|
||||
rightCb,
|
||||
leftCb,
|
||||
topCb,
|
||||
bottomCb,
|
||||
} from '../../edges/controllers/anchorCbUser'; // these are callbacks used to calculate anchor position relative to node
|
||||
import {
|
||||
dynamicCbCreator,
|
||||
fixedCbCreator,
|
||||
} from '../../edges/controllers/anchorCbDev';
|
||||
const pkStringGenerator = () => (Math.random() + 1).toString(36).substring(7) // these are callbacks used to calculate anchor position relative to node
|
||||
import { dynamicCbCreator, fixedCbCreator } from '../../edges/controllers/anchorCbDev'
|
||||
|
||||
import type { AnchorType, AnchorCbType } from '../../edges/types/types';
|
||||
import type { AnchorType, AnchorCbType } from '../../edges/types/types'
|
||||
|
||||
import type {
|
||||
NodeType,
|
||||
EdgeType,
|
||||
StoreType,
|
||||
ResizeNodeType,
|
||||
PotentialAnchorType,
|
||||
} from '../types/types';
|
||||
import type { UserNodeType, UserEdgeType } from '../../types/types';
|
||||
import type { NodeType, EdgeType, StoreType, ResizeNodeType } from '../types/types'
|
||||
import type { UserNodeType, UserEdgeType } from '../../types/types'
|
||||
|
||||
import { ResizeNode } from '../../resizableNodes/models/ResizeNode';
|
||||
import { Anchor } from '../../edges/models/Anchor';
|
||||
import { Node } from '../../nodes/models/Node';
|
||||
import { Edge } from '../../edges/models/Edge';
|
||||
import { writable, derived, get, readable } from 'svelte/store';
|
||||
import { getEdgeById, getAnchors } from '../../edges/controllers/util';
|
||||
|
||||
/**
|
||||
* Creates resize node on the bottom right corner of the targeted Node
|
||||
* @param canvasId The canvasId of the Svelvet component that holds the targeted Node
|
||||
* @param nodeId The id of the Node that the resize node attached to
|
||||
* @param posX The number of pixels on the x-axis relative to the left top corner of the targeted Node
|
||||
* @param posY The number of pixels on the y-axis relative to the left top corner of the targeted Node
|
||||
* @returns A ReziseNode object with randomized id, canvasId, nodeId, posX, and posY
|
||||
*/
|
||||
function createResizeNode(
|
||||
canvasId: string,
|
||||
nodeId: string,
|
||||
posX: number,
|
||||
posY: number
|
||||
) {
|
||||
const id = pkStringGenerator();
|
||||
const resizeNode = new ResizeNode(id, canvasId, nodeId, posX, posY);
|
||||
return resizeNode;
|
||||
}
|
||||
import { Anchor } from '../../edges/models/Anchor'
|
||||
import { Node } from '../../nodes/models/Node'
|
||||
import { Edge } from '../../edges/models/Edge'
|
||||
import { getAnchors } from '../../edges/controllers/util'
|
||||
|
||||
/**
|
||||
* Creates an Anchor on the targeted Node with infomation the userNode holds
|
||||
@@ -58,45 +22,44 @@ function createResizeNode(
|
||||
*/
|
||||
|
||||
function createAnchor(
|
||||
store: StoreType,
|
||||
userNode: UserNodeType | null,
|
||||
sourceOrTarget: 'source' | 'target',
|
||||
canvasId: string,
|
||||
edge: UserEdgeType
|
||||
store: StoreType,
|
||||
userNode: UserNodeType | null,
|
||||
sourceOrTarget: 'source' | 'target',
|
||||
canvasId: string,
|
||||
edge: UserEdgeType
|
||||
) {
|
||||
// edge case
|
||||
if (userNode === null)
|
||||
throw `you cannot create an anchor without a user node (for now)`;
|
||||
// edge case
|
||||
if (userNode === null) throw `you cannot create an anchor without a user node (for now)`
|
||||
|
||||
const edgeId = edge.id;
|
||||
const anchorId = pkStringGenerator();
|
||||
const edgeId = edge.id
|
||||
const anchorId = pkStringGenerator()
|
||||
|
||||
// userCb is the appropriate source or taret callback from userEdge object. It is
|
||||
// possible the user to NOT set userCb in which case userCb will be undefined
|
||||
let userCb: Function | undefined;
|
||||
if (sourceOrTarget === 'target') userCb = edge.targetAnchorCb;
|
||||
else userCb = edge.sourceAnchorCb;
|
||||
// userCb is the appropriate source or taret callback from userEdge object. It is
|
||||
// possible the user to NOT set userCb in which case userCb will be undefined
|
||||
let userCb: Function | undefined
|
||||
if (sourceOrTarget === 'target') userCb = edge.targetAnchorCb
|
||||
else userCb = edge.sourceAnchorCb
|
||||
|
||||
// create anchor callbacks
|
||||
// create anchor callbacks
|
||||
|
||||
let cb: AnchorCbType;
|
||||
if (userCb === undefined) cb = dynamicCbCreator(store, edgeId, anchorId);
|
||||
else cb = fixedCbCreator(store, edgeId, anchorId, userNode.id, userCb);
|
||||
let cb: AnchorCbType
|
||||
if (userCb === undefined) cb = dynamicCbCreator(store, edgeId, anchorId)
|
||||
else cb = fixedCbCreator(store, edgeId, anchorId, userNode.id, userCb)
|
||||
|
||||
// Create a new anchor.
|
||||
const anchor = new Anchor(
|
||||
anchorId,
|
||||
userNode.id,
|
||||
edgeId,
|
||||
sourceOrTarget,
|
||||
-1, // dummy variables for x,y,angle for now
|
||||
-1, // dummy variables for x,y,angle for now
|
||||
cb,
|
||||
canvasId,
|
||||
0 // dummy variables for x,y,angle for now
|
||||
);
|
||||
// return
|
||||
return anchor;
|
||||
// Create a new anchor.
|
||||
const anchor = new Anchor(
|
||||
anchorId,
|
||||
userNode.id,
|
||||
edgeId,
|
||||
sourceOrTarget,
|
||||
-1, // dummy variables for x,y,angle for now
|
||||
-1, // dummy variables for x,y,angle for now
|
||||
cb,
|
||||
canvasId,
|
||||
0 // dummy variables for x,y,angle for now
|
||||
)
|
||||
// return
|
||||
return anchor
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,71 +68,67 @@ function createAnchor(
|
||||
* @param edges An edge that the user specifies. This is NOT the same as a Edge object.
|
||||
* @param canvasId The canvasId of the Svelvet component that holds the Edges
|
||||
*/
|
||||
export function populateEdgesStore(
|
||||
store: StoreType,
|
||||
edges: UserEdgeType[],
|
||||
canvasId: string
|
||||
) {
|
||||
const edgesStore: { [key: string]: EdgeType } = {};
|
||||
for (let i = 0; i < edges.length; i++) {
|
||||
const userEdge = edges[i];
|
||||
// { id: 'e1-2', source: 1, type: 'straight', target: 2, label: 'e1-2' },
|
||||
// source is node.id for the source node
|
||||
// target is node.id for the target node
|
||||
// We need to get the anchors
|
||||
const {
|
||||
source: sourceNodeId,
|
||||
target: targetNodeId,
|
||||
id: edgeId,
|
||||
type,
|
||||
label,
|
||||
labelBgColor,
|
||||
labelTextColor,
|
||||
edgeColor,
|
||||
animate,
|
||||
noHandle,
|
||||
arrow,
|
||||
clickCallback,
|
||||
className,
|
||||
} = userEdge;
|
||||
export function populateEdgesStore(store: StoreType, edges: UserEdgeType[], canvasId: string) {
|
||||
const edgesStore: { [key: string]: EdgeType } = {}
|
||||
for (let i = 0; i < edges.length; i++) {
|
||||
const userEdge = edges[i]
|
||||
// { id: 'e1-2', source: 1, type: 'straight', target: 2, label: 'e1-2' },
|
||||
// source is node.id for the source node
|
||||
// target is node.id for the target node
|
||||
// We need to get the anchors
|
||||
const {
|
||||
source: sourceNodeId,
|
||||
target: targetNodeId,
|
||||
id: edgeId,
|
||||
type,
|
||||
label,
|
||||
labelBgColor,
|
||||
labelTextColor,
|
||||
edgeColor,
|
||||
animate,
|
||||
noHandle,
|
||||
arrow,
|
||||
clickCallback,
|
||||
className
|
||||
} = userEdge
|
||||
|
||||
const anchors = getAnchors(store, { edgeId: edgeId });
|
||||
// check that we have two anchors for every edge
|
||||
if (anchors.length !== 2) throw 'We should have two anchors for every node';
|
||||
// check that we have 1 source anchor and 1 target anchor. Since sourceOrTarget is typed to be either 'source'
|
||||
// or 'target', it suffices to check whether there are two unique elements
|
||||
if (new Set(anchors.map((e) => e.sourceOrTarget)).size !== 2)
|
||||
throw 'we should have one source and one target anchor';
|
||||
// get source and target anchor
|
||||
let sourceAnchor, targetAnchor;
|
||||
if (anchors[0].sourceOrTarget === 'source') {
|
||||
sourceAnchor = anchors[0];
|
||||
targetAnchor = anchors[1];
|
||||
} else {
|
||||
sourceAnchor = anchors[1];
|
||||
targetAnchor = anchors[0];
|
||||
}
|
||||
const anchors = getAnchors(store, { edgeId: edgeId })
|
||||
// check that we have two anchors for every edge
|
||||
if (anchors.length !== 2) throw 'We should have two anchors for every node'
|
||||
// check that we have 1 source anchor and 1 target anchor. Since sourceOrTarget is typed to be either 'source'
|
||||
// or 'target', it suffices to check whether there are two unique elements
|
||||
if (new Set(anchors.map((e) => e.sourceOrTarget)).size !== 2)
|
||||
throw 'we should have one source and one target anchor'
|
||||
// get source and target anchor
|
||||
let sourceAnchor, targetAnchor
|
||||
if (anchors[0].sourceOrTarget === 'source') {
|
||||
sourceAnchor = anchors[0]
|
||||
targetAnchor = anchors[1]
|
||||
} else {
|
||||
sourceAnchor = anchors[1]
|
||||
targetAnchor = anchors[0]
|
||||
}
|
||||
|
||||
edgesStore[edgeId] = new Edge(
|
||||
edgeId,
|
||||
sourceAnchor.positionX,
|
||||
sourceAnchor.positionY,
|
||||
targetAnchor.positionX,
|
||||
targetAnchor.positionY,
|
||||
canvasId,
|
||||
userEdge.label === undefined ? '' : userEdge.label,
|
||||
userEdge.type === undefined ? 'bezier' : userEdge.type,
|
||||
userEdge.labelBgColor === undefined ? 'white' : userEdge.labelBgColor,
|
||||
userEdge.labelTextColor === undefined ? 'black' : userEdge.labelTextColor,
|
||||
userEdge.edgeColor === undefined ? 'black' : userEdge.edgeColor,
|
||||
userEdge.animate === undefined ? false : userEdge.animate,
|
||||
userEdge.noHandle === undefined ? false : userEdge.noHandle,
|
||||
userEdge.arrow === undefined ? false : userEdge.arrow,
|
||||
userEdge.clickCallback === undefined ? () => { } : userEdge.clickCallback,
|
||||
userEdge.className === undefined ? '' : userEdge.className
|
||||
);
|
||||
}
|
||||
store.edgesStore.set(edgesStore);
|
||||
edgesStore[edgeId] = new Edge(
|
||||
edgeId,
|
||||
sourceAnchor.positionX,
|
||||
sourceAnchor.positionY,
|
||||
targetAnchor.positionX,
|
||||
targetAnchor.positionY,
|
||||
canvasId,
|
||||
userEdge.label === undefined ? '' : userEdge.label,
|
||||
userEdge.type === undefined ? 'bezier' : userEdge.type,
|
||||
userEdge.labelBgColor === undefined ? 'white' : userEdge.labelBgColor,
|
||||
userEdge.labelTextColor === undefined ? 'black' : userEdge.labelTextColor,
|
||||
userEdge.edgeColor === undefined ? 'black' : userEdge.edgeColor,
|
||||
userEdge.animate === undefined ? false : userEdge.animate,
|
||||
userEdge.noHandle === undefined ? false : userEdge.noHandle,
|
||||
userEdge.arrow === undefined ? false : userEdge.arrow,
|
||||
userEdge.clickCallback === undefined ? () => {} : userEdge.clickCallback,
|
||||
userEdge.className === undefined ? '' : userEdge.className
|
||||
)
|
||||
}
|
||||
store.edgesStore.set(edgesStore)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,18 +137,14 @@ export function populateEdgesStore(
|
||||
* @param userNodes The array of userNodes (NOT the same as Node object)
|
||||
* @returns The node that user specified or null if not found
|
||||
*/
|
||||
function findUserNodeById(
|
||||
id: string,
|
||||
userNodes: UserNodeType[]
|
||||
): UserNodeType | null {
|
||||
for (let i = 0; i < userNodes.length; i++) {
|
||||
const userNode = userNodes[i];
|
||||
if (userNode.id === id) return userNode;
|
||||
}
|
||||
return null;
|
||||
function findUserNodeById(id: string, userNodes: UserNodeType[]): UserNodeType | null {
|
||||
for (let i = 0; i < userNodes.length; i++) {
|
||||
const userNode = userNodes[i]
|
||||
if (userNode.id === id) return userNode
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Populates the anchorsStore. This will overwrite any data in the AnchorsStore.
|
||||
* @param store The Svelvet store containing the state of the Svelvet component
|
||||
@@ -198,50 +153,38 @@ function findUserNodeById(
|
||||
* @param canvasId The canvasId of the Svelvet component that holds the nodes and edges
|
||||
*/
|
||||
export function populateAnchorsStore(
|
||||
store: StoreType,
|
||||
nodes: UserNodeType[],
|
||||
edges: UserEdgeType[],
|
||||
canvasId: string
|
||||
store: StoreType,
|
||||
nodes: UserNodeType[],
|
||||
edges: UserEdgeType[],
|
||||
canvasId: string
|
||||
) {
|
||||
// anchorsStore will populated and eventaully synchronized to store.anchorsStore
|
||||
const anchorsStore: { [key: string]: AnchorType } = {};
|
||||
// iterate through user edges. Note the user never explicitly defines anchors; we calculate anchors
|
||||
// from the user edge/node information
|
||||
for (let i = 0; i < edges.length; i++) {
|
||||
const userEdge = edges[i];
|
||||
// find the source and target userNodes. These will be used to create the nodeId foreign key and
|
||||
// determine placement of the anchor based on userNode.targetPosition, useNode.sourcePosition
|
||||
const { source: sourceNodeId, target: targetNodeId } = userEdge;
|
||||
const sourceUserNode = findUserNodeById(sourceNodeId, nodes);
|
||||
const targetUserNode = findUserNodeById(targetNodeId, nodes);
|
||||
// create source anchor
|
||||
const sourceAnchor = createAnchor(
|
||||
store,
|
||||
sourceUserNode,
|
||||
'source',
|
||||
canvasId,
|
||||
userEdge
|
||||
);
|
||||
// create target anchor
|
||||
const targetAnchor = createAnchor(
|
||||
store,
|
||||
targetUserNode,
|
||||
'target',
|
||||
canvasId,
|
||||
userEdge
|
||||
);
|
||||
// store source and target anchors
|
||||
anchorsStore[sourceAnchor.id] = sourceAnchor;
|
||||
anchorsStore[targetAnchor.id] = targetAnchor;
|
||||
}
|
||||
// anchorsStore will populated and eventaully synchronized to store.anchorsStore
|
||||
const anchorsStore: { [key: string]: AnchorType } = {}
|
||||
// iterate through user edges. Note the user never explicitly defines anchors; we calculate anchors
|
||||
// from the user edge/node information
|
||||
for (let i = 0; i < edges.length; i++) {
|
||||
const userEdge = edges[i]
|
||||
// find the source and target userNodes. These will be used to create the nodeId foreign key and
|
||||
// determine placement of the anchor based on userNode.targetPosition, useNode.sourcePosition
|
||||
const { source: sourceNodeId, target: targetNodeId } = userEdge
|
||||
const sourceUserNode = findUserNodeById(sourceNodeId, nodes)
|
||||
const targetUserNode = findUserNodeById(targetNodeId, nodes)
|
||||
// create source anchor
|
||||
const sourceAnchor = createAnchor(store, sourceUserNode, 'source', canvasId, userEdge)
|
||||
// create target anchor
|
||||
const targetAnchor = createAnchor(store, targetUserNode, 'target', canvasId, userEdge)
|
||||
// store source and target anchors
|
||||
anchorsStore[sourceAnchor.id] = sourceAnchor
|
||||
anchorsStore[targetAnchor.id] = targetAnchor
|
||||
}
|
||||
|
||||
//populates the anchorsStore
|
||||
store.anchorsStore.set(anchorsStore);
|
||||
//populates the anchorsStore
|
||||
store.anchorsStore.set(anchorsStore)
|
||||
|
||||
// set anchor positions. We can only set anchor positions after anchorsStore and nodesStore
|
||||
// has been populated. TODO: maybe add a check to see that anchorsStore and NodesStore populated?
|
||||
const anchors = getAnchors(store);
|
||||
for (const anchor of anchors) anchor.callback();
|
||||
// set anchor positions. We can only set anchor positions after anchorsStore and nodesStore
|
||||
// has been populated. TODO: maybe add a check to see that anchorsStore and NodesStore populated?
|
||||
const anchors = getAnchors(store)
|
||||
for (const anchor of anchors) anchor.callback()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -250,68 +193,36 @@ export function populateAnchorsStore(
|
||||
* @param nodes An array of user specifed nodes
|
||||
* @param canvasId The canvasId of the Svelvet component that holds the nodes
|
||||
*/
|
||||
export function populateNodesStore(
|
||||
store: StoreType,
|
||||
nodes: UserNodeType[],
|
||||
canvasId: string
|
||||
) {
|
||||
// this is the nodesStore object. THIS IS NOT THE SAME AS A NODESTORE
|
||||
const nodesStore: { [key: string]: NodeType } = {};
|
||||
// iterate through user nodes and create node objects
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
const userNode: UserNodeType = nodes[i];
|
||||
const nodeId = userNode.id;
|
||||
export function populateNodesStore(store: StoreType, nodes: UserNodeType[], canvasId: string) {
|
||||
// this is the nodesStore object. THIS IS NOT THE SAME AS A NODESTORE
|
||||
const nodesStore: { [key: string]: NodeType } = {}
|
||||
// iterate through user nodes and create node objects
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
const userNode: UserNodeType = nodes[i]
|
||||
const nodeId = userNode.id
|
||||
|
||||
// TODO: move sanitizing default values to middleware
|
||||
const node = new Node(
|
||||
nodeId.toString(),
|
||||
userNode.position.x,
|
||||
userNode.position.y,
|
||||
userNode.width,
|
||||
userNode.height,
|
||||
userNode.bgColor ?? 'white',
|
||||
userNode.data,
|
||||
canvasId,
|
||||
userNode.borderColor === undefined ? 'black' : userNode.borderColor,
|
||||
userNode.image === undefined ? false : userNode.image,
|
||||
userNode.src === undefined ? '' : userNode.src,
|
||||
userNode.textColor === undefined ? '' : userNode.textColor,
|
||||
userNode.borderRadius === undefined ? 0 : userNode.borderRadius,
|
||||
userNode.childNodes === undefined ? [] : userNode.childNodes,
|
||||
userNode.className === undefined ? '' : userNode.className,
|
||||
userNode.clickCallback === undefined ? () => { } : userNode.clickCallback
|
||||
);
|
||||
// TODO: move sanitizing default values to middleware
|
||||
const node = new Node(
|
||||
nodeId.toString(),
|
||||
userNode.position.x,
|
||||
userNode.position.y,
|
||||
userNode.width,
|
||||
userNode.height,
|
||||
userNode.bgColor ?? 'white',
|
||||
userNode.data,
|
||||
canvasId,
|
||||
userNode.borderColor === undefined ? 'black' : userNode.borderColor,
|
||||
userNode.image === undefined ? false : userNode.image,
|
||||
userNode.src === undefined ? '' : userNode.src,
|
||||
userNode.textColor === undefined ? '' : userNode.textColor,
|
||||
userNode.borderRadius === undefined ? 0 : userNode.borderRadius,
|
||||
userNode.childNodes === undefined ? [] : userNode.childNodes,
|
||||
userNode.className === undefined ? '' : userNode.className,
|
||||
userNode.clickCallback === undefined ? () => {} : userNode.clickCallback
|
||||
)
|
||||
|
||||
nodesStore[nodeId] = node;
|
||||
}
|
||||
// This is actually what sets the store
|
||||
store.nodesStore.set(nodesStore);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates the resizeNodeStore. If a Node is resizable, a small ResizeNode object is going to be attached to the Node's right bottom corner to react to the mouse drag.
|
||||
* @param store The Svelvet store containing the state of the Svelvet component
|
||||
* @param nodes An array of user specifed nodes (NOT the same as Node)
|
||||
* @param canvasId The canvasId of the Svelvet component that holds the resizeNodes
|
||||
*/
|
||||
export function populateResizeNodeStore(
|
||||
store: StoreType,
|
||||
nodes: UserNodeType[],
|
||||
canvasId: string
|
||||
) {
|
||||
const resizeNodeStore: { [key: string]: ResizeNodeType } = {};
|
||||
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
const userNode = nodes[i];
|
||||
const { id, width, height, position } = userNode;
|
||||
const resizeNode = createResizeNode(
|
||||
canvasId,
|
||||
id,
|
||||
position.x + width,
|
||||
position.y + height
|
||||
);
|
||||
resizeNodeStore[resizeNode.id] = resizeNode;
|
||||
}
|
||||
|
||||
store.resizeNodesStore.set(resizeNodeStore);
|
||||
nodesStore[nodeId] = node
|
||||
}
|
||||
// This is actually what sets the store
|
||||
store.nodesStore.set(nodesStore)
|
||||
}
|
||||
|
||||
@@ -1,124 +1,125 @@
|
||||
import type { CollapsibleType } from '../../collapsible/types/types';
|
||||
import type { Writable } from 'svelte/store';
|
||||
import type { AnchorType } from '../../edges/types/types';
|
||||
import type { SvelteComponentTyped } from "svelte";
|
||||
import type { CollapsibleType } from '../../collapsible/types/types'
|
||||
import type { Writable } from 'svelte/store'
|
||||
import type { AnchorType } from '../../edges/types/types'
|
||||
|
||||
export interface ResizeNodeType {
|
||||
id: string;
|
||||
nodeId: string;
|
||||
edgeId?: string;
|
||||
canvasId: string;
|
||||
anchorId?: string;
|
||||
positionX: number;
|
||||
positionY: number;
|
||||
setPositionAndCascade: Function;
|
||||
setPosition: Function;
|
||||
delete: Function;
|
||||
id: string
|
||||
nodeId: string
|
||||
edgeId?: string
|
||||
canvasId: string
|
||||
anchorId?: string
|
||||
positionX: number
|
||||
positionY: number
|
||||
setPositionAndCascade: Function
|
||||
setPosition: Function
|
||||
delete: Function
|
||||
}
|
||||
|
||||
/*
|
||||
Type for a single svelvet store
|
||||
*/
|
||||
export interface StoreType {
|
||||
nodesStore: Writable<{ [key: string]: NodeType }>;
|
||||
edgesStore: Writable<{ [key: string]: EdgeType }>;
|
||||
anchorsStore: Writable<{ [key: string]: AnchorType }>;
|
||||
resizeNodesStore: Writable<{ [key: string]: ResizeNodeType }>;
|
||||
potentialAnchorsStore: Writable<{ [key: string]: PotentialAnchorType }>;
|
||||
widthStore: Writable<number>;
|
||||
heightStore: Writable<number>;
|
||||
backgroundStore: Writable<boolean>;
|
||||
movementStore: Writable<boolean>;
|
||||
nodeIdSelected: Writable<number>;
|
||||
nodeSelected: Writable<boolean>; // this is used to stop d3 panning when node is being dragged
|
||||
d3Scale: Writable<number>; // for zoom and pan
|
||||
options: Writable<{ [key: string]: any }>;
|
||||
temporaryEdgeStore: Writable<TemporaryEdgeType[]>;
|
||||
nodeCreate: Writable<boolean>; // this option sets whether the "nodeEdit" feature is enabled
|
||||
boundary: Writable<boolean | PositionType>;
|
||||
edgeEditModal: Writable<null | string>; // this options is used to place the edgeEdit modal when an edge is right-clicked. null is no modal, positionType if modal should be placed at position defined by postionType.x, positionType.y
|
||||
collapsibleStore: Writable<CollapsibleType[]>;
|
||||
collapsibleOption: Writable<boolean>;
|
||||
lockedOption: Writable<boolean>;
|
||||
editableOption: Writable<boolean>;
|
||||
d3ZoomParameters: Writable<{
|
||||
[key: string]: number;
|
||||
}>;
|
||||
resizableOption: Writable<boolean>;
|
||||
highlightEdgesOption: Writable<boolean>;
|
||||
nodesStore: Writable<{ [key: string]: NodeType }>
|
||||
edgesStore: Writable<{ [key: string]: EdgeType }>
|
||||
anchorsStore: Writable<{ [key: string]: AnchorType }>
|
||||
potentialAnchorsStore: Writable<{ [key: string]: PotentialAnchorType }>
|
||||
widthStore: Writable<number>
|
||||
heightStore: Writable<number>
|
||||
backgroundStore: Writable<boolean>
|
||||
movementStore: Writable<boolean>
|
||||
nodeIdSelected: Writable<number>
|
||||
nodeSelected: Writable<boolean> // this is used to stop d3 panning when node is being dragged
|
||||
d3Scale: Writable<number> // for zoom and pan
|
||||
options: Writable<{ [key: string]: any }>
|
||||
temporaryEdgeStore: Writable<TemporaryEdgeType[]>
|
||||
nodeCreate: Writable<boolean> // this option sets whether the "nodeEdit" feature is enabled
|
||||
boundary: Writable<boolean | PositionType>
|
||||
edgeEditModal: Writable<null | string> // this options is used to place the edgeEdit modal when an edge is right-clicked. null is no modal, positionType if modal should be placed at position defined by postionType.x, positionType.y
|
||||
collapsibleStore: Writable<CollapsibleType[]>
|
||||
collapsibleOption: Writable<boolean>
|
||||
lockedOption: Writable<boolean>
|
||||
editableOption: Writable<boolean>
|
||||
d3ZoomParameters: Writable<{
|
||||
[key: string]: number
|
||||
}>
|
||||
highlightEdgesOption: Writable<boolean>
|
||||
}
|
||||
|
||||
export interface PositionType {
|
||||
x: number;
|
||||
y: number;
|
||||
x: number
|
||||
y: number
|
||||
}
|
||||
|
||||
export interface NodeType {
|
||||
id: string;
|
||||
width: number;
|
||||
height: number;
|
||||
positionX: number;
|
||||
positionY: number;
|
||||
bgColor: string;
|
||||
data: { html?: any, custom?: { component: any, props?: any, cb?: (e: string, detail: any) => void }, img?: any, label?: string }
|
||||
canvasId: string;
|
||||
setPositionFromMovement: Function;
|
||||
delete: Function; //This is the method to delete the node from the store
|
||||
setSizeFromMovement: Function;
|
||||
setExportableData: Function;
|
||||
borderColor: string;
|
||||
image: boolean;
|
||||
src: string;
|
||||
textColor: string;
|
||||
borderRadius: number;
|
||||
childNodes: string[];
|
||||
className: string; //This is for custom className for node
|
||||
clickCallback: Function; // user-supplied callback that executes when the node is clicked
|
||||
id: string
|
||||
width: number
|
||||
height: number
|
||||
positionX: number
|
||||
positionY: number
|
||||
bgColor: string
|
||||
data: {
|
||||
html?: any
|
||||
custom?: { component: any; props?: any; cb?: (e: string, detail: any) => void }
|
||||
img?: any
|
||||
label?: string
|
||||
}
|
||||
canvasId: string
|
||||
setPositionFromMovement: Function
|
||||
setSizeFromMovement: Function
|
||||
setExportableData: Function
|
||||
borderColor: string
|
||||
image: boolean
|
||||
src: string
|
||||
textColor: string
|
||||
borderRadius: number
|
||||
childNodes: string[]
|
||||
className: string //This is for custom className for node
|
||||
clickCallback: Function // user-supplied callback that executes when the node is clicked
|
||||
}
|
||||
|
||||
export interface EdgeType {
|
||||
id: string;
|
||||
sourceX: number;
|
||||
sourceY: number;
|
||||
targetX: number;
|
||||
targetY: number;
|
||||
canvasId: string;
|
||||
label: string;
|
||||
type: 'straight' | 'smoothstep' | 'step' | 'bezier';
|
||||
labelBgColor: string;
|
||||
labelTextColor: string;
|
||||
edgeColor: string;
|
||||
animate: boolean;
|
||||
noHandle: boolean;
|
||||
arrow: boolean;
|
||||
clickCallback: Function;
|
||||
className: string;
|
||||
delete: Function;
|
||||
setExportableData: Function;
|
||||
id: string
|
||||
sourceX: number
|
||||
sourceY: number
|
||||
targetX: number
|
||||
targetY: number
|
||||
canvasId: string
|
||||
label: string
|
||||
type: 'straight' | 'smoothstep' | 'step' | 'bezier'
|
||||
labelBgColor: string
|
||||
labelTextColor: string
|
||||
edgeColor: string
|
||||
animate: boolean
|
||||
noHandle: boolean
|
||||
arrow: boolean
|
||||
clickCallback: Function
|
||||
className: string
|
||||
delete: Function
|
||||
setExportableData: Function
|
||||
}
|
||||
|
||||
export interface PotentialAnchorType {
|
||||
id: string;
|
||||
nodeId: string;
|
||||
callback: Function; // callback is used to calculate positionX, positionY based on parent node's data, and set the anchor position // TODO: rename to something better
|
||||
positionX: number;
|
||||
positionY: number;
|
||||
angle: number;
|
||||
canvasId: string;
|
||||
delete: Function;
|
||||
id: string
|
||||
nodeId: string
|
||||
callback: Function // callback is used to calculate positionX, positionY based on parent node's data, and set the anchor position // TODO: rename to something better
|
||||
positionX: number
|
||||
positionY: number
|
||||
angle: number
|
||||
canvasId: string
|
||||
delete: Function
|
||||
}
|
||||
|
||||
export interface TemporaryEdgeType {
|
||||
id: string;
|
||||
sourcePotentialAnchorId: string; // this will always be set
|
||||
sourceX: number;
|
||||
sourceY: number;
|
||||
targetPotentialAnchorId: string | null; // this will be null until the temporary edge reaches another temporary anchor
|
||||
targetX: number;
|
||||
targetY: number;
|
||||
canvasId: string;
|
||||
type: string;
|
||||
edgeColor: string;
|
||||
createEdge: Function;
|
||||
createNode: Function;
|
||||
id: string
|
||||
sourcePotentialAnchorId: string // this will always be set
|
||||
sourceX: number
|
||||
sourceY: number
|
||||
targetPotentialAnchorId: string | null // this will be null until the temporary edge reaches another temporary anchor
|
||||
targetX: number
|
||||
targetY: number
|
||||
canvasId: string
|
||||
type: string
|
||||
edgeColor: string
|
||||
createEdge: Function
|
||||
createNode: Function
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"extends": ["tslint-etc"],
|
||||
"rules": {
|
||||
"no-unused-declaration": true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user