diff --git a/src/App.tsx b/src/App.tsx index f6014e8..6d3756d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,16 +1,16 @@ import algoliasearch from 'algoliasearch'; import { createStore, StoreProvider } from 'easy-peasy'; import { useRef } from 'react'; -import Toast from './components/atoms/Toast'; -import ToolTip from './components/atoms/Tooltip'; -import ConfirmationModal from './components/containers/ConfirmationModal'; -import KBarList from './components/containers/KBarList'; -import EditorPane from './components/panes/EditorPane'; -import NavigationPane from './components/panes/NavigationPane'; -import WorkflowsPane from './components/panes/WorkflowsPane'; +import Toast from './core/components/atoms/Toast'; +import ToolTip from './core/components/atoms/Tooltip'; +import ConfirmationModal from './core/components/containers/ConfirmationModal'; +import KBarList from './core/components/containers/KBarList'; +import EditorPane from './core/components/panes/EditorPane'; +import NavigationPane from './core/components/panes/NavigationPane'; +import WorkflowsPane from './core/components/panes/WorkflowsPane'; import './index.css'; -import useWindowDimensions, { useStoreState } from './state/Hooks'; -import Store from './state/Store'; +import useWindowDimensions, { useStoreState } from './core/state/Hooks'; +import Store from './core/state/Store'; export const store = createStore(Store); export const inspectorWidth = 400; diff --git a/src/components/atoms/AddButton.tsx b/src/core/components/atoms/AddButton.tsx similarity index 96% rename from src/components/atoms/AddButton.tsx rename to src/core/components/atoms/AddButton.tsx index 47f9c7a..e2d7f55 100644 --- a/src/components/atoms/AddButton.tsx +++ b/src/core/components/atoms/AddButton.tsx @@ -7,7 +7,7 @@ const AddButton = (props: ButtonHTMLAttributes) => { type="button" {...props} className={ - `bg-circle-gray-300 transition-colors h-8 w-8 rounded ${props.className} + `bg-circle-gray-300 transition-colors h-8 w-8 rounded ${props.className} ${props.disabled ? 'opacity-50 cursor-default' : 'hover:bg-circle-gray-400 '}` } > diff --git a/src/components/atoms/Button.tsx b/src/core/components/atoms/Button.tsx similarity index 100% rename from src/components/atoms/Button.tsx rename to src/core/components/atoms/Button.tsx diff --git a/src/components/atoms/Card.tsx b/src/core/components/atoms/Card.tsx similarity index 100% rename from src/components/atoms/Card.tsx rename to src/core/components/atoms/Card.tsx diff --git a/src/components/atoms/ComponentInfo.tsx b/src/core/components/atoms/ComponentInfo.tsx similarity index 93% rename from src/components/atoms/ComponentInfo.tsx rename to src/core/components/atoms/ComponentInfo.tsx index 27cc6bf..8cc764e 100644 --- a/src/components/atoms/ComponentInfo.tsx +++ b/src/core/components/atoms/ComponentInfo.tsx @@ -1,6 +1,6 @@ import InspectableMapping, { GenerableInfoType, -} from '../../mappings/InspectableMapping'; +} from '../../../mappings/InspectableMapping'; const ComponentInfo = ({ type, diff --git a/src/components/atoms/ConnectionLine.tsx b/src/core/components/atoms/ConnectionLine.tsx similarity index 100% rename from src/components/atoms/ConnectionLine.tsx rename to src/core/components/atoms/ConnectionLine.tsx diff --git a/src/components/atoms/Definition.tsx b/src/core/components/atoms/Definition.tsx similarity index 83% rename from src/components/atoms/Definition.tsx rename to src/core/components/atoms/Definition.tsx index 24d26ae..c5e6e60 100644 --- a/src/components/atoms/Definition.tsx +++ b/src/core/components/atoms/Definition.tsx @@ -5,7 +5,7 @@ import { OrbImport, OrbRef, } from '@circleci/circleci-config-sdk/dist/src/lib/Orb'; -import InspectableMapping from '../../mappings/InspectableMapping'; +import InspectableMapping from '../../../mappings/InspectableMapping'; import { useStoreActions } from '../../state/Hooks'; import { InspectorDefinitionMenuNav } from '../menus/definitions/InspectorDefinitionMenu'; @@ -35,7 +35,7 @@ export const flattenGenerable = (data: Generable, nested?: boolean) => { )[0]; }; -const Definition = (props: { +const Definition = ({ data, ...props}: { data: Generable | OrbRef; type: InspectableMapping; index: number; @@ -47,27 +47,27 @@ const Definition = (props: { return ( ); }; diff --git a/src/components/atoms/Edge.tsx b/src/core/components/atoms/Edge.tsx similarity index 100% rename from src/components/atoms/Edge.tsx rename to src/core/components/atoms/Edge.tsx diff --git a/src/components/atoms/Empty.tsx b/src/core/components/atoms/Empty.tsx similarity index 100% rename from src/components/atoms/Empty.tsx rename to src/core/components/atoms/Empty.tsx diff --git a/src/components/atoms/Footer.tsx b/src/core/components/atoms/Footer.tsx similarity index 91% rename from src/components/atoms/Footer.tsx rename to src/core/components/atoms/Footer.tsx index ef1d469..b749af3 100644 --- a/src/components/atoms/Footer.tsx +++ b/src/core/components/atoms/Footer.tsx @@ -1,5 +1,5 @@ import { PropsWithChildren } from 'react'; -import { inspectorWidth } from '../../App'; +import { inspectorWidth } from '../../../App'; export const Footer = ({ children, diff --git a/src/components/atoms/Info.tsx b/src/core/components/atoms/Info.tsx similarity index 100% rename from src/components/atoms/Info.tsx rename to src/core/components/atoms/Info.tsx diff --git a/src/components/atoms/OpenConfig.tsx b/src/core/components/atoms/OpenConfig.tsx similarity index 96% rename from src/components/atoms/OpenConfig.tsx rename to src/core/components/atoms/OpenConfig.tsx index b94fc35..2a3b6f0 100644 --- a/src/components/atoms/OpenConfig.tsx +++ b/src/core/components/atoms/OpenConfig.tsx @@ -5,7 +5,7 @@ import { useStoreActions, useStoreState, } from '../../state/Hooks'; -import { Button } from '../atoms/Button'; +import { Button } from './Button'; export const OpenConfig = () => { const inputFile = useRef(null); diff --git a/src/components/atoms/Select.tsx b/src/core/components/atoms/Select.tsx similarity index 98% rename from src/components/atoms/Select.tsx rename to src/core/components/atoms/Select.tsx index afd1600..0b7ee26 100644 --- a/src/components/atoms/Select.tsx +++ b/src/core/components/atoms/Select.tsx @@ -61,7 +61,7 @@ const Select = (props: SelectProps) => { props.borderless ? 'border-transparent' : ' border-circle-gray-300 shadow-sm' - } px-2 hover:border-circle-gray-700 border + } px-2 hover:border-circle-gray-700 border ${props.className}`} >
diff --git a/src/components/atoms/Toast.tsx b/src/core/components/atoms/Toast.tsx similarity index 100% rename from src/components/atoms/Toast.tsx rename to src/core/components/atoms/Toast.tsx diff --git a/src/components/atoms/Tooltip.tsx b/src/core/components/atoms/Tooltip.tsx similarity index 100% rename from src/components/atoms/Tooltip.tsx rename to src/core/components/atoms/Tooltip.tsx diff --git a/src/components/atoms/WorkflowSelector.tsx b/src/core/components/atoms/WorkflowSelector.tsx similarity index 95% rename from src/components/atoms/WorkflowSelector.tsx rename to src/core/components/atoms/WorkflowSelector.tsx index 21118e4..ac48d17 100644 --- a/src/components/atoms/WorkflowSelector.tsx +++ b/src/core/components/atoms/WorkflowSelector.tsx @@ -1,7 +1,7 @@ import { v4 } from 'uuid'; import WorkflowIcon from '../../icons/components/WorkflowIcon'; import ExpandIcon from '../../icons/ui/ExpandIcon'; -import { WorkflowStage } from '../../mappings/components/WorkflowMapping'; +import { WorkflowStage } from '../../../mappings/components/WorkflowMapping'; import { useStoreActions, useStoreState } from '../../state/Hooks'; import DropdownContainer from '../containers/DropdownContainer'; diff --git a/src/components/atoms/form/AdjacentStepListItem.tsx b/src/core/components/atoms/form/AdjacentStepListItem.tsx similarity index 100% rename from src/components/atoms/form/AdjacentStepListItem.tsx rename to src/core/components/atoms/form/AdjacentStepListItem.tsx diff --git a/src/components/atoms/form/ExecutorProperty.tsx b/src/core/components/atoms/form/ExecutorProperty.tsx similarity index 98% rename from src/components/atoms/form/ExecutorProperty.tsx rename to src/core/components/atoms/form/ExecutorProperty.tsx index 82ef582..36cd539 100644 --- a/src/components/atoms/form/ExecutorProperty.tsx +++ b/src/core/components/atoms/form/ExecutorProperty.tsx @@ -31,7 +31,7 @@ export const ExecutorProperty = ({ label={label || "Executor"} as="select" name={`${name}.name`} - className="w-full" + className="w-full" {...props} dependent={(executorName: string) => { const splitName = executorName?.split('/'); diff --git a/src/components/atoms/form/InspectorProperty.tsx b/src/core/components/atoms/form/InspectorProperty.tsx similarity index 100% rename from src/components/atoms/form/InspectorProperty.tsx rename to src/core/components/atoms/form/InspectorProperty.tsx diff --git a/src/components/atoms/form/ListProperty.tsx b/src/core/components/atoms/form/ListProperty.tsx similarity index 100% rename from src/components/atoms/form/ListProperty.tsx rename to src/core/components/atoms/form/ListProperty.tsx diff --git a/src/components/atoms/form/MatrixProperty.tsx b/src/core/components/atoms/form/MatrixProperty.tsx similarity index 100% rename from src/components/atoms/form/MatrixProperty.tsx rename to src/core/components/atoms/form/MatrixProperty.tsx diff --git a/src/components/atoms/form/StepListItem.tsx b/src/core/components/atoms/form/StepListItem.tsx similarity index 100% rename from src/components/atoms/form/StepListItem.tsx rename to src/core/components/atoms/form/StepListItem.tsx diff --git a/src/components/atoms/nodes/JobNode.tsx b/src/core/components/atoms/nodes/JobNode.tsx similarity index 100% rename from src/components/atoms/nodes/JobNode.tsx rename to src/core/components/atoms/nodes/JobNode.tsx diff --git a/src/components/atoms/summaries/CommandSummary.tsx b/src/core/components/atoms/summaries/CommandSummary.tsx similarity index 100% rename from src/components/atoms/summaries/CommandSummary.tsx rename to src/core/components/atoms/summaries/CommandSummary.tsx diff --git a/src/components/atoms/summaries/ExecutorSummary.tsx b/src/core/components/atoms/summaries/ExecutorSummary.tsx similarity index 100% rename from src/components/atoms/summaries/ExecutorSummary.tsx rename to src/core/components/atoms/summaries/ExecutorSummary.tsx diff --git a/src/components/atoms/summaries/JobSummary.tsx b/src/core/components/atoms/summaries/JobSummary.tsx similarity index 100% rename from src/components/atoms/summaries/JobSummary.tsx rename to src/core/components/atoms/summaries/JobSummary.tsx diff --git a/src/components/atoms/summaries/ParameterSummary.tsx b/src/core/components/atoms/summaries/ParameterSummary.tsx similarity index 100% rename from src/components/atoms/summaries/ParameterSummary.tsx rename to src/core/components/atoms/summaries/ParameterSummary.tsx diff --git a/src/components/containers/BreadCrumbs.tsx b/src/core/components/containers/BreadCrumbs.tsx similarity index 100% rename from src/components/containers/BreadCrumbs.tsx rename to src/core/components/containers/BreadCrumbs.tsx diff --git a/src/components/containers/CollapsibleList.tsx b/src/core/components/containers/CollapsibleList.tsx similarity index 100% rename from src/components/containers/CollapsibleList.tsx rename to src/core/components/containers/CollapsibleList.tsx diff --git a/src/components/containers/ConfirmationModal.tsx b/src/core/components/containers/ConfirmationModal.tsx similarity index 98% rename from src/components/containers/ConfirmationModal.tsx rename to src/core/components/containers/ConfirmationModal.tsx index 17ab25e..cbcd7e7 100644 --- a/src/components/containers/ConfirmationModal.tsx +++ b/src/core/components/containers/ConfirmationModal.tsx @@ -33,7 +33,7 @@ const confirmDialogue: ConfirmationDialogueTemplates = { }, delete: { header: `Delete ${placeholder} ${placeholder}?`, - body: `When you delete the ${placeholder} named ${placeholder}, it will be removed from each component that uses it. + body: `When you delete the ${placeholder} named ${placeholder}, it will be removed from each component that uses it. This definition has %s dependent components.`, button: 'Delete', buttonVariant: 'dangerous', diff --git a/src/components/containers/DefinitionsContainer.tsx b/src/core/components/containers/DefinitionsContainer.tsx similarity index 98% rename from src/components/containers/DefinitionsContainer.tsx rename to src/core/components/containers/DefinitionsContainer.tsx index 8aaf730..cc6cb8f 100644 --- a/src/components/containers/DefinitionsContainer.tsx +++ b/src/core/components/containers/DefinitionsContainer.tsx @@ -1,5 +1,5 @@ import { useRef } from 'react'; -import InspectableMapping from '../../mappings/InspectableMapping'; +import InspectableMapping from '../../../mappings/InspectableMapping'; import { mapDefinitions, NamedGenerable } from '../../state/DefinitionStore'; import { useStoreActions, useStoreState } from '../../state/Hooks'; import AddButton from '../atoms/AddButton'; diff --git a/src/components/containers/DropdownContainer.tsx b/src/core/components/containers/DropdownContainer.tsx similarity index 100% rename from src/components/containers/DropdownContainer.tsx rename to src/core/components/containers/DropdownContainer.tsx diff --git a/src/components/containers/ExternalLinks.tsx b/src/core/components/containers/ExternalLinks.tsx similarity index 93% rename from src/components/containers/ExternalLinks.tsx rename to src/core/components/containers/ExternalLinks.tsx index 1cdea3a..18c01b3 100644 --- a/src/components/containers/ExternalLinks.tsx +++ b/src/core/components/containers/ExternalLinks.tsx @@ -1,5 +1,5 @@ import MoreIcon from '../../icons/ui/MoreIcon'; -import DropdownContainer from '../containers/DropdownContainer'; +import DropdownContainer from './DropdownContainer'; export const ExternalLinks = () => { const links = [ diff --git a/src/components/containers/FilterPreviewContainer.tsx b/src/core/components/containers/FilterPreviewContainer.tsx similarity index 100% rename from src/components/containers/FilterPreviewContainer.tsx rename to src/core/components/containers/FilterPreviewContainer.tsx diff --git a/src/components/containers/GuideContainer.tsx b/src/core/components/containers/GuideContainer.tsx similarity index 100% rename from src/components/containers/GuideContainer.tsx rename to src/core/components/containers/GuideContainer.tsx diff --git a/src/components/containers/HeaderMenu.tsx b/src/core/components/containers/HeaderMenu.tsx similarity index 83% rename from src/components/containers/HeaderMenu.tsx rename to src/core/components/containers/HeaderMenu.tsx index ff9bc90..efdee59 100644 --- a/src/components/containers/HeaderMenu.tsx +++ b/src/core/components/containers/HeaderMenu.tsx @@ -1,6 +1,6 @@ +import { FlowTools } from '../../../flow/components/FlowTools'; import { Button } from '../atoms/Button'; import { ExternalLinks } from './ExternalLinks'; -import { FlowTools } from '../flow/FlowTools'; import PreviewToolbox from './PreviewToolbox'; export default function HeaderMenu() { diff --git a/src/components/containers/KBarList.tsx b/src/core/components/containers/KBarList.tsx similarity index 100% rename from src/components/containers/KBarList.tsx rename to src/core/components/containers/KBarList.tsx diff --git a/src/components/containers/OrbImportsContainer.tsx b/src/core/components/containers/OrbImportsContainer.tsx similarity index 99% rename from src/components/containers/OrbImportsContainer.tsx rename to src/core/components/containers/OrbImportsContainer.tsx index ef04f34..d9b0173 100644 --- a/src/components/containers/OrbImportsContainer.tsx +++ b/src/core/components/containers/OrbImportsContainer.tsx @@ -21,11 +21,11 @@ const OrbImportsContainer = (props: OrbImportProps) => { const ref = useRef(null); const orbDefinitions = useMemo( () => - + mapDefinitions(items, (orb) => { return ( ) }) diff --git a/src/flow/components/GhostNode.tsx b/src/flow/components/GhostNode.tsx new file mode 100644 index 0000000..eb201ca --- /dev/null +++ b/src/flow/components/GhostNode.tsx @@ -0,0 +1,48 @@ +import { useEffect, useMemo } from 'react'; +import { Handle, NodeProps, Position, ReactFlowState, useStore } from 'reactflow'; +import { useStoreState } from '../../core/state/Hooks'; +import { FlowMode } from '../state/FlowStore'; + +const connectionNodeIdSelector = (state: ReactFlowState) => state.connectionNodeId; + +export default function GhostNode({ id, selected }: NodeProps) { + const connectionNodeId = useStore(connectionNodeIdSelector); + const mode = useStoreState((state) => state.mode); + + const isTarget = connectionNodeId && connectionNodeId !== id; + + const label = 'Ghost'; + const nodeBase = "p-2 pr-3 text-sm bg-white node flex flex-row text-black rounded-md border cursor-pointer" + const outline = selected ? "border-circle-blue" : " border-circle-gray-300" + + const Handles = useMemo(() => { + const connectMode = mode == FlowMode.CONNECT; + const handleBase = `${ connectMode ? '!w-full !h-full' : '!w-0 h-0'} !rounded opacity-50` + const notConnecting = connectionNodeId == null + + return ( + <> + + + + ) + + }, [mode, connectionNodeId]) + + return ( +
{ console.log('hehujasj') }}> + {label} + {Handles} +
+ ); +} diff --git a/src/components/flow/JobNode.tsx b/src/flow/components/JobNode.tsx similarity index 72% rename from src/components/flow/JobNode.tsx rename to src/flow/components/JobNode.tsx index 97ad5d9..6d46276 100644 --- a/src/components/flow/JobNode.tsx +++ b/src/flow/components/JobNode.tsx @@ -1,18 +1,20 @@ + import { useEffect, useMemo } from 'react'; +import {workflow} from '@circleci/circleci-config-sdk'; import { Handle, NodeProps, Position, ReactFlowState, useStore } from 'reactflow'; -import JobIcon from '../../icons/components/JobIcon'; -import { FlowMode } from '../../state/FlowStore'; -import { useStoreState } from '../../state/Hooks'; +import JobIcon from '../../core/icons/components/JobIcon'; +import { useStoreState } from '../../core/state/Hooks'; +import { FlowMode } from '../state/FlowStore'; const connectionNodeIdSelector = (state: ReactFlowState) => state.connectionNodeId; -export default function JobNode({ id, selected }: NodeProps) { +export default function JobNode({ id, selected, data }: NodeProps) { const connectionNodeId = useStore(connectionNodeIdSelector); const mode = useStoreState((state) => state.mode); - + const workflowJob = data as workflow.WorkflowJob; const isTarget = connectionNodeId && connectionNodeId !== id; - const label = isTarget ? 'Drop here' : 'Drag to connect'; + const label = useMemo(() => workflowJob.parameters?.name || workflowJob.name, [data]); const nodeBase = "p-2 pr-3 text-sm bg-white node flex flex-row text-black rounded-md border cursor-pointer" const outline = selected ? "border-circle-blue" : " border-circle-gray-300" @@ -25,13 +27,13 @@ export default function JobNode({ id, selected }: NodeProps) { <> diff --git a/src/flow/hooks/useFlowDnD.ts b/src/flow/hooks/useFlowDnD.ts new file mode 100644 index 0000000..0aa12ed --- /dev/null +++ b/src/flow/hooks/useFlowDnD.ts @@ -0,0 +1,71 @@ +import { useCallback, useState } from "react"; +import { applyNodeChanges, useReactFlow, XYPosition } from "reactflow"; +import { v4 } from "uuid"; +import { useStoreActions, useStoreState } from "../../core/state/Hooks"; +import InspectableMapping from "../../mappings/InspectableMapping"; +import { NodeMapping } from "../../mappings/NodeMapping"; +import { FlowActionsModel, FlowStoreModel } from "../state/FlowStore"; +import { useFlowOffset } from "./useFlowOffset"; + +// Alternative to control a node during drag and drop cycle +// If abstracted a layer higher, useDragAndDrop may come in handy later +export function useFlowDragAndDrop(offset?: DOMRect) { + const { addNodes, getNodes } = useReactFlow(); + const dragging = useStoreState((state) => state.dragging); + const cancel = useCallback((e: React.DragEvent) => { e.preventDefault() }, []); + const onDrop = useFlowOffset((position, droppedItem) => { + const mapping = dragging?.dataMapping as unknown as NodeMapping & InspectableMapping; + + if (dragging == undefined || !mapping?.node) { + return; + } + + const nodes = getNodes(); + const data = mapping.node.transform(dragging.data, nodes); + const id = mapping.node.getId(data) + + addNodes({  id, position, type: mapping.node.key, data}); + }, offset, [dragging]); + + return { onDragEnter: cancel, onDragOver: cancel, onDrop } +} + +// Alternative to control a node during drag and drop cycle +// export function useFlowDragAndDrop(offset?: DOMRect) { +// const { addNodes, setNodes, deleteElements, getNodes } = useReactFlow(); +// const [lastPos, setPos] = useState(); + +// // Upon entering the flow, create the drop ghost +// const onDragEnter = useFlowOffset((position, data) => { +// addNodes({  id: 'ghost', position, type: 'ghost',data: ''}); +// }, offset); + +// // While dragging, move the ghost +// const onDragOver = useFlowOffset((position, data) => { +// if (lastPos) { +// const dx = lastPos.x - position.x; +// const dy = lastPos.y - position.y; +// const dist = Math.abs(dx * dy); + +// if (dist > 150) { +// console.log(dist) +// const nodes = getNodes(); +// const changes = applyNodeChanges([ {  id: 'ghost', type: 'position', position, dragging: true } ], nodes); +// setNodes(changes); +// setPos(position); +// } +// } else { +// setPos(position); +// } +// }, offset, [lastPos]); +// // Upon drop and leave, remove the ghost` +// const onDragLeave = useFlowOffset((pos, data) => { +// deleteElements({ nodes: [{ id: 'ghost' }]}) +// }, offset); +// const onDrop = useFlowOffset((position, data) => { +// addNodes({  id: 'ghost', position, type: 'job', data}); +// }, offset); + + +// return [onDragEnter, onDragOver, onDragLeave, onDrop] +// } diff --git a/src/flow/hooks/useFlowOffset.ts b/src/flow/hooks/useFlowOffset.ts new file mode 100644 index 0000000..5cc8250 --- /dev/null +++ b/src/flow/hooks/useFlowOffset.ts @@ -0,0 +1,18 @@ +import { useCallback } from "react"; +import { useReactFlow, XYPosition } from "reactflow"; +import { useStoreState } from "../../core/state/Hooks"; +import { DataModel } from "../../core/state/Store"; + +type FlowEvent = (position: XYPosition, dragging?: DataModel) => void; + +export function useFlowOffset(func: FlowEvent, offset?: DOMRect, deps?: any[]) { + const { project } = useReactFlow(); + + return useCallback((e: React.MouseEvent) => { + const eventPos = project({ x: e.clientX, y: e.clientY - (offset?.y || 0)}); + + func(eventPos); + + e.preventDefault(); + }, deps ? [project, ...deps] : [project]); +} diff --git a/src/state/FlowStore.tsx b/src/flow/state/FlowStore.tsx similarity index 71% rename from src/state/FlowStore.tsx rename to src/flow/state/FlowStore.tsx index 21a7aed..342ecb7 100644 --- a/src/state/FlowStore.tsx +++ b/src/flow/state/FlowStore.tsx @@ -1,4 +1,5 @@ import { Action, action } from "easy-peasy"; +import { DataModel } from "../../core/state/Store"; export enum FlowMode { SELECT, @@ -10,16 +11,22 @@ export enum FlowMode { export type FlowStoreModel = { mode: FlowMode; + /** Data being dragged from definition */ + dragging?: DataModel; } export type FlowActionsModel = { setMode: Action; + setDragging: Action; } export const FlowActions: FlowActionsModel = { setMode: action((state, mode) => { state.mode = mode; - }) + }), + setDragging: action((state, payload) => { + state.dragging = payload; + }), } /** diff --git a/src/util/FlowUtil.tsx b/src/flow/util/FlowUtil.tsx similarity index 100% rename from src/util/FlowUtil.tsx rename to src/flow/util/FlowUtil.tsx diff --git a/src/hooks/useMapping.tsx b/src/hooks/useMapping.tsx deleted file mode 100644 index 821180c..0000000 --- a/src/hooks/useMapping.tsx +++ /dev/null @@ -1,5 +0,0 @@ -export type MappingOpts = { } - -export const useMapping = () => { - -} \ No newline at end of file diff --git a/src/mappings/GenerableMapping.tsx b/src/mappings/GenerableMapping.tsx index 23ead9e..2746462 100644 --- a/src/mappings/GenerableMapping.tsx +++ b/src/mappings/GenerableMapping.tsx @@ -11,8 +11,8 @@ import { DefinitionType, MappingSubscriptions, NamedGenerable, -} from '../state/DefinitionStore'; -import Store, { StoreActionsModel, StoreModel, UpdateType } from '../state/Store'; +} from '../core/state/DefinitionStore'; +import Store, { StoreActionsModel, StoreModel, UpdateType } from '../core/state/Store'; import { CommandMapping } from './components/CommandMapping'; import { ExecutorMapping } from './components/ExecutorMapping'; import { JobMapping } from './components/JobMapping'; diff --git a/src/mappings/InspectableMapping.tsx b/src/mappings/InspectableMapping.tsx index 4dbe452..862932f 100644 --- a/src/mappings/InspectableMapping.tsx +++ b/src/mappings/InspectableMapping.tsx @@ -1,13 +1,13 @@ import { Generable } from '@circleci/circleci-config-sdk/dist/src/lib/Components'; import { FormikValues } from 'formik'; import { Edge, NodeProps } from 'reactflow'; -import { IconProps } from '../icons/IconProps'; +import { IconProps } from '../core/icons/IconProps'; import { DefinitionsModel, DefinitionSubscriptions, NamedGenerable, -} from '../state/DefinitionStore'; -import { NavigationComponent } from '../state/Store'; +} from '../core/state/DefinitionStore'; +import { NavigationComponent } from '../core/state/Store'; import { WorkflowStage } from './components/WorkflowMapping'; import GenerableMapping, { SubTypeMapping } from './GenerableMapping'; export interface GenerableInfoType { @@ -61,16 +61,6 @@ export default interface InspectableMapping< data: GenerableType, nodeData: ConfigNodeProps, ) => { [K in KeysOfUnion]?: any }; - node?: { - /** Transform definition data */ - transform?: ( - data: GenerableType, - extras?: any, - stage?: { nodes: Node[], edges: Edge[] }, - ) => ConfigNodeProps; - /** @todo: Add store functionality to better support updating definitions and their corresponding workflow nodes */ - component: React.FunctionComponent<{ data: ConfigNodeProps } & NodeProps>; - }; subtypes?: { component: NavigationComponent; getSubtype: (data: GenerableType) => string | undefined; diff --git a/src/mappings/NodeMapping.tsx b/src/mappings/NodeMapping.tsx new file mode 100644 index 0000000..8272837 --- /dev/null +++ b/src/mappings/NodeMapping.tsx @@ -0,0 +1,22 @@ +import { Node, NodeProps } from "reactflow"; +import { NamedGenerable } from "../core/state/DefinitionStore"; + +export interface NodeMapping +{ + node: { + /** + * generated id from managed type + */ + getId: (data: ManagedType) => string; + key: string; + /** Transform definition data */ + transform: ( + data: InType, + nodes: Node[], + extras?: any, + ) => ManagedType; + /** @todo: Add store functionality to better support updating definitions and their corresponding workflow nodes */ + component: React.FunctionComponent<{ data: ManagedType } & NodeProps>; + }; +} diff --git a/src/mappings/components/CommandMapping.tsx b/src/mappings/components/CommandMapping.tsx index ffceda4..5133513 100644 --- a/src/mappings/components/CommandMapping.tsx +++ b/src/mappings/components/CommandMapping.tsx @@ -1,13 +1,13 @@ import { parseReusableCommand } from '@circleci/circleci-config-parser'; import { reusable } from '@circleci/circleci-config-sdk'; -import CommandSummary from '../../components/atoms/summaries/CommandSummary'; -import CommandInspector from '../../components/containers/inspector/CommandInspector'; -import { componentParametersSubtypes } from '../../components/containers/inspector/subtypes/ParameterSubtypes'; -import CommandIcon from '../../icons/components/CommandIcon'; +import CommandSummary from '../../core/components/atoms/summaries/CommandSummary'; +import CommandInspector from '../../core/components/containers/inspector/CommandInspector'; +import { componentParametersSubtypes } from '../../core/components/containers/inspector/subtypes/ParameterSubtypes'; +import CommandIcon from '../../core/icons/components/CommandIcon'; import { DefinitionAction, definitionsAsArray, -} from '../../state/DefinitionStore'; +} from '../../core/state/DefinitionStore'; import InspectableMapping from '../InspectableMapping'; export const CommandMapping: InspectableMapping = { diff --git a/src/mappings/components/ExecutorMapping.tsx b/src/mappings/components/ExecutorMapping.tsx index 23f8677..808f62b 100644 --- a/src/mappings/components/ExecutorMapping.tsx +++ b/src/mappings/components/ExecutorMapping.tsx @@ -5,13 +5,13 @@ import { workflow, } from '@circleci/circleci-config-sdk'; import { parseReusableExecutor } from '@circleci/circleci-config-parser'; -import ExecutorSummary from '../../components/atoms/summaries/ExecutorSummary'; -import ExecutorInspector from '../../components/containers/inspector/ExecutorInspector'; -import { executorSubtypes } from '../../components/containers/inspector/subtypes/ExecutorSubtypes'; -import { componentParametersSubtypes } from '../../components/containers/inspector/subtypes/ParameterSubtypes'; -import ExecutorTypePageNav from '../../components/menus/definitions/subtypes/ExecutorTypePage'; -import ExecutorIcon from '../../icons/components/ExecutorIcon'; -import { DefinitionAction } from '../../state/DefinitionStore'; +import ExecutorSummary from '../../core/components/atoms/summaries/ExecutorSummary'; +import ExecutorInspector from '../../core/components/containers/inspector/ExecutorInspector'; +import { executorSubtypes } from '../../core/components/containers/inspector/subtypes/ExecutorSubtypes'; +import { componentParametersSubtypes } from '../../core/components/containers/inspector/subtypes/ParameterSubtypes'; +import ExecutorTypePageNav from '../../core/components/menus/definitions/subtypes/ExecutorTypePage'; +import ExecutorIcon from '../../core/icons/components/ExecutorIcon'; +import { DefinitionAction } from '../../core/state/DefinitionStore'; import InspectableMapping from '../InspectableMapping'; import { JobMapping } from './JobMapping'; diff --git a/src/mappings/components/JobMapping.tsx b/src/mappings/components/JobMapping.tsx index da70577..9fed511 100644 --- a/src/mappings/components/JobMapping.tsx +++ b/src/mappings/components/JobMapping.tsx @@ -1,18 +1,19 @@ import { parseJob } from '@circleci/circleci-config-parser'; import { Job, orb, reusable, workflow } from '@circleci/circleci-config-sdk'; -import JobNode from '../../components/atoms/nodes/JobNode'; -import JobSummary from '../../components/atoms/summaries/JobSummary'; -import JobInspector from '../../components/containers/inspector/JobInspector'; -import { componentParametersSubtypes } from '../../components/containers/inspector/subtypes/ParameterSubtypes'; -import JobIcon from '../../icons/components/JobIcon'; +import JobSummary from '../../core/components/atoms/summaries/JobSummary'; +import JobInspector from '../../core/components/containers/inspector/JobInspector'; +import { componentParametersSubtypes } from '../../core/components/containers/inspector/subtypes/ParameterSubtypes'; +import JobIcon from '../../core/icons/components/JobIcon'; import { DefinitionAction, definitionsAsArray, -} from '../../state/DefinitionStore'; +} from '../../core/state/DefinitionStore'; +import JobNode from '../../flow/components/JobNode'; import InspectableMapping from '../InspectableMapping'; +import { NodeMapping } from '../NodeMapping'; import { UNDEFINED_EXECUTOR } from './ExecutorMapping'; -export const JobMapping: InspectableMapping = { +export const JobMapping: InspectableMapping & NodeMapping = { key: 'jobs', name: { singular: 'Job', @@ -101,36 +102,40 @@ export const JobMapping: InspectableMapping = { remove: (actions) => actions.delete_jobs, }, dragTarget: 'workflow', - // node: { - // transform: (data, params, elements) => { - // const stagedNames = new Set( - // elements - // ?.filter((element) => element.type === 'jobs') - // .map((job) => job.data.parameters?.name || job.data.name), - // ); - // let name = data.name; + node: { + getId: (data) => { + return data.parameters?.name || data.name; + }, + transform: (data, nodes, params) => { + const stagedNames = new Set( + nodes + ?.filter((element) => element.type === 'workflow_job') + .map((node) => node.data.parameters?.name || node.data.name), + ); + let name = data.name; - // if (stagedNames && stagedNames.has(name)) { - // for (let i = 2; true; i++) { - // const newName = `${name} (${i})`; + if (stagedNames.has(name)) { + for (let i = 2; true; i++) { + const newName = `${name} (${i})`; - // if (!stagedNames.has(newName)) { - // name = newName; - // break; - // } - // } - // } + if (!stagedNames.has(newName)) { + name = newName; + break; + } + } + } - // const newParams = params || {}; + const newParams = params || {}; - // if (name !== data.name) { - // newParams.name = name; - // } + if (name !== data.name) { + newParams.name = name; + } - // return new workflow.WorkflowJob(data, newParams); - // }, - // component: JobNode, - // }, + return new workflow.WorkflowJob(data, newParams); + }, + component: JobNode, + key: 'workflow_job' + }, components: { icon: JobIcon, summary: JobSummary, diff --git a/src/mappings/components/ParameterMapping.tsx b/src/mappings/components/ParameterMapping.tsx index aff6e55..aafa2a7 100644 --- a/src/mappings/components/ParameterMapping.tsx +++ b/src/mappings/components/ParameterMapping.tsx @@ -2,12 +2,12 @@ import { parseParameter } from '@circleci/circleci-config-parser'; import { parameters } from '@circleci/circleci-config-sdk'; import { CustomParameter } from '@circleci/circleci-config-sdk/dist/src/lib/Components/Parameters'; import { PipelineParameterLiteral } from '@circleci/circleci-config-sdk/dist/src/lib/Components/Parameters/types/CustomParameterLiterals.types'; -import ParameterSummary from '../../components/atoms/summaries/ParameterSummary'; -import ParameterInspector from '../../components/containers/inspector/ParameterInspector'; -import { parameterSubtypes } from '../../components/containers/inspector/subtypes/ParameterSubtypes'; -import ParameterTypePageNav from '../../components/menus/definitions/subtypes/ParameterTypePage'; -import ParameterIcon from '../../icons/components/ParameterIcon'; -import { DefinitionAction } from '../../state/DefinitionStore'; +import ParameterSummary from '../../core/components/atoms/summaries/ParameterSummary'; +import ParameterInspector from '../../core/components/containers/inspector/ParameterInspector'; +import { parameterSubtypes } from '../../core/components/containers/inspector/subtypes/ParameterSubtypes'; +import ParameterTypePageNav from '../../core/components/menus/definitions/subtypes/ParameterTypePage'; +import ParameterIcon from '../../core/icons/components/ParameterIcon'; +import { DefinitionAction } from '../../core/state/DefinitionStore'; import InspectableMapping from '../InspectableMapping'; export const ParameterMapping: InspectableMapping< diff --git a/src/mappings/components/WorkflowMapping.tsx b/src/mappings/components/WorkflowMapping.tsx index a434244..922518f 100644 --- a/src/mappings/components/WorkflowMapping.tsx +++ b/src/mappings/components/WorkflowMapping.tsx @@ -2,8 +2,8 @@ import { Job, workflow, Workflow } from '@circleci/circleci-config-sdk'; import { When } from '@circleci/circleci-config-sdk/dist/src/lib/Components/Logic'; import { WorkflowJobAbstract } from '@circleci/circleci-config-sdk/dist/src/lib/Components/Workflow'; import { Edge, Node } from 'reactflow'; -import { Definition, DefinitionAction } from '../../state/DefinitionStore'; -import { StoreModel } from '../../state/Store'; +import { Definition, DefinitionAction } from '../../core/state/DefinitionStore'; +import { StoreModel } from '../../core/state/Store'; import GenerableMapping from '../GenerableMapping'; export type WorkflowElements = { nodes: Node[], edges: Edge[]};