mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-24 00:00:46 +00:00
feat(frontend): multiselect components for apps
This commit is contained in:
@@ -235,7 +235,6 @@
|
||||
<input
|
||||
{autofocus}
|
||||
on:focus={(e) => {
|
||||
window.dispatchEvent(new Event('pointerup'))
|
||||
dispatch('focus')
|
||||
}}
|
||||
{disabled}
|
||||
@@ -254,7 +253,6 @@
|
||||
<Toggle
|
||||
on:pointerdown={(e) => {
|
||||
e?.stopPropagation()
|
||||
window.dispatchEvent(new Event('pointerup'))
|
||||
}}
|
||||
{disabled}
|
||||
class={valid
|
||||
@@ -334,7 +332,6 @@
|
||||
<textarea
|
||||
bind:this={el}
|
||||
on:focus={(e) => {
|
||||
window.dispatchEvent(new Event('pointerup'))
|
||||
dispatch('focus')
|
||||
}}
|
||||
{autofocus}
|
||||
@@ -355,7 +352,6 @@
|
||||
{:else if inputCat == 'enum'}
|
||||
<select
|
||||
on:focus={(e) => {
|
||||
window.dispatchEvent(new Event('pointerup'))
|
||||
dispatch('focus')
|
||||
}}
|
||||
{disabled}
|
||||
@@ -372,7 +368,6 @@
|
||||
<div class="border my-1 mb-4 w-full border-gray-400">
|
||||
<SimpleEditor
|
||||
on:focus={(e) => {
|
||||
window.dispatchEvent(new Event('pointerup'))
|
||||
dispatch('focus')
|
||||
}}
|
||||
on:blur={() => dispatch('blur')}
|
||||
@@ -411,7 +406,6 @@
|
||||
rows="1"
|
||||
bind:this={el}
|
||||
on:focus={(e) => {
|
||||
window.dispatchEvent(new Event('pointerup'))
|
||||
dispatch('focus')
|
||||
}}
|
||||
on:blur={() => dispatch('blur')}
|
||||
|
||||
@@ -194,6 +194,14 @@
|
||||
loadHubScripts()
|
||||
|
||||
function onKeyDown(event: KeyboardEvent) {
|
||||
let classes = event.target?.['className']
|
||||
if (
|
||||
(typeof classes === 'string' && classes.includes('inputarea')) ||
|
||||
['INPUT', 'TEXTAREA'].includes(document.activeElement?.tagName!)
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
switch (event.key) {
|
||||
case 'Z':
|
||||
if (event.ctrlKey) {
|
||||
|
||||
@@ -161,7 +161,6 @@
|
||||
{:else}
|
||||
<input
|
||||
on:focus={(e) => {
|
||||
window.dispatchEvent(new Event('pointerup'))
|
||||
dispatch('focus')
|
||||
}}
|
||||
type="number"
|
||||
@@ -179,7 +178,6 @@
|
||||
<Toggle
|
||||
on:pointerdown={(e) => {
|
||||
e?.stopPropagation()
|
||||
window.dispatchEvent(new Event('pointerup'))
|
||||
}}
|
||||
class={valid
|
||||
? ''
|
||||
@@ -256,7 +254,6 @@
|
||||
<textarea
|
||||
bind:this={el}
|
||||
on:focus={(e) => {
|
||||
window.dispatchEvent(new Event('pointerup'))
|
||||
dispatch('focus')
|
||||
}}
|
||||
use:autosize
|
||||
@@ -274,7 +271,6 @@
|
||||
{:else if inputCat == 'enum'}
|
||||
<select
|
||||
on:focus={(e) => {
|
||||
window.dispatchEvent(new Event('pointerup'))
|
||||
dispatch('focus')
|
||||
}}
|
||||
class="px-6"
|
||||
@@ -307,7 +303,6 @@
|
||||
rows="1"
|
||||
bind:this={el}
|
||||
on:focus={(e) => {
|
||||
window.dispatchEvent(new Event('pointerup'))
|
||||
dispatch('focus')
|
||||
}}
|
||||
on:blur={() => dispatch('blur')}
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
</span>
|
||||
{/if}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div class="relative" on:pointerdown on:click|stopPropagation>
|
||||
<div class="relative" on:click|stopPropagation>
|
||||
<input
|
||||
on:focus
|
||||
on:click
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
event?.stopPropagation()
|
||||
event?.preventDefault()
|
||||
|
||||
$selectedComponent = id
|
||||
$selectedComponent = [id]
|
||||
|
||||
if (preclickAction) {
|
||||
await preclickAction()
|
||||
|
||||
@@ -98,7 +98,6 @@
|
||||
style={css?.button?.style ?? ''}
|
||||
on:pointerdown={(e) => {
|
||||
e?.stopPropagation()
|
||||
window.dispatchEvent(new Event('pointerup'))
|
||||
}}
|
||||
on:click={() => {
|
||||
runnableComponent?.runComponent()
|
||||
|
||||
@@ -106,7 +106,6 @@
|
||||
btnClasses="my-1"
|
||||
on:pointerdown={(e) => {
|
||||
e?.stopPropagation()
|
||||
window.dispatchEvent(new Event('pointerup'))
|
||||
}}
|
||||
on:click={async () => {
|
||||
if (!runnableComponent) {
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
|
||||
function selectComponent() {
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = id
|
||||
$selectedComponent = [id]
|
||||
$focusedGrid = undefined
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,7 +312,7 @@
|
||||
style="padding-top: {controlsHeight ?? 0}px; {css?.container?.style ?? ''}"
|
||||
/>
|
||||
{/if}
|
||||
{#if $mode !== 'preview' && $selectedComponent === id}
|
||||
{#if $mode !== 'preview' && $selectedComponent?.includes(id)}
|
||||
<button
|
||||
class="fixed z-10 bottom-0 left-0 px-2 py-0.5 bg-indigo-500/90
|
||||
hover:bg-indigo-500 focus:bg-indigo-500 duration-200 text-white text-2xs"
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
>
|
||||
<div
|
||||
on:pointerdown|stopPropagation={() => {
|
||||
$selectedComponent = id
|
||||
$selectedComponent = [id]
|
||||
}}
|
||||
style:height="{clientHeight}px"
|
||||
style:width="{clientWidth}px"
|
||||
|
||||
@@ -151,7 +151,7 @@
|
||||
const hasActions = actionButtons.length >= 1
|
||||
|
||||
if (hasActions) {
|
||||
$selectedComponent = actionButtons[0].id
|
||||
$selectedComponent = [actionButtons[0].id]
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -273,13 +273,13 @@
|
||||
$hoverStore = undefined
|
||||
}
|
||||
}}
|
||||
class={(actionButton.id === $selectedComponent ||
|
||||
class={($selectedComponent?.includes(actionButton.id) ||
|
||||
$hoverStore === actionButton.id) &&
|
||||
$mode !== 'preview'
|
||||
? 'outline outline-indigo-500 outline-1 outline-offset-1 relative '
|
||||
: ''}
|
||||
>
|
||||
{#if actionButton.id === $selectedComponent || $hoverStore === actionButton.id}
|
||||
{#if $selectedComponent?.includes(actionButton.id) || $hoverStore === actionButton.id}
|
||||
<span
|
||||
title={`Id: ${actionButton.id}`}
|
||||
class={classNames(
|
||||
@@ -307,10 +307,10 @@
|
||||
controls={{
|
||||
left: () => {
|
||||
if (actionIndex === 0) {
|
||||
$selectedComponent = id
|
||||
$selectedComponent = [id]
|
||||
return true
|
||||
} else if (actionIndex > 0) {
|
||||
$selectedComponent = actionButtons[actionIndex - 1].id
|
||||
$selectedComponent = [actionButtons[actionIndex - 1].id]
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -319,7 +319,7 @@
|
||||
if (actionIndex === actionButtons.length - 1) {
|
||||
return id
|
||||
} else if (actionIndex < actionButtons.length - 1) {
|
||||
$selectedComponent = actionButtons[actionIndex + 1].id
|
||||
$selectedComponent = [actionButtons[actionIndex + 1].id]
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
@@ -14,7 +14,8 @@
|
||||
export let name: string
|
||||
export let inlineScript: InlineScript | undefined
|
||||
export let fields: Record<string, StaticAppInput | ConnectedAppInput | RowAppInput | UserAppInput>
|
||||
export let doNotRecomputeOnInputChanged: boolean = false
|
||||
export let doNotRecomputeOnInputChanged: boolean
|
||||
export let recomputableByRefreshButton: boolean
|
||||
|
||||
let result: any = undefined
|
||||
|
||||
@@ -40,8 +41,8 @@
|
||||
type: 'runnableByName'
|
||||
}}
|
||||
wrapperClass="hidden"
|
||||
recomputable
|
||||
{outputs}
|
||||
{recomputableByRefreshButton}
|
||||
>
|
||||
<slot />
|
||||
</RunnableComponent>
|
||||
|
||||
@@ -4,21 +4,12 @@
|
||||
import type { AppViewerContext } from '../../types'
|
||||
|
||||
export let componentId: string
|
||||
|
||||
const { runnableComponents, worldStore } = getContext<AppViewerContext>('AppViewerContext')
|
||||
export let loading: boolean
|
||||
const { runnableComponents } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
async function refresh() {
|
||||
window.dispatchEvent(new Event('pointerup'))
|
||||
|
||||
await $runnableComponents[componentId]?.()
|
||||
await $runnableComponents[componentId]?.cb?.()
|
||||
}
|
||||
let loading = false
|
||||
$: $worldStore?.outputsById[componentId]?.['loading']?.subscribe({
|
||||
id: 'refresh-' + componentId,
|
||||
next: (value) => {
|
||||
loading = value
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<button
|
||||
|
||||
@@ -29,11 +29,11 @@
|
||||
export let wrapperStyle = ''
|
||||
export let initializing: boolean | undefined = undefined
|
||||
export let render: boolean
|
||||
export let recomputable: boolean = false
|
||||
export let outputs: { result: Output<any>; loading: Output<boolean> }
|
||||
export let extraKey = ''
|
||||
export let doNotRecomputeOnInputChanged: boolean = false
|
||||
export let loading = false
|
||||
export let recomputableByRefreshButton: boolean = true
|
||||
|
||||
const {
|
||||
worldStore,
|
||||
@@ -52,12 +52,13 @@
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
if (recomputable || autoRefresh) {
|
||||
$runnableComponents[id] = async (inlineScript?: InlineScript) => {
|
||||
$runnableComponents[id] = {
|
||||
autoRefresh: autoRefresh && recomputableByRefreshButton,
|
||||
cb: async (inlineScript?: InlineScript) => {
|
||||
await executeComponent(true, inlineScript)
|
||||
}
|
||||
$runnableComponents = $runnableComponents
|
||||
}
|
||||
$runnableComponents = $runnableComponents
|
||||
|
||||
let args: Record<string, any> | undefined = undefined
|
||||
let testIsLoading = false
|
||||
@@ -377,7 +378,7 @@
|
||||
{/if}
|
||||
{#if !initializing && autoRefresh === true}
|
||||
<div class="flex absolute top-1 right-1 z-50">
|
||||
<RefreshButton componentId={id} />
|
||||
<RefreshButton {loading} componentId={id} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
|
||||
export function onSuccess() {
|
||||
if (recomputeIds) {
|
||||
recomputeIds.map((id) => $runnableComponents?.[id]?.())
|
||||
recomputeIds.map((id) => $runnableComponents?.cb?.[id]?.())
|
||||
}
|
||||
if (!doOnSuccess) return
|
||||
|
||||
|
||||
@@ -38,10 +38,6 @@
|
||||
|
||||
<AlignWrapper {render} {horizontalAlignment} {verticalAlignment}>
|
||||
<Toggle
|
||||
on:pointerdown={(e) => {
|
||||
e?.stopPropagation()
|
||||
window.dispatchEvent(new Event('pointerup'))
|
||||
}}
|
||||
checked={defaultValue}
|
||||
options={{ right: labelValue }}
|
||||
textClass={css?.text?.class ?? ''}
|
||||
|
||||
@@ -44,14 +44,14 @@
|
||||
<AlignWrapper {render} {verticalAlignment}>
|
||||
{#if inputType === 'date'}
|
||||
<input
|
||||
on:focus={() => ($selectedComponent = id)}
|
||||
on:focus={() => ($selectedComponent = [id])}
|
||||
on:pointerdown|stopPropagation
|
||||
type="date"
|
||||
bind:value
|
||||
min={minValue}
|
||||
max={maxValue}
|
||||
placeholder="Type..."
|
||||
class={twMerge('mx-0.5', css?.input?.class ?? '')}
|
||||
class={twMerge(css?.input?.class ?? '')}
|
||||
style={css?.input?.style ?? ''}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
<InputValue {id} input={configuration.placeholder} bind:value={placeholder} />
|
||||
|
||||
<AlignWrapper {render} {horizontalAlignment} {verticalAlignment}>
|
||||
<div class="app-select w-full mx-0.5" style="height: 34px" on:pointerdown|stopPropagation>
|
||||
<div class="app-select w-full" style="height: 34px" on:pointerdown|stopPropagation>
|
||||
{#if !value || Array.isArray(value)}
|
||||
<Select
|
||||
--border-radius="0"
|
||||
@@ -74,11 +74,11 @@
|
||||
{placeholder}
|
||||
on:click={() => {
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = id
|
||||
$selectedComponent = [id]
|
||||
}
|
||||
}}
|
||||
on:focus={() => {
|
||||
$selectedComponent = id
|
||||
$selectedComponent = [id]
|
||||
}}
|
||||
floatingConfig={{
|
||||
strategy: 'fixed'
|
||||
|
||||
@@ -46,10 +46,10 @@
|
||||
|
||||
<AlignWrapper {render} {verticalAlignment}>
|
||||
<input
|
||||
on:pointerdown|stopPropagation={() => ($selectedComponent = id)}
|
||||
on:focus={() => ($selectedComponent = id)}
|
||||
on:pointerdown|stopPropagation={() => ($selectedComponent = [id])}
|
||||
on:focus={() => ($selectedComponent = [id])}
|
||||
class={twMerge(
|
||||
'windmillapp w-full py-1.5 text-sm focus:ring-indigo-100 px-2 mx-0.5',
|
||||
'windmillapp w-full py-1.5 text-sm focus:ring-indigo-100 px-2',
|
||||
css?.input?.class ?? ''
|
||||
)}
|
||||
style={css?.input?.style ?? ''}
|
||||
|
||||
@@ -54,7 +54,6 @@
|
||||
|
||||
function onChange(e: CustomEvent) {
|
||||
e?.stopPropagation()
|
||||
window.dispatchEvent(new Event('pointerup'))
|
||||
|
||||
if (create) {
|
||||
listItems = listItems.map((i) => {
|
||||
@@ -105,7 +104,7 @@
|
||||
<InputValue {id} input={configuration.create} bind:value={create} />
|
||||
|
||||
<AlignWrapper {render} {horizontalAlignment} {verticalAlignment}>
|
||||
<div class="app-select w-full mx-0.5" style="height: 34px;" on:pointerdown|stopPropagation>
|
||||
<div class="app-select w-full" style="height: 34px;" on:pointerdown|stopPropagation>
|
||||
<Select
|
||||
--border-radius="0"
|
||||
--border-color="#999"
|
||||
@@ -122,11 +121,11 @@
|
||||
{placeholder}
|
||||
on:click={() => {
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = id
|
||||
$selectedComponent = [id]
|
||||
}
|
||||
}}
|
||||
on:focus={() => {
|
||||
$selectedComponent = id
|
||||
$selectedComponent = [id]
|
||||
}}
|
||||
floatingConfig={{
|
||||
strategy: 'fixed'
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
class="grow"
|
||||
style="--range-handle-focus: {'#7e9abd'}; --range-handle: {'#7e9abd'}; {css?.bar?.style ??
|
||||
''}"
|
||||
on:pointerdown|stopPropagation={() => ($selectedComponent = id)}
|
||||
on:pointerdown|stopPropagation={() => ($selectedComponent = [id])}
|
||||
>
|
||||
<RangeSlider bind:slider bind:values {step} min={+min} max={+max} />
|
||||
</div>
|
||||
|
||||
@@ -2,10 +2,8 @@
|
||||
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, RichConfigurations } from '../../types'
|
||||
import { concatCustomCss } from '../../utils'
|
||||
import { concatCustomCss, selectId } from '../../utils'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import InputValue from '../helpers/InputValue.svelte'
|
||||
|
||||
@@ -46,12 +44,11 @@
|
||||
{#if inputType === 'password'}
|
||||
<input
|
||||
class={twMerge(
|
||||
'windmillapp w-full py-1.5 text-sm focus:ring-indigo-100 px-2 mx-0.5',
|
||||
'windmillapp w-full py-1.5 text-sm focus:ring-indigo-100 px-2 ',
|
||||
css?.input?.class ?? ''
|
||||
)}
|
||||
style={css?.input?.style ?? ''}
|
||||
on:focus|stopPropagation={() => ($selectedComponent = id)}
|
||||
on:pointerdown|stopPropagation={() => ($selectedComponent = id)}
|
||||
on:pointerdown|stopPropagation={(e) => selectId(e, id, selectedComponent, $app)}
|
||||
type="password"
|
||||
bind:value
|
||||
{placeholder}
|
||||
@@ -59,12 +56,11 @@
|
||||
{:else if inputType === 'text'}
|
||||
<input
|
||||
class={twMerge(
|
||||
'windmillapp w-full py-1.5 text-sm focus:ring-indigo-100 px-2 mx-0.5',
|
||||
'windmillapp w-full py-1.5 text-sm focus:ring-indigo-100 px-2 ',
|
||||
css?.input?.class ?? ''
|
||||
)}
|
||||
style={css?.input?.style ?? ''}
|
||||
on:focus={() => ($selectedComponent = id)}
|
||||
on:pointerdown|stopPropagation={() => ($selectedComponent = id)}
|
||||
on:pointerdown|stopPropagation={(e) => selectId(e, id, selectedComponent, $app)}
|
||||
type="text"
|
||||
bind:value
|
||||
{placeholder}
|
||||
@@ -72,12 +68,11 @@
|
||||
{:else if inputType === 'email'}
|
||||
<input
|
||||
class={twMerge(
|
||||
'windmillapp w-full py-1.5 text-sm focus:ring-indigo-100 px-2 mx-0.5',
|
||||
'windmillapp w-full py-1.5 text-sm focus:ring-indigo-100 px-2 ',
|
||||
css?.input?.class ?? ''
|
||||
)}
|
||||
style={css?.input?.style ?? ''}
|
||||
on:focus={() => ($selectedComponent = id)}
|
||||
on:pointerdown|stopPropagation={() => ($selectedComponent = id)}
|
||||
on:pointerdown|stopPropagation={(e) => selectId(e, id, selectedComponent, $app)}
|
||||
type="email"
|
||||
bind:value
|
||||
{placeholder}
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
{#key isNegativeAllowed}
|
||||
{#key locale}
|
||||
{#key currency}
|
||||
<div class="w-full" on:pointerdown|stopPropagation={() => ($selectedComponent = id)}>
|
||||
<div class="w-full" on:pointerdown|stopPropagation={() => ($selectedComponent = [id])}>
|
||||
<CurrencyInput
|
||||
inputClasses={{
|
||||
formatted: twMerge('px-2 w-full py-1.5 windmillapp', css?.input?.class),
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
$: $selectedComponent === id && onFocus()
|
||||
$: $selectedComponent?.includes(id) && onFocus()
|
||||
|
||||
$: css = concatCustomCss($app.css?.containercomponent, customCss)
|
||||
</script>
|
||||
@@ -38,7 +38,7 @@
|
||||
subGridId={`${id}-0`}
|
||||
containerHeight={componentContainerHeight}
|
||||
on:focus={() => {
|
||||
$selectedComponent = id
|
||||
$selectedComponent = [id]
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
@@ -47,7 +47,6 @@
|
||||
{disabled}
|
||||
on:pointerdown={(e) => {
|
||||
e?.stopPropagation()
|
||||
window.dispatchEvent(new Event('pointerup'))
|
||||
}}
|
||||
on:click={async (e) => {
|
||||
$focusedGrid = {
|
||||
@@ -89,7 +88,7 @@
|
||||
subGridId={`${id}-0`}
|
||||
containerHeight={1200}
|
||||
on:focus={() => {
|
||||
$selectedComponent = id
|
||||
$selectedComponent = [id]
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
$: $selectedComponent === id && onFocus()
|
||||
$: $selectedComponent?.includes(id) && onFocus()
|
||||
$: css = concatCustomCss($app.css?.containercomponent, customCss)
|
||||
|
||||
$componentControl[id] = {
|
||||
@@ -73,7 +73,7 @@
|
||||
<div
|
||||
class="w-full h-full"
|
||||
on:pointerdown|stopPropagation={() => {
|
||||
$selectedComponent = id
|
||||
$selectedComponent = [id]
|
||||
$focusedGrid = {
|
||||
parentComponentId: id,
|
||||
subGridIndex: index
|
||||
@@ -90,7 +90,7 @@
|
||||
subGridId={`${id}-${index}`}
|
||||
containerHeight={horizontal ? undefined : componentContainerHeight - 8}
|
||||
on:focus={() => {
|
||||
$selectedComponent = id
|
||||
$selectedComponent = [id]
|
||||
$focusedGrid = {
|
||||
parentComponentId: id,
|
||||
subGridIndex: index
|
||||
|
||||
@@ -118,7 +118,7 @@
|
||||
? componentContainerHeight - tabHeight
|
||||
: componentContainerHeight}
|
||||
on:focus={() => {
|
||||
$selectedComponent = id
|
||||
$selectedComponent = [id]
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
export let fromHub: boolean = false
|
||||
|
||||
const appStore = writable<App>(app)
|
||||
const selectedComponent = writable<string | undefined>(undefined)
|
||||
const selectedComponent = writable<string[] | undefined>(undefined)
|
||||
const mode = writable<EditorMode>(initialMode)
|
||||
const breakpoint = writable<EditorBreakpoint>('lg')
|
||||
const summaryStore = writable(summary)
|
||||
@@ -83,7 +83,7 @@
|
||||
mode,
|
||||
connectingInput,
|
||||
breakpoint,
|
||||
runnableComponents,
|
||||
runnableComponents: writable({}),
|
||||
appPath: path,
|
||||
workspace: $workspaceStore ?? '',
|
||||
onchange: () => saveDraft(),
|
||||
@@ -105,7 +105,7 @@
|
||||
history,
|
||||
pickVariableCallback,
|
||||
ontextfocus: writable(undefined),
|
||||
movingcomponent: writable(undefined),
|
||||
movingcomponents: writable(undefined),
|
||||
selectedComponentInEditor: writable(undefined)
|
||||
})
|
||||
|
||||
@@ -140,10 +140,10 @@
|
||||
|
||||
let selectedTab: 'insert' | 'settings' = 'insert'
|
||||
|
||||
$: if ($selectedComponent) {
|
||||
let befSelected: string | undefined = undefined
|
||||
$: if ($selectedComponent?.[0] != befSelected) {
|
||||
befSelected = $selectedComponent?.[0]
|
||||
selectedTab = 'settings'
|
||||
} else {
|
||||
selectedTab = 'insert'
|
||||
}
|
||||
|
||||
let itemPicker: ItemPicker | undefined = undefined
|
||||
|
||||
@@ -247,7 +247,17 @@
|
||||
let lock = false
|
||||
function onKeyDown(event: KeyboardEvent) {
|
||||
if (lock) return
|
||||
|
||||
let classes = event.target?.['className']
|
||||
if (
|
||||
(typeof classes === 'string' && classes.includes('inputarea')) ||
|
||||
['INPUT', 'TEXTAREA'].includes(document.activeElement?.tagName!)
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
lock = true
|
||||
|
||||
switch (event.key) {
|
||||
case 'Z':
|
||||
if (event.ctrlKey) {
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
export let isLocked = false
|
||||
|
||||
const appStore = writable<App>(app)
|
||||
const selectedComponent = writable<string | undefined>(undefined)
|
||||
const selectedComponent = writable<string[] | undefined>(undefined)
|
||||
const mode = writable<EditorMode>('preview')
|
||||
|
||||
const connectingInput = writable<ConnectingInput>({
|
||||
@@ -42,8 +42,6 @@
|
||||
hoveredComponent: undefined
|
||||
})
|
||||
|
||||
const runnableComponents = writable<Record<string, () => Promise<void>>>({})
|
||||
|
||||
const parentWidth = writable(0)
|
||||
setContext<AppViewerContext>('AppViewerContext', {
|
||||
worldStore: buildWorld(context),
|
||||
@@ -53,7 +51,7 @@
|
||||
mode,
|
||||
connectingInput,
|
||||
breakpoint,
|
||||
runnableComponents,
|
||||
runnableComponents: writable({}),
|
||||
appPath,
|
||||
workspace,
|
||||
onchange: undefined,
|
||||
@@ -118,7 +116,7 @@
|
||||
>
|
||||
<div>
|
||||
<GridViewer
|
||||
onTopId={$selectedComponent}
|
||||
onTopId={$selectedComponent?.[0]}
|
||||
items={app.grid}
|
||||
let:dataItem
|
||||
rowHeight={36}
|
||||
@@ -128,7 +126,7 @@
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
class={'h-full w-full center-center'}
|
||||
on:pointerdown={() => ($selectedComponent = dataItem.id)}
|
||||
on:pointerdown={() => ($selectedComponent = [dataItem.id])}
|
||||
>
|
||||
<Component render={true} component={dataItem.data} selected={false} locked={true} />
|
||||
</div>
|
||||
@@ -159,6 +157,8 @@
|
||||
inlineScript={script.inlineScript}
|
||||
name={script.name}
|
||||
fields={script.fields}
|
||||
doNotRecomputeOnInputChanged={script.doNotRecomputeOnInputChanged ?? false}
|
||||
recomputableByRefreshButton={script.autoRefresh ?? false}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
{#if selected || hover}
|
||||
<span
|
||||
on:mousedown|stopPropagation|capture
|
||||
draggable="false"
|
||||
title={`Id: ${component.id}`}
|
||||
class={twMerge(
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
import { push } from '$lib/history'
|
||||
import { expandGriditem, findGridItem } from './appUtils'
|
||||
import Grid from '../svelte-grid/Grid.svelte'
|
||||
import Toggle from '$lib/components/Toggle.svelte'
|
||||
import { selectId } from '../utils'
|
||||
|
||||
export let policy: Policy
|
||||
|
||||
@@ -64,9 +64,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
function selectComponent(id: string) {
|
||||
function selectComponent(e: PointerEvent, id: string) {
|
||||
if (!$connectingInput.opened) {
|
||||
$selectedComponent = id
|
||||
selectId(e, id, selectedComponent, $app)
|
||||
if ($focusedGrid?.parentComponentId != id) {
|
||||
$focusedGrid = undefined
|
||||
}
|
||||
@@ -108,7 +108,7 @@
|
||||
>
|
||||
<div class={!$focusedGrid && $mode !== 'preview' ? 'border-gray-400 border border-dashed' : ''}>
|
||||
<Grid
|
||||
onTopId={$selectedComponent}
|
||||
selectedIds={$selectedComponent}
|
||||
items={$app.grid}
|
||||
on:redraw={(e) => {
|
||||
push(history, $app)
|
||||
@@ -136,16 +136,16 @@
|
||||
{/if}
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
on:pointerdown={() => selectComponent(dataItem.id)}
|
||||
on:pointerdown={(e) => selectComponent(e, dataItem.id)}
|
||||
class={classNames(
|
||||
'h-full w-full center-center',
|
||||
$selectedComponent === dataItem.id ? 'active-grid-item' : ''
|
||||
Boolean($selectedComponent?.includes(dataItem.id)) ? 'active-grid-item' : ''
|
||||
)}
|
||||
>
|
||||
<Component
|
||||
render={true}
|
||||
component={dataItem.data}
|
||||
selected={$selectedComponent === dataItem.id}
|
||||
selected={Boolean($selectedComponent?.includes(dataItem.id))}
|
||||
locked={isFixed(dataItem)}
|
||||
on:delete={() => removeGridElement(dataItem.data)}
|
||||
on:lock={() => {
|
||||
@@ -157,7 +157,7 @@
|
||||
}}
|
||||
on:expand={() => {
|
||||
push(history, $app)
|
||||
$selectedComponent = dataItem.id
|
||||
$selectedComponent = [dataItem.id]
|
||||
expandGriditem($app.grid, dataItem.id, $breakpoint)
|
||||
$app = $app
|
||||
}}
|
||||
@@ -176,7 +176,8 @@
|
||||
inlineScript={script.inlineScript}
|
||||
name={script.name}
|
||||
fields={script.fields}
|
||||
doNotRecomputeOnInputChanged={script.doNotRecomputeOnInputChanged}
|
||||
doNotRecomputeOnInputChanged={script.doNotRecomputeOnInputChanged ?? false}
|
||||
recomputableByRefreshButton={script.autoRefresh ?? false}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
$worldStore.initializedOutputs ==
|
||||
allItems($app.grid, $app.subgrids).length + $app.hiddenInlineScripts.length &&
|
||||
refresh()
|
||||
$: componentNumber = Object.keys($runnableComponents).length
|
||||
$: componentNumber = Object.values($runnableComponents).filter((x) => x.autoRefresh).length
|
||||
|
||||
function onClick(stopAfterClear = true) {
|
||||
if (timeout) {
|
||||
@@ -46,13 +46,10 @@
|
||||
loading = true
|
||||
Promise.all(
|
||||
Object.keys($runnableComponents).map((id) => {
|
||||
if (id.startsWith('bg_')) {
|
||||
let index = parseInt(id.split('_')[1])
|
||||
if (!$app.hiddenInlineScripts[index]?.autoRefresh) {
|
||||
return
|
||||
}
|
||||
if (!$runnableComponents?.[id]?.autoRefresh) {
|
||||
return
|
||||
}
|
||||
return $runnableComponents?.[id]?.()
|
||||
return $runnableComponents?.[id]?.cb?.()
|
||||
})
|
||||
).finally(() => {
|
||||
loading = false
|
||||
@@ -82,6 +79,7 @@
|
||||
|
||||
<div class="flex items-center">
|
||||
<Button
|
||||
disabled={componentNumber == 0}
|
||||
on:click={() => onClick()}
|
||||
color={timeout ? 'blue' : 'light'}
|
||||
variant={timeout ? 'contained' : 'border'}
|
||||
|
||||
@@ -14,11 +14,11 @@
|
||||
|
||||
$: hiddenInlineScript = $app?.hiddenInlineScripts
|
||||
?.map((x, i) => ({ script: x, index: i }))
|
||||
.find(({ script, index }) => `bg_${index}` === $selectedComponent)
|
||||
.find(({ script, index }) => $selectedComponent?.includes(`bg_${index}`))
|
||||
|
||||
$: componentSettings = findComponentSettings($app, $selectedComponent)
|
||||
$: componentSettings = findComponentSettings($app, $selectedComponent?.[0])
|
||||
|
||||
$: tableActionSettings = findTableActionSettings($app, $selectedComponent)
|
||||
$: tableActionSettings = findTableActionSettings($app, $selectedComponent?.[0])
|
||||
|
||||
function findTableActionSettings(app: App, id: string | undefined) {
|
||||
return allItems(app.grid, app.subgrids)
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import { push } from '$lib/history'
|
||||
import Grid from '../svelte-grid/Grid.svelte'
|
||||
import GridViewer from './GridViewer.svelte'
|
||||
import { selectId } from '../utils'
|
||||
|
||||
export let containerHeight: number | undefined = undefined
|
||||
export let containerWidth: number | undefined = undefined
|
||||
@@ -35,10 +36,9 @@
|
||||
dispatch('focus')
|
||||
}
|
||||
|
||||
function selectComponent(id: string) {
|
||||
function selectComponent(e: PointerEvent, id: string) {
|
||||
if (!$connectingInput.opened) {
|
||||
dispatch('focus')
|
||||
$selectedComponent = id
|
||||
selectId(e, id, selectedComponent, $app)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
$app.subgrids[subGridId] = e.detail
|
||||
}
|
||||
}}
|
||||
onTopId={$selectedComponent}
|
||||
selectedIds={$selectedComponent}
|
||||
let:dataItem
|
||||
rowHeight={36}
|
||||
cols={columnConfiguration}
|
||||
@@ -106,17 +106,17 @@
|
||||
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
on:pointerdown={() => selectComponent(dataItem.id)}
|
||||
on:pointerdown={(e) => selectComponent(e, dataItem.id)}
|
||||
class={classNames(
|
||||
'h-full w-full center-center',
|
||||
$selectedComponent === dataItem.id ? 'active-grid-item' : '',
|
||||
$selectedComponent?.includes(dataItem.id) ? 'active-grid-item' : '',
|
||||
'top-0'
|
||||
)}
|
||||
>
|
||||
<Component
|
||||
render={visible}
|
||||
component={dataItem.data}
|
||||
selected={$selectedComponent === dataItem.id}
|
||||
selected={Boolean($selectedComponent?.includes(dataItem.id))}
|
||||
locked={isFixed(dataItem)}
|
||||
on:lock={() => lock(dataItem)}
|
||||
on:expand={() => {
|
||||
@@ -125,7 +125,7 @@
|
||||
if (!parentGridItem) {
|
||||
return
|
||||
}
|
||||
$selectedComponent = dataItem.id
|
||||
$selectedComponent = [dataItem.id]
|
||||
push(editorContext?.history, $app)
|
||||
|
||||
expandGriditem(
|
||||
@@ -142,7 +142,7 @@
|
||||
</div>
|
||||
{:else}
|
||||
<GridViewer
|
||||
onTopId={$selectedComponent}
|
||||
onTopId={$selectedComponent?.[0]}
|
||||
items={$app.subgrids?.[subGridId] ?? []}
|
||||
let:dataItem
|
||||
rowHeight={36}
|
||||
@@ -153,13 +153,13 @@
|
||||
>
|
||||
<!-- svelte-ignore a11y-click-events-have-key-events -->
|
||||
<div
|
||||
on:pointerdown={() => selectComponent(dataItem.id)}
|
||||
on:pointerdown|stopPropagation={(e) => selectComponent(e, dataItem.id)}
|
||||
class={classNames('h-full w-full center-center', 'top-0')}
|
||||
>
|
||||
<Component
|
||||
render={visible}
|
||||
component={dataItem.data}
|
||||
selected={$selectedComponent === dataItem.id}
|
||||
selected={Boolean($selectedComponent?.includes(dataItem.id))}
|
||||
locked={isFixed(dataItem)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -50,8 +50,9 @@
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
const editorContext = getContext<AppEditorContext>('AppEditorContext')
|
||||
const movingcomponent = editorContext?.movingcomponent
|
||||
$: ismoving = movingcomponent != undefined && $mode == 'dnd' && $movingcomponent === component.id
|
||||
const movingcomponents = editorContext?.movingcomponents
|
||||
$: ismoving =
|
||||
movingcomponents != undefined && $mode == 'dnd' && $movingcomponents?.includes(component.id)
|
||||
|
||||
let initializing: boolean | undefined = undefined
|
||||
let componentContainerHeight: number = 0
|
||||
@@ -91,7 +92,7 @@
|
||||
<button
|
||||
class="border p-0.5 text-xs"
|
||||
on:click={() => {
|
||||
$movingcomponent = undefined
|
||||
$movingcomponents = undefined
|
||||
}}>Cancel move</button
|
||||
>
|
||||
</div>
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
const { app, selectedComponent, worldStore, focusedGrid, componentControl } =
|
||||
getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
const { history, movingcomponent } = getContext<AppEditorContext>('AppEditorContext')
|
||||
const { history, movingcomponents } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
let tempGridItem: GridItem | undefined = undefined
|
||||
let copiedGridItem: GridItem | undefined = undefined
|
||||
let tempGridItems: GridItem[] | undefined = undefined
|
||||
let copiedGridItems: GridItem[] | undefined = undefined
|
||||
|
||||
function getSortedGridItemsOfChildren(): GridItem[] {
|
||||
if (!$focusedGrid) {
|
||||
@@ -32,7 +32,7 @@
|
||||
}
|
||||
|
||||
function getGridItems(): GridItem[] {
|
||||
if ($app.grid.find((item) => item.id === $selectedComponent)) {
|
||||
if ($app.grid.find((item) => item.id === $selectedComponent?.[0])) {
|
||||
return $app.grid
|
||||
}
|
||||
|
||||
@@ -42,23 +42,25 @@
|
||||
|
||||
return (
|
||||
Object.values($app.subgrids ?? {}).find((grid) =>
|
||||
grid.find((item) => item.id === $selectedComponent)
|
||||
grid.find((item) => item.id === $selectedComponent?.[0])
|
||||
) ?? []
|
||||
)
|
||||
}
|
||||
|
||||
function left(event: KeyboardEvent) {
|
||||
if (!$componentControl[$selectedComponent!]?.left?.()) {
|
||||
if (!$componentControl[$selectedComponent?.[0] ?? '']?.left?.()) {
|
||||
const sortedGridItems = getGridItems()
|
||||
const currentIndex = sortedGridItems.findIndex((item) => item.id === $selectedComponent)
|
||||
const currentIndex = sortedGridItems.findIndex(
|
||||
(item) => item.id === $selectedComponent?.[0] ?? ''
|
||||
)
|
||||
|
||||
if (currentIndex !== -1 && currentIndex > 0) {
|
||||
const left = sortedGridItems[currentIndex - 1]
|
||||
|
||||
if (left.data.type === 'tablecomponent' && left.data.actionButtons.length >= 1) {
|
||||
$selectedComponent = left.data.actionButtons[left.data.actionButtons.length - 1].id
|
||||
$selectedComponent = [left.data.actionButtons[left.data.actionButtons.length - 1].id]
|
||||
} else {
|
||||
$selectedComponent = left.id
|
||||
$selectedComponent = [left.id]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,19 +69,21 @@
|
||||
}
|
||||
|
||||
function right(event: KeyboardEvent) {
|
||||
let r = $componentControl[$selectedComponent!]?.right?.()
|
||||
let r = $componentControl[$selectedComponent?.[0] ?? '']?.right?.()
|
||||
|
||||
if (typeof r === 'string') {
|
||||
$selectedComponent = r
|
||||
$selectedComponent = [r]
|
||||
r = $componentControl[r]?.right?.(true)
|
||||
}
|
||||
|
||||
if (!r) {
|
||||
const sortedGridItems = getGridItems()
|
||||
const currentIndex = sortedGridItems.findIndex((item) => item.id === $selectedComponent)
|
||||
const currentIndex = sortedGridItems.findIndex(
|
||||
(item) => item.id === $selectedComponent?.[0] ?? ''
|
||||
)
|
||||
|
||||
if (currentIndex !== -1 && currentIndex < sortedGridItems.length - 1) {
|
||||
$selectedComponent = sortedGridItems[currentIndex + 1].id
|
||||
$selectedComponent = [sortedGridItems[currentIndex + 1].id]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +92,7 @@
|
||||
|
||||
function down(event: KeyboardEvent) {
|
||||
if (!$focusedGrid) {
|
||||
$selectedComponent = getSortedGridItemsOfChildren()[0]?.id
|
||||
$selectedComponent = [getSortedGridItemsOfChildren()[0]?.id]
|
||||
event.preventDefault()
|
||||
} else if ($app.subgrids) {
|
||||
const index = $focusedGrid?.subGridIndex ?? 0
|
||||
@@ -99,7 +103,7 @@
|
||||
}
|
||||
|
||||
if (subgrid) {
|
||||
$selectedComponent = subgrid[0].id
|
||||
$selectedComponent = [subgrid[0].id]
|
||||
}
|
||||
event.preventDefault()
|
||||
}
|
||||
@@ -113,10 +117,10 @@
|
||||
|
||||
function handleArrowUp(event: KeyboardEvent) {
|
||||
if (!$selectedComponent) return
|
||||
let parentId = findGridItemParentGrid($app, $selectedComponent)?.split('-')[0]
|
||||
let parentId = findGridItemParentGrid($app, $selectedComponent?.[0])?.split('-')[0]
|
||||
|
||||
if (parentId) {
|
||||
$selectedComponent = parentId
|
||||
$selectedComponent = [parentId]
|
||||
} else {
|
||||
$selectedComponent = undefined
|
||||
$focusedGrid = undefined
|
||||
@@ -127,72 +131,82 @@
|
||||
if (!$selectedComponent) {
|
||||
return
|
||||
}
|
||||
tempGridItem = undefined
|
||||
copiedGridItem = findGridItem($app, $selectedComponent)
|
||||
tempGridItems = undefined
|
||||
copiedGridItems = $selectedComponent
|
||||
.map((x) => findGridItem($app, x))
|
||||
.filter((x) => x != undefined) as GridItem[]
|
||||
}
|
||||
|
||||
function handleCut(event: KeyboardEvent) {
|
||||
if (!$selectedComponent) {
|
||||
return
|
||||
}
|
||||
$movingcomponent = $selectedComponent
|
||||
$movingcomponents = JSON.parse(JSON.stringify($selectedComponent))
|
||||
push(history, $app)
|
||||
|
||||
const gridItem = findGridItem($app, $selectedComponent)
|
||||
copiedGridItem = gridItem
|
||||
let gridItems = $selectedComponent
|
||||
.map((x) => findGridItem($app, x))
|
||||
.filter((x) => x != undefined) as GridItem[]
|
||||
copiedGridItems = gridItems
|
||||
|
||||
if (!gridItem) {
|
||||
if (!gridItems) {
|
||||
return
|
||||
}
|
||||
|
||||
// Store the grid item in a temp variable so we can paste it later
|
||||
tempGridItem = gridItem
|
||||
tempGridItems = gridItems
|
||||
}
|
||||
|
||||
function handlePaste(event: KeyboardEvent) {
|
||||
push(history, $app)
|
||||
$movingcomponent = undefined
|
||||
if (tempGridItem != undefined) {
|
||||
if (
|
||||
$focusedGrid &&
|
||||
getAllSubgridsAndComponentIds($app, tempGridItem.data)[0].includes(
|
||||
`${$focusedGrid.parentComponentId}-${$focusedGrid.subGridIndex}`
|
||||
$movingcomponents = undefined
|
||||
if (tempGridItems != undefined) {
|
||||
for (let tempGridItem of tempGridItems) {
|
||||
if (
|
||||
$focusedGrid &&
|
||||
getAllSubgridsAndComponentIds($app, tempGridItem.data)[0].includes(
|
||||
`${$focusedGrid.parentComponentId}-${$focusedGrid.subGridIndex}`
|
||||
)
|
||||
) {
|
||||
sendUserToast('Cannot paste a component into itself', true)
|
||||
return
|
||||
}
|
||||
let parentGrid = findGridItemParentGrid($app, tempGridItem.id)
|
||||
if (parentGrid) {
|
||||
$app.subgrids &&
|
||||
($app.subgrids[parentGrid] = $app.subgrids[parentGrid].filter(
|
||||
(item) => item.id !== tempGridItem?.id
|
||||
))
|
||||
} else {
|
||||
$app.grid = $app.grid.filter((item) => item.id !== tempGridItem?.id)
|
||||
}
|
||||
|
||||
const gridItem = tempGridItem
|
||||
insertNewGridItem(
|
||||
$app,
|
||||
(id) => ({ ...gridItem.data, id }),
|
||||
$focusedGrid,
|
||||
Object.fromEntries(gridColumns.map((column) => [column, gridItem[column]])),
|
||||
tempGridItem.id
|
||||
)
|
||||
) {
|
||||
sendUserToast('Cannot paste a component into itself', true)
|
||||
return
|
||||
}
|
||||
let parentGrid = findGridItemParentGrid($app, tempGridItem.id)
|
||||
if (parentGrid) {
|
||||
$app.subgrids &&
|
||||
($app.subgrids[parentGrid] = $app.subgrids[parentGrid].filter(
|
||||
(item) => item.id !== tempGridItem?.id
|
||||
))
|
||||
} else {
|
||||
$app.grid = $app.grid.filter((item) => item.id !== tempGridItem?.id)
|
||||
copiedGridItems = tempGridItems
|
||||
$selectedComponent = tempGridItems.map((x) => x.id)
|
||||
|
||||
tempGridItems = undefined
|
||||
} else if (copiedGridItems) {
|
||||
let nitems: string[] = []
|
||||
for (let copiedGridItem of copiedGridItems) {
|
||||
nitems.push(
|
||||
insertNewGridItem(
|
||||
$app,
|
||||
(id) => ({ ...copiedGridItem.data, id }),
|
||||
$focusedGrid,
|
||||
Object.fromEntries(gridColumns.map((column) => [column, copiedGridItem[column]]))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
const gridItem = tempGridItem
|
||||
insertNewGridItem(
|
||||
$app,
|
||||
(id) => ({ ...gridItem.data, id }),
|
||||
$focusedGrid,
|
||||
Object.fromEntries(gridColumns.map((column) => [column, gridItem[column]])),
|
||||
tempGridItem.id
|
||||
)
|
||||
|
||||
copiedGridItem = tempGridItem
|
||||
$selectedComponent = tempGridItem.id
|
||||
|
||||
tempGridItem = undefined
|
||||
} else if (copiedGridItem) {
|
||||
const gridItem = copiedGridItem
|
||||
$selectedComponent = insertNewGridItem(
|
||||
$app,
|
||||
(id) => ({ ...gridItem.data, id }),
|
||||
$focusedGrid,
|
||||
Object.fromEntries(gridColumns.map((column) => [column, gridItem[column]]))
|
||||
)
|
||||
$selectedComponent = nitems.map((x) => x)
|
||||
}
|
||||
|
||||
$worldStore = $worldStore
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
$focusedGrid
|
||||
)
|
||||
|
||||
$selectedComponent = id
|
||||
$selectedComponent = [id]
|
||||
$app = $app
|
||||
$worldStore = $worldStore
|
||||
}
|
||||
|
||||
@@ -36,12 +36,12 @@
|
||||
function onHeaderClick(manuallyOpen: boolean) {
|
||||
if (manuallyOpen) {
|
||||
if (parentId) {
|
||||
$selectedComponent = parentId
|
||||
$selectedComponent = [parentId]
|
||||
} else {
|
||||
$selectedComponent = undefined
|
||||
}
|
||||
} else {
|
||||
$selectedComponent = gridItem.id
|
||||
$selectedComponent = [gridItem.id]
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
+2
-2
@@ -14,12 +14,12 @@
|
||||
function onHeaderClick(manuallyOpen: boolean) {
|
||||
if (manuallyOpen) {
|
||||
if (id) {
|
||||
$selectedComponent = id
|
||||
$selectedComponent = [id]
|
||||
} else {
|
||||
$selectedComponent = undefined
|
||||
}
|
||||
} else {
|
||||
$selectedComponent = id
|
||||
$selectedComponent = [id]
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -22,7 +22,10 @@
|
||||
($hasResult[id] ||
|
||||
Object.entries($hasResult).some(([key, value]) => value && subids.includes(key)))
|
||||
$: open =
|
||||
$expanded || subids.includes($selectedComponent ?? '') || $manuallyOpened[id] || inSearch
|
||||
$expanded ||
|
||||
subids.some((x) => $selectedComponent?.includes(x)) ||
|
||||
$manuallyOpened[id] ||
|
||||
inSearch
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -64,7 +67,7 @@
|
||||
class={classNames(
|
||||
'flex items-center justify-between p-1 cursor-pointer border-b gap-1 truncate',
|
||||
hoverColor[color],
|
||||
$selectedComponent == id ? openBackground[color] : 'bg-white',
|
||||
$selectedComponent?.includes(id) ? openBackground[color] : 'bg-white',
|
||||
first ? 'border-t' : '',
|
||||
nested ? 'border-l' : ''
|
||||
)}
|
||||
@@ -76,7 +79,7 @@
|
||||
<div
|
||||
class={classNames(
|
||||
'text-2xs ml-0.5 font-bold px-2 py-0.5 rounded-sm',
|
||||
$selectedComponent == id ? idClass[color] : ' bg-gray-100'
|
||||
$selectedComponent?.includes(id) ? idClass[color] : ' bg-gray-100'
|
||||
)}
|
||||
>
|
||||
{id}
|
||||
|
||||
+3
-3
@@ -191,7 +191,7 @@
|
||||
btnClasses="!px-2 !py-1 !bg-gray-700 !text-white hover:!bg-gray-900"
|
||||
on:click={async () => {
|
||||
runLoading = true
|
||||
await $runnableComponents[id]?.(!transformer ? inlineScript : undefined)
|
||||
await $runnableComponents[id]?.cb?.(!transformer ? inlineScript : undefined)
|
||||
runLoading = false
|
||||
}}
|
||||
>
|
||||
@@ -227,7 +227,7 @@
|
||||
inlineScript.content = editor?.getCode() ?? ''
|
||||
}
|
||||
runLoading = true
|
||||
await $runnableComponents[id]?.(inlineScript)
|
||||
await $runnableComponents[id]?.cb?.(inlineScript)
|
||||
runLoading = false
|
||||
}}
|
||||
on:change={async (e) => {
|
||||
@@ -250,7 +250,7 @@
|
||||
bind:this={simpleEditor}
|
||||
cmdEnterAction={async () => {
|
||||
runLoading = true
|
||||
await $runnableComponents[id]?.(!transformer ? inlineScript : undefined)
|
||||
await $runnableComponents[id]?.cb?.(!transformer ? inlineScript : undefined)
|
||||
runLoading = false
|
||||
}}
|
||||
class="h-full"
|
||||
|
||||
+2
-2
@@ -16,7 +16,7 @@
|
||||
function selectScript(id: string) {
|
||||
$selectedComponentInEditor = id
|
||||
if (!id.startsWith('unused-') || !id.startsWith('bg_')) {
|
||||
$selectedComponent = $selectedComponentInEditor.split('_transformer')[0]
|
||||
$selectedComponent = [$selectedComponentInEditor.split('_transformer')[0]]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
$selectedComponent != $selectedComponentInEditor &&
|
||||
!$selectedComponentInEditor?.includes('_transformer')
|
||||
) {
|
||||
$selectedComponentInEditor = $selectedComponent
|
||||
$selectedComponentInEditor = $selectedComponent?.[0]
|
||||
}
|
||||
|
||||
function createBackgroundScript() {
|
||||
|
||||
@@ -300,6 +300,10 @@
|
||||
<Kbd>↑</Kbd><Kbd>→</Kbd>
|
||||
<Kbd>ESC</Kbd>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-gray-600 text-xs mr-2">Add to selection:</span>
|
||||
<Kbd>⇑</Kbd>+<Kbd>click</Kbd>
|
||||
</div>
|
||||
</div>
|
||||
</PanelSection>
|
||||
{/if}
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
$errorByComponent = clearErrorByComponentId(cid, $errorByComponent)
|
||||
$jobs = clearJobsByComponentId(cid, $jobs)
|
||||
|
||||
$selectedComponent = id
|
||||
$selectedComponent = [id]
|
||||
$app = $app
|
||||
}
|
||||
</script>
|
||||
@@ -64,10 +64,10 @@
|
||||
class={classNames(
|
||||
'w-full text-xs font-bold gap-1 truncate py-1.5 px-2 cursor-pointer transition-all justify-between flex items-center border border-gray-3 rounded-md',
|
||||
'bg-white border-gray-300 hover:bg-gray-100 focus:bg-gray-100 text-gray-700',
|
||||
$selectedComponent === component.id ? 'outline outline-blue-500 bg-red-400' : ''
|
||||
$selectedComponent?.includes(component.id) ? 'outline outline-blue-500 bg-red-400' : ''
|
||||
)}
|
||||
on:click={() => {
|
||||
$selectedComponent = component.id
|
||||
$selectedComponent = [component.id]
|
||||
}}
|
||||
on:keypress
|
||||
>
|
||||
|
||||
+1
-1
@@ -34,6 +34,6 @@
|
||||
bind:inlineScript={runnable.inlineScript}
|
||||
{onLoad}
|
||||
{doNotRecomputeOnInputChanged}
|
||||
id={$selectedComponent}
|
||||
id={$selectedComponent?.[0]}
|
||||
{onClick}
|
||||
/>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
export let fastStart = false
|
||||
export let throttleUpdate = 100
|
||||
export let throttleResize = 100
|
||||
export let onTopId: string | undefined = undefined
|
||||
export let selectedIds: string[] | undefined
|
||||
export let containerWidth: number | undefined = undefined
|
||||
|
||||
export let scroller: HTMLElement | undefined = undefined
|
||||
@@ -37,13 +37,6 @@
|
||||
|
||||
$: containerHeight = getContainerHeight(items, yPerPx, getComputedCols)
|
||||
|
||||
const pointerup = (ev) => {
|
||||
dispatch('pointerup', {
|
||||
id: ev.detail.id,
|
||||
cols: getComputedCols
|
||||
})
|
||||
}
|
||||
|
||||
const onResize = throttle(() => {
|
||||
items = specifyUndefinedColumns(items, getComputedCols, cols)
|
||||
dispatch('resize', {
|
||||
@@ -91,6 +84,7 @@
|
||||
|
||||
let initItems: FilledItem<T>[] | undefined = undefined
|
||||
const updateMatrix = ({ detail }) => {
|
||||
console.log('updateMatrix', detail)
|
||||
let isPointerUp = detail.isPointerUp
|
||||
let citems: FilledItem<T>[]
|
||||
if (isPointerUp) {
|
||||
@@ -107,26 +101,32 @@
|
||||
citems = JSON.parse(JSON.stringify(initItems))
|
||||
}
|
||||
|
||||
let activeItem = getItemById(detail.id, citems)
|
||||
sortedItems = citems
|
||||
for (let id of selectedIds ?? []) {
|
||||
let activeItem = getItemById(id, sortedItems)
|
||||
|
||||
if (activeItem) {
|
||||
activeItem = {
|
||||
...activeItem,
|
||||
[getComputedCols]: {
|
||||
...activeItem[getComputedCols],
|
||||
...detail.shadow
|
||||
if (activeItem) {
|
||||
activeItem = {
|
||||
...activeItem,
|
||||
[getComputedCols]: {
|
||||
...activeItem[getComputedCols],
|
||||
...shadows[id]
|
||||
}
|
||||
}
|
||||
|
||||
sortedItems = moveItem(
|
||||
activeItem,
|
||||
sortedItems,
|
||||
getComputedCols,
|
||||
getItemById(id, sortedItems)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
sortedItems = moveItem(activeItem, citems, getComputedCols, getItemById(detail.id, citems))
|
||||
|
||||
if (detail.onUpdate) detail.onUpdate()
|
||||
|
||||
dispatch('change', {
|
||||
unsafeItem: activeItem,
|
||||
id: activeItem.id,
|
||||
cols: getComputedCols
|
||||
})
|
||||
for (let id of selectedIds ?? []) {
|
||||
if (detail.activate) {
|
||||
moveResizes?.[id]?.inActivate()
|
||||
}
|
||||
}
|
||||
|
||||
if (isPointerUp) {
|
||||
@@ -146,15 +146,37 @@
|
||||
updateMatrix({ detail })
|
||||
}
|
||||
}
|
||||
let moveResizes: Record<string, MoveResize> = {}
|
||||
let shadows: Record<string, { x: number; y: number; w: number; h: number } | undefined> = {}
|
||||
|
||||
export function handleMove({ detail }) {
|
||||
Object.entries(moveResizes).forEach(([id, moveResize]) => {
|
||||
if (selectedIds?.includes(id)) {
|
||||
moveResize?.updateMove(JSON.parse(JSON.stringify(detail.cordDiff)), detail.eventY)
|
||||
}
|
||||
})
|
||||
throttleMatrix({ detail: { isPointerUp: false, activate: false } })
|
||||
}
|
||||
|
||||
export function handleInitMove({ detail }) {
|
||||
Object.entries(moveResizes).forEach(([id, moveResize]) => {
|
||||
if (selectedIds?.includes(id)) {
|
||||
moveResize?.initmove()
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="svlt-grid-container" style="height: {containerHeight}px" bind:this={container}>
|
||||
{#if xPerPx || !fastStart}
|
||||
{#each sortedItems as item (item.id)}
|
||||
<MoveResize
|
||||
on:initmove={handleInitMove}
|
||||
on:move={handleMove}
|
||||
bind:shadow={shadows[item.id]}
|
||||
bind:this={moveResizes[item.id]}
|
||||
on:repaint={handleRepaint}
|
||||
on:pointerup={pointerup}
|
||||
onTop={item.id == onTopId}
|
||||
onTop={Boolean(selectedIds?.includes(item.id))}
|
||||
id={item.id}
|
||||
{xPerPx}
|
||||
{yPerPx}
|
||||
@@ -191,5 +213,6 @@
|
||||
.svlt-grid-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
user-select: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -23,9 +23,10 @@
|
||||
|
||||
export let nativeContainer
|
||||
export let onTop
|
||||
export let shadow: { x: number; y: number; w: number; h: number } | undefined = undefined
|
||||
|
||||
const divId = `component-${id}`
|
||||
let shadowElement
|
||||
let shadow: { x: number; y: number; w: number; h: number } | undefined = undefined
|
||||
|
||||
let active = false
|
||||
|
||||
@@ -43,7 +44,7 @@
|
||||
|
||||
let anima
|
||||
|
||||
const inActivate = () => {
|
||||
export function inActivate() {
|
||||
if (shadowElement && shadow != undefined) {
|
||||
let subgrid = shadowElement.closest('.subgrid')
|
||||
let irect = shadowElement.getBoundingClientRect()
|
||||
@@ -80,20 +81,14 @@
|
||||
|
||||
anima = setTimeout(() => {
|
||||
trans = false
|
||||
}, 100)
|
||||
|
||||
dispatch('pointerup', {
|
||||
id
|
||||
})
|
||||
}, 50)
|
||||
}
|
||||
}
|
||||
|
||||
let repaint = (cb: (() => void) | undefined, isPointerUp: boolean) => {
|
||||
let repaint = (activate: boolean, isPointerUp: boolean) => {
|
||||
dispatch('repaint', {
|
||||
id,
|
||||
shadow,
|
||||
isPointerUp,
|
||||
onUpdate: cb
|
||||
activate
|
||||
})
|
||||
}
|
||||
|
||||
@@ -105,7 +100,7 @@
|
||||
|
||||
const getContainerFrame = (element) => {
|
||||
if (element === document.documentElement || !element) {
|
||||
const { height, top, right, bottom, left } = nativeContainer.getBoundingClientRect()
|
||||
const { top, bottom } = nativeContainer.getBoundingClientRect()
|
||||
|
||||
return {
|
||||
top: Math.max(0, top),
|
||||
@@ -118,12 +113,13 @@
|
||||
|
||||
const getScroller = (element) => (!element ? document.documentElement : element)
|
||||
|
||||
function computeRect(target) {
|
||||
let gridItem = target.closest('.svlt-grid-item')
|
||||
function computeRect() {
|
||||
let gridItem = document.getElementById(divId)
|
||||
if (!gridItem) return
|
||||
let subgrid = gridItem.closest('.subgrid')
|
||||
|
||||
let irect = gridItem.getBoundingClientRect()
|
||||
if (subgrid) {
|
||||
if (subgrid && subgrid.parentElement) {
|
||||
let subGridParent = subgrid.parentElement
|
||||
let subGridParentRect = subGridParent.getBoundingClientRect()
|
||||
|
||||
@@ -150,25 +146,27 @@
|
||||
|
||||
initX = clientX
|
||||
initY = clientY
|
||||
|
||||
capturePos = { x: left, y: top }
|
||||
shadow = { x: item.x, y: item.y, w: item.w, h: item.h }
|
||||
newSize = { width, height }
|
||||
|
||||
containerFrame = getContainerFrame(container)
|
||||
scrollElement = getScroller(container)
|
||||
|
||||
cordDiff = { x: 0, y: 0 }
|
||||
|
||||
active = true
|
||||
trans = false
|
||||
computeRect(target)
|
||||
_scrollTop = scrollElement.scrollTop
|
||||
dispatch('initmove')
|
||||
}
|
||||
window.addEventListener('pointermove', pointermove)
|
||||
window.addEventListener('pointerup', pointerup)
|
||||
}
|
||||
|
||||
export function initmove() {
|
||||
computeRect()
|
||||
newSize = { width, height }
|
||||
capturePos = { x: left, y: top }
|
||||
shadow = { x: item.x, y: item.y, w: item.w, h: item.h }
|
||||
cordDiff = { x: 0, y: 0 }
|
||||
active = true
|
||||
trans = false
|
||||
|
||||
containerFrame = getContainerFrame(container)
|
||||
scrollElement = getScroller(container)
|
||||
|
||||
_scrollTop = scrollElement.scrollTop
|
||||
}
|
||||
|
||||
let sign = { x: 0, y: 0 }
|
||||
let vel = { x: 0, y: 0 }
|
||||
let intervalId: NodeJS.Timer | undefined = undefined
|
||||
@@ -181,9 +179,8 @@
|
||||
}
|
||||
|
||||
const update = () => {
|
||||
const _newScrollTop = scrollElement.scrollTop - _scrollTop
|
||||
|
||||
const boundX = capturePos.x + cordDiff.x
|
||||
const _newScrollTop = (scrollElement?.scrollTop ?? 0) - (_scrollTop ?? 0)
|
||||
const boundY = capturePos.y + (cordDiff.y + _newScrollTop)
|
||||
|
||||
let gridX = Math.round(boundX / xPerPx)
|
||||
@@ -193,7 +190,6 @@
|
||||
shadow.x = Math.max(Math.min(gridX, cols - shadow.w), 0)
|
||||
shadow.y = Math.max(gridY, 0)
|
||||
}
|
||||
repaint(undefined, false)
|
||||
}
|
||||
|
||||
const pointermove = (event) => {
|
||||
@@ -203,60 +199,76 @@
|
||||
event.stopImmediatePropagation()
|
||||
|
||||
const { clientX, clientY } = event
|
||||
cordDiff = { x: clientX - initX, y: clientY - initY }
|
||||
const cordDiff = { x: clientX - initX, y: clientY - initY }
|
||||
|
||||
dispatch('move', { cordDiff, clientY })
|
||||
}
|
||||
|
||||
export function updateMove(newCoordDiff, clientY) {
|
||||
if (!active) {
|
||||
active = true
|
||||
}
|
||||
if (trans) {
|
||||
trans = false
|
||||
}
|
||||
cordDiff = newCoordDiff
|
||||
// console.log(cordDiff, id, 'B')
|
||||
const Y_SENSOR = sensor
|
||||
|
||||
let velocityTop = Math.max(0, (containerFrame.top + Y_SENSOR - clientY) / Y_SENSOR)
|
||||
let velocityBottom = Math.max(0, (clientY - (containerFrame.bottom - Y_SENSOR)) / Y_SENSOR)
|
||||
if (containerFrame) {
|
||||
let velocityTop = Math.max(0, (containerFrame.top + Y_SENSOR - clientY) / Y_SENSOR)
|
||||
let velocityBottom = Math.max(0, (clientY - (containerFrame.bottom - Y_SENSOR)) / Y_SENSOR)
|
||||
|
||||
const topSensor = velocityTop > 0 && velocityBottom === 0
|
||||
const bottomSensor = velocityBottom > 0 && velocityTop === 0
|
||||
const topSensor = velocityTop > 0 && velocityBottom === 0
|
||||
const bottomSensor = velocityBottom > 0 && velocityTop === 0
|
||||
|
||||
sign.y = topSensor ? -1 : bottomSensor ? 1 : 0
|
||||
vel.y = sign.y === -1 ? velocityTop : velocityBottom
|
||||
sign.y = topSensor ? -1 : bottomSensor ? 1 : 0
|
||||
vel.y = sign.y === -1 ? velocityTop : velocityBottom
|
||||
|
||||
if (vel.y > 0) {
|
||||
if (!intervalId) {
|
||||
// Start scrolling
|
||||
// TODO Use requestAnimationFrame
|
||||
intervalId = setInterval(() => {
|
||||
scrollElement.scrollTop += 2 * (vel.y + Math.sign(vel.y)) * sign.y
|
||||
update()
|
||||
}, 10)
|
||||
if (vel.y > 0) {
|
||||
if (!intervalId) {
|
||||
// Start scrolling
|
||||
// TODO Use requestAnimationFrame
|
||||
intervalId = setInterval(() => {
|
||||
scrollElement.scrollTop += 2 * (vel.y + Math.sign(vel.y)) * sign.y
|
||||
update()
|
||||
}, 10)
|
||||
}
|
||||
} else if (intervalId) {
|
||||
stopAutoscroll()
|
||||
} else {
|
||||
update()
|
||||
}
|
||||
} else if (intervalId) {
|
||||
stopAutoscroll()
|
||||
} else {
|
||||
update()
|
||||
}
|
||||
}
|
||||
|
||||
const pointerup = (e) => {
|
||||
dragClosure = undefined
|
||||
stopAutoscroll()
|
||||
|
||||
window.removeEventListener('pointerdown', pointerdown)
|
||||
window.removeEventListener('pointermove', pointermove)
|
||||
window.removeEventListener('pointerup', pointerup)
|
||||
repaint(inActivate, true)
|
||||
if (!dragClosure) {
|
||||
repaint(true, true)
|
||||
} else {
|
||||
dragClosure = undefined
|
||||
}
|
||||
}
|
||||
|
||||
// Resize
|
||||
|
||||
let resizeInitPos = { x: 0, y: 0 }
|
||||
let initSize = { width: 0, height: 0 }
|
||||
|
||||
const resizePointerDown = (e) => {
|
||||
e.stopPropagation()
|
||||
const { pageX, pageY, target } = e
|
||||
const { pageX, pageY } = e
|
||||
|
||||
resizeInitPos = { x: pageX, y: pageY }
|
||||
initSize = { width, height }
|
||||
|
||||
cordDiff = { x: 0, y: 0 }
|
||||
|
||||
computeRect(target)
|
||||
computeRect()
|
||||
|
||||
newSize = { width, height }
|
||||
|
||||
@@ -287,14 +299,14 @@
|
||||
shadow.w = Math.round((newSize.width + gapX * 2) / xPerPx)
|
||||
shadow.h = Math.round((newSize.height + gapY * 2) / yPerPx)
|
||||
|
||||
repaint(undefined, false)
|
||||
repaint(false, false)
|
||||
}
|
||||
}
|
||||
|
||||
const resizePointerUp = (e) => {
|
||||
e.stopPropagation()
|
||||
|
||||
repaint(inActivate, true)
|
||||
repaint(true, true)
|
||||
|
||||
window.removeEventListener('pointermove', resizePointerMove)
|
||||
window.removeEventListener('pointerup', resizePointerUp)
|
||||
@@ -305,12 +317,13 @@
|
||||
<div
|
||||
draggable="false"
|
||||
on:pointerdown|stopPropagation|preventDefault={pointerdown}
|
||||
id={divId}
|
||||
class="svlt-grid-item"
|
||||
class:svlt-grid-active={active || (trans && rect)}
|
||||
style="width: {active ? newSize.width : width}px; height:{active
|
||||
? newSize.height
|
||||
: height}px; {onTop ? 'z-index: 1000;' : ''}
|
||||
{active
|
||||
{active && rect
|
||||
? `transform: translate(${cordDiff.x}px, ${cordDiff.y}px);top:${rect.top}px;left:${rect.left}px;`
|
||||
: trans
|
||||
? `transform: translate(${cordDiff.x}px, ${cordDiff.y}px); position:absolute; transition: width 0.2s, height 0.2s;`
|
||||
|
||||
@@ -16,10 +16,6 @@ function makeItem(item) {
|
||||
}
|
||||
|
||||
const gridHelp = {
|
||||
normalize(items, col) {
|
||||
return normalize(items, col)
|
||||
},
|
||||
|
||||
adjust(items, col) {
|
||||
return adjust(items, col)
|
||||
},
|
||||
|
||||
@@ -131,11 +131,13 @@ export type AppViewerContext = {
|
||||
worldStore: Writable<World>
|
||||
app: Writable<App>
|
||||
summary: Writable<string>
|
||||
selectedComponent: Writable<string | undefined>
|
||||
selectedComponent: Writable<string[] | undefined>
|
||||
mode: Writable<EditorMode>
|
||||
connectingInput: Writable<ConnectingInput>
|
||||
breakpoint: Writable<EditorBreakpoint>
|
||||
runnableComponents: Writable<Record<string, (inlineScript?: InlineScript) => Promise<void>>>
|
||||
runnableComponents: Writable<
|
||||
Record<string, { autoRefresh: boolean; cb: (inlineScript?: InlineScript) => Promise<void> }>
|
||||
>
|
||||
staticExporter: Writable<Record<string, () => any>>
|
||||
appPath: string
|
||||
workspace: string
|
||||
@@ -167,7 +169,7 @@ export type AppEditorContext = {
|
||||
pickVariableCallback: Writable<((path: string) => void) | undefined>
|
||||
ontextfocus: Writable<(() => void) | undefined>
|
||||
selectedComponentInEditor: Writable<string | undefined>
|
||||
movingcomponent: Writable<string | undefined>
|
||||
movingcomponents: Writable<string[] | undefined>
|
||||
}
|
||||
|
||||
export type FocusedGrid = { parentComponentId: string; subGridIndex: number }
|
||||
|
||||
@@ -1,12 +1,45 @@
|
||||
import type { Schema } from '$lib/common'
|
||||
import { FlowService, ScriptService } from '$lib/gen'
|
||||
import { inferArgs } from '$lib/infer'
|
||||
import { emptySchema } from '$lib/utils'
|
||||
import { emptySchema, sendUserToast } from '$lib/utils'
|
||||
import type { AppComponent, components } from './editor/component'
|
||||
import type { App, ComponentCssProperty, ComponentCustomCSS, GridItem } from './types'
|
||||
import type { App, ComponentCssProperty, GridItem } from './types'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import type { AppInput, InputType, ResultAppInput, StaticAppInput } from './inputType'
|
||||
import type { Output } from './rx'
|
||||
import { get, type Writable } from 'svelte/store'
|
||||
import { findGridItemParentGrid } from './editor/appUtils'
|
||||
|
||||
export function selectId(
|
||||
e: PointerEvent,
|
||||
id: string,
|
||||
selectedComponent: Writable<string[] | undefined>,
|
||||
app: App
|
||||
) {
|
||||
if (e.shiftKey) {
|
||||
selectedComponent.update((old) => {
|
||||
if (old && old?.[0]) {
|
||||
if (findGridItemParentGrid(app, old[0]) != findGridItemParentGrid(app, id)) {
|
||||
sendUserToast('Cannot multi select items from different grids', true)
|
||||
return old
|
||||
}
|
||||
}
|
||||
if (old == undefined) {
|
||||
return [id]
|
||||
}
|
||||
if (old.includes(id)) {
|
||||
return old
|
||||
}
|
||||
return [...old, id]
|
||||
})
|
||||
} else {
|
||||
if (get(selectedComponent)?.includes(id)) {
|
||||
return
|
||||
} else {
|
||||
selectedComponent.set([id])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function allItems(
|
||||
grid: GridItem[],
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
import { writable } from 'svelte/store'
|
||||
|
||||
let app: AppWithLastVersion | undefined = undefined
|
||||
let user: GlobalUserInfo | undefined = undefined
|
||||
let notExists = false
|
||||
|
||||
setContext(IS_APP_PUBLIC_CONTEXT_KEY, true)
|
||||
@@ -31,14 +30,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function loadUser() {
|
||||
try {
|
||||
user = await UserService.globalWhoami()
|
||||
} catch (e) {}
|
||||
}
|
||||
if (browser) {
|
||||
loadApp()
|
||||
loadUser()
|
||||
}
|
||||
|
||||
const breakpoint = writable<EditorBreakpoint>('lg')
|
||||
@@ -53,16 +46,7 @@
|
||||
>Powered by <WindmillIcon /> Windmill</a
|
||||
>
|
||||
</div>
|
||||
<div class="z-50 text-xs text-gray-500 fixed top-1 left-2">
|
||||
<div>
|
||||
{#if user}
|
||||
Logged in as {user.email}
|
||||
{:else}
|
||||
Not logged in
|
||||
{/if}
|
||||
</div>
|
||||
<a class="text-blue-400" href="/">Go to app</a>
|
||||
</div>
|
||||
|
||||
{#if notExists}
|
||||
<div class="px-4 mt-20"
|
||||
><Alert type="error" title="Not found"
|
||||
|
||||
Reference in New Issue
Block a user