Skip to content

Commit

Permalink
Component status colors (#12124)
Browse files Browse the repository at this point in the history
https://github.com/user-attachments/assets/865f3be3-e963-4f27-97aa-419a66d04d73

Fixes #12001
- Component color reflects pending status
- Edges from component reflect pending/selected status
  • Loading branch information
kazcw authored Jan 24, 2025
1 parent bc32447 commit f281f84
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 40 deletions.
4 changes: 2 additions & 2 deletions app/gui/integration-test/project-view/edgeRendering.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ test('Hover behaviour of edges', async ({ page }) => {

// Expect the top edge part to be dimmed
const topEdge = page.locator('svg.behindNodes g:nth-child(2) path:nth-child(1)')
await expect(topEdge).toHaveClass('edge visible dimmed')
await expect(topEdge).toHaveClass('edge define-node-colors visible dimmed pending')
// Expect the bottom edge part not to be dimmed
const bottomEdge = page.locator('svg.behindNodes g:nth-child(2) path:nth-child(3)')
await expect(bottomEdge).toHaveClass('edge visible')
await expect(bottomEdge).toHaveClass('edge define-node-colors visible pending')
})
13 changes: 12 additions & 1 deletion app/gui/src/project-view/assets/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,15 @@
#aaa 40%
);
--color-node-text-missing-value: white;
--color-edge-from-node: color-mix(
in oklab,
var(--node-group-color) 85%,
white 15%
);

&.pending {
--color-node-primary: var(--color-node-pending);
--color-node-background: var(--color-node-pending);
--color-edge-from-node: var(--color-node-pending);
}

&.selected {
Expand All @@ -109,6 +115,11 @@
var(--color-node-primary) 70%,
black 30%
);
--color-edge-from-node: color-mix(
in oklab,
var(--color-node-primary) 40%,
white 60%
);
}
}

Expand Down
37 changes: 19 additions & 18 deletions app/gui/src/project-view/components/GraphEditor/GraphEdge.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script setup lang="ts">
import { junctionPoints, pathElements, toSvgPath } from '@/components/GraphEditor/GraphEdge/layout'
import { useComponentColors } from '@/composables/componentColors'
import { injectGraphNavigator } from '@/providers/graphNavigator'
import { injectGraphSelection } from '@/providers/graphSelection'
import type { Edge } from '@/stores/graph'
Expand Down Expand Up @@ -135,11 +136,7 @@ const sourceMask = computed<NodeMask | undefined>(() => {
return { id, rect, radius }
})
const edgeColor = computed(() =>
'color' in edge ? edge.color
: sourceNode.value ? graph.db.getNodeColorStyle(sourceNode.value)
: undefined,
)
const { baseColor, selected, pending } = useComponentColors(graph.db, selection, sourceNode)
const sourceOriginPoint = computed(() => {
const source = sourceRect.value
Expand Down Expand Up @@ -240,7 +237,7 @@ const targetEndIsDimmed = computed(() => {
return distances.sourceToMouse < distances.mouseToTarget
})
const baseStyle = computed(() => ({ '--node-base-color': edgeColor.value ?? 'tan' }))
const baseStyle = computed(() => (baseColor.value ? { '--node-group-color': baseColor.value } : {}))
function click(event: PointerEvent) {
const distances = mouseLocationOnEdge.value
Expand Down Expand Up @@ -301,6 +298,9 @@ const sourceHoverAnimationStyle = computed(() => {
const baseClass = computed(() => {
return { dimmed: activePath.value || isSuggestion.value }
})
const colorClasses = computed(() => {
return { selected: selected.value, pending: pending.value }
})
</script>

<template>
Expand Down Expand Up @@ -335,8 +335,8 @@ const baseClass = computed(() => {
<path
ref="base"
:d="basePath"
class="edge visible"
:class="baseClass"
class="edge define-node-colors visible"
:class="{ ...baseClass, ...colorClasses }"
:style="{ ...baseStyle, ...sourceHoverAnimationStyle }"
:data-source-node-id="sourceNode"
:data-target-node-id="targetNode"
Expand All @@ -355,7 +355,8 @@ const baseClass = computed(() => {
<path
v-if="activePath"
:d="basePath"
class="edge visible"
class="edge define-node-colors visible"
:class="colorClasses"
:style="{ ...baseStyle, ...activeStyle }"
:data-source-node-id="sourceNode"
:data-target-node-id="targetNode"
Expand All @@ -364,15 +365,16 @@ const baseClass = computed(() => {
v-if="arrowTransform"
:transform="arrowTransform"
:d="arrowPath"
class="arrow visible"
:class="{ dimmed: targetEndIsDimmed }"
class="arrow define-node-colors visible"
:class="{ ...colorClasses, dimmed: targetEndIsDimmed }"
:style="baseStyle"
/>
<polygon
v-if="backwardEdgeArrowTransform"
:transform="backwardEdgeArrowTransform"
points="0,-9.375 -9.375,9.375 9.375,9.375"
class="arrow visible"
class="arrow define-node-colors visible"
:class="colorClasses"
:style="baseStyle"
:data-source-node-id="sourceNode"
:data-target-node-id="targetNode"
Expand All @@ -384,18 +386,18 @@ const baseClass = computed(() => {
<style scoped>
.visible {
pointer-events: none;
--edge-color: color-mix(in oklab, var(--node-base-color) 85%, white 15%);
--node-group-color: var(--group-color-fallback);
}
.edge {
fill: none;
stroke: var(--edge-color);
stroke: var(--color-edge-from-node);
transition: stroke 0.2s ease;
contain: strict;
}
.arrow {
fill: var(--edge-color);
fill: var(--color-edge-from-node);
transition: fill 0.2s ease;
}
Expand All @@ -410,11 +412,10 @@ const baseClass = computed(() => {
}
.edge.visible.dimmed {
/* stroke: rgba(255, 255, 255, 0.4); */
stroke: color-mix(in oklab, var(--edge-color) 60%, white 40%);
stroke: color-mix(in oklab, var(--color-edge-from-node) 60%, white 40%);
}
.arrow.visible.dimmed {
fill: color-mix(in oklab, var(--edge-color) 60%, white 40%);
fill: color-mix(in oklab, var(--color-edge-from-node) 60%, white 40%);
}
</style>
25 changes: 8 additions & 17 deletions app/gui/src/project-view/components/GraphEditor/GraphNode.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import GraphVisualization from '@/components/GraphEditor/GraphVisualization.vue'
import type { NodeCreationOptions } from '@/components/GraphEditor/nodeCreation'
import PointFloatingMenu from '@/components/PointFloatingMenu.vue'
import SvgIcon from '@/components/SvgIcon.vue'
import { useComponentColors } from '@/composables/componentColors'
import { useDoubleClick } from '@/composables/doubleClick'
import { usePointer, useResizeObserver } from '@/composables/events'
import { provideComponentButtons } from '@/providers/componentButtons'
Expand Down Expand Up @@ -159,8 +160,6 @@ const visibleMessage = computed(
const nodeHovered = ref(false)
const selected = computed(() => nodeSelection?.isSelected(nodeId.value) ?? false)
const isOnlyOneSelected = computed(
() =>
nodeSelection?.committedSelection.size === 1 &&
Expand Down Expand Up @@ -307,9 +306,9 @@ const isRecordingOverridden = computed({
},
})
const expressionInfo = computed(() => graph.db.getExpressionInfo(props.node.innerExpr.externalId))
const executionState = computed(() => expressionInfo.value?.payload.type ?? 'Unknown')
const color = computed(() => graph.db.getNodeColorStyle(nodeId.value))
const typename = computed(
() => graph.db.getExpressionInfo(props.node.innerExpr.externalId)?.rawTypename,
)
const nodeEditHandler = nodeEditBindings.handler({
cancel(e) {
Expand Down Expand Up @@ -362,16 +361,6 @@ const dataSource = computed(
() => ({ type: 'node', nodeId: props.node.rootExpr.externalId }) as const,
)
const pending = computed(() => {
switch (executionState.value) {
case 'Unknown':
case 'Pending':
return true
default:
return false
}
})
// === Recompute node expression ===
function useRecomputation() {
Expand All @@ -394,14 +383,16 @@ const nodeStyle = computed(() => {
return {
transform: transform.value,
minWidth: isVisualizationEnabled.value ? `${visualizationWidth.value ?? 200}px` : undefined,
'--node-group-color': color.value,
'--node-group-color': baseColor.value,
...(props.node.zIndex ? { 'z-index': props.node.zIndex } : {}),
'--viz-below-node': `${graphSelectionSize.value.y - nodeSize.value.y}px`,
'--node-size-x': `${nodeSize.value.x}px`,
'--node-size-y': `${nodeSize.value.y}px`,
}
})
const { baseColor, selected, pending } = useComponentColors(graph.db, nodeSelection, nodeId)
const nodeClass = computed(() => {
return {
selected: selected.value,
Expand Down Expand Up @@ -500,7 +491,7 @@ const showMenuAt = ref<{ x: number; y: number }>()
:isComponentMenuVisible="menuVisible"
:currentType="props.node.vis?.identifier"
:dataSource="dataSource"
:typename="expressionInfo?.rawTypename"
:typename="typename"
:width="visualizationWidth"
:height="visualizationHeight"
:isFocused="isOnlyOneSelected"
Expand Down
27 changes: 27 additions & 0 deletions app/gui/src/project-view/composables/componentColors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { type NodeId } from '@/stores/graph'
import { type GraphDb } from '@/stores/graph/graphDatabase'
import { type ToValue } from '@/util/reactivity'
import { computed, toValue } from 'vue'

/** Returns the component's base color, and information about state that may modify it. */
export function useComponentColors(
graphDb: GraphDb,
nodeSelection: { isSelected: (nodeId: NodeId) => boolean } | undefined,
nodeId: ToValue<NodeId | undefined>,
) {
const nodeIdValue = computed(() => toValue(nodeId))
const node = computed(() => nodeIdValue.value && graphDb.nodeIdToNode.get(nodeIdValue.value))
const expressionInfo = computed(
() => node.value && graphDb.getExpressionInfo(node.value.innerExpr.externalId),
)
const executionState = computed(() => expressionInfo.value?.payload.type ?? 'Unknown')
return {
baseColor: computed(() => nodeIdValue.value && graphDb.getNodeColorStyle(nodeIdValue.value)),
selected: computed(
() => (nodeIdValue.value && nodeSelection?.isSelected(nodeIdValue.value)) ?? false,
),
pending: computed(
() => executionState.value === 'Unknown' || executionState.value === 'Pending',
),
}
}
2 changes: 0 additions & 2 deletions app/gui/src/project-view/stores/graph/unconnectedEdges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ interface AnyUnconnectedEdge {
disconnectedEdgeTarget?: PortId
/** Identifies what the disconnected end should be attached to. */
anchor: UnconnectedEdgeAnchor
/** CSS value; if provided, overrides any color calculation. */
color?: string
}
export interface UnconnectedSource extends AnyUnconnectedEdge {
source: undefined
Expand Down

0 comments on commit f281f84

Please sign in to comment.