diff --git a/mage_ai/frontend/components/CodeBlock/index.style.tsx b/mage_ai/frontend/components/CodeBlock/index.style.tsx index c19443326b7f..f1f174ecc939 100644 --- a/mage_ai/frontend/components/CodeBlock/index.style.tsx +++ b/mage_ai/frontend/components/CodeBlock/index.style.tsx @@ -115,7 +115,7 @@ export const BlockDivider = styled.div` height: ${UNIT * 2}px; justify-content: center; position: relative; - z-index: 1; + z-index: 10; &:hover { .block-divider-inner { diff --git a/mage_ai/frontend/components/PipelineDetail/AddNewBlocks/index.tsx b/mage_ai/frontend/components/PipelineDetail/AddNewBlocks/index.tsx index 1c5d45b25813..4310602425e9 100644 --- a/mage_ai/frontend/components/PipelineDetail/AddNewBlocks/index.tsx +++ b/mage_ai/frontend/components/PipelineDetail/AddNewBlocks/index.tsx @@ -1,27 +1,53 @@ -import BlockType, { BlockTypeEnum } from '@interfaces/BlockType'; +import { useRef, useState } from 'react'; + import FlexContainer from '@oracle/components/FlexContainer'; +import FlyoutMenuWrapper from '@oracle/components/FlyoutMenu/FlyoutMenuWrapper'; import KeyboardShortcutButton from '@oracle/elements/Button/KeyboardShortcutButton'; import Spacing from '@oracle/elements/Spacing'; import { Add } from '@oracle/icons'; +import { BlockRequestPayloadType, BlockTypeEnum } from '@interfaces/BlockType'; +import { + DATA_SOURCE_TYPES, + DATA_SOURCE_TYPE_HUMAN_READABLE_NAME_MAPPING, + DataSourceTypeEnum, +} from '@interfaces/DataSourceType'; import { ICON_SIZE, IconContainerStyle, } from './index.style'; type AddNewBlocksProps = { - addNewBlock: (block: BlockType) => void; + addNewBlock: (block: BlockRequestPayloadType) => void; compact?: boolean; }; +const TRANSFORMER_BUTTON_INDEX = 0; +const DATA_LOADER_BUTTON_INDEX = 1; + function AddNewBlocks({ addNewBlock, compact, }: AddNewBlocksProps) { + const [buttonMenuOpenIndex, setButtonMenuOpenIndex] = useState(null); + const dataLoaderButtonRef = useRef(null); const sharedProps = { compact, inline: true, }; + const dataLoaderMenuItems = DATA_SOURCE_TYPES.map((sourceType: DataSourceTypeEnum) => ({ + label: () => DATA_SOURCE_TYPE_HUMAN_READABLE_NAME_MAPPING[sourceType], + onClick: () => { + addNewBlock({ + config: { + data_source: sourceType, + }, + type: BlockTypeEnum.DATA_LOADER, + }); + }, + uuid: sourceType, + })); + return ( - - - - } - onClick={(e) => { - e.preventDefault(); - addNewBlock({ - type: BlockTypeEnum.DATA_LOADER, - }); - }} - uuid="AddNewBlocks/Data_loader" + setButtonMenuOpenIndex(null)} + open={buttonMenuOpenIndex === DATA_LOADER_BUTTON_INDEX} + parentRef={dataLoaderButtonRef} + uuid="data_loader_button" > - Data loader - + + + + } + onClick={(e) => { + e.preventDefault(); + setButtonMenuOpenIndex(val => + val === DATA_LOADER_BUTTON_INDEX + ? null + : DATA_LOADER_BUTTON_INDEX, + ); + }} + uuid="AddNewBlocks/Data_loader" + > + Data loader + + diff --git a/mage_ai/frontend/components/PipelineDetail/index.tsx b/mage_ai/frontend/components/PipelineDetail/index.tsx index 2ec21f3a7181..f087a5d267b7 100644 --- a/mage_ai/frontend/components/PipelineDetail/index.tsx +++ b/mage_ai/frontend/components/PipelineDetail/index.tsx @@ -9,7 +9,7 @@ import useWebSocket from 'react-use-websocket'; import { CSSTransition } from 'react-transition-group'; import AddNewBlocks from '@components/PipelineDetail/AddNewBlocks'; -import BlockType, { BlockTypeEnum, SetEditingBlockType } from '@interfaces/BlockType'; +import BlockType, { BlockRequestPayloadType, BlockTypeEnum, SetEditingBlockType } from '@interfaces/BlockType'; import CodeBlock from '@components/CodeBlock'; import KernelOutputType, { ExecutionStateEnum } from '@interfaces/KernelOutputType'; import KernelType, { SetMessagesType } from '@interfaces/KernelType'; @@ -43,7 +43,7 @@ import { useKeyboardContext } from '@context/Keyboard'; type PipelineDetailProps = { addNewBlockAtIndex: ( - block: BlockType, + block: BlockRequestPayloadType, idx: number, onCreateCallback?: (block: BlockType) => void, name?: string, @@ -378,7 +378,7 @@ function PipelineDetail({ return ( { + addNewBlock={(b: BlockRequestPayloadType) => { setTextareaFocused(true); return addNewBlockAtIndex(b, idx + 1, setSelectedBlock); @@ -417,7 +417,7 @@ function PipelineDetail({ { + addNewBlock={(b: BlockRequestPayloadType) => { addNewBlockAtIndex(b, numberOfBlocks, setSelectedBlock); setTextareaFocused(true); }} diff --git a/mage_ai/frontend/interfaces/BlockType.ts b/mage_ai/frontend/interfaces/BlockType.ts index 639b8ee66867..b7b01e4f9a65 100644 --- a/mage_ai/frontend/interfaces/BlockType.ts +++ b/mage_ai/frontend/interfaces/BlockType.ts @@ -1,4 +1,6 @@ import FeatureType from '@interfaces/FeatureType'; +import { ActionTypeEnum, AxisEnum } from './ActionPayloadType'; +import { DataSourceTypeEnum } from './DataSourceType'; import { DataTypeEnum } from './KernelOutputType'; export enum BlockTypeEnum { @@ -56,6 +58,16 @@ export interface AnalysisType { variable_uuid: string; } +export interface BlockRequestPayloadType { + name?: string; + type: BlockTypeEnum; + config?: { + data_source?: DataSourceTypeEnum; + action_type?: ActionTypeEnum; + axis?: AxisEnum; + }; +} + export default interface BlockType { content?: string; downstream_blocks?: string[]; diff --git a/mage_ai/frontend/interfaces/DataSourceType.ts b/mage_ai/frontend/interfaces/DataSourceType.ts new file mode 100644 index 000000000000..ae5f1b199089 --- /dev/null +++ b/mage_ai/frontend/interfaces/DataSourceType.ts @@ -0,0 +1,28 @@ +export enum DataSourceTypeEnum { + BIGQUERY = 'bigquery', + FILE = 'file', + POSTGRES = 'postgres', + REDSHIFT = 'redshift', + S3 = 's3', + SNOWFLAKE = 'snowflake', +} + +export const DATA_SOURCE_TYPE_HUMAN_READABLE_NAME_MAPPING = { + [DataSourceTypeEnum.BIGQUERY]: 'Google BigQuery', + [DataSourceTypeEnum.FILE]: 'Local file', + [DataSourceTypeEnum.POSTGRES]: 'PostgreSQL', + [DataSourceTypeEnum.REDSHIFT]: 'Amazon Redshift', + [DataSourceTypeEnum.S3]: 'Amazon S3', + [DataSourceTypeEnum.SNOWFLAKE]: 'Snowflake', +}; + +export const DATA_SOURCE_TYPES: DataSourceTypeEnum[] = [ + DataSourceTypeEnum.FILE, + DataSourceTypeEnum.BIGQUERY, + DataSourceTypeEnum.POSTGRES, + DataSourceTypeEnum.REDSHIFT, + DataSourceTypeEnum.S3, + DataSourceTypeEnum.SNOWFLAKE, +]; + +export default DataSourceTypeEnum; diff --git a/mage_ai/frontend/interfaces/KeyboardShortcutType.ts b/mage_ai/frontend/interfaces/KeyboardShortcutType.ts index 290c5a65822a..cae3341acf16 100644 --- a/mage_ai/frontend/interfaces/KeyboardShortcutType.ts +++ b/mage_ai/frontend/interfaces/KeyboardShortcutType.ts @@ -1,6 +1,6 @@ export type KeyMappingType = { [key: string]: boolean; -} +}; export default interface KeyboardShortcutType { keyHistory: number[]; diff --git a/mage_ai/frontend/oracle/components/FlyoutMenu/FlyoutMenuWrapper.tsx b/mage_ai/frontend/oracle/components/FlyoutMenu/FlyoutMenuWrapper.tsx new file mode 100644 index 000000000000..854df025d054 --- /dev/null +++ b/mage_ai/frontend/oracle/components/FlyoutMenu/FlyoutMenuWrapper.tsx @@ -0,0 +1,38 @@ +import ClickOutside from '@oracle/components/ClickOutside'; +import FlyoutMenu, { FlyoutMenuProps } from './index'; + +type FlyoutMenuWrapperProps = { + children: JSX.Element; + onClickOutside: () => void; +} & FlyoutMenuProps; + +function FlyoutMenuWrapper({ + children, + items, + open, + onClickOutside, + parentRef, + uuid, +}: FlyoutMenuWrapperProps) { + return ( + +
+
+ {children} +
+ +
+
+ ); +} + +export default FlyoutMenuWrapper; diff --git a/mage_ai/frontend/oracle/components/FlyoutMenu/index.tsx b/mage_ai/frontend/oracle/components/FlyoutMenu/index.tsx index 9b644c6b44fd..0da32220876b 100644 --- a/mage_ai/frontend/oracle/components/FlyoutMenu/index.tsx +++ b/mage_ai/frontend/oracle/components/FlyoutMenu/index.tsx @@ -28,7 +28,7 @@ export type FlyoutMenuItemType = { uuid: string; }; -type FlyoutMenuProps = { +export type FlyoutMenuProps = { items: FlyoutMenuItemType[]; onClickCallback?: () => void; open: boolean; diff --git a/mage_ai/frontend/pages/pipelines/[...slug].tsx b/mage_ai/frontend/pages/pipelines/[...slug].tsx index a0920514797d..090206444d8c 100644 --- a/mage_ai/frontend/pages/pipelines/[...slug].tsx +++ b/mage_ai/frontend/pages/pipelines/[...slug].tsx @@ -9,6 +9,7 @@ import { useMutation } from 'react-query'; import { useRouter } from 'next/router'; import BlockType, { + BlockRequestPayloadType, BlockTypeEnum, OutputType, SampleDataType, @@ -47,7 +48,6 @@ import { } from '@components/Sidekick/constants'; import { UNIT } from '@oracle/styles/units/spacing'; import { equals, pushAtIndex, removeAtIndex } from '@utils/array'; -import { getBlockPath } from '@components/FileTree/utils'; import { goToWithQuery } from '@utils/routing'; import { onSuccess } from '@api/utils/response'; import { randomNameGenerator } from '@utils/string';