mirror of https://github.com/nocodb/nocodb
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
167 lines
4.6 KiB
167 lines
4.6 KiB
<script setup lang="ts"> |
|
import { message } from 'ant-design-vue' |
|
import { extractSdkResponseErrorMsg } from './utils' |
|
import { applyNonSelectable, computed, isEeUI, isMac, useCommandPalette, useRouter, useTheme } from '#imports' |
|
import type { CommandPaletteType } from '~/lib' |
|
|
|
const router = useRouter() |
|
|
|
const route = router.currentRoute |
|
|
|
const cmdK = ref(false) |
|
|
|
const cmdL = ref(false) |
|
|
|
const disableBaseLayout = computed(() => route.value.path.startsWith('/nc/view') || route.value.path.startsWith('/nc/form')) |
|
|
|
useTheme() |
|
|
|
const { commandPalette, cmdData, cmdPlaceholder, activeScope, loadTemporaryScope, refreshCommandPalette } = useCommandPalette() |
|
|
|
applyNonSelectable() |
|
useEventListener(document, 'keydown', async (e: KeyboardEvent) => { |
|
const cmdOrCtrl = isMac() ? e.metaKey : e.ctrlKey |
|
if (cmdOrCtrl) { |
|
switch (e.key.toLowerCase()) { |
|
case 'a': |
|
// prevent Ctrl + A selection for non-editable nodes |
|
if (!['input', 'textarea'].includes((e.target as any).nodeName.toLowerCase())) { |
|
e.preventDefault() |
|
} |
|
break |
|
case 'k': |
|
e.preventDefault() |
|
break |
|
case 'l': |
|
e.preventDefault() |
|
break |
|
case 'j': |
|
e.preventDefault() |
|
break |
|
} |
|
} |
|
}) |
|
|
|
// TODO: Remove when https://github.com/vuejs/core/issues/5513 fixed |
|
const key = ref(0) |
|
|
|
const messages = [ |
|
`Uncaught NotFoundError: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.`, // chromium based |
|
`NotFoundError: The object can not be found here.`, // safari |
|
"Cannot read properties of null (reading 'parentNode')", |
|
] |
|
|
|
if (typeof window !== 'undefined') { |
|
// @ts-expect-error using arbitrary window key |
|
if (!window.__ncvue) { |
|
window.addEventListener('error', (event) => { |
|
if (messages.includes(event.message)) { |
|
event.preventDefault() |
|
console.warn('Re-rendering layout because of https://github.com/vuejs/core/issues/5513') |
|
key.value++ |
|
} |
|
}) |
|
} |
|
|
|
// @ts-expect-error using arbitrary window key |
|
window.__ncvue = true |
|
} |
|
|
|
function onScope(scope: string) { |
|
if (scope === 'root' && isEeUI) { |
|
loadTemporaryScope({ scope: 'root', data: {} }) |
|
} |
|
} |
|
|
|
function setActiveCmdView(cmd: CommandPaletteType) { |
|
if (cmd === 'cmd-k') { |
|
cmdK.value = true |
|
cmdL.value = false |
|
} else if (cmd === 'cmd-l') { |
|
cmdL.value = true |
|
cmdK.value = false |
|
} else { |
|
cmdL.value = false |
|
cmdK.value = false |
|
document.dispatchEvent( |
|
new KeyboardEvent('keydown', { |
|
key: 'J', |
|
ctrlKey: !isMac() || undefined, |
|
metaKey: isMac() || undefined, |
|
}), |
|
) |
|
} |
|
} |
|
|
|
onMounted(() => { |
|
nextTick(() => { |
|
refreshCommandPalette() |
|
}) |
|
}) |
|
|
|
let errorCount = 0 |
|
|
|
const handleError = async (error, clearError) => { |
|
console.error('UI ERROR', error.value) |
|
// if error is api error, show toast message with error message |
|
if (error.value?.response) { |
|
message.warn(await extractSdkResponseErrorMsg(error.value)) |
|
} else { |
|
// else show generic error message |
|
message.warn('Something went wrong. Please reload the page if page is not functioning properly.') |
|
} |
|
clearError() |
|
|
|
// if error count is more than 3 within 3 second, navigate to home |
|
// since it's likely endless loop of errors due to some UI issue in certain page |
|
errorCount++ |
|
if (errorCount > 3) { |
|
router.push('/') |
|
} |
|
|
|
// reset error count after 1 second |
|
setTimeout(() => { |
|
errorCount = 0 |
|
}, 3000) |
|
} |
|
</script> |
|
|
|
<template> |
|
<a-config-provider> |
|
<NuxtLayout :name="disableBaseLayout ? false : 'base'"> |
|
<NuxtErrorBoundary> |
|
<NuxtPage :key="key" :transition="false" /> |
|
|
|
<!-- on error, clear error and show toast message --> |
|
<template #error="{ error, clearError }"> |
|
{{ handleError(error, clearError) }} |
|
</template> |
|
</NuxtErrorBoundary> |
|
</NuxtLayout> |
|
</a-config-provider> |
|
|
|
<NuxtErrorBoundary> |
|
<div> |
|
<!-- Command Menu --> |
|
<CmdK |
|
ref="commandPalette" |
|
v-model:open="cmdK" |
|
:scope="activeScope.scope" |
|
:data="cmdData" |
|
:placeholder="cmdPlaceholder" |
|
:load-temporary-scope="loadTemporaryScope" |
|
:set-active-cmd-view="setActiveCmdView" |
|
@scope="onScope" |
|
/> |
|
<!-- Recent Views. Cycles through recently visited Views --> |
|
<CmdL v-model:open="cmdL" :set-active-cmd-view="setActiveCmdView" /> |
|
<!-- Documentation. Integrated NocoDB Docs directly inside the Product --> |
|
<CmdJ /> |
|
</div> |
|
|
|
<!-- on error, clear error and show toast message --> |
|
<template #error="{ error, clearError }"> |
|
{{ handleError(error, clearError) }} |
|
</template> |
|
</NuxtErrorBoundary> |
|
</template>
|
|
|