506 lines
22 KiB
TypeScript
506 lines
22 KiB
TypeScript
/*
|
|
* @Author: ZhaoYing
|
|
* @Date: 2026-02-03 15:40:13
|
|
* @Last Modified by: ZhaoYing
|
|
* @Last Modified time: 2026-04-16 13:57:30
|
|
*/
|
|
import { useState, useRef, useEffect, useLayoutEffect, type FC } from 'react'
|
|
import { createPortal } from 'react-dom'
|
|
import clsx from 'clsx';
|
|
import { Flex, Space, Checkbox } from 'antd'
|
|
import { useTranslation } from 'react-i18next';
|
|
|
|
import type { Suggestion } from '../Editor/plugin/AutocompletePlugin'
|
|
|
|
interface VariableSelectProps {
|
|
options: Suggestion[];
|
|
value?: string | string[];
|
|
allowClear?: boolean;
|
|
filterBooleanType?: boolean;
|
|
multiple?: boolean;
|
|
size?: 'small' | 'middle' | 'large';
|
|
placeholder?: string;
|
|
variant?: 'outlined' | 'borderless' | 'filled';
|
|
className?: string;
|
|
onChange?: (value: string | string[], option: Suggestion | Suggestion[] | undefined) => void;
|
|
}
|
|
|
|
const VariableSelect: FC<VariableSelectProps> = ({
|
|
placeholder,
|
|
options,
|
|
value,
|
|
allowClear = true,
|
|
onChange,
|
|
size = 'middle',
|
|
filterBooleanType = false,
|
|
multiple = false,
|
|
variant = 'outlined',
|
|
className,
|
|
}) => {
|
|
const { t } = useTranslation();
|
|
const [open, setOpen] = useState(false);
|
|
const [search, setSearch] = useState('');
|
|
const [expandedParentKey, setExpandedParentKey] = useState<string | null>(null);
|
|
const [activeIndex, setActiveIndex] = useState<number>(-1);
|
|
const [activePanel, setActivePanel] = useState<'main' | 'child'>('main');
|
|
const [childActiveIndex, setChildActiveIndex] = useState<number>(-1);
|
|
const [dropdownPos, setDropdownPos] = useState({ top: 0, left: 0, width: 0 });
|
|
const [childPanelPos, setChildPanelPos] = useState({ top: 0, right: 0 });
|
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
const dropdownRef = useRef<HTMLDivElement>(null);
|
|
const itemRefs = useRef<Map<string, HTMLElement>>(new Map());
|
|
const childItemRefs = useRef<Map<string, HTMLElement>>(new Map());
|
|
const activeKeyRef = useRef<string | null>(null);
|
|
|
|
const CHILD_PANEL_HEIGHT = 280; // max-h-60 (240) + header (~40)
|
|
|
|
const calcChildPos = (key: string) => {
|
|
const el = itemRefs.current.get(key);
|
|
if (!el) return;
|
|
const rect = el.getBoundingClientRect();
|
|
const dropdownEl = dropdownRef.current;
|
|
if (!dropdownEl) return;
|
|
const dropdownRect = dropdownEl.getBoundingClientRect();
|
|
const dropdownBottom = dropdownRect.bottom;
|
|
const actualChildHeight = Math.min(CHILD_PANEL_HEIGHT, dropdownRect.height);
|
|
// Bottom-align child panel with main panel
|
|
const top = Math.max(10, dropdownBottom - actualChildHeight);
|
|
setChildPanelPos({ top, right: window.innerWidth - rect.left + 8 });
|
|
};
|
|
|
|
// Calculate dropdown position (runs synchronously after DOM paint to avoid flicker)
|
|
useLayoutEffect(() => {
|
|
if (!open || !containerRef.current) return;
|
|
const triggerRect = containerRef.current.getBoundingClientRect();
|
|
const MARGIN = 8;
|
|
const width = triggerRect.width;
|
|
// Set initial width/left immediately; top will be refined once dropdownRef is available
|
|
if (!dropdownRef.current) {
|
|
setDropdownPos({ top: triggerRect.bottom + MARGIN, left: triggerRect.left, width });
|
|
return;
|
|
}
|
|
const dropdownHeight = dropdownRef.current.offsetHeight;
|
|
const dropdownWidth = dropdownRef.current.offsetWidth;
|
|
const left = Math.min(triggerRect.left, window.innerWidth - dropdownWidth - 10);
|
|
const spaceBelow = window.innerHeight - triggerRect.bottom - MARGIN;
|
|
const spaceAbove = triggerRect.top - MARGIN;
|
|
const top = (spaceBelow >= dropdownHeight || spaceBelow >= spaceAbove)
|
|
? triggerRect.bottom + MARGIN
|
|
: Math.max(MARGIN, triggerRect.top - dropdownHeight - MARGIN);
|
|
setDropdownPos({ top, left, width });
|
|
// Re-calculate child panel position if expanded
|
|
if (expandedParentKey) calcChildPos(expandedParentKey);
|
|
}, [open, search, Array.isArray(value) ? value.length : 0, options.length, expandedParentKey]);
|
|
|
|
const filteredOptions = filterBooleanType
|
|
? options.filter(o => o.dataType !== 'boolean')
|
|
: options;
|
|
|
|
const allSuggestions = filteredOptions.flatMap(o => o.children ? [o, ...o.children] : [o]);
|
|
const suggestionMap = new Map(allSuggestions.map(s => [`{{${s.value}}}`, s]));
|
|
|
|
const selectedValues = multiple ? (Array.isArray(value) ? value : []) : [];
|
|
const selectedSuggestion = !multiple && value ? suggestionMap.get(value as string) : undefined;
|
|
const parentOfSelected = !multiple && value
|
|
? filteredOptions.find(o => o.children?.some(c => `{{${c.value}}}` === value))
|
|
: undefined;
|
|
|
|
const expandedParent = expandedParentKey
|
|
? filteredOptions.find(o => o.key === expandedParentKey) ?? null
|
|
: null;
|
|
|
|
const groupedSuggestions = filteredOptions.reduce((groups: Record<string, Suggestion[]>, s) => {
|
|
const nodeId = s.nodeData.id as string;
|
|
if (!groups[nodeId]) groups[nodeId] = [];
|
|
groups[nodeId].push(s);
|
|
return groups;
|
|
}, {});
|
|
|
|
const filteredGroups = search
|
|
? Object.entries(groupedSuggestions).reduce((acc: Record<string, Suggestion[]>, [nodeId, suggestions]) => {
|
|
const matched = suggestions.filter(s =>
|
|
s.label.toLowerCase().includes(search.toLowerCase()) ||
|
|
s.value.toLowerCase().includes(search.toLowerCase()) ||
|
|
s.children?.some(c => c.label.toLowerCase().includes(search.toLowerCase()))
|
|
);
|
|
if (matched.length) acc[nodeId] = matched;
|
|
return acc;
|
|
}, {})
|
|
: groupedSuggestions;
|
|
|
|
useEffect(() => {
|
|
if (!expandedParentKey) return;
|
|
calcChildPos(expandedParentKey);
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [dropdownPos, expandedParentKey]);
|
|
|
|
useEffect(() => {
|
|
if (!open) return;
|
|
const updatePos = () => {
|
|
if (!containerRef.current || !dropdownRef.current) return;
|
|
const triggerRect = containerRef.current.getBoundingClientRect();
|
|
const dropdownHeight = dropdownRef.current.offsetHeight;
|
|
const dropdownWidth = dropdownRef.current.offsetWidth;
|
|
const MARGIN = 8;
|
|
const left = Math.min(triggerRect.left, window.innerWidth - dropdownWidth - 10);
|
|
const spaceBelow = window.innerHeight - triggerRect.bottom - MARGIN;
|
|
const spaceAbove = triggerRect.top - MARGIN;
|
|
let top: number;
|
|
if (spaceBelow >= dropdownHeight || spaceBelow >= spaceAbove) {
|
|
top = triggerRect.bottom + MARGIN;
|
|
} else {
|
|
top = triggerRect.top - dropdownHeight - MARGIN;
|
|
if (top < MARGIN) top = MARGIN;
|
|
}
|
|
setDropdownPos(prev => ({ ...prev, top, left }));
|
|
};
|
|
document.addEventListener('scroll', updatePos, true);
|
|
return () => document.removeEventListener('scroll', updatePos, true);
|
|
}, [open]);
|
|
|
|
useEffect(() => {
|
|
if (!open) return;
|
|
const handler = (e: MouseEvent) => {
|
|
const target = e.target as Node;
|
|
const childPanel = document.getElementById('variable-select-child-panel');
|
|
if (
|
|
!containerRef.current?.contains(target) &&
|
|
!dropdownRef.current?.contains(target) &&
|
|
!childPanel?.contains(target)
|
|
) {
|
|
setOpen(false);
|
|
setSearch('');
|
|
setExpandedParentKey(null);
|
|
setChildPanelPos({ top: 0, right: 0 });
|
|
}
|
|
};
|
|
document.addEventListener('mousedown', handler);
|
|
return () => document.removeEventListener('mousedown', handler);
|
|
}, [open]);
|
|
|
|
// Flat list of all visible selectable items (main panel only, no children expanded inline)
|
|
const flatItems = Object.values(filteredGroups).flat();
|
|
|
|
useEffect(() => {
|
|
setActiveIndex(-1);
|
|
setActivePanel('main');
|
|
setChildActiveIndex(-1);
|
|
}, [open, search]);
|
|
|
|
useEffect(() => {
|
|
if (activeIndex < 0 || activeIndex >= flatItems.length) {
|
|
setExpandedParentKey(null);
|
|
return;
|
|
}
|
|
const s = flatItems[activeIndex];
|
|
activeKeyRef.current = s.key;
|
|
itemRefs.current.get(s.key)?.scrollIntoView({ block: 'nearest' });
|
|
if (s.children?.length) {
|
|
calcChildPos(s.key);
|
|
setExpandedParentKey(s.key);
|
|
} else {
|
|
setExpandedParentKey(null);
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [activeIndex]);
|
|
|
|
useEffect(() => {
|
|
if (!expandedParent?.children?.length || childActiveIndex < 0) return;
|
|
const child = expandedParent.children[childActiveIndex];
|
|
if (child) childItemRefs.current.get(child.key)?.scrollIntoView({ block: 'nearest' });
|
|
}, [childActiveIndex, expandedParent]);
|
|
|
|
useEffect(() => {
|
|
if (!open) return;
|
|
const handler = (e: KeyboardEvent) => {
|
|
const children = expandedParent?.children ?? [];
|
|
if (activePanel === 'child') {
|
|
if (e.key === 'ArrowDown') {
|
|
e.preventDefault();
|
|
setChildActiveIndex(i => Math.min(i + 1, children.length - 1));
|
|
} else if (e.key === 'ArrowUp') {
|
|
e.preventDefault();
|
|
setChildActiveIndex(i => Math.max(i - 1, 0));
|
|
} else if (e.key === 'ArrowRight') {
|
|
e.preventDefault();
|
|
setActivePanel('main');
|
|
setChildActiveIndex(-1);
|
|
} else if (e.key === 'Enter' && childActiveIndex >= 0 && childActiveIndex < children.length) {
|
|
e.preventDefault();
|
|
const child = children[childActiveIndex];
|
|
if (!child.disabled) handleSelect(child);
|
|
} else if (e.key === 'Escape') {
|
|
setOpen(false);
|
|
}
|
|
} else {
|
|
if (e.key === 'ArrowDown') {
|
|
e.preventDefault();
|
|
setActiveIndex(i => Math.min(i + 1, flatItems.length - 1));
|
|
} else if (e.key === 'ArrowUp') {
|
|
e.preventDefault();
|
|
setActiveIndex(i => Math.max(i - 1, 0));
|
|
} else if (e.key === 'ArrowLeft') {
|
|
e.preventDefault();
|
|
if (expandedParent?.children?.length) {
|
|
setActivePanel('child');
|
|
setChildActiveIndex(0);
|
|
}
|
|
} else if (e.key === 'Enter' && activeIndex >= 0 && activeIndex < flatItems.length) {
|
|
e.preventDefault();
|
|
const s = flatItems[activeIndex];
|
|
if (!s.disabled) handleSelect(s);
|
|
} else if (e.key === 'Escape') {
|
|
setOpen(false);
|
|
}
|
|
}
|
|
};
|
|
document.addEventListener('keydown', handler);
|
|
return () => document.removeEventListener('keydown', handler);
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [open, activeIndex, activePanel, childActiveIndex, flatItems, expandedParent]);
|
|
|
|
const handleSelect = (suggestion: Suggestion) => {
|
|
if (multiple) {
|
|
const key = `{{${suggestion.value}}}`;
|
|
const next = selectedValues.includes(key)
|
|
? selectedValues.filter(v => v !== key)
|
|
: [...selectedValues, key];
|
|
const nextOptions = next.map(v => suggestionMap.get(v)).filter(Boolean) as Suggestion[];
|
|
onChange?.(next, nextOptions);
|
|
} else {
|
|
onChange?.(`{{${suggestion.value}}}`, suggestion);
|
|
setOpen(false);
|
|
setSearch('');
|
|
setExpandedParentKey(null);
|
|
}
|
|
};
|
|
|
|
const handleClear = (e: React.MouseEvent) => {
|
|
e.stopPropagation();
|
|
onChange?.(multiple ? [] : '', multiple ? [] : undefined);
|
|
};
|
|
const sep = <span className="rb:text-[#DFE4ED] rb:mx-0.5">/</span>;
|
|
const isConversation = (parentOfSelected ?? selectedSuggestion)?.group === 'CONVERSATION' ||
|
|
(selectedSuggestion ? filteredOptions.some(o => o.group === 'CONVERSATION' && o.children?.some(c => `{{${c.value}}}` === value)) : false);
|
|
const nodeData = (parentOfSelected ?? selectedSuggestion)?.nodeData;
|
|
|
|
return (
|
|
<div ref={containerRef} className={`rb:relative rb:w-full ${className}`}>
|
|
{/* Trigger */}
|
|
<div
|
|
className={clsx(
|
|
'rb:w-full rb:flex rb:items-center rb:justify-between rb:cursor-pointer rb:rounded-lg rb:px-2 rb:transition-colors', {
|
|
'rb:bg-[#F6F6F6] rb:border-none rb:shadow-none': variant === 'filled',
|
|
'rb:border rb:border-[#d9d9d9] hover:rb:border-[#4096ff] rb:bg-white': variant === 'outlined',
|
|
'rb:border-[#171719]!': variant === 'outlined' && open,
|
|
'rb:border-none rb:shadow-none rb:bg-transparent': variant === 'borderless',
|
|
'rb:text-[12px]': size === 'small',
|
|
'rb:text-[14px]': size !== 'small',
|
|
},
|
|
multiple && size === 'small'
|
|
? 'rb:min-h-7 rb:py-0.75'
|
|
: multiple
|
|
? 'rb:min-h-8 rb:py-1'
|
|
: size === 'small'
|
|
? 'rb:h-7 rb:text-[10px]'
|
|
: size === 'large'
|
|
? 'rb:h-10'
|
|
: 'rb:h-8 rb:text-[12px]',
|
|
className
|
|
)}
|
|
onClick={() => setOpen(o => !o)}
|
|
>
|
|
{multiple ? (
|
|
selectedValues.length > 0 ? (
|
|
<Flex wrap gap={4} className="rb:flex-1! rb:min-w-0">
|
|
{selectedValues.map(v => {
|
|
const s = suggestionMap.get(v);
|
|
if (!s) return null;
|
|
const parent = filteredOptions.find(o => o.children?.some(c => `{{${c.value}}}` === v));
|
|
const nd = s.nodeData;
|
|
const isConv = (parent ?? s)?.group === 'CONVERSATION' ||
|
|
filteredOptions.some(o => o.group === 'CONVERSATION' && o.children?.some(c => `{{${c.value}}}` === v));
|
|
return (
|
|
<span
|
|
key={v}
|
|
className="rb-border rb:rounded-md rb:bg-white rb:text-[10px] rb:text-[#212332] rb:h-5! rb:inline-flex rb:items-center rb:p-1 rb:cursor-pointer"
|
|
>
|
|
{!isConv && nd?.icon && <div className={`rb:size-3 rb:bg-cover ${nd.icon}`} />}
|
|
{!isConv && nd?.name && <span className="rb:text-[#5B6167]">{nd.name}{sep}</span>}
|
|
<span>
|
|
{parent ? <>{parent.label}{sep}{s.label}</> : s.label}
|
|
</span>
|
|
<span
|
|
className="rb:cursor-pointer rb:text-[#bfbfbf] hover:rb:text-[#999] rb:leading-none rb:ml-0.5"
|
|
onClick={(e) => { e.stopPropagation(); handleSelect(s); }}
|
|
>✕</span>
|
|
</span>
|
|
);
|
|
})}
|
|
</Flex>
|
|
) : (
|
|
<span className="rb:text-[rgba(23,23,25,0.25)] rb:text-ellipsis rb:overflow-hidden rb:whitespace-nowrap rb:flex-1">{placeholder}</span>
|
|
)
|
|
) : selectedSuggestion ? (
|
|
<div className="rb:flex rb:flex-1 rb:min-w-0 rb:max-w-full">
|
|
<span
|
|
className="rb-border rb:rounded-md rb:bg-white rb:text-[10px] rb:text-[#212332] rb:h-5! rb:inline-flex rb:items-center rb:p-1 rb:cursor-pointer"
|
|
>
|
|
{!isConversation && nodeData?.icon && <div className={`rb:size-3 rb:bg-cover rb:mr-1 ${nodeData.icon}`} />}
|
|
{!isConversation && nodeData?.name && <span className="rb:shrink rb:min-w-0 rb:truncate rb:max-w-[40%]">{nodeData.name}</span>}
|
|
{!isConversation && nodeData?.name && <span>{sep}</span>}
|
|
<span className="rb:shrink rb:min-w-0 rb:truncate">
|
|
{parentOfSelected ? <>{parentOfSelected.label}{sep}{selectedSuggestion.label}</> : selectedSuggestion.label}
|
|
</span>
|
|
</span>
|
|
</div>
|
|
) : (
|
|
<span className="rb:text-[rgba(23,23,25,0.25)] rb:flex-1">{placeholder}</span>
|
|
)}
|
|
<Space size={4} className="rb:shrink-0 rb:ml-1">
|
|
{allowClear && (
|
|
<span
|
|
className={clsx('rb:text-[#bfbfbf] rb:text-[10px] hover:rb:text-[#999] rb:leading-none rb:transition-opacity',
|
|
(multiple ? selectedValues.length > 0 : !!selectedSuggestion) ? 'rb:opacity-100 rb:cursor-pointer' : 'rb:opacity-0 rb:pointer-events-none'
|
|
)}
|
|
onClick={handleClear}
|
|
>✕</span>
|
|
)}
|
|
<div className={clsx("rb:size-3 rb:bg-cover rb:bg-[url('@/assets/images/common/arrow_up.svg')]", {
|
|
'rb:rotate-0': open,
|
|
'rb:rotate-180': !open,
|
|
})}></div>
|
|
</Space>
|
|
</div>
|
|
|
|
{/* Dropdown via portal */}
|
|
{open && createPortal(
|
|
<div
|
|
ref={dropdownRef}
|
|
className="rb:min-w-70 rb:max-h-57.5 rb:overflow-y-auto rb:fixed rb:z-1000 rb:bg-white rb:rounded-lg rb:border-[0.5px] rb:border-[#EBEBEB] rb:shadow-[0px_2px_6px_0px_rgba(0,0,0,0.1)] rb:py-3 rb:px-2"
|
|
style={{ top: dropdownPos.top, left: dropdownPos.left, minWidth: dropdownPos.width }}
|
|
>
|
|
<div className="rb:min-w-70 rb:max-h-57.5 rb:overflow-y-auto">
|
|
{Object.entries(filteredGroups).map(([nodeId, suggestions], index) => {
|
|
const nd = suggestions[0].nodeData;
|
|
return (
|
|
<div key={nodeId} className={clsx("rb:text-[12px]", {
|
|
'rb:mt-3': index !== 0
|
|
})}>
|
|
<div className="rb:px-2 rb:leading-4.25 rb:mb-1.25 rb:font-medium rb:text-[#5B6167]">
|
|
{nd.name}
|
|
</div>
|
|
{suggestions.map(s => {
|
|
const isSelected = multiple
|
|
? selectedValues.includes(`{{${s.value}}}`)
|
|
: `{{${s.value}}}` === value;
|
|
const isExpanded = expandedParent?.key === s.key;
|
|
const hasChildren = !!s.children?.length;
|
|
return (
|
|
<Flex
|
|
key={s.key}
|
|
ref={(el) => { if (el) itemRefs.current.set(s.key, el); }}
|
|
className={clsx("rb:px-2! rb:py-0.75! rb:rounded-sm rb:leading-4.5 rb:text-[#5B6167] rb:hover:bg-[#F6F6F6]", {
|
|
'rb:bg-[#F6F6F6]': isSelected || isExpanded || flatItems.indexOf(s) === activeIndex,
|
|
'rb:cursor-not-allowed rb:opacity-65': s.disabled,
|
|
'rb:cursor-pointer': !s.disabled,
|
|
})}
|
|
align="center"
|
|
justify="space-between"
|
|
onClick={() => {
|
|
if (s.disabled) return;
|
|
if (hasChildren) {
|
|
calcChildPos(s.key);
|
|
setExpandedParentKey(prev => prev === s.key ? null : s.key);
|
|
}
|
|
handleSelect(s);
|
|
}}
|
|
onMouseEnter={() => {
|
|
if (hasChildren) {
|
|
calcChildPos(s.key);
|
|
setExpandedParentKey(s.key);
|
|
} else {
|
|
setExpandedParentKey(null);
|
|
}
|
|
}}
|
|
>
|
|
<div className="rb:font-medium">
|
|
{multiple && (
|
|
<Checkbox checked={isSelected} className="rb:mr-2!" />
|
|
)}
|
|
<span className="rb:text-[#155EEF]">{`{x}`}</span> {s.label}
|
|
</div>
|
|
|
|
<Space size={2}>
|
|
{s.dataType && <span>{s.dataType}</span>}
|
|
{hasChildren && <div className="rb:size-3 rb:bg-cover rb:bg-[url('@/assets/images/common/arrow_up.svg')] rb:rotate-90"></div>}
|
|
</Space>
|
|
</Flex>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
})}
|
|
{Object.keys(filteredGroups).length === 0 && (
|
|
<div className="rb:px-3 rb:py-4 rb:text-center rb:text-[#bfbfbf] rb:text-[14px]">
|
|
{t('workflow.variableSelect.empty')}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>,
|
|
document.body
|
|
)}
|
|
|
|
{/* Child panel via portal — escapes overflow clipping */}
|
|
{open && expandedParent?.children?.length && createPortal(
|
|
<div
|
|
id="variable-select-child-panel"
|
|
className="rb:min-w-70 rb:max-h-57.5 rb:overflow-y-auto rb:text-[12px] rb:fixed rb:z-1000 rb:bg-white rb:rounded-lg rb:border-[0.5px] rb:border-[#EBEBEB] rb:shadow-[0px_2px_6px_0px_rgba(0,0,0,0.1)] rb:py-3 rb:px-2"
|
|
style={{ top: childPanelPos.top, right: childPanelPos.right }}
|
|
onMouseEnter={() => setExpandedParentKey(expandedParentKey)}
|
|
>
|
|
<div className="rb:pb-2 rb:mb-1 rb:font-medium rb:text-[#5B6167] rb-border-b">
|
|
<Flex justify="space-between" align="center" gap={8}>
|
|
<span>{expandedParent.nodeData.name}.{expandedParent.label}</span>
|
|
<span>{expandedParent.dataType}</span>
|
|
</Flex>
|
|
</div>
|
|
{expandedParent.children.map((child, ci) => {
|
|
const isSelected = multiple
|
|
? selectedValues.includes(`{{${child.value}}}`)
|
|
: `{{${child.value}}}` === value;
|
|
const isChildActive = activePanel === 'child' && ci === childActiveIndex;
|
|
return (
|
|
<Flex
|
|
key={child.key}
|
|
ref={(el) => { if (el) childItemRefs.current.set(child.key, el); }}
|
|
className={clsx("rb:px-2! rb:py-0.75! rb:rounded-sm rb:leading-4.5 rb:text-[#5B6167] rb:hover:bg-[#F6F6F6]", {
|
|
'rb:bg-[#F6F6F6]': isSelected || isChildActive,
|
|
'rb:cursor-not-allowed rb:opacity-65': child.disabled,
|
|
'rb:cursor-pointer': !child.disabled,
|
|
})}
|
|
align="center"
|
|
justify="space-between"
|
|
onClick={() => !child.disabled && handleSelect(child)}
|
|
>
|
|
<Flex align="center" gap={8}>
|
|
{multiple && (
|
|
<Checkbox checked={isSelected} />
|
|
)}
|
|
<span className="rb:font-medium">{child.label}</span>
|
|
</Flex>
|
|
<Space size={2}>
|
|
{child.dataType && <span>{child.dataType}</span>}
|
|
</Space>
|
|
</Flex>
|
|
);
|
|
})}
|
|
</div>,
|
|
document.body
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default VariableSelect
|