Files
windmill/frontend/src/lib/components/triggers/AddTriggersButton.svelte
T
6186a0645d feat: edit variables, resources and triggers in their own session tab (#11206)
* feat: edit variables, resources and triggers in their own session tab

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: take a trigger's flow kind from its config and name the written trigger

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* refactor: one operating-workspace context for editors acting on a session's workspace

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: reload page item tabs only in sessions acting on the tool's workspace

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: resolve open in workspace for every preview tab kind in one place

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: guard every component under a session editor against the navigation workspace

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: show a page item tab whose item is gone instead of mounting its editor

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: follow a saved page item in the tab's own workspace and restore in-frame rows

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* feat: close a page item tab from its editor header

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: judge trigger permissions by the user acting in the session workspace

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: judge every session-editor permission by the user acting in that workspace

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: keep session permissions reactive to the acting role and restore tabs as last seen

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix: default Path's owner controls to the user acting in its workspace

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
2026-09-19 23:29:14 +02:00

191 lines
5.0 KiB
Svelte

<script lang="ts">
// Aliased to the desaturated map: every mark below is a dropdown row sitting beside
// lucide glyphs, where a coloured brand mark reads as a different kind of thing.
import { triggerIconMapMono as triggerIconMap, type TriggerType } from './utils'
import DropdownV2 from '$lib/components/DropdownV2.svelte'
import { SchedulePollIcon } from '$lib/components/icons'
import type { Placement } from '@floating-ui/core'
import { isCloudHosted } from '$lib/cloud'
import { CloudOff } from 'lucide-svelte'
import { isServiceAvailable } from './native/utils'
import { useOperatingWorkspace } from '$lib/components/operatingWorkspace.svelte'
const operatingWorkspace = useOperatingWorkspace()
interface Props {
setDropdownWidthToButtonWidth?: boolean
children?: import('svelte').Snippet
triggerScriptPicker?: import('svelte').Snippet | undefined
class?: string
placement?: Placement
isEditor?: boolean
onAddDraftTrigger?: (type: TriggerType) => void
onAddScheduledPoll?: () => void
onClose?: () => void
}
let {
setDropdownWidthToButtonWidth = false,
children,
class: className,
placement = 'bottom-start',
isEditor = false,
onAddDraftTrigger,
onAddScheduledPoll,
onClose
}: Props = $props()
let dropdown: DropdownV2 | undefined
const cloudHosted = isCloudHosted()
let nextcloudAvailable = $state(false)
let googleAvailable = $state(false)
let githubAvailable = $state(false)
async function setNextcloudState() {
nextcloudAvailable = await isServiceAvailable('nextcloud', $operatingWorkspace!)
}
async function setGoogleState() {
googleAvailable = await isServiceAvailable('google', $operatingWorkspace!)
}
async function setGithubState() {
githubAvailable = await isServiceAvailable('github', $operatingWorkspace!)
}
setNextcloudState()
setGoogleState()
setGithubState()
const addTriggerItems = $derived(
[
{
displayName: 'Schedule',
action: () => onAddDraftTrigger?.('schedule'),
icon: triggerIconMap.schedule
},
{ displayName: 'HTTP', action: () => onAddDraftTrigger?.('http'), icon: triggerIconMap.http },
{
displayName: 'WebSocket',
action: () => onAddDraftTrigger?.('websocket'),
icon: triggerIconMap.websocket,
extra: cloudHosted ? extra : undefined
},
{
displayName: 'Postgres',
action: () => onAddDraftTrigger?.('postgres'),
icon: triggerIconMap.postgres,
extra: cloudHosted ? extra : undefined
},
{
displayName: 'Kafka',
action: () => onAddDraftTrigger?.('kafka'),
icon: triggerIconMap.kafka,
extra: cloudHosted ? extra : undefined
},
{
displayName: 'NATS',
action: () => onAddDraftTrigger?.('nats'),
icon: triggerIconMap.nats,
extra: cloudHosted ? extra : undefined
},
{
displayName: 'MQTT',
action: () => onAddDraftTrigger?.('mqtt'),
icon: triggerIconMap.mqtt,
extra: cloudHosted ? extra : undefined
},
{
displayName: 'AMQP',
action: () => onAddDraftTrigger?.('amqp'),
icon: triggerIconMap.amqp,
extra: cloudHosted ? extra : undefined
},
{
displayName: 'SQS',
action: () => onAddDraftTrigger?.('sqs'),
icon: triggerIconMap.sqs,
extra: cloudHosted ? extra : undefined
},
{
displayName: 'GCP Pub/Sub',
action: () => onAddDraftTrigger?.('gcp'),
icon: triggerIconMap.gcp,
extra: cloudHosted ? extra : undefined
},
{
displayName: 'Azure Event Grid',
action: () => onAddDraftTrigger?.('azure'),
icon: triggerIconMap.azure,
extra: cloudHosted ? extra : undefined
},
{
displayName: 'Email',
action: () => onAddDraftTrigger?.('email'),
icon: triggerIconMap.email,
extra: cloudHosted ? extra : undefined
},
{
displayName: 'Scheduled Poll',
action: (e) => {
onAddDraftTrigger?.('poll')
onAddScheduledPoll?.()
},
icon: SchedulePollIcon,
hidden: !isEditor
},
{
displayName: 'Nextcloud',
action: () => onAddDraftTrigger?.('nextcloud'),
icon: triggerIconMap.nextcloud,
hidden: !nextcloudAvailable
},
{
displayName: 'Google',
action: () => onAddDraftTrigger?.('google'),
icon: triggerIconMap.google,
hidden: !googleAvailable
},
{
displayName: 'GitHub',
action: () => onAddDraftTrigger?.('github'),
icon: triggerIconMap.github,
hidden: !githubAvailable
}
].filter((item) => !item.hidden)
)
let triggersButtonWidth = $state(0)
export function close() {
dropdown?.close()
}
</script>
{#snippet extra()}
<p
class="text-xs text-yellow-700 dark:text-yellow-100/90 bg-yellow-50 dark:bg-yellow-900/40 rounded-md p-1 px-2 -my-1"
title="Disabled in multi-tenant cloud"
>
<CloudOff size={14} />
</p>
{/snippet}
<DropdownV2
enableFlyTransition
bind:this={dropdown}
items={addTriggerItems}
{placement}
class={className}
customWidth={setDropdownWidthToButtonWidth ? triggersButtonWidth : undefined}
usePointerDownOutside
on:close={() => onClose?.()}
>
{#snippet buttonReplacement()}
<div class={className} bind:clientWidth={triggersButtonWidth}>
{@render children?.()}
</div>
{/snippet}
</DropdownV2>