Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,4 @@ exponent.txt
.erk/scratch/
.impl/
.erk/config.local.toml
.superpowers/
1 change: 1 addition & 0 deletions js_modules/ui-core/src/app/Route.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {ComponentProps, ReactNode, memo, useLayoutEffect, useMemo} from 'react';
// eslint-disable-next-line dagster-rules/no-react-router-route
import {Route as ReactRouterRoute, useRouteMatch} from 'react-router-dom';
import {useSetRecoilState} from 'recoil';

Expand Down
292 changes: 244 additions & 48 deletions js_modules/ui-core/src/asset-graph/AssetGraphExplorer.tsx

Large diffs are not rendered by default.

75 changes: 70 additions & 5 deletions js_modules/ui-core/src/asset-graph/AssetNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {AssetNodeHealthRow} from './AssetNodeHealthRow';
import {AssetNodeMenuProps, useAssetNodeMenu} from './AssetNodeMenu';
import {assetNodeLatestEventContent, buildAssetNodeStatusContent} from './AssetNodeStatusContent';
import {ContextMenuWrapper} from './ContextMenuWrapper';
import {ManualPositionHandle} from './ManualPositionHandle';
import {LiveDataForNode, LiveDataForNodeWithStaleData} from './Utils';
import {withMiddleTruncation} from '../app/Util';
import {useAssetAutomationData} from '../asset-data/AssetAutomationDataProvider';
Expand All @@ -40,6 +41,10 @@ interface Props2025 {
selected: boolean;
facets: Set<AssetNodeFacet>;
onChangeAssetSelection?: (selection: string) => void;
isManuallyPositioned?: boolean;
isDragging?: boolean;
onDragStart?: (e: React.MouseEvent<HTMLButtonElement>) => void;
onResetPosition?: () => void;
}

export const ASSET_NODE_HOVER_EXPAND_HEIGHT = 3;
Expand All @@ -60,6 +65,10 @@ export const AssetNodeWithLiveData = ({
selected,
facets,
onChangeAssetSelection,
isManuallyPositioned,
isDragging,
onDragStart,
onResetPosition,
liveData,
automationData,
assetHealthEnabled,
Expand All @@ -68,7 +77,7 @@ export const AssetNodeWithLiveData = ({
assetHealthEnabled: boolean;
}) => {
return (
<AssetNodeContainer $selected={selected}>
<AssetNodeContainer $selected={selected} style={{position: 'relative'}}>
{facets.has(AssetNodeFacet.UnsyncedTag) ? (
<Box
flex={{direction: 'row', justifyContent: 'space-between', alignItems: 'center'}}
Expand All @@ -86,7 +95,13 @@ export const AssetNodeWithLiveData = ({
<div style={{minHeight: ASSET_NODE_HOVER_EXPAND_HEIGHT}} />
)}
<AssetNodeBox $selected={selected} $isMaterializable={definition.isMaterializable}>
<AssetNameRow definition={definition} />
<AssetNameRow
definition={definition}
isDragging={!!isDragging}
isManuallyPositioned={!!isManuallyPositioned}
onDragStart={onDragStart}
onResetPosition={onResetPosition}
/>
{facets.has(AssetNodeFacet.Description) && (
<AssetNodeRow label={null}>
{definition.description ? (
Expand Down Expand Up @@ -434,7 +449,19 @@ export const AutomationConditionEvaluationLink = ({
);
};

export const AssetNameRow = ({definition}: {definition: AssetNodeFragment}) => {
export const AssetNameRow = ({
definition,
isDragging = false,
isManuallyPositioned = false,
onDragStart,
onResetPosition,
}: {
definition: AssetNodeFragment;
isDragging?: boolean;
isManuallyPositioned?: boolean;
onDragStart?: (e: React.MouseEvent<HTMLButtonElement>) => void;
onResetPosition?: () => void;
}) => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const displayName = definition.assetKey.path[definition.assetKey.path.length - 1]!;

Expand All @@ -453,6 +480,14 @@ export const AssetNameRow = ({definition}: {definition: AssetNodeFragment}) => {
})}
</div>
<div style={{flex: 1}} />
{onDragStart ? (
<ManualPositionHandle
isDragging={isDragging}
isManuallyPositioned={isManuallyPositioned}
onDragStart={onDragStart}
onReset={onResetPosition}
/>
) : null}
</AssetName>
);
};
Expand Down Expand Up @@ -490,6 +525,10 @@ type AssetNodeMinimalProps = {
definition: AssetNodeFragment;
facets: Set<AssetNodeFacet> | null;
height: number;
isManuallyPositioned?: boolean;
isDragging?: boolean;
onDragStart?: (e: React.MouseEvent<HTMLButtonElement>) => void;
onResetPosition?: () => void;
};

export const AssetNodeMinimal = (props: AssetNodeMinimalProps) => {
Expand All @@ -505,6 +544,10 @@ export const AssetNodeMinimalWithHealth = ({
facets,
height,
selected,
isManuallyPositioned,
isDragging,
onDragStart,
onResetPosition,
}: AssetNodeMinimalProps) => {
const {isMaterializable, assetKey} = definition;
const {liveData} = useAssetLiveData(assetKey);
Expand Down Expand Up @@ -549,7 +592,7 @@ export const AssetNodeMinimalWithHealth = ({
}

return (
<MinimalAssetNodeContainer $selected={selected} style={{paddingTop}}>
<MinimalAssetNodeContainer $selected={selected} style={{paddingTop, position: 'relative'}}>
<Tooltip content={displayName} canShow={displayName.length > 14} position="top">
<MinimalAssetNodeBox
$selected={selected}
Expand All @@ -560,6 +603,15 @@ export const AssetNodeMinimalWithHealth = ({
$isQueued={!!queuedRuns}
$height={nodeHeight}
>
{onDragStart ? (
<ManualPositionHandle
placement="edge"
isDragging={!!isDragging}
isManuallyPositioned={!!isManuallyPositioned}
onDragStart={onDragStart}
onReset={onResetPosition}
/>
) : null}
{isChanged ? (
<MinimalNodeChangedDot changedReasons={definition.changedReasons} assetKey={assetKey} />
) : null}
Expand All @@ -578,6 +630,10 @@ export const AssetNodeMinimalWithoutHealth = ({
facets,
height,
selected,
isManuallyPositioned,
isDragging,
onDragStart,
onResetPosition,
}: AssetNodeMinimalProps) => {
const {isMaterializable, assetKey} = definition;
const {liveData} = useAssetLiveData(assetKey);
Expand Down Expand Up @@ -613,7 +669,7 @@ export const AssetNodeMinimalWithoutHealth = ({
}

return (
<MinimalAssetNodeContainer $selected={selected} style={{paddingTop}}>
<MinimalAssetNodeContainer $selected={selected} style={{paddingTop, position: 'relative'}}>
<Tooltip content={displayName} canShow={displayName.length > 14} position="top">
<MinimalAssetNodeBox
$selected={selected}
Expand All @@ -624,6 +680,15 @@ export const AssetNodeMinimalWithoutHealth = ({
$isQueued={!!queuedRuns}
$height={nodeHeight}
>
{onDragStart ? (
<ManualPositionHandle
placement="edge"
isDragging={!!isDragging}
isManuallyPositioned={!!isManuallyPositioned}
onDragStart={onDragStart}
onReset={onResetPosition}
/>
) : null}
{isChanged ? (
<MinimalNodeChangedDot changedReasons={definition.changedReasons} assetKey={assetKey} />
) : null}
Expand Down
17 changes: 17 additions & 0 deletions js_modules/ui-core/src/asset-graph/CollapsedGroupNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import styled from 'styled-components';
import {AssetDescription, NameTooltipCSS} from './AssetNode';
import {StatusCase} from './AssetNodeStatusContent';
import {ContextMenuWrapper} from './ContextMenuWrapper';
import {ManualPositionHandle} from './ManualPositionHandle';
import {GraphNode} from './Utils';
import {GroupLayout} from './layout';
import {groupAssetsByStatus} from './util';
Expand Down Expand Up @@ -85,13 +86,21 @@ export const CollapsedGroupNode = ({
toggleSelectAllNodes,
preferredJobName,
onFilterToGroup,
isManuallyPositioned,
isDragging,
onDragStart,
onResetPosition,
}: {
minimal: boolean;
onExpand: () => void;
toggleSelectAllNodes?: (e: React.MouseEvent) => void;
group: GroupLayout & {assetCount: number; assets: GraphNode[]};
preferredJobName: string;
onFilterToGroup: () => void;
isManuallyPositioned?: boolean;
isDragging?: boolean;
onDragStart?: (e: React.MouseEvent<HTMLButtonElement>) => void;
onResetPosition?: () => void;
}) => {
const {menu, dialog} = useGroupNodeContextMenu({
onFilterToGroup,
Expand All @@ -113,6 +122,14 @@ export const CollapsedGroupNode = ({
<CollapsedGroupNodeBox $minimal={minimal}>
<Box padding={{top: 8, bottom: 4, left: 12, right: 8}} flex={{}}>
<GroupNodeNameAndRepo group={group} minimal={minimal} />
{onDragStart ? (
<ManualPositionHandle
isDragging={!!isDragging}
isManuallyPositioned={!!isManuallyPositioned}
onDragStart={onDragStart}
onReset={onResetPosition}
/>
) : null}
<Box padding={{vertical: 4}}>
<Icon name="unfold_more" />
</Box>
Expand Down
17 changes: 17 additions & 0 deletions js_modules/ui-core/src/asset-graph/ExpandedGroupNode.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import styled from 'styled-components';

import {GroupNodeNameAndRepo, useGroupNodeContextMenu} from './CollapsedGroupNode';
import {ContextMenuWrapper} from './ContextMenuWrapper';
import {ManualPositionHandle} from './ManualPositionHandle';
import {GraphNode} from './Utils';
import {GroupLayout} from './layout';
import {SVGRelativeContainerForSafari} from '../graph/SVGComponents';
Expand All @@ -16,6 +17,10 @@ export const ExpandedGroupNode = ({
preferredJobName,
onFilterToGroup,
setHighlighted,
isManuallyPositioned,
isDragging,
onDragStart,
onResetPosition,
}: {
group: GroupLayout & {assets: GraphNode[]};
minimal: boolean;
Expand All @@ -24,6 +29,10 @@ export const ExpandedGroupNode = ({
preferredJobName?: string;
onFilterToGroup?: () => void;
setHighlighted: (ids: string[] | null) => void;
isManuallyPositioned?: boolean;
isDragging?: boolean;
onDragStart?: (e: React.MouseEvent<HTMLButtonElement>) => void;
onResetPosition?: () => void;
}) => {
const {menu, dialog} = useGroupNodeContextMenu({
onFilterToGroup,
Expand All @@ -47,6 +56,14 @@ export const ExpandedGroupNode = ({
}}
>
<GroupNodeNameAndRepo group={group} minimal={minimal} />
{onDragStart ? (
<ManualPositionHandle
isDragging={!!isDragging}
isManuallyPositioned={!!isManuallyPositioned}
onDragStart={onDragStart}
onReset={onResetPosition}
/>
) : null}
{onCollapse && (
<Box padding={{vertical: 4}}>
<Icon name="unfold_less" />
Expand Down
Loading