Browse Source

Merge branch 'develop' into fix/gui-v2-pagination

pull/3393/head
Wing-Kam Wong 2 years ago
parent
commit
1788525c48
  1. 16
      .github/workflows/ci-cd-v2.yml
  2. 3
      packages/nc-gui-v2/components/template/Editor.vue
  3. 19
      packages/nc-gui-v2/composables/useInjectionState/index.ts
  4. 93
      packages/nc-gui-v2/composables/useProject.ts
  5. 2
      packages/nc-gui-v2/composables/useTheme/index.ts
  6. 10
      packages/nc-gui-v2/pages/[projectType]/[projectId]/index.vue
  7. 48
      packages/nc-gui-v2/pages/index/index/[projectId].vue
  8. 104
      packages/nc-gui-v2/pages/index/index/index.vue

16
.github/workflows/ci-cd-v2.yml

@ -52,7 +52,7 @@ jobs:
npm run start:web-v2 npm run start:web-v2
docker-compose -f ./scripts/docker-compose-cypress.yml up -d docker-compose -f ./scripts/docker-compose-cypress.yml up -d
spec: "./scripts/cypress-v2/integration/test/restTableOps.js" spec: "./scripts/cypress-v2/integration/test/restTableOps.js"
wait-on: "http://localhost:8080" wait-on: "http://localhost:8080, http://localhost:3000/_nuxt/entry.mjs"
wait-on-timeout: 1200 wait-on-timeout: 1200
config-file: scripts/cypress-v2/cypress.json config-file: scripts/cypress-v2/cypress.json
- name: Upload screenshots - name: Upload screenshots
@ -96,7 +96,7 @@ jobs:
npm run start:web-v2 npm run start:web-v2
docker-compose -f ./scripts/docker-compose-cypress.yml up -d docker-compose -f ./scripts/docker-compose-cypress.yml up -d
spec: "./scripts/cypress-v2/integration/test/restViews.js" spec: "./scripts/cypress-v2/integration/test/restViews.js"
wait-on: "http://localhost:8080" wait-on: "http://localhost:8080, http://localhost:3000/_nuxt/entry.mjs"
wait-on-timeout: 1200 wait-on-timeout: 1200
config-file: scripts/cypress-v2/cypress.json config-file: scripts/cypress-v2/cypress.json
- name: Upload screenshots - name: Upload screenshots
@ -140,7 +140,7 @@ jobs:
npm run start:web-v2 npm run start:web-v2
docker-compose -f ./scripts/docker-compose-cypress.yml up -d docker-compose -f ./scripts/docker-compose-cypress.yml up -d
spec: "./scripts/cypress-v2/integration/test/restRoles.js" spec: "./scripts/cypress-v2/integration/test/restRoles.js"
wait-on: "http://localhost:8080" wait-on: "http://localhost:8080, http://localhost:3000/_nuxt/entry.mjs"
wait-on-timeout: 1200 wait-on-timeout: 1200
config-file: scripts/cypress-v2/cypress.json config-file: scripts/cypress-v2/cypress.json
- name: Upload screenshots - name: Upload screenshots
@ -184,7 +184,7 @@ jobs:
npm run start:web-v2 npm run start:web-v2
docker-compose -f ./scripts/docker-compose-cypress.yml up -d docker-compose -f ./scripts/docker-compose-cypress.yml up -d
spec: "./scripts/cypress-v2/integration/test/restMisc.js" spec: "./scripts/cypress-v2/integration/test/restMisc.js"
wait-on: "http://localhost:8080" wait-on: "http://localhost:8080, http://localhost:3000/_nuxt/entry.mjs"
wait-on-timeout: 1200 wait-on-timeout: 1200
config-file: scripts/cypress-v2/cypress.json config-file: scripts/cypress-v2/cypress.json
- name: Upload screenshots - name: Upload screenshots
@ -360,7 +360,7 @@ jobs:
npm run start:web-v2 npm run start:web-v2
docker-compose -f ./scripts/docker-compose-cypress.yml up -d docker-compose -f ./scripts/docker-compose-cypress.yml up -d
spec: "./scripts/cypress-v2/integration/test/xcdb-restMisc.js" spec: "./scripts/cypress-v2/integration/test/xcdb-restMisc.js"
wait-on: "http://localhost:8080" wait-on: "http://localhost:8080, http://localhost:3000/_nuxt/entry.mjs"
wait-on-timeout: 1200 wait-on-timeout: 1200
config-file: scripts/cypress-v2/cypress.json config-file: scripts/cypress-v2/cypress.json
- name: Upload screenshots - name: Upload screenshots
@ -625,7 +625,7 @@ jobs:
npm run start:web-v2 npm run start:web-v2
docker-compose -f ./scripts/cypress-v2/docker-compose-pg.yml up -d docker-compose -f ./scripts/cypress-v2/docker-compose-pg.yml up -d
spec: "./scripts/cypress-v2/integration/test/pg-restMisc.js" spec: "./scripts/cypress-v2/integration/test/pg-restMisc.js"
wait-on: "http://localhost:8080" wait-on: "http://localhost:8080, http://localhost:3000/_nuxt/entry.mjs"
wait-on-timeout: 1200 wait-on-timeout: 1200
config-file: scripts/cypress-v2/cypress.json config-file: scripts/cypress-v2/cypress.json
- name: Upload screenshots - name: Upload screenshots
@ -670,7 +670,7 @@ jobs:
npm run start:web-v2 npm run start:web-v2
docker-compose -f ./scripts/docker-compose-cypress.yml up -d docker-compose -f ./scripts/docker-compose-cypress.yml up -d
spec: "./scripts/cypress-v2/integration/test/quickTest.js" spec: "./scripts/cypress-v2/integration/test/quickTest.js"
wait-on: "http://localhost:8080" wait-on: "http://localhost:8080, http://localhost:3000/_nuxt/entry.mjs"
wait-on-timeout: 1200 wait-on-timeout: 1200
config-file: scripts/cypress-v2/cypress.json config-file: scripts/cypress-v2/cypress.json
- name: Upload screenshots - name: Upload screenshots
@ -714,7 +714,7 @@ jobs:
npm run start:api:cache:pg:cyquick npm run start:api:cache:pg:cyquick
npm run start:web-v2 npm run start:web-v2
spec: "./scripts/cypress-v2/integration/test/quickTest.js" spec: "./scripts/cypress-v2/integration/test/quickTest.js"
wait-on: "http://localhost:8080" wait-on: "http://localhost:8080, http://localhost:3000/_nuxt/entry.mjs"
wait-on-timeout: 1200 wait-on-timeout: 1200
config-file: scripts/cypress-v2/cypress.json config-file: scripts/cypress-v2/cypress.json
- name: Upload screenshots - name: Upload screenshots

3
packages/nc-gui-v2/components/template/Editor.vue

@ -332,7 +332,8 @@ async function importTemplate() {
const tableName = meta.value.title const tableName = meta.value.title
const data = importData[tableName] // only one file is allowed currently
const data = importData[Object.keys(importData)[0]]
const projectName = project.value.title! const projectName = project.value.title!

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

@ -1,4 +1,5 @@
import type { InjectionKey } from 'vue' import type { InjectionKey } from 'vue'
import { inject, provide, tryOnScopeDispose } from '#imports'
export function useInjectionState<Arguments extends any[], Return>( export function useInjectionState<Arguments extends any[], Return>(
composable: (...args: Arguments) => Return, composable: (...args: Arguments) => Return,
@ -6,15 +7,31 @@ export function useInjectionState<Arguments extends any[], Return>(
): readonly [useInjectionState: (...args: Arguments) => Return, useInjectedState: () => Return | undefined] { ): readonly [useInjectionState: (...args: Arguments) => Return, useInjectedState: () => Return | undefined] {
const key: string | InjectionKey<Return> = Symbol(keyName) const key: string | InjectionKey<Return> = Symbol(keyName)
let providableState: Return | undefined
const useProvidingState = (...args: Arguments) => { const useProvidingState = (...args: Arguments) => {
const providedState = composable(...args) const providedState = composable(...args)
provide(key, providedState) provide(key, providedState)
providableState = providedState
return providedState return providedState
} }
const useInjectedState = () => inject(key, undefined) const useInjectedState = () => {
let injection = inject(key, undefined)
if (typeof injection === 'undefined') {
injection = providableState
}
return injection
}
tryOnScopeDispose(() => {
providableState = undefined
})
return [useProvidingState, useInjectedState] return [useProvidingState, useInjectedState]
} }

93
packages/nc-gui-v2/composables/useProject.ts

@ -1,34 +1,25 @@
import type { MaybeRef } from '@vueuse/core'
import { SqlUiFactory } from 'nocodb-sdk' import { SqlUiFactory } from 'nocodb-sdk'
import type { OracleUi, ProjectType, TableType } from 'nocodb-sdk' import type { OracleUi, ProjectType, TableType } from 'nocodb-sdk'
import type { MaybeRef } from '@vueuse/core' import { useNuxtApp, useRoute } from '#app'
import { useNuxtApp, useRoute, useState } from '#app'
import type { ProjectMetaInfo } from '~/lib' import type { ProjectMetaInfo } from '~/lib'
import { USER_PROJECT_ROLES } from '~/lib'
import type { ThemeConfig } from '@/composables/useTheme' import type { ThemeConfig } from '@/composables/useTheme'
import { useInjectionState } from '#imports'
export function useProject(projectId?: MaybeRef<string>) { const [setup, use] = useInjectionState((_projectId?: MaybeRef<string>) => {
const projectRoles = useState<Record<string, boolean>>(USER_PROJECT_ROLES, () => ({}))
const { $api } = useNuxtApp() const { $api } = useNuxtApp()
let _projectId = $ref('')
const project = useState<ProjectType>('project')
const tables = useState<TableType[]>('tables', () => [] as TableType[])
const route = useRoute() const route = useRoute()
const { includeM2M } = useGlobal() const { includeM2M } = useGlobal()
const { setTheme } = useTheme() const { setTheme } = useTheme()
const projectMetaInfo = useState<ProjectMetaInfo | undefined>('projectMetaInfo')
const projectId = computed(() => (_projectId ? unref(_projectId) : (route.params.projectId as string)))
const project = ref<ProjectType>({})
const tables = ref<TableType[]>([])
const projectRoles = ref<Record<string, boolean>>({})
const projectMetaInfo = ref<ProjectMetaInfo | undefined>()
// todo: refactor path param name and variable name // todo: refactor path param name and variable name
const projectType = $computed(() => route.params.projectType as string) const projectType = $computed(() => route.params.projectType as string)
const isLoaded = ref(false)
const projectBaseType = $computed(() => project.value?.bases?.[0]?.type || '')
const isMysql = computed(() => ['mysql', 'mysql2'].includes(projectBaseType))
const isMssql = computed(() => projectBaseType === 'mssql')
const isPg = computed(() => projectBaseType === 'pg')
const sqlUi = computed(
() => SqlUiFactory.create({ client: projectBaseType }) as Exclude<ReturnType<typeof SqlUiFactory['create']>, typeof OracleUi>,
)
const isSharedBase = computed(() => projectType === 'base')
const projectMeta = computed(() => { const projectMeta = computed(() => {
try { try {
@ -38,6 +29,17 @@ export function useProject(projectId?: MaybeRef<string>) {
} }
}) })
const projectBaseType = $computed(() => project.value?.bases?.[0]?.type || '')
const sqlUi = computed(
() => SqlUiFactory.create({ client: projectBaseType }) as Exclude<ReturnType<typeof SqlUiFactory['create']>, typeof OracleUi>,
)
const isMysql = computed(() => ['mysql', 'mysql2'].includes(projectBaseType))
const isMssql = computed(() => projectBaseType === 'mssql')
const isPg = computed(() => projectBaseType === 'pg')
const isSharedBase = computed(() => projectType === 'base')
async function loadProjectMetaInfo(force?: boolean) { async function loadProjectMetaInfo(force?: boolean) {
if (!projectMetaInfo.value || force) { if (!projectMetaInfo.value || force) {
const data = await $api.project.metaGet(project.value.id!, {}, {}) const data = await $api.project.metaGet(project.value.id!, {}, {})
@ -74,51 +76,54 @@ export function useProject(projectId?: MaybeRef<string>) {
} }
async function loadProject() { async function loadProject() {
if (unref(projectId)) { if (projectType === 'base') {
_projectId = unref(projectId)!
} else if (projectType === 'base') {
const baseData = await $api.public.sharedBaseGet(route.params.projectId as string) const baseData = await $api.public.sharedBaseGet(route.params.projectId as string)
_projectId = baseData.project_id! project.value = await $api.project.read(baseData.project_id!)
} else { } else {
_projectId = route.params.projectId as string project.value = await $api.project.read(projectId.value)
} }
isLoaded.value = true
project.value = await $api.project.read(_projectId!)
await loadProjectRoles() await loadProjectRoles()
await loadTables() await loadTables()
setTheme(projectMeta.value?.theme) setTheme(projectMeta.value?.theme)
} }
async function updateProject(data: Partial<ProjectType>) { async function updateProject(data: Partial<ProjectType>) {
if (unref(projectId)) { if (projectType === 'base') {
_projectId = unref(projectId)!
} else if (projectType === 'base') {
return return
}
if (data.meta && typeof data.meta === 'string') {
await $api.project.update(projectId.value, data)
} else { } else {
_projectId = route.params.projectId as string await $api.project.update(projectId.value, { ...data, meta: JSON.stringify(data.meta) })
} }
await $api.project.update(_projectId, data)
} }
async function saveTheme(theme: Partial<ThemeConfig>) { async function saveTheme(theme: Partial<ThemeConfig>) {
await updateProject({ await updateProject({
meta: JSON.stringify({ color: theme.primaryColor,
meta: {
...projectMeta.value, ...projectMeta.value,
theme, theme,
}), },
}) })
setTheme(theme) setTheme(theme)
} }
watch(
() => route.params,
(v) => {
if (!v?.projectId) {
setTheme()
}
},
)
// TODO useProject should only called inside a project for now this doesn't work
onScopeDispose(() => { onScopeDispose(() => {
if (isLoaded.value === true) {
project.value = {} project.value = {}
tables.value = [] tables.value = []
projectMetaInfo.value = undefined projectMetaInfo.value = undefined
projectRoles.value = {} projectRoles.value = {}
setTheme({})
}
}) })
return { return {
@ -138,4 +143,16 @@ export function useProject(projectId?: MaybeRef<string>) {
projectMeta, projectMeta,
saveTheme, saveTheme,
} }
}, 'useProject')
export const provideProject = setup
export function useProject(projectId?: MaybeRef<string>) {
const state = use()
if (!state) {
return setup(projectId)
}
return state
} }

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

@ -22,7 +22,7 @@ const [setup, use] = useInjectionState((config?: Partial<ThemeConfig>) => {
setTheme(config ?? currentTheme.value) setTheme(config ?? currentTheme.value)
/** set theme (persists in localstorage) */ /** set theme (persists in localstorage) */
function setTheme(theme: Partial<ThemeConfig>) { function setTheme(theme?: Partial<ThemeConfig>) {
const themePrimary = theme?.primaryColor ? tinycolor(theme.primaryColor) : tinycolor(themeV2Colors['royal-blue'].DEFAULT) const themePrimary = theme?.primaryColor ? tinycolor(theme.primaryColor) : tinycolor(themeV2Colors['royal-blue'].DEFAULT)
const themeAccent = theme?.accentColor ? tinycolor(theme.accentColor) : tinycolor(themeV2Colors.pink['500']) const themeAccent = theme?.accentColor ? tinycolor(theme.accentColor) : tinycolor(themeV2Colors.pink['500'])

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

@ -87,21 +87,21 @@ const themePrimaryColor = ref<any>(theme.value.primaryColor)
const themeAccentColor = ref<any>(theme.value.accentColor) const themeAccentColor = ref<any>(theme.value.accentColor)
// Chrome provides object so if custom picker used we only edit primary otherwise use analogous as accent // Chrome provides object so if custom picker used we only edit primary otherwise use complement as accent
watch(themePrimaryColor, (nextColor) => { watch(themePrimaryColor, (nextColor) => {
const hexColor = nextColor.hex ? nextColor.hex : nextColor const hexColor = nextColor.hex8 ? nextColor.hex8 : nextColor
const tcolor = tinycolor(hexColor) const tcolor = tinycolor(hexColor)
if (tcolor) { if (tcolor) {
const analogous = tcolor.complement() const complement = tcolor.complement()
saveTheme({ saveTheme({
primaryColor: hexColor, primaryColor: hexColor,
accentColor: nextColor.hex ? theme.value.accentColor : analogous.toHexString(), accentColor: nextColor.hex8 ? theme.value.accentColor : complement.toHex8String(),
}) })
} }
}) })
watch(themeAccentColor, (nextColor) => { watch(themeAccentColor, (nextColor) => {
const hexColor = nextColor.hex ? nextColor.hex : nextColor const hexColor = nextColor.hex8 ? nextColor.hex8 : nextColor
saveTheme({ saveTheme({
primaryColor: theme.value.primaryColor, primaryColor: theme.value.primaryColor,
accentColor: hexColor, accentColor: hexColor,

48
packages/nc-gui-v2/pages/index/index/[id].vue → packages/nc-gui-v2/pages/index/index/[projectId].vue

@ -2,7 +2,6 @@
import type { Form } from 'ant-design-vue' import type { Form } from 'ant-design-vue'
import { message } from 'ant-design-vue' import { message } from 'ant-design-vue'
import type { ProjectType } from 'nocodb-sdk' import type { ProjectType } from 'nocodb-sdk'
import tinycolor from 'tinycolor2'
import { import {
extractSdkResponseErrorMsg, extractSdkResponseErrorMsg,
navigateTo, navigateTo,
@ -22,7 +21,7 @@ useSidebar({ hasSidebar: false })
const route = useRoute() const route = useRoute()
const { project, loadProject, updateProject } = useProject(route.params.id as string) const { project, loadProject, updateProject } = useProject(route.params.projectId as string)
await loadProject() await loadProject()
@ -38,15 +37,13 @@ const form = ref<typeof Form>()
const formState = reactive<Partial<ProjectType>>({ const formState = reactive<Partial<ProjectType>>({
title: '', title: '',
color: '#FFFFFF00',
}) })
const renameProject = async () => { const renameProject = async () => {
formState.color = formState.color === '#FFFFFF00' ? '' : formState.color
try { try {
await updateProject(formState) await updateProject(formState)
navigateTo(`/nc/${route.params.id}`) navigateTo(`/nc/${route.params.projectId}`)
} catch (e: any) { } catch (e: any) {
message.error(await extractSdkResponseErrorMsg(e)) message.error(await extractSdkResponseErrorMsg(e))
} }
@ -55,7 +52,6 @@ const renameProject = async () => {
// select and focus title field on load // select and focus title field on load
onMounted(async () => { onMounted(async () => {
formState.title = project.value.title as string formState.title = project.value.title as string
formState.color = project.value.color && tinycolor(project.value.color).isValid() ? project.value.color : '#FFFFFF00'
await nextTick(() => { await nextTick(() => {
// todo: replace setTimeout and follow better approach // todo: replace setTimeout and follow better approach
setTimeout(() => { setTimeout(() => {
@ -98,27 +94,6 @@ onMounted(async () => {
<a-input v-model:value="formState.title" name="title" class="nc-metadb-project-name" /> <a-input v-model:value="formState.title" name="title" class="nc-metadb-project-name" />
</a-form-item> </a-form-item>
<div class="flex items-center">
<span>Project color: </span>
<a-menu class="!border-0 !m-0 !p-0">
<a-sub-menu key="project-color">
<template #title>
<button type="button" class="color-selector" :style="{ 'background-color': formState.color }">
<MdiNull v-if="formState.color === '#FFFFFF00'" />
</button>
</template>
<template #expandIcon></template>
<GeneralColorPicker v-model="formState.color" name="color" class="nc-metadb-project-color" />
</a-sub-menu>
</a-menu>
<MdiClose
v-show="formState.color !== '#FFFFFF00'"
class="cursor-pointer"
:style="{ color: 'red' }"
@click="formState.color = '#FFFFFF00'"
/>
</div>
<div class="text-center"> <div class="text-center">
<button type="submit" class="submit"> <button type="submit" class="submit">
<span class="flex items-center gap-2"> <span class="flex items-center gap-2">
@ -156,23 +131,4 @@ onMounted(async () => {
} }
} }
} }
:deep(.ant-menu-submenu-title) {
@apply !p-0 !mx-2;
}
.color-selector {
position: relative;
height: 32px;
width: 32px;
border-radius: 5px;
-webkit-text-stroke-width: 1px;
-webkit-text-stroke-color: white;
@apply flex text-gray-500 border-4 items-center justify-center;
}
.color-selector:hover {
filter: brightness(90%);
-webkit-filter: brightness(90%);
}
</style> </style>

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

@ -1,6 +1,8 @@
<script lang="ts" setup> <script lang="ts" setup>
import { Modal, message } from 'ant-design-vue' import { Modal, message } from 'ant-design-vue'
import type { ProjectType } from 'nocodb-sdk' import type { ProjectType } from 'nocodb-sdk'
import { Chrome } from '@ckpack/vue-color'
import tinycolor from 'tinycolor2'
import { import {
computed, computed,
definePageMeta, definePageMeta,
@ -17,7 +19,7 @@ definePageMeta({
title: 'title.myProject', title: 'title.myProject',
}) })
const { $e } = useNuxtApp() const { $api, $e } = useNuxtApp()
const { api, isLoading } = useApi() const { api, isLoading } = useApi()
@ -64,6 +66,51 @@ const deleteProject = (project: ProjectType) => {
} }
await loadProjects() await loadProjects()
const themePrimaryColors = $ref(
(() => {
const colors: Record<string, any> = {}
for (const project of projects?.value || []) {
if (project?.id) {
try {
const projectMeta = typeof project.meta === 'string' ? JSON.parse(project.meta) : project.meta
colors[project.id] = tinycolor(projectMeta?.theme?.primaryColor).isValid()
? projectMeta?.theme?.primaryColor
: themeV2Colors['royal-blue'].DEFAULT
} catch (e) {
colors[project.id] = themeV2Colors['royal-blue'].DEFAULT
}
}
}
return colors
})(),
)
const oldPrimaryColors = ref({ ...themePrimaryColors })
watch(themePrimaryColors, async (nextColors) => {
for (const [projectId, nextColor] of Object.entries(nextColors)) {
if (oldPrimaryColors.value[projectId] === nextColor) continue
const hexColor = nextColor.hex8 ? nextColor.hex8 : nextColor
const tcolor = tinycolor(hexColor)
if (tcolor) {
const complement = tcolor.complement()
const project: ProjectType = await $api.project.read(projectId)
const meta = project?.meta && typeof project.meta === 'string' ? JSON.parse(project.meta) : project.meta || {}
await $api.project.update(projectId, {
color: hexColor,
meta: JSON.stringify({
...meta,
theme: {
primaryColor: hexColor,
accentColor: complement.toHex8String(),
},
}),
})
}
}
oldPrimaryColors.value = { ...themePrimaryColors }
})
</script> </script>
<template> <template>
@ -159,15 +206,50 @@ await loadProjects()
<!-- Title --> <!-- Title -->
<a-table-column key="title" :title="$t('general.title')" data-index="title"> <a-table-column key="title" :title="$t('general.title')" data-index="title">
<template #default="{ text, record }"> <template #default="{ text, record }">
<div class="flex items-center">
<div @click.stop>
<a-menu class="!border-0 !m-0 !p-0" trigger-sub-menu-action="click">
<template v-if="isUIAllowed('projectTheme')">
<a-sub-menu key="theme" popup-class-name="custom-color">
<template #title>
<div <div
class="capitalize color-transition group-hover:text-primary !w-[400px] h-full overflow-hidden overflow-ellipsis whitespace-nowrap pl-2" class="color-selector"
:class="{ 'border-l-4': record.color }"
:style="{ :style="{
'border-color': record.color, 'background-color': themePrimaryColors[record.id].hex8 || themePrimaryColors[record.id],
'width': '8px',
'height': '100%',
}" }"
/>
</template>
<template #expandIcon></template>
<GeneralColorPicker
v-model="themePrimaryColors[record.id]"
:colors="enumColor.dark"
:row-size="5"
:advanced="false"
/>
<a-sub-menu key="pick-primary">
<template #title>
<div class="nc-project-menu-item group !py-0">
<ClarityColorPickerSolid class="group-hover:text-accent" />
Custom Color
</div>
</template>
<template #expandIcon></template>
<Chrome v-model="themePrimaryColors[record.id]" />
</a-sub-menu>
</a-sub-menu>
</template>
</a-menu>
</div>
<div
class="capitalize color-transition group-hover:text-primary !w-[400px] h-full overflow-hidden overflow-ellipsis whitespace-nowrap pl-2"
> >
{{ text }} {{ text }}
</div> </div>
</div>
</template> </template>
</a-table-column> </a-table-column>
<!-- Actions --> <!-- Actions -->
@ -220,4 +302,18 @@ await loadProjects()
:deep(.ant-table) { :deep(.ant-table) {
@apply min-h-[428px]; @apply min-h-[428px];
} }
:deep(.ant-menu-submenu-title) {
@apply !p-0 !mr-1 !my-0 !h-5;
}
.color-selector:hover {
filter: brightness(1.5);
}
</style>
<style>
.custom-color .ant-menu-submenu-title {
height: auto !important;
}
</style> </style>

Loading…
Cancel
Save