improvement(selectors): consolidate selector input logic (#3375)

This commit is contained in:
Vikhyath Mondreti
2026-02-27 10:18:25 -08:00
committed by GitHub
parent e3ff595a84
commit 49db3ca50b
14 changed files with 232 additions and 980 deletions

View File

@@ -1,118 +0,0 @@
'use client'
import { useCallback, useMemo } from 'react'
import { Tooltip } from '@/components/emcn'
import { buildCanonicalIndex, resolveDependencyValue } from '@/lib/workflows/subblocks/visibility'
import { SelectorCombobox } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/selector-combobox/selector-combobox'
import { useDependsOnGate } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate'
import { resolvePreviewContextValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/utils'
import { getBlock } from '@/blocks/registry'
import type { SubBlockConfig } from '@/blocks/types'
import type { SelectorContext } from '@/hooks/selectors/types'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
interface DocumentSelectorProps {
blockId: string
subBlock: SubBlockConfig
disabled?: boolean
onDocumentSelect?: (documentId: string) => void
isPreview?: boolean
previewValue?: string | null
previewContextValues?: Record<string, unknown>
}
export function DocumentSelector({
blockId,
subBlock,
disabled = false,
onDocumentSelect,
isPreview = false,
previewValue,
previewContextValues,
}: DocumentSelectorProps) {
const { activeWorkflowId } = useWorkflowRegistry()
const { finalDisabled } = useDependsOnGate(blockId, subBlock, {
disabled,
isPreview,
previewContextValues,
})
const blockState = useWorkflowStore((state) => state.blocks[blockId])
const blockConfig = blockState?.type ? getBlock(blockState.type) : null
const canonicalIndex = useMemo(
() => buildCanonicalIndex(blockConfig?.subBlocks || []),
[blockConfig?.subBlocks]
)
const canonicalModeOverrides = blockState?.data?.canonicalModes
const blockValues = useSubBlockStore((state) => {
if (!activeWorkflowId) return {}
const workflowValues = state.workflowValues[activeWorkflowId] || {}
return (workflowValues as Record<string, Record<string, unknown>>)[blockId] || {}
})
const knowledgeBaseIdValue = useMemo(
() =>
previewContextValues
? resolvePreviewContextValue(previewContextValues.knowledgeBaseId)
: resolveDependencyValue(
'knowledgeBaseId',
blockValues,
canonicalIndex,
canonicalModeOverrides
),
[previewContextValues, blockValues, canonicalIndex, canonicalModeOverrides]
)
const normalizedKnowledgeBaseId =
typeof knowledgeBaseIdValue === 'string' && knowledgeBaseIdValue.trim().length > 0
? knowledgeBaseIdValue
: null
const selectorContext = useMemo<SelectorContext>(
() => ({
knowledgeBaseId: normalizedKnowledgeBaseId ?? undefined,
}),
[normalizedKnowledgeBaseId]
)
const handleDocumentChange = useCallback(
(documentId: string) => {
if (isPreview) return
onDocumentSelect?.(documentId)
},
[isPreview, onDocumentSelect]
)
const missingKnowledgeBase = !normalizedKnowledgeBaseId
const isDisabled = finalDisabled || missingKnowledgeBase
const placeholder = subBlock.placeholder || 'Select document'
return (
<Tooltip.Root>
<Tooltip.Trigger asChild>
<div className='w-full'>
<SelectorCombobox
blockId={blockId}
subBlock={subBlock}
selectorKey='knowledge.documents'
selectorContext={selectorContext}
disabled={isDisabled}
isPreview={isPreview}
previewValue={previewValue ?? null}
placeholder={placeholder}
onOptionChange={handleDocumentChange}
/>
</div>
</Tooltip.Trigger>
{missingKnowledgeBase && (
<Tooltip.Content side='top'>
<p>Select a knowledge base first.</p>
</Tooltip.Content>
)}
</Tooltip.Root>
)
}

View File

@@ -13,20 +13,15 @@ import {
} from '@/components/emcn'
import { cn } from '@/lib/core/utils/cn'
import { FIELD_TYPE_LABELS, getPlaceholderForFieldType } from '@/lib/knowledge/constants'
import { buildCanonicalIndex, resolveDependencyValue } from '@/lib/workflows/subblocks/visibility'
import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/formatted-text'
import { TagDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown'
import { useDependsOnGate } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate'
import { useSubBlockInput } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-input'
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value'
import { resolvePreviewContextValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/utils'
import { useAccessibleReferencePrefixes } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-accessible-reference-prefixes'
import { getBlock } from '@/blocks/registry'
import type { SubBlockConfig } from '@/blocks/types'
import { useKnowledgeBaseTagDefinitions } from '@/hooks/kb/use-knowledge-base-tag-definitions'
import { useTagSelection } from '@/hooks/kb/use-tag-selection'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
interface DocumentTag {
id: string
@@ -65,26 +60,11 @@ export function DocumentTagEntry({
previewValue,
previewContextValues,
}: DocumentTagEntryProps) {
const { activeWorkflowId } = useWorkflowRegistry()
const [storeValue, setStoreValue] = useSubBlockValue<string>(blockId, subBlock.id)
const accessiblePrefixes = useAccessibleReferencePrefixes(blockId)
const valueInputRefs = useRef<Record<string, HTMLInputElement>>({})
const overlayRefs = useRef<Record<string, HTMLDivElement>>({})
const blockState = useWorkflowStore((state) => state.blocks[blockId])
const blockConfig = blockState?.type ? getBlock(blockState.type) : null
const canonicalIndex = useMemo(
() => buildCanonicalIndex(blockConfig?.subBlocks || []),
[blockConfig?.subBlocks]
)
const canonicalModeOverrides = blockState?.data?.canonicalModes
const blockValues = useSubBlockStore((state) => {
if (!activeWorkflowId) return {}
const workflowValues = state.workflowValues[activeWorkflowId] || {}
return (workflowValues as Record<string, Record<string, unknown>>)[blockId] || {}
})
const inputController = useSubBlockInput({
blockId,
subBlockId: subBlock.id,
@@ -97,18 +77,12 @@ export function DocumentTagEntry({
disabled,
})
const knowledgeBaseIdValue = useMemo(
() =>
previewContextValues
? resolvePreviewContextValue(previewContextValues.knowledgeBaseId)
: resolveDependencyValue(
'knowledgeBaseId',
blockValues,
canonicalIndex,
canonicalModeOverrides
),
[previewContextValues, blockValues, canonicalIndex, canonicalModeOverrides]
)
const { dependencyValues } = useDependsOnGate(blockId, subBlock, {
disabled,
isPreview,
previewContextValues,
})
const knowledgeBaseIdValue = dependencyValues.knowledgeBaseSelector
const knowledgeBaseId =
typeof knowledgeBaseIdValue === 'string' && knowledgeBaseIdValue.trim().length > 0
? knowledgeBaseIdValue

View File

@@ -1,209 +0,0 @@
'use client'
import { useMemo } from 'react'
import { useParams } from 'next/navigation'
import { Tooltip } from '@/components/emcn'
import { getProviderIdFromServiceId } from '@/lib/oauth'
import { buildCanonicalIndex, resolveDependencyValue } from '@/lib/workflows/subblocks/visibility'
import { SelectorCombobox } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/selector-combobox/selector-combobox'
import { useDependsOnGate } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate'
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value'
import { resolvePreviewContextValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/utils'
import { getBlock } from '@/blocks/registry'
import type { SubBlockConfig } from '@/blocks/types'
import { isDependency } from '@/blocks/utils'
import { resolveSelectorForSubBlock, type SelectorResolution } from '@/hooks/selectors/resolution'
import { useCollaborativeWorkflow } from '@/hooks/use-collaborative-workflow'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
interface FileSelectorInputProps {
blockId: string
subBlock: SubBlockConfig
disabled: boolean
isPreview?: boolean
previewValue?: any | null
previewContextValues?: Record<string, any>
}
export function FileSelectorInput({
blockId,
subBlock,
disabled,
isPreview = false,
previewValue,
previewContextValues,
}: FileSelectorInputProps) {
const { collaborativeSetSubblockValue } = useCollaborativeWorkflow()
const { activeWorkflowId } = useWorkflowRegistry()
const params = useParams()
const workflowIdFromUrl = (params?.workflowId as string) || activeWorkflowId || ''
const { finalDisabled } = useDependsOnGate(blockId, subBlock, {
disabled,
isPreview,
previewContextValues,
})
const blockState = useWorkflowStore((state) => state.blocks[blockId])
const blockConfig = blockState?.type ? getBlock(blockState.type) : null
const canonicalIndex = useMemo(
() => buildCanonicalIndex(blockConfig?.subBlocks || []),
[blockConfig?.subBlocks]
)
const canonicalModeOverrides = blockState?.data?.canonicalModes
const blockValues = useSubBlockStore((state) => {
if (!activeWorkflowId) return {}
const workflowValues = state.workflowValues[activeWorkflowId] || {}
return (workflowValues as Record<string, Record<string, unknown>>)[blockId] || {}
})
const [domainValueFromStore] = useSubBlockValue(blockId, 'domain')
const connectedCredential = previewContextValues
? resolvePreviewContextValue(previewContextValues.credential)
: blockValues.credential
const domainValue = previewContextValues
? resolvePreviewContextValue(previewContextValues.domain)
: domainValueFromStore
const teamIdValue = useMemo(
() =>
previewContextValues
? resolvePreviewContextValue(previewContextValues.teamId)
: resolveDependencyValue('teamId', blockValues, canonicalIndex, canonicalModeOverrides),
[previewContextValues, blockValues, canonicalIndex, canonicalModeOverrides]
)
const siteIdValue = useMemo(
() =>
previewContextValues
? resolvePreviewContextValue(previewContextValues.siteId)
: resolveDependencyValue('siteId', blockValues, canonicalIndex, canonicalModeOverrides),
[previewContextValues, blockValues, canonicalIndex, canonicalModeOverrides]
)
const collectionIdValue = useMemo(
() =>
previewContextValues
? resolvePreviewContextValue(previewContextValues.collectionId)
: resolveDependencyValue(
'collectionId',
blockValues,
canonicalIndex,
canonicalModeOverrides
),
[previewContextValues, blockValues, canonicalIndex, canonicalModeOverrides]
)
const projectIdValue = useMemo(
() =>
previewContextValues
? resolvePreviewContextValue(previewContextValues.projectId)
: resolveDependencyValue('projectId', blockValues, canonicalIndex, canonicalModeOverrides),
[previewContextValues, blockValues, canonicalIndex, canonicalModeOverrides]
)
const planIdValue = useMemo(
() =>
previewContextValues
? resolvePreviewContextValue(previewContextValues.planId)
: resolveDependencyValue('planId', blockValues, canonicalIndex, canonicalModeOverrides),
[previewContextValues, blockValues, canonicalIndex, canonicalModeOverrides]
)
const normalizedCredentialId =
typeof connectedCredential === 'string'
? connectedCredential
: typeof connectedCredential === 'object' && connectedCredential !== null
? ((connectedCredential as Record<string, any>).id ?? '')
: ''
const serviceId = subBlock.serviceId || ''
const effectiveProviderId = useMemo(() => getProviderIdFromServiceId(serviceId), [serviceId])
const selectorResolution = useMemo<SelectorResolution | null>(() => {
return resolveSelectorForSubBlock(subBlock, {
workflowId: workflowIdFromUrl,
credentialId: normalizedCredentialId,
domain: (domainValue as string) || undefined,
projectId: (projectIdValue as string) || undefined,
planId: (planIdValue as string) || undefined,
teamId: (teamIdValue as string) || undefined,
siteId: (siteIdValue as string) || undefined,
collectionId: (collectionIdValue as string) || undefined,
})
}, [
subBlock,
workflowIdFromUrl,
normalizedCredentialId,
domainValue,
projectIdValue,
planIdValue,
teamIdValue,
siteIdValue,
collectionIdValue,
])
const missingCredential = !normalizedCredentialId
const missingDomain =
selectorResolution?.key &&
(selectorResolution.key === 'confluence.pages' || selectorResolution.key === 'jira.issues') &&
!selectorResolution.context.domain
const missingProject =
selectorResolution?.key === 'jira.issues' &&
isDependency(subBlock.dependsOn, 'projectId') &&
!selectorResolution.context.projectId
const missingPlan =
selectorResolution?.key === 'microsoft.planner' && !selectorResolution.context.planId
const missingSite =
selectorResolution?.key === 'webflow.collections' && !selectorResolution.context.siteId
const missingCollection =
selectorResolution?.key === 'webflow.items' && !selectorResolution.context.collectionId
const disabledReason =
finalDisabled ||
missingCredential ||
missingDomain ||
missingProject ||
missingPlan ||
missingSite ||
missingCollection ||
!selectorResolution?.key
if (!selectorResolution?.key) {
return (
<Tooltip.Root>
<Tooltip.Trigger asChild>
<div className='w-full rounded border p-4 text-center text-muted-foreground text-sm'>
File selector not supported for service: {serviceId || 'unknown'}
</div>
</Tooltip.Trigger>
<Tooltip.Content side='top'>
<p>This file selector is not implemented for {serviceId || 'unknown'}</p>
</Tooltip.Content>
</Tooltip.Root>
)
}
return (
<SelectorCombobox
blockId={blockId}
subBlock={subBlock}
selectorKey={selectorResolution.key}
selectorContext={selectorResolution.context}
disabled={disabledReason}
isPreview={isPreview}
previewValue={previewValue ?? null}
placeholder={subBlock.placeholder || 'Select resource'}
allowSearch={selectorResolution.allowSearch}
onOptionChange={(value) => {
if (!isPreview) {
collaborativeSetSubblockValue(blockId, subBlock.id, value)
}
}}
/>
)
}

View File

@@ -1,124 +0,0 @@
'use client'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { getProviderIdFromServiceId } from '@/lib/oauth'
import { SelectorCombobox } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/selector-combobox/selector-combobox'
import { useDependsOnGate } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate'
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value'
import { resolvePreviewContextValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/utils'
import type { SubBlockConfig } from '@/blocks/types'
import { resolveSelectorForSubBlock } from '@/hooks/selectors/resolution'
import { useCollaborativeWorkflow } from '@/hooks/use-collaborative-workflow'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
interface FolderSelectorInputProps {
blockId: string
subBlock: SubBlockConfig
disabled?: boolean
isPreview?: boolean
previewValue?: any | null
previewContextValues?: Record<string, unknown>
}
export function FolderSelectorInput({
blockId,
subBlock,
disabled = false,
isPreview = false,
previewValue,
previewContextValues,
}: FolderSelectorInputProps) {
const [storeValue] = useSubBlockValue(blockId, subBlock.id)
const [credentialFromStore] = useSubBlockValue(blockId, 'credential')
const connectedCredential = previewContextValues
? resolvePreviewContextValue(previewContextValues.credential)
: credentialFromStore
const { collaborativeSetSubblockValue } = useCollaborativeWorkflow()
const { activeWorkflowId } = useWorkflowRegistry()
const [selectedFolderId, setSelectedFolderId] = useState<string>('')
// Derive provider from serviceId using OAuth config (same pattern as credential-selector)
const serviceId = subBlock.serviceId || ''
const effectiveProviderId = useMemo(() => getProviderIdFromServiceId(serviceId), [serviceId])
const providerKey = serviceId.toLowerCase()
const isCopyDestinationSelector =
subBlock.canonicalParamId === 'copyDestinationId' ||
subBlock.id === 'copyDestinationFolder' ||
subBlock.id === 'manualCopyDestinationFolder'
// Central dependsOn gating
const { finalDisabled } = useDependsOnGate(blockId, subBlock, {
disabled,
isPreview,
previewContextValues,
})
// Get the current value from the store or prop value if in preview mode
useEffect(() => {
if (finalDisabled) return
if (isPreview && previewValue !== undefined) {
setSelectedFolderId(previewValue)
return
}
const current = storeValue as string | undefined
if (current) {
setSelectedFolderId(current)
return
}
const shouldDefaultInbox = providerKey === 'gmail' && !isCopyDestinationSelector
if (shouldDefaultInbox) {
setSelectedFolderId('INBOX')
if (!isPreview) {
collaborativeSetSubblockValue(blockId, subBlock.id, 'INBOX')
}
}
}, [
blockId,
subBlock.id,
storeValue,
collaborativeSetSubblockValue,
isPreview,
previewValue,
finalDisabled,
providerKey,
isCopyDestinationSelector,
])
const credentialId = (connectedCredential as string) || ''
const missingCredential = credentialId.length === 0
const selectorResolution = useMemo(
() =>
resolveSelectorForSubBlock(subBlock, {
credentialId: credentialId || undefined,
workflowId: activeWorkflowId || undefined,
}),
[subBlock, credentialId, activeWorkflowId]
)
const handleChange = useCallback(
(value: string) => {
setSelectedFolderId(value)
if (!isPreview) {
collaborativeSetSubblockValue(blockId, subBlock.id, value)
}
},
[blockId, subBlock.id, collaborativeSetSubblockValue, isPreview]
)
return (
<SelectorCombobox
blockId={blockId}
subBlock={subBlock}
selectorKey={selectorResolution?.key ?? 'gmail.labels'}
selectorContext={
selectorResolution?.context ?? { credentialId, workflowId: activeWorkflowId || '' }
}
disabled={finalDisabled || missingCredential || !selectorResolution?.key}
isPreview={isPreview}
previewValue={previewValue ?? null}
placeholder={subBlock.placeholder || 'Select folder'}
onOptionChange={handleChange}
/>
)
}

View File

@@ -3,14 +3,11 @@ export { Code } from './code/code'
export { ComboBox } from './combobox/combobox'
export { ConditionInput } from './condition-input/condition-input'
export { CredentialSelector } from './credential-selector/credential-selector'
export { DocumentSelector } from './document-selector/document-selector'
export { DocumentTagEntry } from './document-tag-entry/document-tag-entry'
export { Dropdown } from './dropdown/dropdown'
export { EvalInput } from './eval-input/eval-input'
export { FileSelectorInput } from './file-selector/file-selector-input'
export { FileUpload } from './file-upload/file-upload'
export { FilterBuilder } from './filter-builder/filter-builder'
export { FolderSelectorInput } from './folder-selector/components/folder-selector-input'
export { GroupedCheckboxList } from './grouped-checkbox-list/grouped-checkbox-list'
export { InputMapping } from './input-mapping/input-mapping'
export { KnowledgeBaseSelector } from './knowledge-base-selector/knowledge-base-selector'
@@ -20,13 +17,11 @@ export { McpDynamicArgs } from './mcp-dynamic-args/mcp-dynamic-args'
export { McpServerSelector } from './mcp-server-modal/mcp-server-selector'
export { McpToolSelector } from './mcp-server-modal/mcp-tool-selector'
export { MessagesInput } from './messages-input/messages-input'
export { ProjectSelectorInput } from './project-selector/project-selector-input'
export { ResponseFormat } from './response/response-format'
export { ScheduleInfo } from './schedule-info/schedule-info'
export { SheetSelectorInput } from './sheet-selector/sheet-selector-input'
export { SelectorInput, type SelectorOverrides } from './selector-input/selector-input'
export { ShortInput } from './short-input/short-input'
export { SkillInput } from './skill-input/skill-input'
export { SlackSelectorInput } from './slack-selector/slack-selector-input'
export { SliderInput } from './slider-input/slider-input'
export { SortBuilder } from './sort-builder/sort-builder'
export { InputFormat } from './starter/input-format'

View File

@@ -1,6 +1,6 @@
'use client'
import { useMemo, useRef } from 'react'
import { useRef } from 'react'
import { Plus } from 'lucide-react'
import {
Badge,
@@ -14,19 +14,14 @@ import {
import { cn } from '@/lib/core/utils/cn'
import { FIELD_TYPE_LABELS, getPlaceholderForFieldType } from '@/lib/knowledge/constants'
import { type FilterFieldType, getOperatorsForFieldType } from '@/lib/knowledge/filters/types'
import { buildCanonicalIndex, resolveDependencyValue } from '@/lib/workflows/subblocks/visibility'
import { formatDisplayText } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/formatted-text'
import { TagDropdown } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/tag-dropdown/tag-dropdown'
import { useDependsOnGate } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate'
import { useSubBlockInput } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-input'
import { resolvePreviewContextValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/utils'
import { useAccessibleReferencePrefixes } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-accessible-reference-prefixes'
import { getBlock } from '@/blocks/registry'
import type { SubBlockConfig } from '@/blocks/types'
import { useKnowledgeBaseTagDefinitions } from '@/hooks/kb/use-knowledge-base-tag-definitions'
import { useTagSelection } from '@/hooks/kb/use-tag-selection'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
import { useSubBlockValue } from '../../hooks/use-sub-block-value'
interface TagFilter {
@@ -69,38 +64,17 @@ export function KnowledgeTagFilters({
previewValue,
previewContextValues,
}: KnowledgeTagFiltersProps) {
const { activeWorkflowId } = useWorkflowRegistry()
const [storeValue, setStoreValue] = useSubBlockValue<string | null>(blockId, subBlock.id)
const emitTagSelection = useTagSelection(blockId, subBlock.id)
const valueInputRefs = useRef<Record<string, HTMLInputElement>>({})
const overlayRefs = useRef<Record<string, HTMLDivElement>>({})
const blockState = useWorkflowStore((state) => state.blocks[blockId])
const blockConfig = blockState?.type ? getBlock(blockState.type) : null
const canonicalIndex = useMemo(
() => buildCanonicalIndex(blockConfig?.subBlocks || []),
[blockConfig?.subBlocks]
)
const canonicalModeOverrides = blockState?.data?.canonicalModes
const blockValues = useSubBlockStore((state) => {
if (!activeWorkflowId) return {}
const workflowValues = state.workflowValues[activeWorkflowId] || {}
return (workflowValues as Record<string, Record<string, unknown>>)[blockId] || {}
const { dependencyValues } = useDependsOnGate(blockId, subBlock, {
disabled,
isPreview,
previewContextValues,
})
const knowledgeBaseIdValue = useMemo(
() =>
previewContextValues
? resolvePreviewContextValue(previewContextValues.knowledgeBaseId)
: resolveDependencyValue(
'knowledgeBaseId',
blockValues,
canonicalIndex,
canonicalModeOverrides
),
[previewContextValues, blockValues, canonicalIndex, canonicalModeOverrides]
)
const knowledgeBaseIdValue = dependencyValues.knowledgeBaseSelector
const knowledgeBaseId =
typeof knowledgeBaseIdValue === 'string' && knowledgeBaseIdValue.trim().length > 0
? knowledgeBaseIdValue

View File

@@ -1,140 +0,0 @@
'use client'
import { useEffect, useMemo, useState } from 'react'
import { useParams } from 'next/navigation'
import { Tooltip } from '@/components/emcn'
import { getProviderIdFromServiceId } from '@/lib/oauth'
import { buildCanonicalIndex, resolveDependencyValue } from '@/lib/workflows/subblocks/visibility'
import { SelectorCombobox } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/selector-combobox/selector-combobox'
import { useDependsOnGate } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate'
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value'
import { resolvePreviewContextValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/utils'
import { getBlock } from '@/blocks/registry'
import type { SubBlockConfig } from '@/blocks/types'
import { resolveSelectorForSubBlock } from '@/hooks/selectors/resolution'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
interface ProjectSelectorInputProps {
blockId: string
subBlock: SubBlockConfig
disabled?: boolean
onProjectSelect?: (projectId: string) => void
isPreview?: boolean
previewValue?: any | null
previewContextValues?: Record<string, any>
}
export function ProjectSelectorInput({
blockId,
subBlock,
disabled = false,
onProjectSelect,
isPreview = false,
previewValue,
previewContextValues,
}: ProjectSelectorInputProps) {
const params = useParams()
const activeWorkflowId = useWorkflowRegistry((s) => s.activeWorkflowId) as string | null
const [selectedProjectId, setSelectedProjectId] = useState<string>('')
const [storeValue] = useSubBlockValue(blockId, subBlock.id)
const [jiraDomainFromStore] = useSubBlockValue(blockId, 'domain')
const blockState = useWorkflowStore((state) => state.blocks[blockId])
const blockConfig = blockState?.type ? getBlock(blockState.type) : null
const canonicalIndex = useMemo(
() => buildCanonicalIndex(blockConfig?.subBlocks || []),
[blockConfig?.subBlocks]
)
const canonicalModeOverrides = blockState?.data?.canonicalModes
const blockValues = useSubBlockStore((state) => {
if (!activeWorkflowId) return {}
const workflowValues = state.workflowValues[activeWorkflowId] || {}
return (workflowValues as Record<string, Record<string, unknown>>)[blockId] || {}
})
const connectedCredential = previewContextValues
? resolvePreviewContextValue(previewContextValues.credential)
: blockValues.credential
const jiraDomain = previewContextValues
? resolvePreviewContextValue(previewContextValues.domain)
: jiraDomainFromStore
const linearTeamId = useMemo(
() =>
previewContextValues
? resolvePreviewContextValue(previewContextValues.teamId)
: resolveDependencyValue('teamId', blockValues, canonicalIndex, canonicalModeOverrides),
[previewContextValues, blockValues, canonicalIndex, canonicalModeOverrides]
)
const serviceId = subBlock.serviceId || ''
const effectiveProviderId = useMemo(() => getProviderIdFromServiceId(serviceId), [serviceId])
const workflowIdFromUrl = (params?.workflowId as string) || activeWorkflowId || ''
const { finalDisabled } = useDependsOnGate(blockId, subBlock, {
disabled,
isPreview,
previewContextValues,
})
const domain = (jiraDomain as string) || ''
useEffect(() => {
if (isPreview && previewValue !== undefined) {
setSelectedProjectId(previewValue)
} else if (typeof storeValue === 'string') {
setSelectedProjectId(storeValue)
} else {
setSelectedProjectId('')
}
}, [isPreview, previewValue, storeValue])
const selectorResolution = useMemo(() => {
return resolveSelectorForSubBlock(subBlock, {
workflowId: workflowIdFromUrl || undefined,
credentialId: (connectedCredential as string) || undefined,
domain,
teamId: (linearTeamId as string) || undefined,
})
}, [subBlock, workflowIdFromUrl, connectedCredential, domain, linearTeamId])
const missingCredential = !selectorResolution?.context.credentialId
const handleChange = (value: string) => {
setSelectedProjectId(value)
onProjectSelect?.(value)
}
return (
<Tooltip.Root>
<Tooltip.Trigger asChild>
<div className='w-full'>
{selectorResolution?.key ? (
<SelectorCombobox
blockId={blockId}
subBlock={subBlock}
selectorKey={selectorResolution.key}
selectorContext={selectorResolution.context}
disabled={finalDisabled || missingCredential}
isPreview={isPreview}
previewValue={previewValue ?? null}
placeholder={subBlock.placeholder || 'Select project'}
onOptionChange={handleChange}
/>
) : (
<div className='w-full rounded border p-4 text-center text-muted-foreground text-sm'>
Project selector not supported for service: {serviceId}
</div>
)}
</div>
</Tooltip.Trigger>
{missingCredential && (
<Tooltip.Content side='top'>
<p>Please select an account first</p>
</Tooltip.Content>
)}
</Tooltip.Root>
)
}

View File

@@ -0,0 +1,106 @@
'use client'
import { useEffect, useRef } from 'react'
import { Tooltip } from '@/components/emcn'
import { SelectorCombobox } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/selector-combobox/selector-combobox'
import { useSelectorSetup } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-selector-setup'
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value'
import type { SubBlockConfig } from '@/blocks/types'
import type { SelectorContext } from '@/hooks/selectors/types'
import { useCollaborativeWorkflow } from '@/hooks/use-collaborative-workflow'
export interface SelectorOverrides {
transformContext?: (context: SelectorContext, deps: Record<string, unknown>) => SelectorContext
getDefaultValue?: (subBlock: SubBlockConfig) => string | null
}
interface SelectorInputProps {
blockId: string
subBlock: SubBlockConfig
disabled?: boolean
isPreview?: boolean
previewValue?: any
previewContextValues?: Record<string, any>
overrides?: SelectorOverrides
}
export function SelectorInput({
blockId,
subBlock,
disabled = false,
isPreview = false,
previewValue,
previewContextValues,
overrides,
}: SelectorInputProps) {
const { collaborativeSetSubblockValue } = useCollaborativeWorkflow()
const [storeValue] = useSubBlockValue(blockId, subBlock.id)
const defaultAppliedRef = useRef(false)
const {
selectorKey,
selectorContext: autoContext,
allowSearch,
disabled: selectorDisabled,
dependencyValues,
} = useSelectorSetup(blockId, subBlock, { disabled, isPreview, previewContextValues })
const selectorContext = overrides?.transformContext
? overrides.transformContext(autoContext, dependencyValues)
: autoContext
useEffect(() => {
if (defaultAppliedRef.current || isPreview || selectorDisabled) return
if (storeValue) return
const defaultValue = overrides?.getDefaultValue?.(subBlock)
if (defaultValue) {
defaultAppliedRef.current = true
collaborativeSetSubblockValue(blockId, subBlock.id, defaultValue)
}
}, [
blockId,
subBlock,
storeValue,
isPreview,
selectorDisabled,
overrides,
collaborativeSetSubblockValue,
])
const serviceId = subBlock.serviceId || 'unknown'
if (!selectorKey) {
return (
<Tooltip.Root>
<Tooltip.Trigger asChild>
<div className='w-full rounded border p-4 text-center text-muted-foreground text-sm'>
Selector not supported for service: {serviceId}
</div>
</Tooltip.Trigger>
<Tooltip.Content side='top'>
<p>This selector is not implemented for {serviceId}</p>
</Tooltip.Content>
</Tooltip.Root>
)
}
return (
<SelectorCombobox
blockId={blockId}
subBlock={subBlock}
selectorKey={selectorKey}
selectorContext={selectorContext}
disabled={selectorDisabled}
isPreview={isPreview}
previewValue={previewValue ?? null}
placeholder={subBlock.placeholder || 'Select resource'}
allowSearch={allowSearch}
onOptionChange={(value) => {
if (!isPreview) {
collaborativeSetSubblockValue(blockId, subBlock.id, value)
}
}}
/>
)
}

View File

@@ -1,136 +0,0 @@
'use client'
import { useMemo } from 'react'
import { useParams } from 'next/navigation'
import { Tooltip } from '@/components/emcn'
import { getProviderIdFromServiceId } from '@/lib/oauth'
import { buildCanonicalIndex, resolveDependencyValue } from '@/lib/workflows/subblocks/visibility'
import { SelectorCombobox } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/selector-combobox/selector-combobox'
import { useDependsOnGate } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate'
import { resolvePreviewContextValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/utils'
import { getBlock } from '@/blocks/registry'
import type { SubBlockConfig } from '@/blocks/types'
import { resolveSelectorForSubBlock, type SelectorResolution } from '@/hooks/selectors/resolution'
import { useCollaborativeWorkflow } from '@/hooks/use-collaborative-workflow'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import { useSubBlockStore } from '@/stores/workflows/subblock/store'
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
interface SheetSelectorInputProps {
blockId: string
subBlock: SubBlockConfig
disabled: boolean
isPreview?: boolean
previewValue?: any | null
previewContextValues?: Record<string, any>
}
export function SheetSelectorInput({
blockId,
subBlock,
disabled,
isPreview = false,
previewValue,
previewContextValues,
}: SheetSelectorInputProps) {
const { collaborativeSetSubblockValue } = useCollaborativeWorkflow()
const { activeWorkflowId } = useWorkflowRegistry()
const params = useParams()
const workflowIdFromUrl = (params?.workflowId as string) || activeWorkflowId || ''
const { finalDisabled } = useDependsOnGate(blockId, subBlock, {
disabled,
isPreview,
previewContextValues,
})
const blockState = useWorkflowStore((state) => state.blocks[blockId])
const blockConfig = blockState?.type ? getBlock(blockState.type) : null
const canonicalIndex = useMemo(
() => buildCanonicalIndex(blockConfig?.subBlocks || []),
[blockConfig?.subBlocks]
)
const canonicalModeOverrides = blockState?.data?.canonicalModes
const blockValues = useSubBlockStore((state) => {
if (!activeWorkflowId) return {}
const workflowValues = state.workflowValues[activeWorkflowId] || {}
return (workflowValues as Record<string, Record<string, unknown>>)[blockId] || {}
})
const connectedCredentialFromStore = blockValues.credential
const spreadsheetIdFromStore = useMemo(
() =>
resolveDependencyValue('spreadsheetId', blockValues, canonicalIndex, canonicalModeOverrides),
[blockValues, canonicalIndex, canonicalModeOverrides]
)
const connectedCredential = previewContextValues
? resolvePreviewContextValue(previewContextValues.credential)
: connectedCredentialFromStore
const spreadsheetId = previewContextValues
? (resolvePreviewContextValue(previewContextValues.spreadsheetId) ??
resolvePreviewContextValue(previewContextValues.manualSpreadsheetId))
: spreadsheetIdFromStore
const normalizedCredentialId =
typeof connectedCredential === 'string'
? connectedCredential
: typeof connectedCredential === 'object' && connectedCredential !== null
? ((connectedCredential as Record<string, any>).id ?? '')
: ''
const normalizedSpreadsheetId = typeof spreadsheetId === 'string' ? spreadsheetId.trim() : ''
const serviceId = subBlock.serviceId || ''
const effectiveProviderId = useMemo(() => getProviderIdFromServiceId(serviceId), [serviceId])
const selectorResolution = useMemo<SelectorResolution | null>(() => {
return resolveSelectorForSubBlock(subBlock, {
workflowId: workflowIdFromUrl,
credentialId: normalizedCredentialId,
spreadsheetId: normalizedSpreadsheetId,
})
}, [subBlock, workflowIdFromUrl, normalizedCredentialId, normalizedSpreadsheetId])
const missingCredential = !normalizedCredentialId
const missingSpreadsheet = !normalizedSpreadsheetId
const disabledReason =
finalDisabled || missingCredential || missingSpreadsheet || !selectorResolution?.key
if (!selectorResolution?.key) {
return (
<Tooltip.Root>
<Tooltip.Trigger asChild>
<div className='w-full rounded border p-4 text-center text-muted-foreground text-sm'>
Sheet selector not supported for service: {serviceId || 'unknown'}
</div>
</Tooltip.Trigger>
<Tooltip.Content side='top'>
<p>This sheet selector is not implemented for {serviceId || 'unknown'}</p>
</Tooltip.Content>
</Tooltip.Root>
)
}
return (
<SelectorCombobox
blockId={blockId}
subBlock={subBlock}
selectorKey={selectorResolution.key}
selectorContext={selectorResolution.context}
disabled={disabledReason}
isPreview={isPreview}
previewValue={previewValue ?? null}
placeholder={subBlock.placeholder || 'Select sheet'}
allowSearch={selectorResolution.allowSearch}
onOptionChange={(value) => {
if (!isPreview) {
collaborativeSetSubblockValue(blockId, subBlock.id, value)
}
}}
/>
)
}

View File

@@ -1,148 +0,0 @@
'use client'
import { useEffect, useMemo, useState } from 'react'
import { useParams } from 'next/navigation'
import { Tooltip } from '@/components/emcn'
import { getProviderIdFromServiceId } from '@/lib/oauth'
import { SelectorCombobox } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/components/selector-combobox/selector-combobox'
import { useDependsOnGate } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-depends-on-gate'
import { useSubBlockValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/hooks/use-sub-block-value'
import { resolvePreviewContextValue } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/sub-block/utils'
import type { SubBlockConfig } from '@/blocks/types'
import type { SelectorContext, SelectorKey } from '@/hooks/selectors/types'
type SlackSelectorType = 'channel-selector' | 'user-selector'
const SELECTOR_CONFIG: Record<
SlackSelectorType,
{ selectorKey: SelectorKey; placeholder: string; label: string }
> = {
'channel-selector': {
selectorKey: 'slack.channels',
placeholder: 'Select Slack channel',
label: 'Channel',
},
'user-selector': {
selectorKey: 'slack.users',
placeholder: 'Select Slack user',
label: 'User',
},
}
interface SlackSelectorInputProps {
blockId: string
subBlock: SubBlockConfig
disabled?: boolean
onSelect?: (value: string) => void
isPreview?: boolean
previewValue?: any | null
previewContextValues?: Record<string, any>
}
export function SlackSelectorInput({
blockId,
subBlock,
disabled = false,
onSelect,
isPreview = false,
previewValue,
previewContextValues,
}: SlackSelectorInputProps) {
const selectorType = subBlock.type as SlackSelectorType
const config = SELECTOR_CONFIG[selectorType]
const params = useParams()
const workflowIdFromUrl = (params?.workflowId as string) || ''
const [storeValue] = useSubBlockValue(blockId, subBlock.id)
const [authMethod] = useSubBlockValue(blockId, 'authMethod')
const [botToken] = useSubBlockValue(blockId, 'botToken')
const [connectedCredential] = useSubBlockValue(blockId, 'credential')
const effectiveAuthMethod = previewContextValues
? resolvePreviewContextValue(previewContextValues.authMethod)
: authMethod
const effectiveBotToken = previewContextValues
? resolvePreviewContextValue(previewContextValues.botToken)
: botToken
const effectiveCredential = previewContextValues
? resolvePreviewContextValue(previewContextValues.credential)
: connectedCredential
const [_selectedValue, setSelectedValue] = useState<string | null>(null)
const serviceId = subBlock.serviceId || ''
const effectiveProviderId = useMemo(() => getProviderIdFromServiceId(serviceId), [serviceId])
const isSlack = serviceId === 'slack'
const { finalDisabled, dependsOn } = useDependsOnGate(blockId, subBlock, {
disabled,
isPreview,
previewContextValues,
})
const credential: string =
(effectiveAuthMethod as string) === 'bot_token'
? (effectiveBotToken as string) || ''
: (effectiveCredential as string) || ''
useEffect(() => {
const val = isPreview && previewValue !== undefined ? previewValue : storeValue
if (typeof val === 'string') {
setSelectedValue(val)
}
}, [isPreview, previewValue, storeValue])
const requiresCredential = dependsOn.includes('credential')
const missingCredential = !credential || credential.trim().length === 0
const shouldForceDisable = requiresCredential && missingCredential
const context: SelectorContext = useMemo(
() => ({
credentialId: credential,
workflowId: workflowIdFromUrl,
}),
[credential, workflowIdFromUrl]
)
if (!isSlack) {
return (
<Tooltip.Root>
<Tooltip.Trigger asChild>
<div className='w-full rounded border p-4 text-center text-muted-foreground text-sm'>
{config.label} selector not supported for service: {serviceId || 'unknown'}
</div>
</Tooltip.Trigger>
<Tooltip.Content side='top'>
<p>
This {config.label.toLowerCase()} selector is not yet implemented for{' '}
{serviceId || 'unknown'}
</p>
</Tooltip.Content>
</Tooltip.Root>
)
}
return (
<Tooltip.Root>
<Tooltip.Trigger asChild>
<div className='w-full'>
<SelectorCombobox
blockId={blockId}
subBlock={subBlock}
selectorKey={config.selectorKey}
selectorContext={context}
disabled={finalDisabled || shouldForceDisable}
isPreview={isPreview}
previewValue={previewValue ?? null}
placeholder={subBlock.placeholder || config.placeholder}
onOptionChange={(value) => {
setSelectedValue(value)
if (!isPreview) {
onSelect?.(value)
}
}}
/>
</div>
</Tooltip.Trigger>
</Tooltip.Root>
)
}

View File

@@ -177,5 +177,7 @@ export function useDependsOnGate(
depsSatisfied,
blocked,
finalDisabled,
dependencyValues: dependencyValuesMap,
canonicalIndex,
}
}

View File

@@ -0,0 +1,81 @@
'use client'
import { useMemo } from 'react'
import { useParams } from 'next/navigation'
import type { SubBlockConfig } from '@/blocks/types'
import type { SelectorContext, SelectorKey } from '@/hooks/selectors/types'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import { useDependsOnGate } from './use-depends-on-gate'
/**
* Resolves all selector configuration from a sub-block's declarative properties.
*
* Builds a `SelectorContext` by mapping each `dependsOn` entry through the
* canonical index to its `canonicalParamId`, which maps directly to
* `SelectorContext` field names (e.g. `siteId`, `teamId`, `collectionId`).
* The one special case is `oauthCredential` which maps to `credentialId`.
*
* @param blockId - The block containing the selector sub-block
* @param subBlock - The sub-block config (must have `selectorKey` set)
* @param opts - Standard disabled/preview/previewContextValues options
* @returns Everything `SelectorCombobox` needs: key, context, disabled, allowSearch, plus raw dependency values
*/
export function useSelectorSetup(
blockId: string,
subBlock: SubBlockConfig,
opts?: { disabled?: boolean; isPreview?: boolean; previewContextValues?: Record<string, any> }
) {
const params = useParams()
const activeWorkflowId = useWorkflowRegistry((s) => s.activeWorkflowId)
const workflowId = (params?.workflowId as string) || activeWorkflowId || ''
const { finalDisabled, dependencyValues, canonicalIndex } = useDependsOnGate(
blockId,
subBlock,
opts
)
const selectorContext = useMemo<SelectorContext>(() => {
const context: SelectorContext = {
workflowId,
mimeType: subBlock.mimeType,
}
for (const [depKey, value] of Object.entries(dependencyValues)) {
if (value === null || value === undefined) continue
const strValue = String(value)
if (!strValue) continue
const canonicalParamId = canonicalIndex.canonicalIdBySubBlockId[depKey] ?? depKey
if (canonicalParamId === 'oauthCredential') {
context.credentialId = strValue
} else if (canonicalParamId in CONTEXT_FIELD_SET) {
;(context as Record<string, unknown>)[canonicalParamId] = strValue
}
}
return context
}, [dependencyValues, canonicalIndex, workflowId, subBlock.mimeType])
return {
selectorKey: (subBlock.selectorKey ?? null) as SelectorKey | null,
selectorContext,
allowSearch: subBlock.selectorAllowSearch ?? true,
disabled: finalDisabled || !subBlock.selectorKey,
dependencyValues,
}
}
const CONTEXT_FIELD_SET: Record<string, true> = {
credentialId: true,
domain: true,
teamId: true,
projectId: true,
knowledgeBaseId: true,
planId: true,
siteId: true,
collectionId: true,
spreadsheetId: true,
fileId: true,
}

View File

@@ -18,14 +18,11 @@ import {
ComboBox,
ConditionInput,
CredentialSelector,
DocumentSelector,
DocumentTagEntry,
Dropdown,
EvalInput,
FileSelectorInput,
FileUpload,
FilterBuilder,
FolderSelectorInput,
GroupedCheckboxList,
InputFormat,
InputMapping,
@@ -36,13 +33,12 @@ import {
McpServerSelector,
McpToolSelector,
MessagesInput,
ProjectSelectorInput,
ResponseFormat,
ScheduleInfo,
SheetSelectorInput,
SelectorInput,
type SelectorOverrides,
ShortInput,
SkillInput,
SlackSelectorInput,
SliderInput,
SortBuilder,
Switch,
@@ -58,6 +54,23 @@ import { useDependsOnGate } from '@/app/workspace/[workspaceId]/w/[workflowId]/c
import type { SubBlockConfig } from '@/blocks/types'
import { useWebhookManagement } from '@/hooks/use-webhook-management'
const SLACK_OVERRIDES: SelectorOverrides = {
transformContext: (context, deps) => {
const authMethod = deps.authMethod as string
const credentialId =
authMethod === 'bot_token' ? String(deps.botToken ?? '') : String(deps.credential ?? '')
return { ...context, credentialId }
},
}
const FOLDER_OVERRIDES: SelectorOverrides = {
getDefaultValue: (subBlock) => {
const isGmail = subBlock.serviceId === 'gmail'
const isCopyDest = subBlock.canonicalParamId === 'copyDestinationId'
return isGmail && !isCopyDest ? 'INBOX' : null
},
}
/**
* Interface for wand control handlers exposed by sub-block inputs
*/
@@ -901,32 +914,10 @@ function SubBlockComponent({
)
case 'file-selector':
return (
<FileSelectorInput
blockId={blockId}
subBlock={config}
disabled={isDisabled}
isPreview={isPreview}
previewValue={previewValue}
previewContextValues={contextValues}
/>
)
case 'sheet-selector':
return (
<SheetSelectorInput
blockId={blockId}
subBlock={config}
disabled={isDisabled}
isPreview={isPreview}
previewValue={previewValue}
previewContextValues={contextValues}
/>
)
case 'project-selector':
return (
<ProjectSelectorInput
<SelectorInput
blockId={blockId}
subBlock={config}
disabled={isDisabled}
@@ -938,13 +929,14 @@ function SubBlockComponent({
case 'folder-selector':
return (
<FolderSelectorInput
<SelectorInput
blockId={blockId}
subBlock={config}
disabled={isDisabled}
isPreview={isPreview}
previewValue={previewValue}
previewContextValues={contextValues}
overrides={FOLDER_OVERRIDES}
/>
)
@@ -985,12 +977,12 @@ function SubBlockComponent({
case 'document-selector':
return (
<DocumentSelector
<SelectorInput
blockId={blockId}
subBlock={config}
disabled={isDisabled}
isPreview={isPreview}
previewValue={previewValue as any}
previewValue={previewValue}
previewContextValues={contextValues}
/>
)
@@ -1068,13 +1060,14 @@ function SubBlockComponent({
case 'channel-selector':
case 'user-selector':
return (
<SlackSelectorInput
<SelectorInput
blockId={blockId}
subBlock={config}
disabled={isDisabled}
isPreview={isPreview}
previewValue={previewValue}
previewContextValues={contextValues}
overrides={SLACK_OVERRIDES}
/>
)

View File

@@ -69,6 +69,7 @@ export const KnowledgeBlock: BlockConfig = {
title: 'Tag Filters',
type: 'knowledge-tag-filters',
placeholder: 'Add tag filters',
dependsOn: ['knowledgeBaseSelector'],
condition: { field: 'operation', value: 'search' },
},
{
@@ -112,6 +113,7 @@ export const KnowledgeBlock: BlockConfig = {
id: 'documentTags',
title: 'Document Tags',
type: 'document-tag-entry',
dependsOn: ['knowledgeBaseSelector'],
condition: { field: 'operation', value: 'create_document' },
},
],