mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-15 11:58:34 +08:00
fix(ui/workflows): canvas controls dark-theme + reliable node-click
This commit is contained in:
parent
12bd05382e
commit
789af09772
@ -276,39 +276,66 @@ html.dark {
|
|||||||
|
|
||||||
/* Vue Flow theme bridge — its default stylesheet ships hard-coded
|
/* Vue Flow theme bridge — its default stylesheet ships hard-coded
|
||||||
light colors for handles / mini-map / control buttons. Map them to
|
light colors for handles / mini-map / control buttons. Map them to
|
||||||
our theme tokens so dark mode inherits cleanly. */
|
our theme tokens so dark mode inherits cleanly.
|
||||||
.vue-flow__handle {
|
|
||||||
|
Each selector is bumped to (0,2,0) specificity by scoping to
|
||||||
|
`.workflow-canvas` because vue-flow's component CSS imports inside
|
||||||
|
WorkflowCanvas.vue land in the bundle AFTER main.css; equal
|
||||||
|
specificity would lose. */
|
||||||
|
.workflow-canvas .vue-flow__handle {
|
||||||
background: var(--mc-edge-strong, #777);
|
background: var(--mc-edge-strong, #777);
|
||||||
border-color: var(--mc-bg-elevated, #fff);
|
border-color: var(--mc-bg-elevated, #fff);
|
||||||
}
|
}
|
||||||
.vue-flow__edge-path {
|
.workflow-canvas .vue-flow__edge-path {
|
||||||
stroke: var(--mc-edge, #999);
|
stroke: var(--mc-edge, #999);
|
||||||
}
|
}
|
||||||
.vue-flow__edge-text {
|
.workflow-canvas .vue-flow__edge-text {
|
||||||
fill: var(--mc-text-secondary, #555);
|
fill: var(--mc-text-secondary, #555);
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
}
|
}
|
||||||
.vue-flow__edge-textbg {
|
.workflow-canvas .vue-flow__edge-textbg {
|
||||||
fill: var(--mc-bg-elevated, #fff);
|
fill: var(--mc-bg-elevated, #fff);
|
||||||
}
|
}
|
||||||
.vue-flow__minimap {
|
.workflow-canvas .vue-flow__background {
|
||||||
background: var(--mc-bg-sunken, rgba(0, 0, 0, 0.04)) !important;
|
background: transparent;
|
||||||
|
}
|
||||||
|
.workflow-canvas .vue-flow__minimap {
|
||||||
|
background: var(--mc-bg-sunken, rgba(0, 0, 0, 0.04));
|
||||||
|
border: 1px solid var(--mc-border-light, rgba(0, 0, 0, 0.08));
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
.workflow-canvas .vue-flow__minimap-mask {
|
||||||
|
fill: var(--mc-bg-muted, rgba(0, 0, 0, 0.06));
|
||||||
|
stroke: var(--mc-border, transparent);
|
||||||
|
}
|
||||||
|
.workflow-canvas .vue-flow__controls {
|
||||||
|
box-shadow: var(--mc-shadow-soft, 0 2px 6px rgba(0, 0, 0, 0.1));
|
||||||
|
border-radius: 6px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: var(--mc-bg-elevated, #ffffff);
|
||||||
border: 1px solid var(--mc-border-light, rgba(0, 0, 0, 0.08));
|
border: 1px solid var(--mc-border-light, rgba(0, 0, 0, 0.08));
|
||||||
}
|
}
|
||||||
.vue-flow__minimap-mask {
|
.workflow-canvas .vue-flow__controls-button {
|
||||||
fill: var(--mc-bg-muted, rgba(0, 0, 0, 0.06));
|
background: var(--mc-bg-elevated, #ffffff);
|
||||||
}
|
border-bottom: 1px solid var(--mc-border-light, rgba(0, 0, 0, 0.08));
|
||||||
.vue-flow__controls {
|
|
||||||
box-shadow: var(--mc-shadow-soft, 0 2px 6px rgba(0, 0, 0, 0.1));
|
|
||||||
}
|
|
||||||
.vue-flow__controls-button {
|
|
||||||
background: var(--mc-bg-elevated, #fff);
|
|
||||||
border-bottom-color: var(--mc-border-light, rgba(0, 0, 0, 0.08));
|
|
||||||
fill: var(--mc-text-primary, #333);
|
fill: var(--mc-text-primary, #333);
|
||||||
|
color: var(--mc-text-primary, #333);
|
||||||
}
|
}
|
||||||
.vue-flow__controls-button:hover {
|
.workflow-canvas .vue-flow__controls-button svg {
|
||||||
|
fill: currentColor;
|
||||||
|
}
|
||||||
|
.workflow-canvas .vue-flow__controls-button:hover {
|
||||||
background: var(--mc-bg-muted, rgba(0, 0, 0, 0.05));
|
background: var(--mc-bg-muted, rgba(0, 0, 0, 0.05));
|
||||||
}
|
}
|
||||||
|
/* Round the bottom button so the corner doesn't show the hardcoded grey
|
||||||
|
border vue-flow ships with. */
|
||||||
|
.workflow-canvas .vue-flow__controls-button:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
/* Selection ring matches the brand accent in both modes. */
|
||||||
|
.workflow-canvas .vue-flow__node.selected {
|
||||||
|
box-shadow: 0 0 0 2px var(--mc-primary, #4084ff);
|
||||||
|
}
|
||||||
|
|
||||||
/* ================================================================
|
/* ================================================================
|
||||||
Base resets
|
Base resets
|
||||||
|
|||||||
@ -3,8 +3,7 @@
|
|||||||
class="step-node"
|
class="step-node"
|
||||||
:class="`mode-${data.modeType}`"
|
:class="`mode-${data.modeType}`"
|
||||||
:data-selected="props.selected"
|
:data-selected="props.selected"
|
||||||
@click.stop="handleClick"
|
@click="handleClick"
|
||||||
@pointerdown.stop
|
|
||||||
>
|
>
|
||||||
<Handle type="target" :position="targetPosition" />
|
<Handle type="target" :position="targetPosition" />
|
||||||
<div class="step-band" />
|
<div class="step-band" />
|
||||||
|
|||||||
@ -79,9 +79,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, markRaw, onBeforeUnmount, provide, ref, watch } from 'vue'
|
import { computed, markRaw, onBeforeUnmount, onMounted, provide, ref, watch } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { VueFlow, type Node } from '@vue-flow/core'
|
import { VueFlow, useVueFlow, type Node } from '@vue-flow/core'
|
||||||
import { Background } from '@vue-flow/background'
|
import { Background } from '@vue-flow/background'
|
||||||
import { Controls } from '@vue-flow/controls'
|
import { Controls } from '@vue-flow/controls'
|
||||||
import { MiniMap } from '@vue-flow/minimap'
|
import { MiniMap } from '@vue-flow/minimap'
|
||||||
@ -139,19 +139,31 @@ const miniNodeColor = (node: Node<StepNodeData>) => {
|
|||||||
}
|
}
|
||||||
const miniNodeStrokeColor = () => 'transparent'
|
const miniNodeStrokeColor = () => 'transparent'
|
||||||
|
|
||||||
// Vue-flow's `@node-click` event signature has shifted across minor
|
// Three independent paths for getting a node click out of vue-flow into
|
||||||
// versions and sometimes doesn't fire reliably for custom-node templates
|
// the parent's inspector — at least one always works regardless of how
|
||||||
// because the click can be swallowed by the inner DOM. Bypass it: we
|
// the click lands inside vue-flow's internal DOM.
|
||||||
// provide a selection callback to the StepNode via inject, and the node
|
//
|
||||||
// invokes it directly on its own click handler.
|
// 1. `useVueFlow().onNodeClick` — the official subscription API tied
|
||||||
|
// to the flow instance keyed by props.canvasId.
|
||||||
|
// 2. `provide('selectStepCallback', ...)` — the StepNode injects this
|
||||||
|
// and fires it on its own @click. Survives vue-flow re-renders.
|
||||||
|
// 3. The template `@node-click` attribute below — kept as a third
|
||||||
|
// fallback for completeness.
|
||||||
function selectStep(data: StepNodeData | null) {
|
function selectStep(data: StepNodeData | null) {
|
||||||
emit('select-step', data)
|
emit('select-step', data)
|
||||||
}
|
}
|
||||||
provide('selectStepCallback', selectStep)
|
provide('selectStepCallback', selectStep)
|
||||||
|
|
||||||
|
const flow = useVueFlow(props.canvasId)
|
||||||
|
onMounted(() => {
|
||||||
|
flow.onNodeClick((evt) => {
|
||||||
|
const data = (evt?.node as Node<StepNodeData> | undefined)?.data
|
||||||
|
if (data) selectStep(data)
|
||||||
|
})
|
||||||
|
flow.onPaneClick(() => selectStep(null))
|
||||||
|
})
|
||||||
|
|
||||||
function handleNodeClick(payload: { node: Node<StepNodeData> } | unknown) {
|
function handleNodeClick(payload: { node: Node<StepNodeData> } | unknown) {
|
||||||
// Backup path: if vue-flow does fire its own node-click event with the
|
|
||||||
// expected shape, route it through the same emit.
|
|
||||||
const node = (payload as { node?: Node<StepNodeData> })?.node
|
const node = (payload as { node?: Node<StepNodeData> })?.node
|
||||||
if (node?.data) selectStep(node.data)
|
if (node?.data) selectStep(node.data)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user