refactor(web): remove typescript
This commit is contained in:
@@ -2,27 +2,11 @@ import Paper from '@mui/material/Paper';
|
||||
import MuiPopper from '@mui/material/Popper';
|
||||
import Tab from '@mui/material/Tab';
|
||||
import * as React from 'react';
|
||||
|
||||
import Suggestions from 'components/PowerInput/Suggestions';
|
||||
import TabPanel from 'components/TabPanel';
|
||||
|
||||
import { Tabs } from './style';
|
||||
|
||||
interface PopperProps {
|
||||
open: boolean;
|
||||
anchorEl: any;
|
||||
data: any;
|
||||
onSuggestionClick: any;
|
||||
};
|
||||
|
||||
const Popper = (props: PopperProps) => {
|
||||
const {
|
||||
open,
|
||||
anchorEl,
|
||||
data,
|
||||
onSuggestionClick,
|
||||
} = props;
|
||||
|
||||
const Popper = (props) => {
|
||||
const { open, anchorEl, data, onSuggestionClick } = props;
|
||||
return (
|
||||
<MuiPopper
|
||||
open={open}
|
||||
@@ -39,23 +23,15 @@ const Popper = (props: PopperProps) => {
|
||||
]}
|
||||
>
|
||||
<Paper elevation={5} sx={{ width: '100%' }}>
|
||||
<Tabs
|
||||
sx={{ mb: 2 }}
|
||||
value={0}
|
||||
>
|
||||
<Tabs sx={{ mb: 2 }} value={0}>
|
||||
<Tab label="Insert data..." />
|
||||
</Tabs>
|
||||
|
||||
<TabPanel value={0} index={0}>
|
||||
<Suggestions
|
||||
data={data}
|
||||
onSuggestionClick={onSuggestionClick}
|
||||
/>
|
||||
<Suggestions data={data} onSuggestionClick={onSuggestionClick} />
|
||||
</TabPanel>
|
||||
</Paper>
|
||||
</MuiPopper>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
export default Popper;
|
@@ -1,4 +1,3 @@
|
||||
import type { IStep } from 'types';
|
||||
import ExpandLess from '@mui/icons-material/ExpandLess';
|
||||
import ExpandMore from '@mui/icons-material/ExpandMore';
|
||||
import Box from '@mui/material/Box';
|
||||
@@ -11,39 +10,23 @@ import Paper from '@mui/material/Paper';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import throttle from 'lodash/throttle';
|
||||
import * as React from 'react';
|
||||
import { FixedSizeList, ListChildComponentProps } from 'react-window';
|
||||
|
||||
import { FixedSizeList } from 'react-window';
|
||||
import SearchInput from 'components/SearchInput';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
|
||||
type SuggestionsProps = {
|
||||
data: {
|
||||
id: string;
|
||||
name: string;
|
||||
output: Record<string, unknown>[];
|
||||
}[];
|
||||
onSuggestionClick: (variable: any) => void;
|
||||
};
|
||||
|
||||
const SHORT_LIST_LENGTH = 4;
|
||||
const LIST_ITEM_HEIGHT = 64;
|
||||
|
||||
const computeListHeight = (currentLength: number) => {
|
||||
const computeListHeight = (currentLength) => {
|
||||
const numberOfRenderedItems = Math.min(SHORT_LIST_LENGTH, currentLength);
|
||||
return LIST_ITEM_HEIGHT * numberOfRenderedItems;
|
||||
};
|
||||
|
||||
const getPartialArray = (array: any[], length = array.length) => {
|
||||
const getPartialArray = (array, length = array.length) => {
|
||||
return array.slice(0, length);
|
||||
};
|
||||
|
||||
const renderItemFactory =
|
||||
({ onSuggestionClick }: Pick<SuggestionsProps, 'onSuggestionClick'>) =>
|
||||
(props: ListChildComponentProps) => {
|
||||
({ onSuggestionClick }) =>
|
||||
(props) => {
|
||||
const { index, style, data } = props;
|
||||
|
||||
const suboption = data[index];
|
||||
|
||||
return (
|
||||
<ListItemButton
|
||||
sx={{ pl: 4 }}
|
||||
@@ -70,57 +53,47 @@ const renderItemFactory =
|
||||
</ListItemButton>
|
||||
);
|
||||
};
|
||||
|
||||
const Suggestions = (props: SuggestionsProps) => {
|
||||
const Suggestions = (props) => {
|
||||
const formatMessage = useFormatMessage();
|
||||
const { data, onSuggestionClick = () => null } = props;
|
||||
const [current, setCurrent] = React.useState<number | null>(0);
|
||||
const [listLength, setListLength] = React.useState<number>(SHORT_LIST_LENGTH);
|
||||
const [filteredData, setFilteredData] = React.useState<any[]>(data);
|
||||
|
||||
const [current, setCurrent] = React.useState(0);
|
||||
const [listLength, setListLength] = React.useState(SHORT_LIST_LENGTH);
|
||||
const [filteredData, setFilteredData] = React.useState(data);
|
||||
React.useEffect(
|
||||
function syncOptions() {
|
||||
setFilteredData((filteredData) => {
|
||||
if (filteredData.length === 0 && filteredData.length !== data.length) {
|
||||
return data;
|
||||
}
|
||||
|
||||
return filteredData;
|
||||
});
|
||||
},
|
||||
[data]
|
||||
[data],
|
||||
);
|
||||
|
||||
const renderItem = React.useMemo(
|
||||
() =>
|
||||
renderItemFactory({
|
||||
onSuggestionClick,
|
||||
}),
|
||||
[onSuggestionClick]
|
||||
[onSuggestionClick],
|
||||
);
|
||||
|
||||
const expandList = () => {
|
||||
setListLength(Infinity);
|
||||
};
|
||||
|
||||
const collapseList = () => {
|
||||
setListLength(SHORT_LIST_LENGTH);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
setListLength(SHORT_LIST_LENGTH);
|
||||
}, [current]);
|
||||
|
||||
const onSearchChange = React.useMemo(
|
||||
() =>
|
||||
throttle((event: React.ChangeEvent) => {
|
||||
const search = (event.target as HTMLInputElement).value.toLowerCase();
|
||||
|
||||
throttle((event) => {
|
||||
const search = event.target.value.toLowerCase();
|
||||
if (!search) {
|
||||
setFilteredData(data);
|
||||
return;
|
||||
}
|
||||
|
||||
const newFilteredData = data
|
||||
.map((stepWithOutput) => {
|
||||
return {
|
||||
@@ -129,17 +102,15 @@ const Suggestions = (props: SuggestionsProps) => {
|
||||
output: stepWithOutput.output.filter((option) =>
|
||||
`${option.label}\n${option.sampleValue}`
|
||||
.toLowerCase()
|
||||
.includes(search.toLowerCase())
|
||||
.includes(search.toLowerCase()),
|
||||
),
|
||||
};
|
||||
})
|
||||
.filter((stepWithOutput) => stepWithOutput.output.length);
|
||||
|
||||
setFilteredData(newFilteredData);
|
||||
}, 400),
|
||||
[data]
|
||||
[data],
|
||||
);
|
||||
|
||||
return (
|
||||
<Paper elevation={0} sx={{ width: '100%' }}>
|
||||
<Box px={2} pb={2}>
|
||||
@@ -148,13 +119,13 @@ const Suggestions = (props: SuggestionsProps) => {
|
||||
|
||||
{filteredData.length > 0 && (
|
||||
<List disablePadding>
|
||||
{filteredData.map((option: IStep, index: number) => (
|
||||
{filteredData.map((option, index) => (
|
||||
<React.Fragment key={`${index}-${option.name}`}>
|
||||
<ListItemButton
|
||||
divider
|
||||
onClick={() =>
|
||||
setCurrent((currentIndex) =>
|
||||
currentIndex === index ? null : index
|
||||
currentIndex === index ? null : index,
|
||||
)
|
||||
}
|
||||
sx={{ py: 0.5 }}
|
||||
@@ -172,20 +143,15 @@ const Suggestions = (props: SuggestionsProps) => {
|
||||
>
|
||||
<FixedSizeList
|
||||
height={computeListHeight(
|
||||
getPartialArray((option.output as any) || [], listLength)
|
||||
.length
|
||||
getPartialArray(option.output || [], listLength).length,
|
||||
)}
|
||||
width="100%"
|
||||
itemSize={LIST_ITEM_HEIGHT}
|
||||
itemCount={
|
||||
getPartialArray((option.output as any) || [], listLength)
|
||||
.length
|
||||
getPartialArray(option.output || [], listLength).length
|
||||
}
|
||||
overscanCount={2}
|
||||
itemData={getPartialArray(
|
||||
(option.output as any) || [],
|
||||
listLength
|
||||
)}
|
||||
itemData={getPartialArray(option.output || [], listLength)}
|
||||
data-test="power-input-suggestion-group"
|
||||
>
|
||||
{renderItem}
|
||||
@@ -216,5 +182,4 @@ const Suggestions = (props: SuggestionsProps) => {
|
||||
</Paper>
|
||||
);
|
||||
};
|
||||
|
||||
export default Suggestions;
|
@@ -1,21 +1,6 @@
|
||||
import type { IStep } from 'types';
|
||||
|
||||
const joinBy = (delimiter = '.', ...args: string[]) =>
|
||||
const joinBy = (delimiter = '.', ...args) =>
|
||||
args.filter(Boolean).join(delimiter);
|
||||
|
||||
type TProcessPayload = {
|
||||
data: any;
|
||||
parentKey: string;
|
||||
index?: number;
|
||||
parentLabel?: string;
|
||||
};
|
||||
|
||||
const process = ({
|
||||
data,
|
||||
parentKey,
|
||||
index,
|
||||
parentLabel = '',
|
||||
}: TProcessPayload): any[] => {
|
||||
const process = ({ data, parentKey, index, parentLabel = '' }) => {
|
||||
if (typeof data !== 'object') {
|
||||
return [
|
||||
{
|
||||
@@ -25,14 +10,10 @@ const process = ({
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const entries = Object.entries(data);
|
||||
|
||||
return entries.flatMap(([name, sampleValue]) => {
|
||||
const label = joinBy('.', parentLabel, (index as number)?.toString(), name);
|
||||
|
||||
const value = joinBy('.', parentKey, (index as number)?.toString(), name);
|
||||
|
||||
const label = joinBy('.', parentLabel, index?.toString(), name);
|
||||
const value = joinBy('.', parentKey, index?.toString(), name);
|
||||
if (Array.isArray(sampleValue)) {
|
||||
return sampleValue.flatMap((item, index) =>
|
||||
process({
|
||||
@@ -43,7 +24,6 @@ const process = ({
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof sampleValue === 'object' && sampleValue !== null) {
|
||||
return process({
|
||||
data: sampleValue,
|
||||
@@ -51,7 +31,6 @@ const process = ({
|
||||
parentLabel: label,
|
||||
});
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
label,
|
||||
@@ -61,17 +40,14 @@ const process = ({
|
||||
];
|
||||
});
|
||||
};
|
||||
|
||||
export const processStepWithExecutions = (steps: IStep[]): any[] => {
|
||||
export const processStepWithExecutions = (steps) => {
|
||||
if (!steps) return [];
|
||||
|
||||
return steps
|
||||
.filter((step: IStep) => {
|
||||
.filter((step) => {
|
||||
const hasExecutionSteps = !!step.executionSteps?.length;
|
||||
|
||||
return hasExecutionSteps;
|
||||
})
|
||||
.map((step: IStep, index: number) => ({
|
||||
.map((step, index) => ({
|
||||
id: step.id,
|
||||
// TODO: replace with step.name once introduced
|
||||
name: `${index + 1}. ${
|
@@ -5,10 +5,8 @@ import * as React from 'react';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
import { createEditor } from 'slate';
|
||||
import { Editable } from 'slate-react';
|
||||
|
||||
import Slate from 'components/Slate';
|
||||
import Element from 'components/Slate/Element';
|
||||
|
||||
import {
|
||||
customizeEditor,
|
||||
deserialize,
|
||||
@@ -16,29 +14,10 @@ import {
|
||||
serialize,
|
||||
} from 'components/Slate/utils';
|
||||
import { StepExecutionsContext } from 'contexts/StepExecutions';
|
||||
|
||||
import { VariableElement } from 'components/Slate/types';
|
||||
import Popper from './Popper';
|
||||
import { processStepWithExecutions } from './data';
|
||||
import { ChildrenWrapper, FakeInput, InputLabelWrapper } from './style';
|
||||
|
||||
type PowerInputProps = {
|
||||
onChange?: (value: string) => void;
|
||||
onBlur?: (value: string) => void;
|
||||
defaultValue?: string;
|
||||
name: string;
|
||||
label?: string;
|
||||
type?: string;
|
||||
required?: boolean;
|
||||
readOnly?: boolean;
|
||||
description?: string;
|
||||
docUrl?: string;
|
||||
clickToCopy?: boolean;
|
||||
disabled?: boolean;
|
||||
shouldUnregister?: boolean;
|
||||
};
|
||||
|
||||
const PowerInput = (props: PowerInputProps) => {
|
||||
const PowerInput = (props) => {
|
||||
const { control } = useFormContext();
|
||||
const {
|
||||
defaultValue = '',
|
||||
@@ -51,41 +30,34 @@ const PowerInput = (props: PowerInputProps) => {
|
||||
shouldUnregister,
|
||||
} = props;
|
||||
const priorStepsWithExecutions = React.useContext(StepExecutionsContext);
|
||||
const editorRef = React.useRef<HTMLDivElement | null>(null);
|
||||
const editorRef = React.useRef(null);
|
||||
const renderElement = React.useCallback(
|
||||
(props) => <Element {...props} />,
|
||||
[]
|
||||
[],
|
||||
);
|
||||
const [editor] = React.useState(() => customizeEditor(createEditor()));
|
||||
const [showVariableSuggestions, setShowVariableSuggestions] =
|
||||
React.useState(false);
|
||||
|
||||
const disappearSuggestionsOnShift = (
|
||||
event: React.KeyboardEvent<HTMLInputElement>
|
||||
) => {
|
||||
const disappearSuggestionsOnShift = (event) => {
|
||||
if (event.code === 'Tab') {
|
||||
setShowVariableSuggestions(false);
|
||||
}
|
||||
};
|
||||
|
||||
const stepsWithVariables = React.useMemo(() => {
|
||||
return processStepWithExecutions(priorStepsWithExecutions);
|
||||
}, [priorStepsWithExecutions]);
|
||||
|
||||
const handleBlur = React.useCallback(
|
||||
(value) => {
|
||||
onBlur?.(value);
|
||||
},
|
||||
[onBlur]
|
||||
[onBlur],
|
||||
);
|
||||
|
||||
const handleVariableSuggestionClick = React.useCallback(
|
||||
(variable: Pick<VariableElement, 'name' | 'value'>) => {
|
||||
(variable) => {
|
||||
insertVariable(editor, variable, stepsWithVariables);
|
||||
},
|
||||
[stepsWithVariables]
|
||||
[stepsWithVariables],
|
||||
);
|
||||
|
||||
return (
|
||||
<Controller
|
||||
rules={{ required }}
|
||||
@@ -165,5 +137,4 @@ const PowerInput = (props: PowerInputProps) => {
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default PowerInput;
|
@@ -1,23 +1,20 @@
|
||||
import MuiTabs from '@mui/material/Tabs';
|
||||
import { styled } from '@mui/material/styles';
|
||||
|
||||
export const ChildrenWrapper = styled('div')`
|
||||
overflow-wrap: break-word;
|
||||
word-wrap: break-word;
|
||||
word-break: break-word;
|
||||
hyphens: auto;
|
||||
`;
|
||||
|
||||
export const InputLabelWrapper = styled('div')`
|
||||
position: absolute;
|
||||
left: ${({ theme }) => theme.spacing(1.75)};
|
||||
inset: 0;
|
||||
left: -6px;
|
||||
`;
|
||||
|
||||
export const FakeInput = styled('div', {
|
||||
shouldForwardProp: (prop) => prop !== 'disabled',
|
||||
}) <{ disabled?: boolean }>`
|
||||
})`
|
||||
border: 1px solid #eee;
|
||||
min-height: 56px;
|
||||
width: 100%;
|
||||
@@ -28,7 +25,8 @@ export const FakeInput = styled('div', {
|
||||
position: relative;
|
||||
|
||||
${({ disabled, theme }) =>
|
||||
!!disabled && `
|
||||
!!disabled &&
|
||||
`
|
||||
color: ${theme.palette.action.disabled};
|
||||
border-color: ${theme.palette.action.disabled};
|
||||
`}
|
||||
@@ -37,7 +35,8 @@ export const FakeInput = styled('div', {
|
||||
border-color: ${({ theme }) => theme.palette.text.primary};
|
||||
}
|
||||
|
||||
&:focus-within, &:focus {
|
||||
&:focus-within,
|
||||
&:focus {
|
||||
&:before {
|
||||
border-color: ${({ theme }) => theme.palette.primary.main};
|
||||
border-radius: ${({ theme }) => theme.spacing(0.5)};
|
||||
@@ -53,7 +52,6 @@ export const FakeInput = styled('div', {
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const Tabs = styled(MuiTabs)`
|
||||
border-bottom: 1px solid ${({ theme }) => theme.palette.divider};
|
||||
`;
|
Reference in New Issue
Block a user