[Workflow] Add search in variable dropdown (#8479)
- fix Icon variable Plus - add search input - fix dropdown height ## Before  ## After 
This commit is contained in:
@ -15,9 +15,10 @@ const StyledHeader = styled.li`
|
|||||||
border-top-right-radius: ${({ theme }) => theme.border.radius.sm};
|
border-top-right-radius: ${({ theme }) => theme.border.radius.sm};
|
||||||
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
|
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
|
||||||
|
|
||||||
padding: ${({ theme }) => theme.spacing(1)};
|
padding: ${({ theme }) => theme.spacing(1)} 0;
|
||||||
|
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
width: inherit;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: ${({ theme, onClick }) =>
|
background: ${({ theme, onClick }) =>
|
||||||
|
|||||||
@ -3,12 +3,17 @@ import styled from '@emotion/styled';
|
|||||||
type StyledDropdownButtonProps = {
|
type StyledDropdownButtonProps = {
|
||||||
isUnfolded: boolean;
|
isUnfolded: boolean;
|
||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
|
transparentBackground?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const StyledDropdownButtonContainer = styled.div<StyledDropdownButtonProps>`
|
export const StyledDropdownButtonContainer = styled.div<StyledDropdownButtonProps>`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
background: ${({ theme, isUnfolded }) =>
|
background: ${({ theme, isUnfolded, transparentBackground }) =>
|
||||||
isUnfolded ? theme.background.transparent.light : theme.background.primary};
|
transparentBackground
|
||||||
|
? 'none'
|
||||||
|
: isUnfolded
|
||||||
|
? theme.background.transparent.light
|
||||||
|
: theme.background.primary};
|
||||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||||
color: ${({ isActive, theme }) =>
|
color: ${({ isActive, theme }) =>
|
||||||
isActive ? theme.color.blue : theme.font.color.secondary};
|
isActive ? theme.color.blue : theme.font.color.secondary};
|
||||||
@ -22,9 +27,11 @@ export const StyledDropdownButtonContainer = styled.div<StyledDropdownButtonProp
|
|||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: ${({ theme, isUnfolded }) =>
|
background: ${({ theme, isUnfolded, transparentBackground }) =>
|
||||||
isUnfolded
|
transparentBackground
|
||||||
? theme.background.transparent.medium
|
? 'transparent'
|
||||||
: theme.background.transparent.light};
|
: isUnfolded
|
||||||
|
? theme.background.transparent.medium
|
||||||
|
: theme.background.transparent.light};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -11,15 +11,17 @@ import { useTheme } from '@emotion/react';
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { Editor } from '@tiptap/react';
|
import { Editor } from '@tiptap/react';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { IconVariable } from 'twenty-ui';
|
import { IconVariablePlus } from 'twenty-ui';
|
||||||
|
|
||||||
const StyledDropdownVariableButtonContainer = styled(
|
const StyledDropdownVariableButtonContainer = styled(
|
||||||
StyledDropdownButtonContainer,
|
StyledDropdownButtonContainer,
|
||||||
)`
|
)<{ transparentBackground?: boolean }>`
|
||||||
background-color: ${({ theme }) => theme.background.transparent.lighter};
|
background-color: ${({ theme, transparentBackground }) =>
|
||||||
|
transparentBackground
|
||||||
|
? 'transparent'
|
||||||
|
: theme.background.transparent.lighter};
|
||||||
color: ${({ theme }) => theme.font.color.tertiary};
|
color: ${({ theme }) => theme.font.color.tertiary};
|
||||||
padding: ${({ theme }) => theme.spacing(0)};
|
padding: ${({ theme }) => theme.spacing(2)};
|
||||||
margin: ${({ theme }) => theme.spacing(2)};
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const SearchVariablesDropdown = ({
|
const SearchVariablesDropdown = ({
|
||||||
@ -65,12 +67,15 @@ const SearchVariablesDropdown = ({
|
|||||||
scope: dropdownId,
|
scope: dropdownId,
|
||||||
}}
|
}}
|
||||||
clickableComponent={
|
clickableComponent={
|
||||||
<StyledDropdownVariableButtonContainer isUnfolded={isDropdownOpen}>
|
<StyledDropdownVariableButtonContainer
|
||||||
<IconVariable size={theme.icon.size.sm} />
|
isUnfolded={isDropdownOpen}
|
||||||
|
transparentBackground
|
||||||
|
>
|
||||||
|
<IconVariablePlus size={theme.icon.size.sm} />
|
||||||
</StyledDropdownVariableButtonContainer>
|
</StyledDropdownVariableButtonContainer>
|
||||||
}
|
}
|
||||||
dropdownComponents={
|
dropdownComponents={
|
||||||
<DropdownMenuItemsContainer>
|
<DropdownMenuItemsContainer hasMaxHeight>
|
||||||
{selectedStep ? (
|
{selectedStep ? (
|
||||||
<SearchVariablesDropdownStepSubItem
|
<SearchVariablesDropdownStepSubItem
|
||||||
step={selectedStep}
|
step={selectedStep}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { StepOutputSchema } from '@/workflow/search-variables/types/StepOutputSc
|
|||||||
import { isObject } from '@sniptt/guards';
|
import { isObject } from '@sniptt/guards';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { IconChevronLeft, MenuItemSelect } from 'twenty-ui';
|
import { IconChevronLeft, MenuItemSelect } from 'twenty-ui';
|
||||||
|
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
|
||||||
|
|
||||||
type SearchVariablesDropdownStepSubItemProps = {
|
type SearchVariablesDropdownStepSubItemProps = {
|
||||||
step: StepOutputSchema;
|
step: StepOutputSchema;
|
||||||
@ -16,6 +17,7 @@ const SearchVariablesDropdownStepSubItem = ({
|
|||||||
onBack,
|
onBack,
|
||||||
}: SearchVariablesDropdownStepSubItemProps) => {
|
}: SearchVariablesDropdownStepSubItemProps) => {
|
||||||
const [currentPath, setCurrentPath] = useState<string[]>([]);
|
const [currentPath, setCurrentPath] = useState<string[]>([]);
|
||||||
|
const [searchInputValue, setSearchInputValue] = useState('');
|
||||||
|
|
||||||
const getSelectedObject = () => {
|
const getSelectedObject = () => {
|
||||||
let selected = step.outputSchema;
|
let selected = step.outputSchema;
|
||||||
@ -30,6 +32,7 @@ const SearchVariablesDropdownStepSubItem = ({
|
|||||||
|
|
||||||
if (isObject(selectedObject[key])) {
|
if (isObject(selectedObject[key])) {
|
||||||
setCurrentPath([...currentPath, key]);
|
setCurrentPath([...currentPath, key]);
|
||||||
|
setSearchInputValue('');
|
||||||
} else {
|
} else {
|
||||||
onSelect(`{{${step.id}.${[...currentPath, key].join('.')}}}`);
|
onSelect(`{{${step.id}.${[...currentPath, key].join('.')}}}`);
|
||||||
}
|
}
|
||||||
@ -45,12 +48,25 @@ const SearchVariablesDropdownStepSubItem = ({
|
|||||||
|
|
||||||
const headerLabel = currentPath.length === 0 ? step.name : currentPath.at(-1);
|
const headerLabel = currentPath.length === 0 ? step.name : currentPath.at(-1);
|
||||||
|
|
||||||
|
const options = Object.entries(getSelectedObject());
|
||||||
|
|
||||||
|
const filteredOptions = searchInputValue
|
||||||
|
? options.filter(([key]) =>
|
||||||
|
key.toLowerCase().includes(searchInputValue.toLowerCase()),
|
||||||
|
)
|
||||||
|
: options;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<DropdownMenuHeader StartIcon={IconChevronLeft} onClick={goBack}>
|
<DropdownMenuHeader StartIcon={IconChevronLeft} onClick={goBack}>
|
||||||
{headerLabel}
|
{headerLabel}
|
||||||
</DropdownMenuHeader>
|
</DropdownMenuHeader>
|
||||||
{Object.entries(getSelectedObject()).map(([key, value]) => (
|
<DropdownMenuSearchInput
|
||||||
|
autoFocus
|
||||||
|
value={searchInputValue}
|
||||||
|
onChange={(event) => setSearchInputValue(event.target.value)}
|
||||||
|
/>
|
||||||
|
{filteredOptions.map(([key, value]) => (
|
||||||
<MenuItemSelect
|
<MenuItemSelect
|
||||||
key={key}
|
key={key}
|
||||||
selected={false}
|
selected={false}
|
||||||
|
|||||||
@ -231,7 +231,7 @@ export {
|
|||||||
IconUser,
|
IconUser,
|
||||||
IconUserCircle,
|
IconUserCircle,
|
||||||
IconUsers,
|
IconUsers,
|
||||||
IconVariable,
|
IconVariablePlus,
|
||||||
IconVideo,
|
IconVideo,
|
||||||
IconWand,
|
IconWand,
|
||||||
IconWorld,
|
IconWorld,
|
||||||
|
|||||||
Reference in New Issue
Block a user