Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use default behavior for platform wide anchors (part I) #4834

Merged
merged 22 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
929245f
use an anchor for the page header logo/home button
cstns Nov 26, 2024
da43160
qf passing method to router link
cstns Nov 26, 2024
5a3522d
replace html anchor with vue router in the ff-button component
cstns Nov 26, 2024
0a87b52
use an anchor for the invite members button and inline the to method
cstns Nov 26, 2024
fd7f288
qf hardcoded leftover
cstns Nov 26, 2024
08c7f61
qf invalid to param passed to router-link
cstns Nov 26, 2024
da730cf
replace buttons and programmatic navigation from the teamApplications…
cstns Nov 26, 2024
a41d0c0
add a navigation helper composable to centralize custom navigation logic
cstns Nov 26, 2024
1476245
swap the click with mouseup events, allow disabling default navigatio…
cstns Nov 26, 2024
06bcaa0
addresses editorLink not working as expected from the applicationsIns…
cstns Nov 26, 2024
39e7193
add a new open in a new tab navigation helper method
cstns Nov 26, 2024
782ef75
alter datatable events to include the event so we can handle mouse+ke…
cstns Nov 26, 2024
afe1d6b
update the applications overview / instances list to allow mouse+key …
cstns Nov 26, 2024
bbd10e8
fix stripping the ff-button of the default click event preventing mod…
cstns Nov 27, 2024
00142c2
allow row mouseup event to trickle down, fix e2e tests
cstns Nov 27, 2024
bf0b22a
extend the NavigationHelper to handle external urls, remove the ancho…
cstns Nov 27, 2024
db414ca
qf e2e tests
cstns Nov 27, 2024
4fb7bf3
fix dashboard link opening multiple tabs on click+keyboard
cstns Nov 27, 2024
2bec70b
qf page name
cstns Nov 27, 2024
6109c8c
Merge remote-tracking branch 'origin/main' into use-default-behavior-…
cstns Dec 5, 2024
7980433
Merge remote-tracking branch 'origin/main' into use-default-behavior-…
cstns Dec 6, 2024
d57b40b
Merge branch 'main' into use-default-behavior-for-platform-wide-anchors
cstns Dec 6, 2024
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
24 changes: 6 additions & 18 deletions frontend/src/components/PageHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
</transition>
</i>
<!-- FlowFuse Logo -->
<img class="ff-logo" src="/ff-logo--wordmark-caps--dark.png" @click="home()">
<router-link :to="homeLink">
<img class="ff-logo" src="/ff-logo--wordmark-caps--dark.png">
</router-link>
<global-search v-if="hasAMinimumTeamRoleOf(Roles.Viewer)" />
<!-- Mobile: Toggle(User Options) -->
<div class="flex ff-mobile-navigation-right" data-el="mobile-nav-right">
Expand Down Expand Up @@ -44,7 +46,7 @@
<div class="hidden lg:flex ff-desktop-navigation-right" data-el="desktop-nav-right">
<ff-team-selection data-action="team-selection" />
<div class="px-4 flex flex-col justify-center" v-if="showInviteButton">
<ff-button kind="secondary" @click="inviteTeamMembers">
<ff-button kind="secondary" type="anchor" :to="{ name: 'team-members', params: { team_slug: team.slug }, query: { action: 'invite' } }">
<template #icon-left><UserAddIcon /></template>
Invite Members
</ff-button>
Expand Down Expand Up @@ -89,7 +91,7 @@ import TeamSelection from './TeamSelection.vue'
import GlobalSearch from './global-search/GlobalSearch.vue'

export default {
name: 'NavBar',
name: 'PageHeader',
mixins: [navigationMixin, permissionsMixin],
computed: {
Roles () {
Expand Down Expand Up @@ -121,7 +123,7 @@ export default {
label: 'Documentation',
icon: QuestionMarkCircleIcon,
tag: 'documentation',
onclick: this.to,
onclick: (route) => window.open(route.url, '_blank'),
onclickparams: { url: 'https://flowfuse.com/docs/' }
},
this.isTrialAccount
Expand Down Expand Up @@ -176,20 +178,6 @@ export default {
},
methods: {
...mapActions('ux', ['toggleLeftDrawer']),
to (route) {
window.open(route.url, '_blank')
},
inviteTeamMembers () {
this.$router.push({
name: 'team-members',
params: {
team_slug: this.team.slug
},
query: {
action: 'invite'
}
})
},
...mapActions('ux', ['activateTour']),
openEducationModal () {
this.activateTour('education')
Expand Down
56 changes: 56 additions & 0 deletions frontend/src/composables/NavigationHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { useRouter } from 'vue-router'

export function useNavigationHelper () {
const _router = useRouter()

const openInANewTab = (href) => {
return new Promise(resolve => {
window.open(href, '_blank')
resolve()
})
}

const _isExternalLink = (href) => {
try {
const link = new URL(href, window.location.origin)

return link.origin !== window.location.origin
} catch {
return false
}
}

const navigateTo = (to, $event = null) => {
const internalHref = _router.resolve(to).href
const isMiddleButtonClick = $event?.button === 1
const newTabKeyCombination = $event && ($event?.ctrlKey || $event.metaKey || isMiddleButtonClick)
const isExternalLink = _isExternalLink(to)

switch (true) {
case isExternalLink && newTabKeyCombination:
return openInANewTab(to)

case isExternalLink:
return navigateToExternal(to)

case newTabKeyCombination:
return openInANewTab(internalHref)

default:
return _router.push(to)
}
}

const navigateToExternal = (href) => {
return new Promise(resolve => {
window.location.href = href
resolve()
})
}

return {
openInANewTab,
navigateTo,
navigateToExternal
}
}
16 changes: 8 additions & 8 deletions frontend/src/mixins/Navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@ import { mapState } from 'vuex'

export default {
computed: {
...mapState('account', ['team', 'defaultUserTeam'])
},
methods: {
home () {
...mapState('account', ['team', 'defaultUserTeam']),
homeLink () {
if (this.team?.slug) {
this.$router.push({ name: 'Team', params: { team_slug: this.team.slug } })
return { name: 'Team', params: { team_slug: this.team.slug } }
} else if (this.defaultUserTeam?.slug) {
this.$router.push({ name: 'Team', params: { team_slug: this.defaultUserTeam?.slug } })
return { name: 'Team', params: { team_slug: this.defaultUserTeam?.slug } }
} else {
this.$router.push({ name: 'Home' })
return { name: 'Home' }
}
},
}
},
methods: {
signOut () {
this.$router.push({ name: 'Sign out' })
}
Expand Down
16 changes: 13 additions & 3 deletions frontend/src/pages/application/Overview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
v-if="hasPermission('project:create')"
data-action="create-instance"
:to="{ name: 'ApplicationCreateInstance' }"
type="anchor"
>
<template #icon-left><PlusSmIcon /></template>
Add Instance
Expand Down Expand Up @@ -79,6 +80,7 @@
<ff-button
v-if="hasPermission('project:create')"
:to="{ name: 'ApplicationCreateInstance' }"
type="anchor"
>
<template #icon-left><PlusSmIcon /></template>
Add Instance
Expand All @@ -104,6 +106,7 @@ import { mapState } from 'vuex'

import EmptyState from '../../components/EmptyState.vue'
import SectionTopMenu from '../../components/SectionTopMenu.vue'
import { useNavigationHelper } from '../../composables/NavigationHelper.js'

import permissionsMixin from '../../mixins/Permissions.js'
import { Roles } from '../../utils/roles.js'
Expand Down Expand Up @@ -134,6 +137,13 @@ export default {
}
},
emits: ['instance-delete', 'instance-suspend', 'instance-restart', 'instance-start'],
setup () {
const { navigateTo } = useNavigationHelper()

return {
navigateTo
}
},
data () {
return {
searchTerm: ''
Expand Down Expand Up @@ -180,13 +190,13 @@ export default {
}
},
methods: {
selectedCloudRow (cloudInstance) {
this.$router.push({
selectedCloudRow (cloudInstance, event) {
this.navigateTo({
name: 'Instance',
params: {
id: cloudInstance.id
}
})
}, event)
},
updateSearch (searchTerm) {
this.searchTerm = searchTerm
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/application/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ import ConfirmInstanceDeleteDialog from '../instance/Settings/dialogs/ConfirmIns
import ConfirmApplicationDeleteDialog from './Settings/dialogs/ConfirmApplicationDeleteDialog.vue'

export default {
name: 'ProjectPage',
name: 'ApplicationPage',
components: {
ConfirmApplicationDeleteDialog,
ConfirmInstanceDeleteDialog,
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/pages/instance/components/DashboardLink.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
<template>
<ff-button
v-if="!hidden"
type="anchor"
kind="secondary"
data-action="open-dashboard"
:to="dashboardURL"
:target="target"
:disabled="buttonDisabled"
class="whitespace-nowrap"
@click.stop.prevent
@mouseup.stop.prevent
Comment on lines +10 to +11
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are these required?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to prevent the click event from bubbling up/down. In the case of the dashboard link being nested inside another anchor/link/button, this prevents opening both parent anchor and dashboard link when clicking (or any other click + keyboard combination) directly on the dashboard link element

>
<template v-if="showText" #icon-left>
<ChartPieIcon />
Expand Down
36 changes: 22 additions & 14 deletions frontend/src/pages/instance/components/EditorLink.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
<template>
<div :data-type="`${isImmersiveEditor ? 'immersive' : 'standard'}-editor`" @click.stop="openEditor()">
<div :data-type="`${isImmersiveEditor ? 'immersive' : 'standard'}-editor`" @mouseup.stop.prevent="openEditor">
<slot name="default">
<ff-button
v-ff-tooltip:left="(editorDisabled || disabled) ? disabledReason : undefined"
type="anchor"
:to="editorURL"
kind="secondary"
data-action="open-editor"
:disabled="buttonDisabled"
class="whitespace-nowrap"
@click.stop="openEditor"
:emit-instead-of-navigate="true"
@select="openEditor"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why @select?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another way of preventing click events from bubbling up/down. This is specific to the editor link button, having to open different urls based on the type of click/combination acted upon. Emitting an event instead of propagating the click event when clicked gets rid of a lot of headaches and changes when dealing with event propagation in parent components.

I have to admit, 'select' is not the best choice of name for the event being emitted when clicked

>
<template v-if="showText" #icon-left>
<ProjectIcon />
Expand All @@ -32,6 +31,7 @@ import SemVer from 'semver'
import { mapState } from 'vuex'

import ProjectIcon from '../../../components/icons/Projects.js'
import { useNavigationHelper } from '../../../composables/NavigationHelper.js'

export default {
name: 'InstanceEditorLink',
Expand Down Expand Up @@ -59,6 +59,13 @@ export default {
type: Boolean
}
},
setup () {
const { openInANewTab } = useNavigationHelper()

return {
openInANewTab
}
},
computed: {
...mapState('account', ['team', 'teamMembership']),
isImmersiveEditor () {
Expand All @@ -85,20 +92,21 @@ export default {
},
methods: {
openEditor (evt) {
evt.preventDefault()
if (this.disabled) {
return false
}
let href = this.url
let target = !this.isImmersiveEditor ? '_blank' : '_self'
// On Mac Keyboard, ⌘ + click opens in new tab (⌘ is `metaKey`)
// Otherwise Ctrl + click opens in new tab (Ctrl is `ctrlKey`)
if (evt.ctrlKey || evt.metaKey) {
target = '_blank'
href = this.editorURL

switch (true) {
case !this.isImmersiveEditor:
return this.openInANewTab(this.editorURL)
case evt.ctrlKey || evt.metaKey || evt.button === 1:
// On Mac Keyboard, ⌘ + click opens in new tab (⌘ is `metaKey`)
// Otherwise Ctrl + click opens in new tab (Ctrl is `ctrlKey`)
// Middle mouse button click opens in a new tab (button === 1)
return this.openInANewTab(this.url)
default:
return this.$router.push(this.url)
}
window.open(href, target)
return false
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
<template>
<div class="ff-application-list--app gap-x-4 flex flex-col gap-2 sm:gap-0 justify-between sm:flex-row sm:items-center" data-action="view-application" @click="openApplication(application)">
<router-link
:to="{ name: 'Application', params: { id: application.id } }"
data-action="view-application"
class="ff-application-list--app gap-x-4 flex flex-col gap-2 sm:gap-0 justify-between sm:flex-row sm:items-center"
>
<div class="flex items-cente flex-wrap">
<span class="ff-application-list--icon flex flex-shrink-0 flex-grow-0 whitespace-nowrap gap-2 w-full"><TemplateIcon class="ff-icon text-gray-600" />{{ application.name }}</span>
<span class="!inline-block !flex-shrink !flex-grow italic text-gray-500 dark:text-gray-400 truncate"> {{ application.description }} </span>
<span class="ff-application-list--icon flex flex-shrink-0 flex-grow-0 whitespace-nowrap gap-2 w-full">
<TemplateIcon class="ff-icon text-gray-600" />
{{ application.name }}
</span>
<span class="!inline-block !flex-shrink !flex-grow italic text-gray-500 dark:text-gray-400 truncate">
{{ application.description }}
</span>
</div>
<ApplicationSummaryLabel :application="application" />
</div>
</router-link>
</template>

<script>
Expand All @@ -25,16 +34,6 @@ export default {
required: true,
default: null
}
},
methods: {
openApplication (application) {
this.$router.push({
name: 'Application',
params: {
id: application.id
}
})
}
}
}
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
</div>
<div class="details">
<div class="detail-wrapper">
<span class="cursor-pointer name" @click="openDevice(device)">{{ device.name }}</span>
<router-link :to="{ name: 'Device', params: { id: device.id } }" class="name" :title="device.name">
{{ device.name }}
</router-link>
</div>
<div class="detail-wrapper">
<span class="detail">
Expand Down Expand Up @@ -69,17 +71,7 @@ export default {
type: Object
}
},
emits: ['device-action'],
methods: {
openDevice (device) {
this.$router.push({
name: 'Device',
params: {
id: device.id
}
})
}
}
emits: ['device-action']
}
</script>

Expand Down
Loading
Loading