Browse Source

feat(gui-v2): add `useSidebar` composable

# What's changed?

* replace global sidebar state with `useSidebar` composable
pull/3023/head
braks 2 years ago
parent
commit
bd96a78ce0
  1. 34
      packages/nc-gui-v2/app.vue
  2. 2
      packages/nc-gui-v2/assets/style-v2.scss
  3. 5
      packages/nc-gui-v2/components/cell/attachment/index.vue
  4. 2
      packages/nc-gui-v2/components/dashboard/TreeView.vue
  5. 15
      packages/nc-gui-v2/components/smartsheet/Grid.vue
  6. 10
      packages/nc-gui-v2/components/smartsheet/sidebar/index.vue
  7. 1
      packages/nc-gui-v2/composables/index.ts
  8. 4
      packages/nc-gui-v2/composables/useGlobal/state.ts
  9. 1
      packages/nc-gui-v2/composables/useGlobal/types.ts
  10. 2
      packages/nc-gui-v2/composables/useInjectionState/index.ts
  11. 48
      packages/nc-gui-v2/composables/useSidebar/index.ts
  12. 2
      packages/nc-gui-v2/layouts/default.vue
  13. 199
      packages/nc-gui-v2/package-lock.json
  14. 2
      packages/nc-gui-v2/package.json
  15. 34
      packages/nc-gui-v2/pages/index/index.vue
  16. 14
      packages/nc-gui-v2/pages/index/user/index/index.vue
  17. 22
      packages/nc-gui-v2/pages/nc/[projectId]/index.vue
  18. 11
      packages/nc-gui-v2/pages/nc/[projectId]/index/index.vue
  19. 18
      packages/nc-gui-v2/pages/project/index/[id].vue
  20. 13
      packages/nc-gui-v2/pages/project/index/create-external.vue
  21. 22
      packages/nc-gui-v2/pages/project/index/create.vue
  22. 8
      packages/nc-gui-v2/plugins/state.ts

34
packages/nc-gui-v2/app.vue

@ -1,21 +1,27 @@
<script lang="ts" setup>
import { breakpointsTailwind } from '@vueuse/core'
import { navigateTo } from '#app'
import { useGlobal } from '#imports'
import { computed, ref, useBreakpoints, useGlobal, useSidebar } from '#imports'
const state = useGlobal()
/** get current breakpoints (for enabling sidebar) */
const breakpoints = useBreakpoints(breakpointsTailwind)
const { signOut, signedIn, isLoading, user } = $(useGlobal())
const { isOpen, hasSidebar } = useSidebar({ isOpen: signedIn && breakpoints.greater('md').value })
const sidebar = ref<HTMLDivElement>()
const email = computed(() => state.user.value?.email ?? '---')
const email = computed(() => user?.email ?? '---')
const signOut = () => {
state.signOut()
const logout = () => {
signOut()
navigateTo('/signin')
}
const sidebarCollapsed = computed({
get: () => !state.sidebarOpen.value,
set: (val) => (state.sidebarOpen.value = !val),
get: () => !isOpen.value,
set: (val) => (isOpen.value = !val),
})
const toggleSidebar = () => {
@ -25,8 +31,8 @@ const toggleSidebar = () => {
<template>
<a-layout class="min-h-[100vh]">
<a-layout-header class="flex !bg-primary items-center text-white px-4 shadow-md">
<material-symbols-menu v-if="state.signedIn.value" class="text-xl cursor-pointer" @click="toggleSidebar" />
<a-layout-header class="hidden flex !bg-primary items-center text-white px-4 shadow-md">
<material-symbols-menu v-if="signedIn && hasSidebar" class="text-xl cursor-pointer" @click="toggleSidebar" />
<div class="flex-1" />
@ -38,16 +44,16 @@ const toggleSidebar = () => {
</div>
<div class="flex-1 text-left">
<div v-show="state.isLoading.value" class="flex items-center gap-2 ml-3">
<div v-show="isLoading" class="flex items-center gap-2 ml-3">
{{ $t('general.loading') }}
<mdi-reload :class="{ 'animate-infinite animate-spin': state.isLoading.value }" />
<mdi-reload :class="{ 'animate-infinite animate-spin': isLoading }" />
</div>
</div>
<div class="flex justify-end gap-4">
<general-language class="mr-3" />
<template v-if="state.signedIn.value">
<template v-if="signedIn">
<a-dropdown :trigger="['click']">
<mdi-dots-vertical class="md:text-xl cursor-pointer nc-user-menu" @click.prevent />
@ -63,7 +69,7 @@ const toggleSidebar = () => {
<a-menu-divider class="!m-0" />
<a-menu-item key="1" class="!rounded-b">
<div v-t="['a:navbar:user:sign-out']" class="group flex items-center py-2" @click="signOut">
<div v-t="['a:navbar:user:sign-out']" class="group flex items-center py-2" @click="logout">
<mdi-logout class="dark:text-white group-hover:(!text-red-500)" />&nbsp;
<span class="prose font-semibold text-gray-500 group-hover:text-black nc-user-menu-signout">
@ -78,7 +84,6 @@ const toggleSidebar = () => {
</div>
</a-layout-header>
<a-layout>
<a-layout-sider
v-model:collapsed="sidebarCollapsed"
width="300"
@ -92,5 +97,4 @@ const toggleSidebar = () => {
<NuxtPage />
</a-layout>
</a-layout>
</template>

2
packages/nc-gui-v2/assets/style-v2.scss

@ -2,7 +2,7 @@
@import 'ant-design-vue/dist/antd.min.css';
:root {
--header-height: 56px;
--header-height: 0px;
}
.ant-layout-header {

5
packages/nc-gui-v2/components/cell/attachment/index.vue

@ -107,7 +107,7 @@ onMounted(() => {
:key="item.url || item.title"
style="flex: 1 1 50px"
:class="isImage(item.title, item.mimetype) ? '' : 'border-1 rounded'"
class="nc-attachment flex items-center justify-center"
class="nc-attachment flex items-center justify-center min-h-[50px]"
>
<a-tooltip placement="bottom">
<template #title>
@ -116,9 +116,8 @@ onMounted(() => {
<nuxt-img
v-if="isImage(item.title, item.mimetype)"
quality="75"
placeholder
width="150"
height="150"
:alt="item.title || `#${i}`"
:src="item.url || item.data"
class="ring-1 ring-gray-300 rounded"

2
packages/nc-gui-v2/components/dashboard/TreeView.vue

@ -143,7 +143,7 @@ const addTableTab = (table: TableType) => {
<template>
<div class="nc-treeview-container flex flex-column">
<div class="px-3 py-2">
<div class="px-2 py-[11.75px] border-b-1">
<a-input-search
v-model:value="filterQuery"
size="small"

15
packages/nc-gui-v2/components/smartsheet/Grid.vue

@ -295,22 +295,23 @@ const onNavigate = (dir: NavigateDir) => {
</tr>
</thead>
<tbody>
<tr v-for="(row, rowIndex) in data" :key="rowIndex" class="nc-grid-row group">
<td key="row-index" class="caption nc-grid-cell">
<div class="align-center flex w-[80px]">
<tr v-for="(row, rowIndex) of data" :key="rowIndex" class="nc-grid-row">
<td key="row-index" class="caption nc-grid-cell group">
<div class="flex items-center w-[80px]">
<div class="group-hover:hidden" :class="{ hidden: row.rowMeta.selected }">{{ rowIndex + 1 }}</div>
<div
:class="{ hidden: !row.rowMeta.selected, flex: row.rowMeta.selected }"
class="group-hover:flex w-full align-center"
class="group-hover:flex w-full items-center justify-between p-1"
>
<a-checkbox v-model:checked="row.rowMeta.selected" />
<span class="flex-1" />
<MdiArrowExpand class="text-sm text-pink hidden group-hover:inline-block" />
<div class="cursor-pointer flex items-center border-1 active:ring rounded p-1 hover:bg-primary/10">
<MdiArrowExpand class="select-none transform hover:(text-pink-500 scale-120)" />
</div>
</div>
</div>
</td>
<td
v-for="(columnObj, colIndex) in fields"
v-for="(columnObj, colIndex) of fields"
:key="rowIndex + columnObj.title"
class="cell pointer nc-grid-cell"
:class="{

10
packages/nc-gui-v2/components/smartsheet/sidebar/index.vue

@ -68,15 +68,7 @@ function onCreate(view: GridType | FormType | KanbanType | GalleryType) {
</script>
<template>
<a-layout-sider
:collapsed="sidebarCollapsed"
collapsiple
collapsed-width="50"
width="250"
class="shadow !mt-[-9px]"
style="height: calc(100% + 9px)"
theme="light"
>
<a-layout-sider :collapsed="sidebarCollapsed" collapsiple collapsed-width="50" width="250" class="shadow h-full" theme="light">
<Toolbar v-if="sidebarOpen" class="flex items-center py-3 px-3 justify-between border-b-1" />
<Toolbar v-else class="py-3 px-2 max-w-[50px] flex !flex-col-reverse gap-4 items-center mt-[-1px]">

1
packages/nc-gui-v2/composables/index.ts

@ -1,6 +1,7 @@
export * from './useApi'
export * from './useGlobal'
export * from './useInjectionState'
export * from './useSidebar'
export * from './useUIPermission'
export * from './useAttachment'
export * from './useColors'

4
packages/nc-gui-v2/composables/useGlobal/state.ts

@ -74,9 +74,6 @@ export function useGlobalState(storageKey = 'nocodb-gui-v2'): State {
/** reactive token payload */
const { payload } = useJwt<JwtPayload & User>(token)
/** is sidebar open */
const sidebarOpen = ref(false)
/** currently running requests */
const runningRequests = useCounter()
@ -88,7 +85,6 @@ export function useGlobalState(storageKey = 'nocodb-gui-v2'): State {
storage,
token,
jwtPayload: payload,
sidebarOpen,
timestamp,
runningRequests,
error,

1
packages/nc-gui-v2/composables/useGlobal/types.ts

@ -24,7 +24,6 @@ export type State = ToRefs<Omit<StoredState, 'token'>> & {
storage: Ref<StoredState>
token: WritableComputedRef<StoredState['token']>
jwtPayload: ComputedRef<(JwtPayload & User) | null>
sidebarOpen: Ref<boolean>
timestamp: Ref<number>
runningRequests: ReturnType<typeof useCounter>
error: Ref<any>

2
packages/nc-gui-v2/composables/useInjectionState/index.ts

@ -14,7 +14,7 @@ export function useInjectionState<Arguments extends any[], Return>(
return providedState
}
const useInjectedState = () => inject(key)
const useInjectedState = () => inject(key, undefined)
return [useProvidingState, useInjectedState]
}

48
packages/nc-gui-v2/composables/useSidebar/index.ts

@ -0,0 +1,48 @@
import { useInjectionState, useToggle, watch } from '#imports'
interface UseSidebarProps {
hasSidebar?: boolean
isOpen?: boolean
}
const [setup, use] = useInjectionState((props: UseSidebarProps = {}) => {
const [isOpen, toggle] = useToggle(props.isOpen ?? false)
const [hasSidebar, toggleHasSidebar] = useToggle(props.hasSidebar ?? true)
watch(
hasSidebar,
(nextHasSidebar) => {
if (!nextHasSidebar) toggle(false)
},
{ immediate: true },
)
watch(
isOpen,
(nextIsOpen) => {
if (nextIsOpen && !hasSidebar.value) toggleHasSidebar(true)
},
{ immediate: true },
)
return {
isOpen,
toggle,
hasSidebar,
toggleHasSidebar,
}
}, 'useSidebar')
export function useSidebar(props: UseSidebarProps = {}) {
const state = use()
if (!state) {
return setup(props)
} else {
// set state if props were passed
if (typeof props.isOpen !== 'undefined') state.isOpen.value = props.isOpen
if (typeof props.hasSidebar !== 'undefined') state.hasSidebar.value = props.hasSidebar
}
return state
}

2
packages/nc-gui-v2/layouts/default.vue

@ -18,7 +18,7 @@ export default {
</script>
<template>
<a-layout-content class="pl-2 pt-2">
<a-layout-content>
<teleport v-if="$slots.sidebar" to="#sidebar">
<slot name="sidebar" />
</teleport>

199
packages/nc-gui-v2/package-lock.json generated

@ -55,7 +55,7 @@
"happy-dom": "^6.0.3",
"less": "^4.1.3",
"nuxt": "3.0.0-rc.4",
"nuxt-windicss": "^2.4.2",
"nuxt-windicss": "^2.5.0",
"prettier": "^2.7.1",
"sass": "^1.53.0",
"unplugin-icons": "^0.14.7",
@ -3264,9 +3264,9 @@
}
},
"node_modules/@windicss/config": {
"version": "1.8.6",
"resolved": "https://registry.npmjs.org/@windicss/config/-/config-1.8.6.tgz",
"integrity": "sha512-WVS41qUJtd44g2iWzTAE8tpgk8gD0yAr1RwwaWi7FAECKm3LVNMLOoToum9R/QKFE2n64EUVJpIvSUNby8rlhg==",
"version": "1.8.7",
"resolved": "https://registry.npmjs.org/@windicss/config/-/config-1.8.7.tgz",
"integrity": "sha512-8n+/Y36j5L3rw2tgMdLjeGRuNV7VYfKoHoraLK6Bk9OJ1MTPd5vv7pekof/uOPWVV7WWjVeZ6CTO8SDbDDW3iw==",
"dev": true,
"dependencies": {
"debug": "^4.3.4",
@ -3296,13 +3296,13 @@
"dev": true
},
"node_modules/@windicss/plugin-utils": {
"version": "1.8.6",
"resolved": "https://registry.npmjs.org/@windicss/plugin-utils/-/plugin-utils-1.8.6.tgz",
"integrity": "sha512-YY6EcUsgkosaQkIseFiIoHfU1H5boOAs/l74QWLI6ryNeHLMq2e04QVsFz+Rt+U8b8PRNxXPC8ADbxE05X7I7g==",
"version": "1.8.7",
"resolved": "https://registry.npmjs.org/@windicss/plugin-utils/-/plugin-utils-1.8.7.tgz",
"integrity": "sha512-dfj95olNZyGFDPFMBvE5oq8hA5f0ooUJZjVdWlthS4ek4W1/xNOHDxB6ygWR8LE9zCOXZykApjt1LOhy9Ky2QA==",
"dev": true,
"dependencies": {
"@antfu/utils": "^0.5.2",
"@windicss/config": "1.8.6",
"@windicss/config": "1.8.7",
"debug": "^4.3.4",
"fast-glob": "^3.2.11",
"magic-string": "^0.26.2",
@ -7640,15 +7640,15 @@
}
},
"node_modules/h3": {
"version": "0.7.10",
"resolved": "https://registry.npmjs.org/h3/-/h3-0.7.10.tgz",
"integrity": "sha512-HcUQxlOocG2uf6PQLu3Bxk0KSXcTuxSF6kRclFBDO7y74Ml0wCwTvmoN5zmrzoxbNHecDNUrcGr8qSNGcOqAHQ==",
"version": "0.7.13",
"resolved": "https://registry.npmjs.org/h3/-/h3-0.7.13.tgz",
"integrity": "sha512-3rs+iokAwin4GnToWc+4JeASAYPujojMWicaTgv5WMucED94cFVVGctlk7N6iJfQ7SZ2N5CtmQ2C3L2aDFpd2w==",
"dev": true,
"dependencies": {
"cookie-es": "^0.5.0",
"destr": "^1.1.1",
"radix3": "^0.1.2",
"ufo": "^0.8.4"
"ufo": "^0.8.5"
}
},
"node_modules/happy-dom": {
@ -9085,9 +9085,9 @@
}
},
"node_modules/lilconfig": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.5.tgz",
"integrity": "sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==",
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz",
"integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==",
"dev": true,
"engines": {
"node": ">=10"
@ -10144,54 +10144,26 @@
}
},
"node_modules/nuxt-windicss": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/nuxt-windicss/-/nuxt-windicss-2.4.2.tgz",
"integrity": "sha512-coHvXGw4CqkRqG47aJqNKbQKMO17ZGXFVyeOKGaRnvsS4FcdfKtYKukpDgJm0sCIG57z8CljfX3nBy71+vu3mg==",
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/nuxt-windicss/-/nuxt-windicss-2.5.0.tgz",
"integrity": "sha512-h5Wh1Pj5OrYvbQmfarOxi6I366xyPxYVlUIZhu5OPGn0uUaoQIOl5ASR1QD3eR/BH3kdpM/r8wyjikDoW7oSbg==",
"dev": true,
"dependencies": {
"@nuxt/kit": "3.0.0-rc.3",
"@windicss/plugin-utils": "^1.8.4",
"@nuxt/kit": "3.0.0-rc.6",
"@windicss/config": "^1.8.7",
"@windicss/plugin-utils": "^1.8.7",
"consola": "^2.15.3",
"defu": "^6.0.0",
"fs-extra": "^10.1.0",
"h3": "^0.7.8",
"listhen": "^0.2.11",
"pathe": "^0.3.0",
"h3": "^0.7.12",
"listhen": "^0.2.13",
"pathe": "^0.3.2",
"read-cache": "^1.0.0",
"sirv": "^2.0.2",
"vite-plugin-windicss": "^1.8.4",
"windicss": "^3.5.4",
"vite-plugin-windicss": "^1.8.7",
"windicss": "^3.5.6",
"windicss-analysis": "^0.3.5",
"windicss-webpack-plugin": "^1.7.3"
}
},
"node_modules/nuxt-windicss/node_modules/@nuxt/kit": {
"version": "3.0.0-rc.3",
"resolved": "https://registry.npmjs.org/@nuxt/kit/-/kit-3.0.0-rc.3.tgz",
"integrity": "sha512-aD993HKXZ76cwxkM2LCwWRQaOI3RjLCg/kj+8ZS6qN4VrpwrZp4xM59YYIYs3/Vze4OG3D4+0gr0u5zdgf8v8g==",
"dev": true,
"dependencies": {
"@nuxt/schema": "^3.0.0-rc.3",
"c12": "^0.2.7",
"consola": "^2.15.3",
"defu": "^6.0.0",
"globby": "^13.1.1",
"hash-sum": "^2.0.0",
"ignore": "^5.2.0",
"jiti": "^1.13.0",
"knitwork": "^0.1.1",
"lodash.template": "^4.5.0",
"mlly": "^0.5.2",
"pathe": "^0.3.0",
"pkg-types": "^0.3.2",
"scule": "^0.2.1",
"semver": "^7.3.7",
"unctx": "^1.1.4",
"unimport": "^0.1.9",
"untyped": "^0.4.4"
},
"engines": {
"node": "^14.16.0 || ^16.11.0 || ^17.0.0 || ^18.0.0"
"windicss-webpack-plugin": "^1.7.5"
}
},
"node_modules/nuxt/node_modules/unimport": {
@ -14004,12 +13976,12 @@
}
},
"node_modules/vite-plugin-windicss": {
"version": "1.8.6",
"resolved": "https://registry.npmjs.org/vite-plugin-windicss/-/vite-plugin-windicss-1.8.6.tgz",
"integrity": "sha512-D4G4qmumgklPiPrq/ZALqq8Mby6krskFVShbmb5c+0VCSsLUN96qyFRTwi81rNIHwFvlbpqflgh+BpUM/9VjQg==",
"version": "1.8.7",
"resolved": "https://registry.npmjs.org/vite-plugin-windicss/-/vite-plugin-windicss-1.8.7.tgz",
"integrity": "sha512-/zwQ8+RV+MSkbG0IGqsEma6r2R01NzN/aNpNjJD7VVAkxAptNznqDXOObFTskkWfZ+9m6KJZCOuCPgAFtQIzEA==",
"dev": true,
"dependencies": {
"@windicss/plugin-utils": "1.8.6",
"@windicss/plugin-utils": "1.8.7",
"debug": "^4.3.4",
"kolorist": "^1.5.1",
"windicss": "^3.5.6"
@ -14018,7 +13990,7 @@
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"vite": "^2.0.1"
"vite": "^2.0.1 || ^3.0.0"
}
},
"node_modules/vitest": {
@ -14580,19 +14552,19 @@
}
},
"node_modules/windicss-webpack-plugin": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/windicss-webpack-plugin/-/windicss-webpack-plugin-1.7.3.tgz",
"integrity": "sha512-vESTMEUqzNlvOmnOCCxeqDkj1q2u69FfKMQHyTF9Y9tLJbWttEHgt+qSJZ3cYndUHRxbz06INanEyL08dQr79A==",
"version": "1.7.5",
"resolved": "https://registry.npmjs.org/windicss-webpack-plugin/-/windicss-webpack-plugin-1.7.5.tgz",
"integrity": "sha512-+DwZwFcjgYrD/UU1UEs1TOTo4ijAwRkXEgJMttPd05NSC3ULvguvNKL5kNxrCTYs4OMJn68qbCfkjpI0mir4cQ==",
"dev": true,
"dependencies": {
"@windicss/plugin-utils": "^1.8.4",
"@windicss/plugin-utils": "^1.8.7",
"debug": "^4.3.4",
"get-port": "^6.1.2",
"loader-utils": "^2.0.0",
"lodash": "^4.17.21",
"pathe": "^0.2.0",
"webpack-virtual-modules": "^0.4.3",
"windicss": "^3.5.4"
"windicss": "^3.5.6"
}
},
"node_modules/windicss-webpack-plugin/node_modules/pathe": {
@ -17300,9 +17272,9 @@
}
},
"@windicss/config": {
"version": "1.8.6",
"resolved": "https://registry.npmjs.org/@windicss/config/-/config-1.8.6.tgz",
"integrity": "sha512-WVS41qUJtd44g2iWzTAE8tpgk8gD0yAr1RwwaWi7FAECKm3LVNMLOoToum9R/QKFE2n64EUVJpIvSUNby8rlhg==",
"version": "1.8.7",
"resolved": "https://registry.npmjs.org/@windicss/config/-/config-1.8.7.tgz",
"integrity": "sha512-8n+/Y36j5L3rw2tgMdLjeGRuNV7VYfKoHoraLK6Bk9OJ1MTPd5vv7pekof/uOPWVV7WWjVeZ6CTO8SDbDDW3iw==",
"dev": true,
"requires": {
"debug": "^4.3.4",
@ -17329,13 +17301,13 @@
"dev": true
},
"@windicss/plugin-utils": {
"version": "1.8.6",
"resolved": "https://registry.npmjs.org/@windicss/plugin-utils/-/plugin-utils-1.8.6.tgz",
"integrity": "sha512-YY6EcUsgkosaQkIseFiIoHfU1H5boOAs/l74QWLI6ryNeHLMq2e04QVsFz+Rt+U8b8PRNxXPC8ADbxE05X7I7g==",
"version": "1.8.7",
"resolved": "https://registry.npmjs.org/@windicss/plugin-utils/-/plugin-utils-1.8.7.tgz",
"integrity": "sha512-dfj95olNZyGFDPFMBvE5oq8hA5f0ooUJZjVdWlthS4ek4W1/xNOHDxB6ygWR8LE9zCOXZykApjt1LOhy9Ky2QA==",
"dev": true,
"requires": {
"@antfu/utils": "^0.5.2",
"@windicss/config": "1.8.6",
"@windicss/config": "1.8.7",
"debug": "^4.3.4",
"fast-glob": "^3.2.11",
"magic-string": "^0.26.2",
@ -20489,15 +20461,15 @@
}
},
"h3": {
"version": "0.7.10",
"resolved": "https://registry.npmjs.org/h3/-/h3-0.7.10.tgz",
"integrity": "sha512-HcUQxlOocG2uf6PQLu3Bxk0KSXcTuxSF6kRclFBDO7y74Ml0wCwTvmoN5zmrzoxbNHecDNUrcGr8qSNGcOqAHQ==",
"version": "0.7.13",
"resolved": "https://registry.npmjs.org/h3/-/h3-0.7.13.tgz",
"integrity": "sha512-3rs+iokAwin4GnToWc+4JeASAYPujojMWicaTgv5WMucED94cFVVGctlk7N6iJfQ7SZ2N5CtmQ2C3L2aDFpd2w==",
"dev": true,
"requires": {
"cookie-es": "^0.5.0",
"destr": "^1.1.1",
"radix3": "^0.1.2",
"ufo": "^0.8.4"
"ufo": "^0.8.5"
}
},
"happy-dom": {
@ -21557,9 +21529,9 @@
}
},
"lilconfig": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.5.tgz",
"integrity": "sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==",
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz",
"integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==",
"dev": true
},
"lines-and-columns": {
@ -22461,53 +22433,26 @@
}
},
"nuxt-windicss": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/nuxt-windicss/-/nuxt-windicss-2.4.2.tgz",
"integrity": "sha512-coHvXGw4CqkRqG47aJqNKbQKMO17ZGXFVyeOKGaRnvsS4FcdfKtYKukpDgJm0sCIG57z8CljfX3nBy71+vu3mg==",
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/nuxt-windicss/-/nuxt-windicss-2.5.0.tgz",
"integrity": "sha512-h5Wh1Pj5OrYvbQmfarOxi6I366xyPxYVlUIZhu5OPGn0uUaoQIOl5ASR1QD3eR/BH3kdpM/r8wyjikDoW7oSbg==",
"dev": true,
"requires": {
"@nuxt/kit": "3.0.0-rc.3",
"@windicss/plugin-utils": "^1.8.4",
"@nuxt/kit": "3.0.0-rc.6",
"@windicss/config": "^1.8.7",
"@windicss/plugin-utils": "^1.8.7",
"consola": "^2.15.3",
"defu": "^6.0.0",
"fs-extra": "^10.1.0",
"h3": "^0.7.8",
"listhen": "^0.2.11",
"pathe": "^0.3.0",
"h3": "^0.7.12",
"listhen": "^0.2.13",
"pathe": "^0.3.2",
"read-cache": "^1.0.0",
"sirv": "^2.0.2",
"vite-plugin-windicss": "^1.8.4",
"windicss": "^3.5.4",
"vite-plugin-windicss": "^1.8.7",
"windicss": "^3.5.6",
"windicss-analysis": "^0.3.5",
"windicss-webpack-plugin": "^1.7.3"
},
"dependencies": {
"@nuxt/kit": {
"version": "3.0.0-rc.3",
"resolved": "https://registry.npmjs.org/@nuxt/kit/-/kit-3.0.0-rc.3.tgz",
"integrity": "sha512-aD993HKXZ76cwxkM2LCwWRQaOI3RjLCg/kj+8ZS6qN4VrpwrZp4xM59YYIYs3/Vze4OG3D4+0gr0u5zdgf8v8g==",
"dev": true,
"requires": {
"@nuxt/schema": "^3.0.0-rc.3",
"c12": "^0.2.7",
"consola": "^2.15.3",
"defu": "^6.0.0",
"globby": "^13.1.1",
"hash-sum": "^2.0.0",
"ignore": "^5.2.0",
"jiti": "^1.13.0",
"knitwork": "^0.1.1",
"lodash.template": "^4.5.0",
"mlly": "^0.5.2",
"pathe": "^0.3.0",
"pkg-types": "^0.3.2",
"scule": "^0.2.1",
"semver": "^7.3.7",
"unctx": "^1.1.4",
"unimport": "^0.1.9",
"untyped": "^0.4.4"
}
}
"windicss-webpack-plugin": "^1.7.5"
}
},
"nwsapi": {
@ -25248,12 +25193,12 @@
}
},
"vite-plugin-windicss": {
"version": "1.8.6",
"resolved": "https://registry.npmjs.org/vite-plugin-windicss/-/vite-plugin-windicss-1.8.6.tgz",
"integrity": "sha512-D4G4qmumgklPiPrq/ZALqq8Mby6krskFVShbmb5c+0VCSsLUN96qyFRTwi81rNIHwFvlbpqflgh+BpUM/9VjQg==",
"version": "1.8.7",
"resolved": "https://registry.npmjs.org/vite-plugin-windicss/-/vite-plugin-windicss-1.8.7.tgz",
"integrity": "sha512-/zwQ8+RV+MSkbG0IGqsEma6r2R01NzN/aNpNjJD7VVAkxAptNznqDXOObFTskkWfZ+9m6KJZCOuCPgAFtQIzEA==",
"dev": true,
"requires": {
"@windicss/plugin-utils": "1.8.6",
"@windicss/plugin-utils": "1.8.7",
"debug": "^4.3.4",
"kolorist": "^1.5.1",
"windicss": "^3.5.6"
@ -25656,19 +25601,19 @@
}
},
"windicss-webpack-plugin": {
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/windicss-webpack-plugin/-/windicss-webpack-plugin-1.7.3.tgz",
"integrity": "sha512-vESTMEUqzNlvOmnOCCxeqDkj1q2u69FfKMQHyTF9Y9tLJbWttEHgt+qSJZ3cYndUHRxbz06INanEyL08dQr79A==",
"version": "1.7.5",
"resolved": "https://registry.npmjs.org/windicss-webpack-plugin/-/windicss-webpack-plugin-1.7.5.tgz",
"integrity": "sha512-+DwZwFcjgYrD/UU1UEs1TOTo4ijAwRkXEgJMttPd05NSC3ULvguvNKL5kNxrCTYs4OMJn68qbCfkjpI0mir4cQ==",
"dev": true,
"requires": {
"@windicss/plugin-utils": "^1.8.4",
"@windicss/plugin-utils": "^1.8.7",
"debug": "^4.3.4",
"get-port": "^6.1.2",
"loader-utils": "^2.0.0",
"lodash": "^4.17.21",
"pathe": "^0.2.0",
"webpack-virtual-modules": "^0.4.3",
"windicss": "^3.5.4"
"windicss": "^3.5.6"
},
"dependencies": {
"pathe": {

2
packages/nc-gui-v2/package.json

@ -61,7 +61,7 @@
"happy-dom": "^6.0.3",
"less": "^4.1.3",
"nuxt": "3.0.0-rc.4",
"nuxt-windicss": "^2.4.2",
"nuxt-windicss": "^2.5.0",
"prettier": "^2.7.1",
"sass": "^1.53.0",
"unplugin-icons": "^0.14.7",

34
packages/nc-gui-v2/pages/index/index.vue

@ -3,7 +3,7 @@ import { Modal } from 'ant-design-vue'
import type { ProjectType } from 'nocodb-sdk'
import { useToast } from 'vue-toastification'
import { navigateTo } from '#app'
import { computed, onMounted } from '#imports'
import { computed, onMounted, ref, useApi, useNuxtApp, useSidebar } from '#imports'
import { extractSdkResponseErrorMsg } from '~/utils'
import MdiDeleteOutline from '~icons/mdi/delete-outline'
import MdiEditOutline from '~icons/mdi/edit-outline'
@ -12,30 +12,31 @@ import MdiMenuDown from '~icons/mdi/menu-down'
import MdiPlus from '~icons/mdi/plus'
import MdiDatabaseOutline from '~icons/mdi/database-outline'
const { $api, $state, $e } = useNuxtApp()
const { $e } = useNuxtApp()
const { api, isLoading } = useApi()
useSidebar({ hasSidebar: false })
const toast = useToast()
const filterQuery = ref('')
const loading = ref(true)
const projects = ref<ProjectType[]>()
const loadProjects = async () => {
loading.value = true
const response = await $api.project.list({})
const response = await api.project.list({})
projects.value = response.list
loading.value = false
}
const filteredProjects = computed(() => {
return (
const filteredProjects = computed(
() =>
projects.value?.filter(
(project) => !filterQuery.value || project.title?.toLowerCase?.().includes(filterQuery.value.toLowerCase()),
) ?? []
) ?? [],
)
})
const deleteProject = (project: ProjectType) => {
$e('c:project:delete')
Modal.confirm({
title: `Do you want to delete '${project.title}' project?`,
okText: 'Yes',
@ -45,9 +46,9 @@ const deleteProject = (project: ProjectType) => {
try {
$e('c:project:delete')
await $api.project.delete(project.id as string)
projects.value?.splice(projects.value.indexOf(project), 1)
} catch (e) {
toast.error(await extractSdkResponseErrorMsg(e))
return projects.value?.splice(projects.value.indexOf(project), 1)
} catch (e: any) {
return toast.error(await extractSdkResponseErrorMsg(e))
}
},
})
@ -56,9 +57,6 @@ const deleteProject = (project: ProjectType) => {
onMounted(() => {
loadProjects()
})
// hide sidebar
$state.sidebarOpen.value = false
</script>
<template>
@ -114,7 +112,7 @@ $state.sidebarOpen.value = false
</a-dropdown>
</div>
<div v-if="loading">
<div v-if="isLoading">
<a-skeleton />
</div>

14
packages/nc-gui-v2/pages/index/user/index/index.vue

@ -1,13 +1,14 @@
<script lang="ts" setup>
import { useI18n } from 'vue-i18n'
import { message } from 'ant-design-vue'
import { extractSdkResponseErrorMsg } from '~/utils/errorUtils'
import { useNuxtApp } from '#app'
import { reactive, ref } from '#imports'
import { extractSdkResponseErrorMsg } from '~/utils'
import { reactive, ref, useNuxtApp, useSidebar } from '#imports'
import MaterialSymbolsWarning from '~icons/material-symbols/warning'
import MdiKeyChange from '~icons/mdi/key-change'
const { $api, $state } = useNuxtApp()
const { $api } = useNuxtApp()
const { isOpen } = useSidebar()
const { t } = useI18n()
@ -71,10 +72,7 @@ const resetError = () => {
<template>
<a-form ref="formValidator" layout="vertical" :model="form" class="change-password h-full w-full" @finish="passwordChange">
<div
class="md:relative flex flex-col gap-2 w-full h-full p-8 lg:(max-w-1/2)"
:class="{ 'mx-auto': !$state.sidebarOpen.value }"
>
<div class="md:relative flex flex-col gap-2 w-full h-full p-8 lg:(max-w-1/2)" :class="{ 'mx-auto': isOpen }">
<h1 class="prose-2xl font-bold mb-4">{{ $t('activity.changePwd') }}</h1>
<Transition name="layout">

22
packages/nc-gui-v2/pages/nc/[projectId]/index.vue

@ -1,11 +1,14 @@
<script setup lang="ts">
import { useTabs } from '#imports'
import { useProject, useRoute, useSidebar, useTabs } from '#imports'
import { TabType } from '~/composables'
const route = useRoute()
const { loadProject, loadTables } = useProject(route.params.projectId as string)
const { addTab, clearTabs } = useTabs()
const { $state } = useNuxtApp()
useSidebar({ isOpen: true })
clearTabs()
if (!route.params.type) {
@ -13,16 +16,21 @@ if (!route.params.type) {
}
await loadProject(route.params.projectId as string)
await loadTables()
$state.sidebarOpen.value = true
await loadTables()
</script>
<template>
<NuxtLayout>
<template #sidebar>
<NuxtLayout id="sidebar-right" class="flex">
<a-layout-sider
width="250"
collapsed-width="0"
class="bg-white dark:!bg-gray-800 border-r-1 border-gray-200 dark:!border-gray-600 h-full"
:trigger="null"
collapsible
>
<DashboardTreeView />
</template>
</a-layout-sider>
<NuxtPage />
</NuxtLayout>

11
packages/nc-gui-v2/pages/nc/[projectId]/index/index.vue

@ -34,8 +34,8 @@ function openQuickImportDialog(type: string) {
</script>
<template>
<div class="flex w-full h-full">
<div class="nc-container flex flex-col">
<div class="nc-container pt-[9px]">
<div class="h-full w-full flex flex-col">
<div>
<a-tabs v-model:activeKey="activeTabIndex" type="editable-card" @edit="closeTab">
<a-tab-pane v-for="(tab, i) in tabs" :key="i" :tab="tab.title" />
@ -129,22 +129,17 @@ function openQuickImportDialog(type: string) {
</a-tabs>
</div>
<div class="flex-1 min-h-0">
<NuxtPage />
<NuxtPage class="p-2" />
</div>
<DlgTableCreate v-if="tableCreateDialog" v-model="tableCreateDialog" />
<DlgQuickImport v-if="quickImportDialog" v-model="quickImportDialog" :import-type="importType" />
<DlgAirtableImport v-if="airtableImportDialog" v-model="airtableImportDialog" />
</div>
<div id="sidebar-right" class="h-full" />
</div>
</template>
<style scoped>
.nc-container {
height: calc(100vh - var(--header-height) - 8px);
@apply overflow-hidden;
flex: 1 1 100%;
}

18
packages/nc-gui-v2/pages/project/index/[id].vue

@ -8,11 +8,14 @@ import { navigateTo, useNuxtApp, useRoute } from '#app'
import { extractSdkResponseErrorMsg } from '~/utils/errorUtils'
import { projectTitleValidator } from '~/utils/validation'
import MaterialSymbolsRocketLaunchOutline from '~icons/material-symbols/rocket-launch-outline'
import { nextTick, reactive, useSidebar } from '#imports'
const loading = ref(false)
const { api, isLoading } = useApi()
useSidebar({ hasSidebar: false })
const { $api, $state } = useNuxtApp()
const toast = useToast()
const route = useRoute()
const nameValidationRules = [
@ -35,27 +38,24 @@ const getProject = async () => {
toast.error(await extractSdkResponseErrorMsg(e))
}
}
const renameProject = async () => {
loading.value = true
try {
await $api.project.update(route.params.id as string, formState)
await api.project.update(route.params.id as string, formState)
navigateTo(`/nc/${route.params.id}`)
} catch (e: any) {
toast.error(await extractSdkResponseErrorMsg(e))
}
loading.value = false
}
const form = ref<typeof Form>()
// hide sidebar
$state.sidebarOpen.value = false
// select and focus title field on load
onMounted(async () => {
await getProject()
nextTick(() => {
await nextTick(() => {
// todo: replace setTimeout and follow better approach
setTimeout(() => {
const input = form.value?.$el?.querySelector('input[type=text]')

13
packages/nc-gui-v2/pages/project/index/create-external.vue

@ -3,7 +3,7 @@ import { onMounted } from '@vue/runtime-core'
import { Form, Modal } from 'ant-design-vue'
import { useI18n } from 'vue-i18n'
import { useToast } from 'vue-toastification'
import { ref } from '#imports'
import { computed, ref, useSidebar, watch } from '#imports'
import { navigateTo, useNuxtApp } from '#app'
import { ClientType } from '~/lib'
import type { ProjectCreateForm } from '~/utils'
@ -23,8 +23,12 @@ const useForm = Form.useForm
const loading = ref(false)
const testSuccess = ref(false)
const { $api, $e, $state } = useNuxtApp()
const { $api, $e } = useNuxtApp()
useSidebar({ hasSidebar: false })
const toast = useToast()
const { t } = useI18n()
const formState = $ref<ProjectCreateForm>({
@ -66,7 +70,7 @@ const validators = computed(() => {
}
})
const { resetFields, validate, validateInfos } = useForm(formState, validators)
const { validate, validateInfos } = useForm(formState, validators)
const onClientChange = () => {
formState.dataSource = { ...getDefaultConnectionConfig(formState.dataSource.client) }
@ -192,9 +196,6 @@ const testConnection = async () => {
}
}
// hide sidebar
$state.sidebarOpen.value = false
// reset test status on config change
watch(
() => formState.dataSource,

22
packages/nc-gui-v2/pages/project/index/create.vue

@ -2,17 +2,22 @@
import { onMounted, onUpdated } from '@vue/runtime-core'
import type { Form } from 'ant-design-vue'
import { useToast } from 'vue-toastification'
import { nextTick, ref } from '#imports'
import { nextTick, reactive, ref, useApi, useSidebar } from '#imports'
import { navigateTo, useNuxtApp } from '#app'
import { extractSdkResponseErrorMsg } from '~/utils/errorUtils'
import { projectTitleValidator } from '~/utils/validation'
import MaterialSymbolsRocketLaunchOutline from '~icons/material-symbols/rocket-launch-outline'
const name = ref('')
const loading = ref(false)
const valid = ref(false)
const { $api, $state, $e } = useNuxtApp()
const { $e } = useNuxtApp()
const { api, isLoading } = useApi()
useSidebar({ hasSidebar: false })
const toast = useToast()
const nameValidationRules = [
@ -29,9 +34,8 @@ const formState = reactive({
const createProject = async () => {
$e('a:project:create:xcdb')
loading.value = true
try {
const result = await $api.project.create({
const result = await api.project.create({
title: formState.title,
})
@ -39,17 +43,13 @@ const createProject = async () => {
} catch (e: any) {
toast.error(await extractSdkResponseErrorMsg(e))
}
loading.value = false
}
const form = ref<typeof Form>()
// hide sidebar
$state.sidebarOpen.value = false
// select and focus title field on load
onMounted(async () => {
nextTick(() => {
await nextTick(() => {
// todo: replace setTimeout and follow better approach
setTimeout(() => {
const input = form.value?.$el?.querySelector('input[type=text]')
@ -61,7 +61,7 @@ onMounted(async () => {
</script>
<template>
<a-card class="w-[500px] mx-auto !mt-100px shadow-md">
<a-card :loading="isLoading" class="w-[500px] mx-auto !mt-100px shadow-md">
<h3 class="text-3xl text-center font-semibold mb-2">{{ $t('activity.createProject') }}</h3>
<a-form ref="form" :model="formState" name="basic" layout="vertical" autocomplete="off" @finish="createProject">

8
packages/nc-gui-v2/plugins/state.ts

@ -1,6 +1,6 @@
import { breakpointsTailwind } from '@vueuse/core'
import { defineNuxtPlugin } from '#app'
import { useBreakpoints, useDark, useGlobal, watch } from '#imports'
import { useBreakpoints, useDark, useGlobal, useSidebar, watch } from '#imports'
/**
* Initialize global state and watches for changes
@ -19,9 +19,6 @@ export default defineNuxtPlugin((nuxtApp) => {
const darkMode = useDark()
/** get current breakpoints (for enabling sidebar) */
const breakpoints = useBreakpoints(breakpointsTailwind)
/** set i18n locale to stored language */
nuxtApp.vueApp.i18n.locale.value = state.lang.value
@ -33,7 +30,4 @@ export default defineNuxtPlugin((nuxtApp) => {
},
{ immediate: true },
)
/** is initial sidebar open */
state.sidebarOpen.value = state.signedIn.value && breakpoints.greater('md').value
})

Loading…
Cancel
Save