|
@@ -17,6 +17,11 @@ interface ExpressionInputProps {
|
|
|
availableFields?: AvailableField[]
|
|
availableFields?: AvailableField[]
|
|
|
multiline?: boolean
|
|
multiline?: boolean
|
|
|
className?: string
|
|
className?: string
|
|
|
|
|
+ // 'path' when the setting wants a bare path - data.result.items - rather than
|
|
|
|
|
+ // an expression. The available-data panel always hands over the {{ }} form,
|
|
|
|
|
+ // because that is what most settings take; dropping one into a path field
|
|
|
|
|
+ // would otherwise leave braces the node has to be lenient about.
|
|
|
|
|
+ valueKind?: 'path' | 'expression'
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
type ContentKind = 'plain' | 'json' | 'templated-json' | 'broken-json' | 'markdown'
|
|
type ContentKind = 'plain' | 'json' | 'templated-json' | 'broken-json' | 'markdown'
|
|
@@ -102,7 +107,24 @@ export function ExpressionInput({
|
|
|
availableFields = [],
|
|
availableFields = [],
|
|
|
multiline = false,
|
|
multiline = false,
|
|
|
className = '',
|
|
className = '',
|
|
|
|
|
+ valueKind = 'expression',
|
|
|
}: ExpressionInputProps) {
|
|
}: ExpressionInputProps) {
|
|
|
|
|
+
|
|
|
|
|
+ // A setting that wants a path gets the path, not the {{ }} wrapping it. Only
|
|
|
|
|
+ // the plain form is unwrapped: {{$node["X"].y}} names a different node, which
|
|
|
|
|
+ // a path cannot express, so that is left alone rather than silently turned
|
|
|
|
|
+ // into something that would read the wrong node.
|
|
|
|
|
+ const handleDrop = (event: React.DragEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
|
|
|
|
+ const dropped = event.dataTransfer.getData('text/plain')
|
|
|
|
|
+ if (!dropped) return
|
|
|
|
|
+ if (valueKind !== 'path') return
|
|
|
|
|
+
|
|
|
|
|
+ const plain = /^\{\{\s*([^${}][^{}]*?)\s*\}\}$/.exec(dropped)
|
|
|
|
|
+ if (!plain) return
|
|
|
|
|
+
|
|
|
|
|
+ event.preventDefault()
|
|
|
|
|
+ onChange(plain[1])
|
|
|
|
|
+ }
|
|
|
const [showAutocomplete, setShowAutocomplete] = useState(false)
|
|
const [showAutocomplete, setShowAutocomplete] = useState(false)
|
|
|
const [autocompletePosition, setAutocompletePosition] = useState({ top: 0, left: 0 })
|
|
const [autocompletePosition, setAutocompletePosition] = useState({ top: 0, left: 0 })
|
|
|
const [filterText, setFilterText] = useState('')
|
|
const [filterText, setFilterText] = useState('')
|
|
@@ -309,7 +331,13 @@ export function ExpressionInput({
|
|
|
onChange={handleChange}
|
|
onChange={handleChange}
|
|
|
onSelect={handleSelect}
|
|
onSelect={handleSelect}
|
|
|
onKeyDown={handleKeyDown}
|
|
onKeyDown={handleKeyDown}
|
|
|
- placeholder={placeholder || 'Enter value or use {{ }} for expressions'}
|
|
|
|
|
|
|
+ onDrop={handleDrop}
|
|
|
|
|
+ placeholder={
|
|
|
|
|
+ placeholder ||
|
|
|
|
|
+ (valueKind === 'path'
|
|
|
|
|
+ ? 'A path such as data.result.items'
|
|
|
|
|
+ : 'Enter value or use {{ }} for expressions')
|
|
|
|
|
+ }
|
|
|
className={`w-full px-3 py-2 pr-10 border border-gray-200 dark:border-slate-600 rounded-l-lg text-sm bg-white dark:bg-slate-900 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-primary-500 focus:border-primary-500 resize-y overflow-y-auto ${
|
|
className={`w-full px-3 py-2 pr-10 border border-gray-200 dark:border-slate-600 rounded-l-lg text-sm bg-white dark:bg-slate-900 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-primary-500 focus:border-primary-500 resize-y overflow-y-auto ${
|
|
|
isCode ? 'font-mono text-xs' : ''
|
|
isCode ? 'font-mono text-xs' : ''
|
|
|
}`}
|
|
}`}
|