mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-25 04:34:06 -08:00
fix(editor): Tweak hover area of workflow / cred cards (#7108)
Context When a user is attempting to interact with a foreground action inside an entity card (workflow, credential, community node, logging destination), they might accidentally open that entity instead of interacting with a foreground action. For these card components, actions are always placed on right side. A/C Area around right "column" of entity cards (workflow, cred, community node, logging destination) should not be a hoverable area (that opens that entity when clicked). This area is roughly highlighted in screen shot below in orange. ![image](https://github.com/n8n-io/n8n/assets/5410822/0916bcd5-e972-4367-a862-41d2086a2334)
This commit is contained in:
parent
67092c0a1b
commit
217de21605
|
@ -14,7 +14,7 @@
|
||||||
<slot name="footer" />
|
<slot name="footer" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="$slots.append">
|
<div v-if="$slots.append" :class="$style.append">
|
||||||
<slot name="append" />
|
<slot name="append" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -82,7 +82,6 @@ export default defineComponent({
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
width: 24px;
|
width: 24px;
|
||||||
height: 24px;
|
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -101,4 +100,10 @@ export default defineComponent({
|
||||||
border-color: var(--color-primary);
|
border-color: var(--color-primary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.append {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,24 +1,26 @@
|
||||||
<template>
|
<template>
|
||||||
<n8n-card :class="$style['card-link']" @click="onClick">
|
<n8n-card :class="$style.cardLink" @click="onClick">
|
||||||
<template #prepend>
|
<template #prepend>
|
||||||
<credential-icon :credential-type-name="credentialType ? credentialType.name : ''" />
|
<credential-icon :credential-type-name="credentialType ? credentialType.name : ''" />
|
||||||
</template>
|
</template>
|
||||||
<template #header>
|
<template #header>
|
||||||
<n8n-heading tag="h2" bold :class="$style['card-heading']">
|
<n8n-heading tag="h2" bold :class="$style.cardHeading">
|
||||||
{{ data.name }}
|
{{ data.name }}
|
||||||
</n8n-heading>
|
</n8n-heading>
|
||||||
</template>
|
</template>
|
||||||
<n8n-text color="text-light" size="small">
|
<div :class="$style.cardDescription">
|
||||||
<span v-if="credentialType">{{ credentialType.displayName }} | </span>
|
<n8n-text color="text-light" size="small">
|
||||||
<span v-show="data"
|
<span v-if="credentialType">{{ credentialType.displayName }} | </span>
|
||||||
>{{ $locale.baseText('credentials.item.updated') }} <time-ago :date="data.updatedAt" /> |
|
<span v-show="data"
|
||||||
</span>
|
>{{ $locale.baseText('credentials.item.updated') }} <time-ago :date="data.updatedAt" /> |
|
||||||
<span v-show="data"
|
</span>
|
||||||
>{{ $locale.baseText('credentials.item.created') }} {{ formattedCreatedAtDate }}
|
<span v-show="data"
|
||||||
</span>
|
>{{ $locale.baseText('credentials.item.created') }} {{ formattedCreatedAtDate }}
|
||||||
</n8n-text>
|
</span>
|
||||||
|
</n8n-text>
|
||||||
|
</div>
|
||||||
<template #append>
|
<template #append>
|
||||||
<div :class="$style['card-actions']">
|
<div :class="$style.cardActions" ref="cardActions">
|
||||||
<enterprise-edition :features="[EnterpriseEditionFeature.Sharing]">
|
<enterprise-edition :features="[EnterpriseEditionFeature.Sharing]">
|
||||||
<n8n-badge v-if="credentialPermissions.isOwner" class="mr-xs" theme="tertiary" bold>
|
<n8n-badge v-if="credentialPermissions.isOwner" class="mr-xs" theme="tertiary" bold>
|
||||||
{{ $locale.baseText('credentials.item.owner') }}
|
{{ $locale.baseText('credentials.item.owner') }}
|
||||||
|
@ -128,7 +130,14 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async onClick() {
|
async onClick(event: Event) {
|
||||||
|
if (
|
||||||
|
this.$refs.cardActions === event.target ||
|
||||||
|
this.$refs.cardActions?.contains(event.target)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.uiStore.openExistingCredential(this.data.id);
|
this.uiStore.openExistingCredential(this.data.id);
|
||||||
},
|
},
|
||||||
async onAction(action: string) {
|
async onAction(action: string) {
|
||||||
|
@ -162,23 +171,36 @@ export default defineComponent({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" module>
|
<style lang="scss" module>
|
||||||
.card-link {
|
.cardLink {
|
||||||
transition: box-shadow 0.3s ease;
|
transition: box-shadow 0.3s ease;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
padding: 0 0 0 var(--spacing-s);
|
||||||
|
align-items: stretch;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
box-shadow: 0 2px 8px rgba(#441c17, 0.1);
|
box-shadow: 0 2px 8px rgba(#441c17, 0.1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-heading {
|
.cardHeading {
|
||||||
font-size: var(--font-size-s);
|
font-size: var(--font-size-s);
|
||||||
|
padding: var(--spacing-s) 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-actions {
|
.cardDescription {
|
||||||
|
min-height: 19px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 0 var(--spacing-s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cardActions {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
align-self: stretch;
|
||||||
|
padding: 0 var(--spacing-s) 0 0;
|
||||||
|
cursor: default;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #append>
|
<template #append>
|
||||||
<div :class="$style.cardActions">
|
<div :class="$style.cardActions" ref="cardActions">
|
||||||
<div :class="$style.activeStatusText" data-test-id="destination-activator-status">
|
<div :class="$style.activeStatusText" data-test-id="destination-activator-status">
|
||||||
<n8n-text v-if="nodeParameters.enabled" :color="'success'" size="small" bold>
|
<n8n-text v-if="nodeParameters.enabled" :color="'success'" size="small" bold>
|
||||||
{{ $locale.baseText('workflowActivator.active') }}
|
{{ $locale.baseText('workflowActivator.active') }}
|
||||||
|
@ -83,9 +83,7 @@ export default defineComponent({
|
||||||
destination: {
|
destination: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true,
|
required: true,
|
||||||
default: deepCopy(
|
default: deepCopy(defaultMessageEventBusDestinationOptions),
|
||||||
defaultMessageEventBusDestinationOptions,
|
|
||||||
) as MessageEventBusDestinationOptions,
|
|
||||||
},
|
},
|
||||||
isInstanceOwner: Boolean,
|
isInstanceOwner: Boolean,
|
||||||
},
|
},
|
||||||
|
@ -130,17 +128,16 @@ export default defineComponent({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async onClick(event?: PointerEvent) {
|
async onClick(event: Event) {
|
||||||
if (
|
if (
|
||||||
event &&
|
this.$refs.cardActions === event.target ||
|
||||||
event.target &&
|
this.$refs.cardActions?.contains(event.target) ||
|
||||||
'className' in event.target &&
|
event.target?.contains(this.$refs.cardActions)
|
||||||
event.target['className'] === 'el-switch__core'
|
|
||||||
) {
|
) {
|
||||||
event.stopPropagation();
|
return;
|
||||||
} else {
|
|
||||||
this.$emit('edit', this.destination.id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.$emit('edit', this.destination.id);
|
||||||
},
|
},
|
||||||
onEnabledSwitched(state: boolean, destinationId: string) {
|
onEnabledSwitched(state: boolean, destinationId: string) {
|
||||||
this.nodeParameters.enabled = state;
|
this.nodeParameters.enabled = state;
|
||||||
|
@ -184,6 +181,8 @@ export default defineComponent({
|
||||||
.cardLink {
|
.cardLink {
|
||||||
transition: box-shadow 0.3s ease;
|
transition: box-shadow 0.3s ease;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
padding: 0 0 0 var(--spacing-s);
|
||||||
|
align-items: stretch;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
box-shadow: 0 2px 8px rgba(#441c17, 0.1);
|
box-shadow: 0 2px 8px rgba(#441c17, 0.1);
|
||||||
|
@ -201,12 +200,14 @@ export default defineComponent({
|
||||||
.cardHeading {
|
.cardHeading {
|
||||||
font-size: var(--font-size-s);
|
font-size: var(--font-size-s);
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
|
padding: var(--spacing-s) 0 0 var(--spacing-s);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cardDescription {
|
.cardDescription {
|
||||||
min-height: 19px;
|
min-height: 19px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
padding: 0 0 var(--spacing-s) var(--spacing-s);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cardActions {
|
.cardActions {
|
||||||
|
@ -214,5 +215,7 @@ export default defineComponent({
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
padding: 0 var(--spacing-s) 0 0;
|
||||||
|
cursor: default;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
</n8n-text>
|
</n8n-text>
|
||||||
</div>
|
</div>
|
||||||
<template #append>
|
<template #append>
|
||||||
<div :class="$style.cardActions">
|
<div :class="$style.cardActions" ref="cardActions">
|
||||||
<enterprise-edition :features="[EnterpriseEditionFeature.Sharing]">
|
<enterprise-edition :features="[EnterpriseEditionFeature.Sharing]">
|
||||||
<n8n-badge v-if="workflowPermissions.isOwner" class="mr-xs" theme="tertiary" bold>
|
<n8n-badge v-if="workflowPermissions.isOwner" class="mr-xs" theme="tertiary" bold>
|
||||||
{{ $locale.baseText('workflows.item.owner') }}
|
{{ $locale.baseText('workflows.item.owner') }}
|
||||||
|
@ -40,7 +40,6 @@
|
||||||
class="mr-s"
|
class="mr-s"
|
||||||
:workflow-active="data.active"
|
:workflow-active="data.active"
|
||||||
:workflow-id="data.id"
|
:workflow-id="data.id"
|
||||||
ref="activator"
|
|
||||||
data-test-id="workflow-card-activator"
|
data-test-id="workflow-card-activator"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -78,8 +77,6 @@ import { useUsersStore } from '@/stores/users.store';
|
||||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||||
import TimeAgo from '@/components/TimeAgo.vue';
|
import TimeAgo from '@/components/TimeAgo.vue';
|
||||||
|
|
||||||
type ActivatorRef = InstanceType<typeof WorkflowActivator>;
|
|
||||||
|
|
||||||
export const WORKFLOW_LIST_ITEM_ACTIONS = {
|
export const WORKFLOW_LIST_ITEM_ACTIONS = {
|
||||||
OPEN: 'open',
|
OPEN: 'open',
|
||||||
SHARE: 'share',
|
SHARE: 'share',
|
||||||
|
@ -171,21 +168,22 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async onClick(event?: PointerEvent) {
|
async onClick(event: Event) {
|
||||||
if (event) {
|
if (
|
||||||
if ((this.$refs.activator as ActivatorRef)?.$el.contains(event.target as HTMLElement)) {
|
this.$refs.cardActions === event.target ||
|
||||||
return;
|
this.$refs.cardActions?.contains(event.target)
|
||||||
}
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (event.metaKey || event.ctrlKey) {
|
if (event.metaKey || event.ctrlKey) {
|
||||||
const route = this.$router.resolve({
|
const route = this.$router.resolve({
|
||||||
name: VIEWS.WORKFLOW,
|
name: VIEWS.WORKFLOW,
|
||||||
params: { name: this.data.id },
|
params: { name: this.data.id },
|
||||||
});
|
});
|
||||||
window.open(route.href, '_blank');
|
window.open(route.href, '_blank');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.$router.push({
|
await this.$router.push({
|
||||||
|
@ -267,6 +265,8 @@ export default defineComponent({
|
||||||
.cardLink {
|
.cardLink {
|
||||||
transition: box-shadow 0.3s ease;
|
transition: box-shadow 0.3s ease;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
padding: 0;
|
||||||
|
align-items: stretch;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
box-shadow: 0 2px 8px rgba(#441c17, 0.1);
|
box-shadow: 0 2px 8px rgba(#441c17, 0.1);
|
||||||
|
@ -276,12 +276,14 @@ export default defineComponent({
|
||||||
.cardHeading {
|
.cardHeading {
|
||||||
font-size: var(--font-size-s);
|
font-size: var(--font-size-s);
|
||||||
word-break: break-word;
|
word-break: break-word;
|
||||||
|
padding: var(--spacing-s) 0 0 var(--spacing-s);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cardDescription {
|
.cardDescription {
|
||||||
min-height: 19px;
|
min-height: 19px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
padding: 0 0 var(--spacing-s) var(--spacing-s);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cardActions {
|
.cardActions {
|
||||||
|
@ -289,5 +291,8 @@ export default defineComponent({
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
align-self: stretch;
|
||||||
|
padding: 0 var(--spacing-s) 0 0;
|
||||||
|
cursor: default;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
Loading…
Reference in a new issue