Browse Source

Merge pull request #3249 from nocodb/feat/theme-switcher

pull/3253/head
Braks 2 years ago committed by GitHub
parent
commit
eee13ae701
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 14
      packages/nc-gui-v2/app.vue
  2. 4
      packages/nc-gui-v2/assets/css/color.css
  3. 23
      packages/nc-gui-v2/assets/css/global.css
  4. 19
      packages/nc-gui-v2/assets/style.css
  5. 46
      packages/nc-gui-v2/assets/style.scss
  6. 6
      packages/nc-gui-v2/components.d.ts
  7. 2
      packages/nc-gui-v2/components/cell/Decimal.vue
  8. 2
      packages/nc-gui-v2/components/cell/Float.vue
  9. 2
      packages/nc-gui-v2/components/cell/Integer.vue
  10. 4
      packages/nc-gui-v2/components/cell/attachment/Carousel.vue
  11. 14
      packages/nc-gui-v2/components/cell/attachment/Modal.vue
  12. 12
      packages/nc-gui-v2/components/cell/attachment/index.vue
  13. 45
      packages/nc-gui-v2/components/dashboard/TreeView.vue
  14. 2
      packages/nc-gui-v2/components/dashboard/settings/AppStore.vue
  15. 2
      packages/nc-gui-v2/components/dashboard/settings/Metadata.vue
  16. 2
      packages/nc-gui-v2/components/dashboard/settings/Misc.vue
  17. 4
      packages/nc-gui-v2/components/dashboard/settings/UIAcl.vue
  18. 4
      packages/nc-gui-v2/components/dlg/AirtableImport.vue
  19. 3
      packages/nc-gui-v2/components/general/ColorPicker.vue
  20. 12
      packages/nc-gui-v2/components/general/MiniSidebar.vue
  21. 4
      packages/nc-gui-v2/components/general/PreviewAs.vue
  22. 4
      packages/nc-gui-v2/components/general/ReleaseInfo.vue
  23. 2
      packages/nc-gui-v2/components/general/language/Menu.vue
  24. 14
      packages/nc-gui-v2/components/shared-view/Form.vue
  25. 6
      packages/nc-gui-v2/components/smartsheet-column/EditOrAdd.vue
  26. 2
      packages/nc-gui-v2/components/smartsheet-column/LinkedToAnotherRecordOptions.vue
  27. 4
      packages/nc-gui-v2/components/smartsheet-column/SelectOptions.vue
  28. 6
      packages/nc-gui-v2/components/smartsheet-header/VirtualCellIcon.vue
  29. 8
      packages/nc-gui-v2/components/smartsheet-toolbar/ColumnFilter.vue
  30. 4
      packages/nc-gui-v2/components/smartsheet-toolbar/ColumnFilterMenu.vue
  31. 2
      packages/nc-gui-v2/components/smartsheet-toolbar/FieldListAutoCompleteDropdown.vue
  32. 7
      packages/nc-gui-v2/components/smartsheet-toolbar/FieldsMenu.vue
  33. 2
      packages/nc-gui-v2/components/smartsheet-toolbar/MoreActions.vue
  34. 2
      packages/nc-gui-v2/components/smartsheet-toolbar/SearchData.vue
  35. 8
      packages/nc-gui-v2/components/smartsheet-toolbar/ShareView.vue
  36. 2
      packages/nc-gui-v2/components/smartsheet-toolbar/SharedViewList.vue
  37. 19
      packages/nc-gui-v2/components/smartsheet-toolbar/SortListMenu.vue
  38. 24
      packages/nc-gui-v2/components/smartsheet/Form.vue
  39. 19
      packages/nc-gui-v2/components/smartsheet/Grid.vue
  40. 2
      packages/nc-gui-v2/components/smartsheet/Pagination.vue
  41. 9
      packages/nc-gui-v2/components/smartsheet/expanded-form/Comments.vue
  42. 6
      packages/nc-gui-v2/components/smartsheet/expanded-form/Header.vue
  43. 4
      packages/nc-gui-v2/components/smartsheet/expanded-form/index.vue
  44. 4
      packages/nc-gui-v2/components/smartsheet/sidebar/MenuTop.vue
  45. 2
      packages/nc-gui-v2/components/smartsheet/sidebar/toolbar/AddRow.vue
  46. 4
      packages/nc-gui-v2/components/smartsheet/sidebar/toolbar/LockMenu.vue
  47. 2
      packages/nc-gui-v2/components/smartsheet/sidebar/toolbar/ToggleDrawer.vue
  48. 2
      packages/nc-gui-v2/components/tabs/Auth.vue
  49. 2
      packages/nc-gui-v2/components/tabs/Smartsheet.vue
  50. 10
      packages/nc-gui-v2/components/template/Editor.vue
  51. 6
      packages/nc-gui-v2/components/virtual-cell/BelongsTo.vue
  52. 2
      packages/nc-gui-v2/components/virtual-cell/Formula.vue
  53. 6
      packages/nc-gui-v2/components/virtual-cell/HasMany.vue
  54. 6
      packages/nc-gui-v2/components/virtual-cell/ManyToMany.vue
  55. 4
      packages/nc-gui-v2/components/virtual-cell/components/ItemChip.vue
  56. 10
      packages/nc-gui-v2/components/virtual-cell/components/ListChildItems.vue
  57. 4
      packages/nc-gui-v2/components/virtual-cell/components/ListItems.vue
  58. 2
      packages/nc-gui-v2/components/webhook/Drawer.vue
  59. 4
      packages/nc-gui-v2/components/webhook/List.vue
  60. 1
      packages/nc-gui-v2/composables/index.ts
  61. 60
      packages/nc-gui-v2/composables/useTheme/index.ts
  62. 2
      packages/nc-gui-v2/lang/nl.json
  63. 22
      packages/nc-gui-v2/layouts/base.vue
  64. 1
      packages/nc-gui-v2/nuxt-shim.d.ts
  65. 16
      packages/nc-gui-v2/nuxt.config.ts
  66. 567
      packages/nc-gui-v2/package-lock.json
  67. 6
      packages/nc-gui-v2/package.json
  68. 123
      packages/nc-gui-v2/pages/[projectType]/[projectId]/index.vue
  69. 13
      packages/nc-gui-v2/pages/[projectType]/[projectId]/index/index.vue
  70. 2
      packages/nc-gui-v2/pages/[projectType]/[projectId]/index/index/index.vue
  71. 74
      packages/nc-gui-v2/pages/forgot-password.vue
  72. 19
      packages/nc-gui-v2/pages/index/index.vue
  73. 8
      packages/nc-gui-v2/pages/index/user/index/index.vue
  74. 16
      packages/nc-gui-v2/pages/projects/index.vue
  75. 2
      packages/nc-gui-v2/pages/projects/index/list.vue
  76. 56
      packages/nc-gui-v2/pages/signin.vue
  77. 37
      packages/nc-gui-v2/pages/signup/[[token]].vue
  78. 10
      packages/nc-gui-v2/plugins/ant.ts
  79. 17
      packages/nc-gui-v2/plugins/state.ts
  80. 29
      packages/nc-gui-v2/plugins/vuetify.ts
  81. 42
      packages/nc-gui-v2/utils/colorsUtils.ts
  82. 31
      packages/nc-gui-v2/windi.config.ts

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

@ -1,11 +1,17 @@
<script setup lang="ts">
import { computed, provideTheme, useRoute } from '#imports'
const route = useRoute()
const disableBaseLayout = $computed(() => route.path.startsWith('/nc/view') || route.path.startsWith('/nc/form'))
const disableBaseLayout = computed(() => route.path.startsWith('/nc/view') || route.path.startsWith('/nc/form'))
provideTheme()
</script>
<template>
<NuxtLayout :name="disableBaseLayout ? false : 'base'">
<NuxtPage />
</NuxtLayout>
<a-config-provider>
<NuxtLayout :name="disableBaseLayout ? false : 'base'">
<NuxtPage />
</NuxtLayout>
</a-config-provider>
</template>

4
packages/nc-gui-v2/assets/css/color.css

@ -1,4 +0,0 @@
:root {
--primary: #00b786;
--secondary: #8ceaf6;
}

23
packages/nc-gui-v2/assets/css/global.css

@ -1,4 +1,3 @@
@import './color.css';
html {
font-size: 16px;
word-spacing: 1px;
@ -8,30 +7,10 @@ html {
-webkit-font-smoothing: antialiased;
box-sizing: border-box;
}
body {
font-family: "Source Sans Pro", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, Vazirmatn, sans-serif;
}
*, *:before, *:after {
box-sizing: border-box;
margin: 0;
}
.btn, .pointer {
cursor: pointer;
}
.primary {
color: var(--primary);
}
.secondary {
color: var(--secondary);
}
.btn-primary {
background-color: var(--primary);
color: #fff;
}
.btn-secondary {
background-color: var(--secondary);
color: #000;
}
/*
Apply Vazirmatn for rtl

19
packages/nc-gui-v2/assets/style.css

@ -1,19 +0,0 @@
::-webkit-scrollbar {
width: .7em;
height: .7em
}
::-webkit-scrollbar-button {
background: #77777722
}
::-webkit-scrollbar-track-piece {
background: #66666622
}
::-webkit-scrollbar-thumb {
background: #888;
border-radius: .7em;
border: .15em solid #00000000;
background-clip: padding-box;
}

46
packages/nc-gui-v2/assets/style-v2.scss → packages/nc-gui-v2/assets/style.scss

@ -1,9 +1,9 @@
@import 'ant-design-vue/dist/antd.variable.min.css';
@import 'ant-design-vue/dist/antd.min.css';
:root {
--header-height: 50px;
--toolbar-height: 48px;
--tw-text-opacity: 1;
}
.ant-layout-header {
@ -27,21 +27,8 @@ main {
overflow-x: hidden;
}
nav,
nav .v-list {
@apply dark:(!bg-gray-900 text-white)
}
.v-divider {
@apply dark:bg-white
}
a {
@apply prose text-primary underline hover:opacity-75 dark:(text-secondary);
}
h1, h2, h3, h4, h5, h6, p, label, button, textarea, select {
@apply dark:(!text-white);
@apply !text-primary !underline hover:!text-accent;
}
.nc-icon {
@ -62,7 +49,7 @@ h1, h2, h3, h4, h5, h6, p, label, button, textarea, select {
&:hover {
.nc-icon {
@apply text-pink-500;
@apply text-accent;
}
}
}
@ -99,7 +86,7 @@ h1, h2, h3, h4, h5, h6, p, label, button, textarea, select {
// for highlighting toolbar menu item
.nc-active-btn > .ant-btn{
@apply bg-primary/20 hover:(bg-primary/20);
@apply bg-primary bg-opacity-20 hover:(bg-primary bg-opacity-20);
}
.nc-locked-overlay {
@ -159,25 +146,24 @@ h1, h2, h3, h4, h5, h6, p, label, button, textarea, select {
.page-leave-active,
.layout-enter-active,
.layout-leave-active {
@apply transition-opacity duration-300 ease-in-out;
@apply transition-opacity duration-400 ease-in-out;
}
.page-enter-active,
.page-leave-active,
.layout-enter-active,
.layout-leave-active {
.page-enter-from,
.page-leave-to,
.layout-enter-from,
.layout-leave-to {
@apply opacity-0;
}
.slide-enter-active,
.slide-leave-active {
@apply transition-all duration-200 ease-in-out;
transform: translate(100%, 0);
}
.slide-enter,
.slide-enter-from,
.slide-leave-active {
.slide-leave-to {
transform: translate(-100%, 0);
}
@ -190,8 +176,8 @@ h1, h2, h3, h4, h5, h6, p, label, button, textarea, select {
@apply ring ring-xl;
}
.glow-enter,
.glow-leave-active {
.glow-enter-from,
.glow-leave-to {
@apply opacity-0;
}
@ -205,11 +191,11 @@ h1, h2, h3, h4, h5, h6, p, label, button, textarea, select {
}
&:hover::after {
@apply transform scale-110 ring ring-pink-500;
@apply transform scale-110 ring ring-accent;
}
&:active::after {
@apply ring ring-pink-500;
@apply ring ring-accent;
}
}
@ -246,7 +232,7 @@ h1, h2, h3, h4, h5, h6, p, label, button, textarea, select {
}
.ant-dropdown-menu-item, .ant-menu-item {
@apply !py-0 active:(ring ring-pink-500);
@apply !py-0 active:(ring ring-accent);
}
.ant-dropdown-menu-title-content,

6
packages/nc-gui-v2/components.d.ts vendored

@ -19,6 +19,7 @@ declare module '@vue/runtime-core' {
ACol: typeof import('ant-design-vue/es')['Col']
ACollapse: typeof import('ant-design-vue/es')['Collapse']
ACollapsePanel: typeof import('ant-design-vue/es')['CollapsePanel']
AConfigProvider: typeof import('ant-design-vue/es')['ConfigProvider']
ADatePicker: typeof import('ant-design-vue/es')['DatePicker']
ADivider: typeof import('ant-design-vue/es')['Divider']
ADrawer: typeof import('ant-design-vue/es')['Drawer']
@ -70,6 +71,9 @@ declare module '@vue/runtime-core' {
BiFiletypeXlsx: typeof import('~icons/bi/filetype-xlsx')['default']
CilFullscreen: typeof import('~icons/cil/fullscreen')['default']
CilFullscreenExit: typeof import('~icons/cil/fullscreen-exit')['default']
ClarityColorPickerLine: typeof import('~icons/clarity/color-picker-line')['default']
ClarityColorPickerSolid: typeof import('~icons/clarity/color-picker-solid')['default']
ClarityImageLine: typeof import('~icons/clarity/image-line')['default']
ClaritySuccessLine: typeof import('~icons/clarity/success-line')['default']
EvaEmailOutline: typeof import('~icons/eva/email-outline')['default']
IcBaselineMoreVert: typeof import('~icons/ic/baseline-more-vert')['default']
@ -77,6 +81,7 @@ declare module '@vue/runtime-core' {
IcRoundEdit: typeof import('~icons/ic/round-edit')['default']
IcRoundKeyboardArrowDown: typeof import('~icons/ic/round-keyboard-arrow-down')['default']
IcRoundSearch: typeof import('~icons/ic/round-search')['default']
LogosGoogleGmail: typeof import('~icons/logos/google-gmail')['default']
MaterialSymbolsArrowCircleLeftRounded: typeof import('~icons/material-symbols/arrow-circle-left-rounded')['default']
MaterialSymbolsArrowCircleRightRounded: typeof import('~icons/material-symbols/arrow-circle-right-rounded')['default']
MaterialSymbolsAttachFile: typeof import('~icons/material-symbols/attach-file')['default']
@ -148,6 +153,7 @@ declare module '@vue/runtime-core' {
MdiFunction: typeof import('~icons/mdi/function')['default']
MdiGestureDoubleTap: typeof import('~icons/mdi/gesture-double-tap')['default']
MdiGithub: typeof import('~icons/mdi/github')['default']
MdiGmail: typeof import('~icons/mdi/gmail')['default']
MdiGridLarge: typeof import('~icons/mdi/grid-large')['default']
MdiHeart: typeof import('~icons/mdi/heart')['default']
MdiHook: typeof import('~icons/mdi/hook')['default']

2
packages/nc-gui-v2/components/cell/Decimal.vue

@ -27,7 +27,7 @@ const focus: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
v-if="editEnabled"
:ref="focus"
v-model="vModel"
class="outline-none pa-0 border-none w-full h-full prose-sm"
class="outline-none p-0 border-none w-full h-full prose-sm"
type="number"
step="0.1"
@blur="editEnabled = false"

2
packages/nc-gui-v2/components/cell/Float.vue

@ -27,7 +27,7 @@ const focus: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
v-if="editEnabled"
:ref="focus"
v-model="vModel"
class="outline-none pa-0 border-none w-full h-full prose-sm"
class="outline-none p-0 border-none w-full h-full prose-sm"
type="number"
step="0.1"
@blur="editEnabled = false"

2
packages/nc-gui-v2/components/cell/Integer.vue

@ -31,7 +31,7 @@ function onKeyDown(evt: KeyboardEvent) {
v-if="editEnabled"
:ref="focus"
v-model="vModel"
class="outline-none pa-0 border-none w-full h-full prose-sm"
class="outline-none p-0 border-none w-full h-full prose-sm"
type="number"
@blur="editEnabled = false"
@keydown="onKeyDown"

4
packages/nc-gui-v2/components/cell/attachment/Carousel.vue

@ -55,7 +55,7 @@ onClickOutside(carouselRef, () => {
</div>
<div
class="select-none group hover:ring active:ring-pink-500 cursor-pointer leading-8 inline-block px-3 py-1 bg-gray-300 text-white mb-4 text-center rounded shadow"
class="select-none group hover:ring active:ring-accent cursor-pointer leading-8 inline-block px-3 py-1 bg-gray-300 text-white mb-4 text-center rounded shadow"
@click.stop="downloadFile(selectedImage)"
>
<h3 class="group-hover:text-primary">{{ selectedImage && selectedImage.title }}</h3>
@ -133,7 +133,7 @@ onClickOutside(carouselRef, () => {
}
.ant-carousel :deep(.slick-arrow.custom-slick-arrow) {
@apply text-4xl text-white hover:text-primary active:text-pink-500 opacity-100 cursor-pointer z-1;
@apply text-4xl text-white hover:text-primary active:text-accent opacity-100 cursor-pointer z-1;
}
.ant-carousel :deep(.custom-slick-arrow:before) {
display: none;

14
packages/nc-gui-v2/components/cell/attachment/Modal.vue

@ -66,7 +66,7 @@ function onClick(item: Record<string, any>) {
class="nc-attach-file group"
@click="open"
>
<MaterialSymbolsAttachFile class="transform group-hover:(text-pink-500 scale-120)" />
<MaterialSymbolsAttachFile class="transform group-hover:(text-accent scale-120)" />
Attach File
</div>
@ -83,9 +83,9 @@ function onClick(item: Record<string, any>) {
<general-overlay
v-model="isOverDropZone"
inline
class="text-white ring ring-pink-500 bg-gray-700/75 flex items-center justify-center gap-2 backdrop-blur-xl"
class="text-white ring ring-accent bg-gray-700/75 flex items-center justify-center gap-2 backdrop-blur-xl"
>
<MaterialSymbolsFileCopyOutline class="text-pink-500" height="35" width="35" />
<MaterialSymbolsFileCopyOutline class="text-accent" height="35" width="35" />
<div class="text-white text-3xl">Drop here</div>
</general-overlay>
</template>
@ -154,8 +154,8 @@ function onClick(item: Record<string, any>) {
.nc-attachment-modal {
.nc-attach-file {
@apply select-none cursor-pointer color-transition flex items-center gap-1 border-1 p-2 rounded
@apply hover:(bg-primary/10 text-primary ring);
@apply active:(ring-pink-500 bg-primary/20);
@apply hover:(bg-primary bg-opacity-10 text-primary ring);
@apply active:(ring-accent bg-primary bg-opacity-20);
}
.nc-attachment-item {
@ -176,7 +176,7 @@ function onClick(item: Record<string, any>) {
}
&:active::after {
@apply ring ring-pink-500 shadow transform scale-103;
@apply ring ring-accent shadow transform scale-103;
}
}
}
@ -185,7 +185,7 @@ function onClick(item: Record<string, any>) {
@apply bg-white absolute bottom-2 right-2;
@apply transition-opacity duration-150 ease-in opacity-0 hover:ring;
@apply cursor-pointer rounded shadow flex items-center p-1 border-1;
@apply active:(ring border-0 ring-pink-500);
@apply active:(ring border-0 ring-accent);
}
.nc-attachment-remove {

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

@ -100,16 +100,16 @@ const { isSharedForm } = useSmartsheetStoreOrThrow()
v-model="isOverDropZone"
inline
:target="currentCellRef"
class="text-white text-lg ring ring-pink-500 bg-gray-700/75 flex items-center justify-center gap-2 backdrop-blur-xl"
class="text-white text-lg ring ring-accent bg-gray-700/75 flex items-center justify-center gap-2 backdrop-blur-xl"
>
<MaterialSymbolsFileCopyOutline class="text-pink-500" /> Drop here
<MaterialSymbolsFileCopyOutline class="text-accent" /> Drop here
</general-overlay>
</template>
<div
v-if="!isReadonly"
:class="{ 'mx-auto px-4': !visibleItems.length }"
class="group flex gap-1 items-center active:ring rounded border-1 p-1 hover:bg-primary/10"
class="group flex gap-1 items-center active:ring rounded border-1 p-1 hover:(bg-primary bg-opacity-10)"
@click.stop="open"
>
<MdiReload v-if="isLoading" :class="{ 'animate-infinite animate-spin': isLoading }" />
@ -118,7 +118,7 @@ const { isSharedForm } = useSmartsheetStoreOrThrow()
<template #title> Click or drop a file into cell </template>
<div class="flex items-center gap-2">
<MaterialSymbolsAttachFile class="transform group-hover:(text-pink-500 scale-120) text-gray-500 text-[10px]" />
<MaterialSymbolsAttachFile class="transform group-hover:(text-accent scale-120) text-gray-500 text-[10px]" />
<div v-if="!visibleItems.length" class="group-hover:text-primary text-gray-500 text-xs">Add file(s)</div>
</div>
@ -160,14 +160,14 @@ const { isSharedForm } = useSmartsheetStoreOrThrow()
</div>
</div>
<div class="group flex gap-1 items-center border-1 active:ring rounded p-1 hover:bg-primary/10">
<div class="group flex gap-1 items-center border-1 active:ring rounded p-1 hover:(bg-primary bg-opacity-10)">
<MdiReload v-if="isLoading" :class="{ 'animate-infinite animate-spin': isLoading }" />
<a-tooltip v-else placement="bottom">
<template #title> View attachments </template>
<MdiArrowExpand
class="select-none transform group-hover:(text-pink-500 scale-120) text-[10px] text-gray-500"
class="select-none transform group-hover:(text-accent scale-120) text-[10px] text-gray-500"
@click.stop="modalVisible = true"
/>
</a-tooltip>

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

@ -220,12 +220,9 @@ function openTableCreateDialog() {
<template>
<div class="nc-treeview-container flex flex-col">
<a-dropdown :trigger="['contextmenu']">
<div
class="pt-2 pl-2 pb-2 flex-1 overflow-y-auto flex flex-column scrollbar-thin-dull"
:class="{ 'mb-[20px]': isSharedBase }"
>
<div class="py-1 px-3 flex w-full align-center gap-1 cursor-pointer" @contextmenu="setMenuContext('main')">
<span class="flex-grow text-bold uppercase nc-project-tree text-gray-500 font-weight-bold">
<div class="pt-2 pl-2 pb-2 flex-1 overflow-y-auto flex flex-col scrollbar-thin-dull" :class="{ 'mb-[20px]': isSharedBase }">
<div class="py-1 px-3 flex w-full items-center gap-1 cursor-pointer" @contextmenu="setMenuContext('main')">
<span class="flex-1 text-bold uppercase nc-project-tree text-gray-500 font-weight-bold">
{{ $t('objects.tables') }}
<template v-if="tables?.length"> ({{ tables.length }}) </template>
@ -253,7 +250,7 @@ function openTableCreateDialog() {
@click="openAirtableImportDialog"
>
<div class="color-transition nc-project-menu-item group">
<MdiTableLarge class="group-hover:text-pink-500" />
<MdiTableLarge class="group-hover:text-accent" />
<!-- TODO: i18n -->
Airtable
</div>
@ -261,7 +258,7 @@ function openTableCreateDialog() {
<a-menu-item v-if="isUIAllowed('csvImport')" key="quick-import-csv" @click="openQuickImportDialog('csv')">
<div class="color-transition nc-project-menu-item group">
<MdiFileDocumentOutline class="group-hover:text-pink-500" />
<MdiFileDocumentOutline class="group-hover:text-accent" />
<!-- TODO: i18n -->
CSV file
</div>
@ -269,7 +266,7 @@ function openTableCreateDialog() {
<a-menu-item v-if="isUIAllowed('jsonImport')" key="quick-import-json" @click="openQuickImportDialog('json')">
<div class="color-transition nc-project-menu-item group">
<MdiCodeJson class="group-hover:text-pink-500" />
<MdiCodeJson class="group-hover:text-accent" />
<!-- TODO: i18n -->
JSON file
</div>
@ -281,7 +278,7 @@ function openTableCreateDialog() {
@click="openQuickImportDialog('excel')"
>
<div class="color-transition nc-project-menu-item group">
<MdiFileExcel class="group-hover:text-pink-500" />
<MdiFileExcel class="group-hover:text-accent" />
<!-- TODO: i18n -->
Microsoft Excel
</div>
@ -297,7 +294,7 @@ function openTableCreateDialog() {
target="_blank"
class="prose-sm hover:(!text-primary !opacity-100) color-transition nc-project-menu-item group after:(!rounded-b)"
>
<MdiOpenInNew class="group-hover:text-pink-500" />
<MdiOpenInNew class="group-hover:text-accent" />
<!-- TODO: i18n -->
Request a data source you need?
</a>
@ -322,7 +319,7 @@ function openTableCreateDialog() {
:data-id="table.id"
@click="addTableTab(table)"
>
<div class="flex align-center gap-2 h-full" @contextmenu="setMenuContext('table', table)">
<div class="flex items-center gap-2 h-full" @contextmenu="setMenuContext('table', table)">
<div class="flex w-auto">
<MdiDrag
v-if="isUIAllowed('treeview-drag-n-drop')"
@ -369,7 +366,7 @@ function openTableCreateDialog() {
</div>
<a-card v-else class="mt-4 mx-4 !bg-gray-50">
<div class="flex flex-col align-center">
<div class="flex flex-col items-center">
<a-empty :image="Empty.PRESENTED_IMAGE_SIMPLE" />
<a-button type="primary" @click.stop="openTableCreateDialog">
@ -410,10 +407,10 @@ function openTableCreateDialog() {
</template>
</a-dropdown>
<a-divider class="mt-0 mb-0" />
<a-divider class="!my-0" />
<div class="items-center flex justify-center p-2">
<GeneralShareBaseButton class="!mr-0" />
<div class="flex items-center justify-center p-2">
<GeneralShareBaseButton />
</div>
</div>
</template>
@ -424,7 +421,7 @@ function openTableCreateDialog() {
}
.nc-treeview-footer-item {
@apply cursor-pointer px-4 py-2 flex align-center hover:bg-gray-200/20 text-xs text-current;
@apply cursor-pointer px-4 py-2 flex items-center hover:bg-gray-200/20 text-xs text-current;
}
:deep(.nc-filter-input input::placeholder) {
@ -460,7 +457,7 @@ function openTableCreateDialog() {
}
.sortable-chosen {
@apply !bg-primary/25 text-primary;
@apply !bg-primary bg-opacity-25 text-primary;
}
}
@ -469,20 +466,20 @@ function openTableCreateDialog() {
}
.nc-tree-item svg {
@apply text-primary/60;
@apply text-primary text-opacity-60;
}
.nc-tree-item.active {
@apply !text-primary font-weight-bold after:(!opacity-20);
@apply border-r-3 border-indigo-500;
@apply text-primary font-weight-bold after:(!opacity-20);
@apply border-r-3 border-primary;
svg {
@apply !text-primary;
@apply text-primary !text-opacity-100;
}
}
.nc-tree-item:hover {
@apply !text-grey after:(!opacity-5);
@apply text-primary after:(!opacity-5);
}
:deep(.nc-filter-input) {
@ -508,7 +505,7 @@ function openTableCreateDialog() {
}
:deep(.ant-dropdown-menu-item) {
@apply !py-0 active:(ring ring-pink-500);
@apply !py-0 active:(ring ring-accent);
}
:deep(.ant-dropdown-menu-title-content) {

2
packages/nc-gui-v2/components/dashboard/settings/AppStore.vue

@ -130,7 +130,7 @@ onMounted(async () => {
/>
<div v-else />
</div>
<div class="flex flex-col flex-grow-1 w-3/5 pl-3">
<div class="flex flex-col flex-1 w-3/5 pl-3">
<a-typography-title :level="5">{{ app.title }}</a-typography-title>
{{ app.description }}
</div>

2
packages/nc-gui-v2/components/dashboard/settings/Metadata.vue

@ -73,7 +73,7 @@ const columns = [
<template>
<div class="flex flex-row w-full">
<div class="flex flex-column w-3/5">
<div class="flex flex-col w-3/5">
<div class="flex flex-row justify-end items-center w-full mb-4">
<a-button class="self-start nc-btn-metasync-reload" @click="loadMetaDiff">
<div class="flex items-center gap-2 text-gray-600 font-light">

2
packages/nc-gui-v2/components/dashboard/settings/Misc.vue

@ -7,7 +7,7 @@ watch(includeM2M, async () => await loadTables())
<template>
<div class="flex flex-row w-full">
<div class="flex flex-column w-full">
<div class="flex flex-col w-full">
<div class="flex flex-row items-center w-full mb-4 gap-2">
<a-checkbox v-model:checked="includeM2M">Show M2M Tables</a-checkbox>
</div>

4
packages/nc-gui-v2/components/dashboard/settings/UIAcl.vue

@ -99,7 +99,7 @@ const columns = [
<template>
<div class="flex flex-row w-full">
<div class="flex flex-column w-full">
<div class="flex flex-col w-full">
<div class="flex flex-row items-center w-full mb-4 gap-2">
<a-input v-model:value="searchInput" placeholder="Search models" class="nc-acl-search">
<template #prefix>
@ -137,7 +137,7 @@ const columns = [
<template #bodyCell="{ record, column }">
<div v-if="column.name === 'table_name'">{{ record._ptn }}</div>
<div v-if="column.name === 'view_name'">
<div class="flex align-center">
<div class="flex items-center">
<component :is="viewIcons[record.type].icon" :class="`text-${viewIcons[record.type].color} mr-1`" />
{{ record.title }}
</div>

4
packages/nc-gui-v2/components/dlg/AirtableImport.vue

@ -229,7 +229,7 @@ onBeforeUnmount(() => {
</script>
<template>
<a-modal v-model:visible="dialogShow" width="max(30vw, 600px)" class="pa-2" @keydown.esc="dialogShow = false">
<a-modal v-model:visible="dialogShow" width="max(30vw, 600px)" class="p-2" @keydown.esc="dialogShow = false">
<div class="px-5">
<div class="mt-5 prose-xl font-weight-bold">QUICK IMPORT - AIRTABLE</div>
@ -245,7 +245,7 @@ onBeforeUnmount(() => {
</a>
</div>
<a-form ref="form" :model="syncSource" name="quick-import-airtable-form" layout="horizontal" class="ma-0">
<a-form ref="form" :model="syncSource" name="quick-import-airtable-form" layout="horizontal" class="m-0">
<a-form-item v-bind="validateInfos['details.apiKey']">
<a-input-password
v-model:value="syncSource.details.apiKey"

3
packages/nc-gui-v2/components/general/ColorPicker.vue

@ -1,7 +1,6 @@
<script lang="ts" setup>
import { Chrome } from '@ckpack/vue-color'
import { enumColor } from '@/utils'
import { computed, ref, watch } from '#imports'
import { computed, enumColor, ref, watch } from '#imports'
interface Props {
modelValue?: string | any

12
packages/nc-gui-v2/components/general/MiniSidebar.vue

@ -48,7 +48,7 @@ const logout = () => {
<a-menu-item key="signout" class="!rounded-b">
<div v-t="['a:navbar:user:sign-out']" class="group flex items-center py-2" @click="logout">
<MdiLogout class="dark:text-white group-hover:(!text-red-500)" />&nbsp;
<MdiLogout class="group-hover:(!text-red-500)" />&nbsp;
<span class="prose font-semibold text-gray-500 group-hover:text-black nc-user-menu-signout">
{{ $t('general.signOut') }}
</span>
@ -74,24 +74,24 @@ const logout = () => {
</span>
</template>
<a-menu-item class="active:(ring ring-pink-500)">
<a-menu-item class="active:(ring ring-accent)">
<div
v-t="['c:project:create:xcdb']"
class="group flex items-center gap-2 py-2 hover:text-primary"
@click="navigateTo('/project/create')"
>
<MdiPlus class="text-lg group-hover:text-pink-500" />
<MdiPlus class="text-lg group-hover:text-accent" />
{{ $t('activity.createProject') }}
</div>
</a-menu-item>
<a-menu-item class="rounded-b active:(ring ring-pink-500)">
<a-menu-item class="rounded-b active:(ring ring-accent)">
<div
v-t="['c:project:create:extdb']"
class="group flex items-center gap-2 py-2 hover:text-primary"
@click="navigateTo('/project/create-external')"
>
<MdiDatabaseOutline class="text-lg group-hover:text-pink-500" />
<MdiDatabaseOutline class="text-lg group-hover:text-accent" />
<div v-html="$t('activity.createProjectExtended.extDB')" />
</div>
</a-menu-item>
@ -125,7 +125,7 @@ const logout = () => {
@apply flex w-full justify-center items-center h-12 group p-2;
&.active {
@apply bg-pink-500 border-t-1 border-b-1;
@apply bg-accent border-t-1 border-b-1;
}
}
}

4
packages/nc-gui-v2/components/general/PreviewAs.vue

@ -80,7 +80,7 @@ watch(previewAs, () => window.location.reload())
<template v-for="role of roleList" :key="role.title">
<a-menu-item @click="previewAs = role.title">
<div class="nc-project-menu-item group">
<component :is="roleIcon[role.title]" class="group-hover:text-pink-500" />
<component :is="roleIcon[role.title]" class="group-hover:text-accent" />
<span class="capitalize" :class="{ 'x-active--text': role.title === previewAs }">
{{ role.title }}
@ -92,7 +92,7 @@ watch(previewAs, () => window.location.reload())
<template v-if="previewAs">
<a-menu-item @click="previewAs = null">
<div class="nc-project-menu-item group">
<MdiClose class="group-hover:text-pink-500" />
<MdiClose class="group-hover:text-accent" />
<!-- Reset Preview -->
<span class="text-capitalize text-xs whitespace-nowrap">
{{ $t('activity.resetReview') }}

4
packages/nc-gui-v2/components/general/ReleaseInfo.vue

@ -36,7 +36,7 @@ onMounted(async () => await fetchReleaseInfo())
<div v-if="releaseAlert" class="flex items-center">
<a-dropdown :trigger="['click']" placement="bottom">
<a-button class="bg-primary border-none">
<div class="flex gap-1 align-center text-white">
<div class="flex gap-1 items-center text-white">
<span class="text-sm font-weight-medium">{{ $t('activity.upgrade.available') }}</span>
<mdi-menu-down />
</div>
@ -56,7 +56,7 @@ onMounted(async () => await fetchReleaseInfo())
{{ $t('activity.upgrade.howTo') }}
</div>
</nuxt-link>
<a-divider class="ma-0" />
<a-divider class="m-0" />
<div class="nc-menu-item" @click="latestRelease = null">
<mdi-close />
<!-- Hide menu -->

2
packages/nc-gui-v2/components/general/language/Menu.vue

@ -39,7 +39,7 @@ onMounted(() => {
<a-menu-item
v-for="lang of languages"
:key="lang"
:class="lang === locale ? '!bg-primary/10 text-primary' : ''"
:class="lang === locale ? '!bg-primary bg-opacity-10 text-primary' : ''"
class="group"
:value="lang"
@click="changeLanguage(lang)"

14
packages/nc-gui-v2/components/shared-view/Form.vue

@ -34,7 +34,7 @@ function isRequired(_columnObj: Record<string, any>, required = false) {
</script>
<template>
<div class="bg-primary/100 !h-[100vh] overflow-auto w-100 flex flex-col">
<div class="bg-primary !h-[100vh] overflow-auto w-full flex flex-col">
<div>
<img src="~/assets/img/icons/512x512-trans.png" width="30" class="mx-4 mt-2" />
</div>
@ -59,21 +59,21 @@ function isRequired(_columnObj: Record<string, any>, required = false) {
<a-row class="justify-center">
<a-col :md="20">
<div>
<div class="h-full ma-0 rounded-b-0">
<div class="h-full m-0 rounded-b-0">
<div
class="nc-form-wrapper pb-10 rounded shadow-xl"
style="background: linear-gradient(180deg, #dbdbdb 0, #dbdbdb 200px, white 200px)"
>
<div class="mt-10 flex items-center justify-center flex-col">
<div class="nc-form-banner backgroundColor darken-1 flex-column justify-center d-flex">
<div class="flex items-center justify-center grow h-[100px]">
<div class="nc-form-banner backgroundColor darken-1 flex-col justify-center flex">
<div class="flex items-center justify-center flex-1 h-[100px]">
<img src="~/assets/img/icon.png" width="50" class="mx-4" />
<span class="text-4xl font-weight-bold">NocoDB</span>
</div>
</div>
</div>
<div class="mx-auto nc-form bg-white shadow-lg pa-2 mb-10 max-w-[600px] mx-auto rounded">
<div class="mx-auto nc-form bg-white shadow-lg p-2 mb-10 max-w-[600px] mx-auto rounded">
<h2 class="mt-4 text-4xl font-weight-bold text-left mx-4 mb-3 px-1">
{{ sharedFormView.heading }}
</h2>
@ -81,7 +81,7 @@ function isRequired(_columnObj: Record<string, any>, required = false) {
<div class="text-lg text-left mx-4 py-2 px-1 text-gray-500">
{{ sharedFormView.subheading }}
</div>
<div class="h-100">
<div class="h-full">
<div v-for="(field, index) in formColumns" :key="index" class="flex flex-col mt-4 px-4 space-y-2">
<div class="flex">
<SmartsheetHeaderVirtualCell
@ -159,7 +159,7 @@ function isRequired(_columnObj: Record<string, any>, required = false) {
<style scoped lang="scss">
.nc-input {
@apply w-full !bg-white rounded px-2 py-2 min-h-[40px] mt-2 mb-2 flex align-center border-solid border-1 border-primary;
@apply w-full !bg-white rounded px-2 py-2 min-h-[40px] mt-2 mb-2 flex items-center border-solid border-1 border-primary;
}
.nc-form-wrapper {

6
packages/nc-gui-v2/components/smartsheet-column/EditOrAdd.vue

@ -117,7 +117,7 @@ useEventListener(document, 'click', handleClose)
>
<a-select v-model:value="formState.uidt" show-search class="nc-column-type-input" @change="onUidtOrIdTypeChange">
<a-select-option v-for="opt of uiTypesOptions" :key="opt.name" :value="opt.name" v-bind="validateInfos.uidt">
<div class="flex gap-1 align-center">
<div class="flex gap-1 items-center">
<component :is="opt.icon" class="text-grey" />
{{ opt.name }}
</div>
@ -144,7 +144,7 @@ useEventListener(document, 'click', handleClose)
</div>
<div
v-if="!isVirtualCol(formState.uidt)"
class="text-xs cursor-pointer text-grey nc-more-options mb-1 mt-4 flex align-center gap-1 justify-end"
class="text-xs cursor-pointer text-grey nc-more-options mb-1 mt-4 flex items-center gap-1 justify-end"
@click="advancedOptions = !advancedOptions"
>
{{ advancedOptions ? $t('general.hideAll') : $t('general.showMore') }}
@ -197,7 +197,7 @@ useEventListener(document, 'click', handleClose)
}
:deep(.ant-select-selection-item) {
@apply flex align-center;
@apply flex items-center;
}
:deep(.ant-form-item-explain-error) {

2
packages/nc-gui-v2/components/smartsheet-column/LinkedToAnotherRecordOptions.vue

@ -68,7 +68,7 @@ const refTables = $computed(() => {
</div>
<div
class="text-xs cursor-pointer text-grey nc-more-options my-2 flex align-center gap-1 justify-end"
class="text-xs cursor-pointer text-grey nc-more-options my-2 flex items-center gap-1 justify-end"
@click="advancedOptions = !advancedOptions"
>
{{ advancedOptions ? $t('general.hideAll') : $t('general.showMore') }}

4
packages/nc-gui-v2/components/smartsheet-column/SelectOptions.vue

@ -95,7 +95,7 @@ watch(inputs, () => {
<div class="w-full">
<Draggable :list="options" item-key="id" handle=".nc-child-draggable-icon">
<template #item="{ element, index }">
<div class="flex py-1 align-center">
<div class="flex py-1 items-center">
<MdiDragIcon small class="nc-child-draggable-icon handle" />
<a-dropdown v-model:visible="colorMenus[index]" :trigger="['click']">
<template #overlay>
@ -109,7 +109,7 @@ watch(inputs, () => {
</template>
<template #footer>
<a-button type="dashed" class="w-full caption mt-2" @click="addNewOption()">
<div class="flex align-center"><MdiPlusIcon /><span class="flex-auto">Add option</span></div>
<div class="flex items-center"><MdiPlusIcon /><span class="flex-auto">Add option</span></div>
</a-button>
</template>
</Draggable>

6
packages/nc-gui-v2/components/smartsheet-header/VirtualCellIcon.vue

@ -38,7 +38,7 @@ const icon = computed(() => {
case UITypes.LinkToAnotherRecord:
switch ((column?.value?.colOptions as LinkToAnotherRecordType)?.type) {
case RelationTypes.MANY_TO_MANY:
return { icon: MMIcon, color: 'text-pink-500' }
return { icon: MMIcon, color: 'text-accent' }
case RelationTypes.HAS_MANY:
return { icon: HMIcon, color: 'text-yellow-500' }
case RelationTypes.BELONGS_TO:
@ -52,7 +52,7 @@ const icon = computed(() => {
case UITypes.Lookup:
switch ((relationColumn?.colOptions as LinkToAnotherRecordType)?.type) {
case RelationTypes.MANY_TO_MANY:
return { icon: TableColumnPlusBefore, color: 'text-pink-500' }
return { icon: TableColumnPlusBefore, color: 'text-accent' }
case RelationTypes.HAS_MANY:
return { icon: TableColumnPlusBefore, color: 'text-yellow-500' }
case RelationTypes.BELONGS_TO:
@ -62,7 +62,7 @@ const icon = computed(() => {
case UITypes.Rollup:
switch ((relationColumn?.colOptions as LinkToAnotherRecordType)?.type) {
case RelationTypes.MANY_TO_MANY:
return { icon: RollupIcon, color: 'text-pink-500' }
return { icon: RollupIcon, color: 'text-accent' }
case RelationTypes.HAS_MANY:
return { icon: RollupIcon, color: 'text-yellow-500' }
case RelationTypes.BELONGS_TO:

8
packages/nc-gui-v2/components/smartsheet-toolbar/ColumnFilter.vue

@ -148,7 +148,7 @@ defineExpose({
/>
<span v-else :key="`${i}dummy`" />
<div :key="`${i}nested`" class="d-flex">
<div :key="`${i}nested`" class="flex">
<a-select
v-model:value="filter.logical_op"
:dropdown-match-select-width="false"
@ -187,7 +187,7 @@ defineExpose({
<MdiCloseBox
v-if="!filter.readOnly"
class="nc-filter-item-remove-btn text-grey align-self-center"
class="nc-filter-item-remove-btn text-grey self-center"
@click.stop="deleteFilter(filter, i)"
/>
<span v-else />
@ -269,7 +269,7 @@ defineExpose({
<div class="flex gap-2 mb-2 mt-4">
<a-button class="elevation-0 text-capitalize" type="primary" ghost @click.stop="addFilter">
<div class="flex align-center gap-1">
<div class="flex items-center gap-1">
<!-- <v-icon small color="grey"> mdi-plus </v-icon> -->
<MdiPlus />
<!-- Add Filter -->
@ -277,7 +277,7 @@ defineExpose({
</div>
</a-button>
<a-button class="text-capitalize !text-gray-500" @click.stop="addFilterGroup">
<div class="flex align-center gap-1">
<div class="flex items-center gap-1">
<!-- <v-icon small color="grey"> mdi-plus </v-icon> -->
<MdiPlus />
Add Filter Group

4
packages/nc-gui-v2/components/smartsheet-toolbar/ColumnFilterMenu.vue

@ -36,7 +36,7 @@ const applyChanges = async () => await filterComp.value?.applyChanges()
<a-dropdown :trigger="['click']">
<div :class="{ 'nc-badge nc-active-btn': filtersLength }">
<a-button v-t="['c:filter']" class="nc-filter-menu-btn nc-toolbar-btn txt-sm" :disabled="isLocked">
<div class="flex align-center gap-1">
<div class="flex items-center gap-1">
<MdiFilterOutline />
<!-- Filter -->
<span class="text-capitalize !text-sm font-weight-medium">{{ $t('activity.filter') }}</span>
@ -51,7 +51,7 @@ const applyChanges = async () => await filterComp.value?.applyChanges()
:auto-save="filterAutoSave"
@update:filters-length="filtersLength = $event"
>
<div v-if="!isPublic" class="d-flex align-end mt-2 min-h-[30px]" @click.stop>
<div v-if="!isPublic" class="flex items-end mt-2 min-h-[30px]" @click.stop>
<a-checkbox id="col-filter-checkbox" v-model:checked="filterAutoSave" class="col-filter-checkbox" hide-details dense>
<span class="text-grey text-xs">
{{ $t('msg.info.filterAutoApply') }}

2
packages/nc-gui-v2/components/smartsheet-toolbar/FieldListAutoCompleteDropdown.vue

@ -86,7 +86,7 @@ const filterOption = (input: string, option: any) => {
:filter-option="filterOption"
>
<a-select-option v-for="option in options" :key="option.value" :value="option.value">
<div class="flex gap-2 items-center align-center h-full">
<div class="flex gap-2 items-center items-center h-full">
<component :is="option.icon" class="min-w-5 !mx-0" />
<span class="min-w-0"> {{ option.label }}</span>
</div>

7
packages/nc-gui-v2/components/smartsheet-toolbar/FieldsMenu.vue

@ -82,7 +82,7 @@ const onMove = (event: { moved: { newIndex: number } }) => {
<a-dropdown :trigger="['click']">
<div :class="{ 'nc-badge nc-active-btn': isAnyFieldHidden }">
<a-button v-t="['c:fields']" class="nc-fields-menu-btn nc-toolbar-btn" :disabled="isLocked">
<div class="flex align-center gap-1">
<div class="flex items-center gap-1">
<MdiEyeOffOutline />
<!-- Fields -->
@ -104,7 +104,7 @@ const onMove = (event: { moved: { newIndex: number } }) => {
<Draggable v-model="fields" item-key="id" @change="onMove($event)">
<template #item="{ element: field, index: index }">
<div v-show="filteredFieldList.includes(field)" :key="field.id" class="px-2 py-1 flex" @click.stop>
<a-checkbox v-model:checked="field.show" class="flex-shrink" @change="saveOrUpdate(field, index)">
<a-checkbox v-model:checked="field.show" class="shrink" @change="saveOrUpdate(field, index)">
<span class="">{{ field.title }}</span>
</a-checkbox>
<div class="flex-1" />
@ -113,7 +113,8 @@ const onMove = (event: { moved: { newIndex: number } }) => {
</template>
</Draggable>
</div>
<v-divider class="my-2" />
<a-divider class="!my-2" />
<div v-if="!isPublic" class="p-2 py-1 flex" @click.stop>
<a-checkbox v-model:checked="showSystemFields">

2
packages/nc-gui-v2/components/smartsheet-toolbar/MoreActions.vue

@ -92,7 +92,7 @@ const exportFile = async (exportType: ExportTypes) => {
<div>
<a-dropdown>
<a-button v-t="['c:actions']" class="nc-actions-menu-btn nc-toolbar-btn">
<div class="flex gap-1 align-center">
<div class="flex gap-1 items-center">
<MdiFlashOutline />
<!-- More -->

2
packages/nc-gui-v2/components/smartsheet-toolbar/SearchData.vue

@ -24,7 +24,7 @@ function onPressEnter() {
<template>
<a-input v-model:value="search.query" size="small" class="max-w-[200px]" placeholder="Filter query" @press-enter="onPressEnter">
<template #addonBefore>
<div class="flex align-center relative" @click="isDropdownOpen = true">
<div class="flex items-center relative" @click="isDropdownOpen = true">
<MdiMagnify class="text-grey" />
<MdiMenuDown class="text-grey" />

8
packages/nc-gui-v2/components/smartsheet-toolbar/ShareView.vue

@ -1,10 +1,8 @@
<script lang="ts" setup>
import { useClipboard } from '@vueuse/core'
import { ViewTypes } from 'nocodb-sdk'
import { computed } from 'vue'
import { message } from 'ant-design-vue'
import { useNuxtApp } from '#app'
import { extractSdkResponseErrorMsg, useProject, useSmartsheetStoreOrThrow } from '#imports'
import { computed, extractSdkResponseErrorMsg, useNuxtApp, useProject, useSmartsheetStoreOrThrow } from '#imports'
import MdiOpenInNewIcon from '~icons/mdi/open-in-new'
import MdiCopyIcon from '~icons/mdi/content-copy'
@ -125,7 +123,7 @@ onMounted(() => {
outlined
class="nc-btn-share-view nc-toolbar-btn"
>
<div class="flex align-center gap-1" @click="genShareLink">
<div class="flex items-center gap-1" @click="genShareLink">
<MdiOpenInNewIcon />
<!-- Share View -->
<span class="!text-sm font-weight-medium"> {{ $t('activity.shareView') }}</span>
@ -179,7 +177,7 @@ onMounted(() => {
<style scoped>
.share-link-box {
@apply flex p-2 w-full items-center align-center gap-1 bg-gray-100 rounded;
@apply flex p-2 w-full items-center items-center gap-1 bg-gray-100 rounded;
}
:deep(.ant-collapse-header) {

2
packages/nc-gui-v2/components/smartsheet-toolbar/SharedViewList.vue

@ -110,7 +110,7 @@ const deleteLink = async (id: string) => {
<!-- Password -->
<a-table-column key="password" :title="$t('labels.password')" data-index="title">
<template #default="{ record }">
<div class="flex align-center items-center gap-1">
<div class="flex items-center items-center gap-1">
<template v-if="record.password">
<span class="h-min">{{ record.showPassword ? record.password : '***************************' }}</span>
<component

19
packages/nc-gui-v2/components/smartsheet-toolbar/SortListMenu.vue

@ -37,7 +37,7 @@ watch(
<a-dropdown offset-y class="" :trigger="['click']">
<div :class="{ 'nc-badge nc-active-btn': sorts?.length }">
<a-button v-t="['c:sort']" class="nc-sort-menu-btn nc-toolbar-btn" :disabled="isLocked"
><div class="flex align-center gap-1">
><div class="flex items-center gap-1">
<MdiSortIcon />
<!-- Sort -->
<span class="text-capitalize !text-sm font-weight-medium">{{ $t('activity.sort') }}</span>
@ -49,12 +49,8 @@ watch(
<div class="bg-gray-50 p-6 shadow-lg menu-filter-dropdown min-w-[400px] max-h-[max(80vh,500px)] overflow-auto !border">
<div v-if="sorts?.length" class="sort-grid mb-2" @click.stop>
<template v-for="(sort, i) in sorts || []" :key="i">
<!-- <v-icon :key="`${i}icon`" class="nc-sort-item-remove-btn" small @click.stop="deleteSort(sort)"> mdi-close-box </v-icon> -->
<MdiDeleteIcon
class="nc-sort-item-remove-btn text-grey align-self-center"
small
@click.stop="deleteSort(sort, i)"
></MdiDeleteIcon>
<MdiDeleteIcon class="nc-sort-item-remove-btn text-grey self-center" small @click.stop="deleteSort(sort, i)" />
<FieldListAutoCompleteDropdown
v-model="sort.fk_column_id"
class="caption nc-sort-field-select"
@ -63,9 +59,10 @@ watch(
@click.stop
@update:model-value="saveOrUpdate(sort, i)"
/>
<a-select
v-model:value="sort.direction"
class="flex-shrink-1 flex-grow-0 caption nc-sort-dir-select !text-xs"
class="shrink grow-0 nc-sort-dir-select !text-xs"
:label="$t('labels.operation')"
@click.stop
@update:value="saveOrUpdate(sort, i)"
@ -78,14 +75,10 @@ watch(
<span>{{ option.text }}</span>
</a-select-option>
</a-select>
<!-- <template #item="{ item }"> -->
<!-- <span class="caption font-weight-regular">{{ item.text }}</span> -->
<!-- </template> -->
<!-- </v-select> -->
</template>
</div>
<a-button class="text-capitalize mb-1 mt-4" type="primary" ghost @click.stop="addSort">
<div class="flex gap-1 align-center">
<div class="flex gap-1 items-center">
<MdiAddIcon />
<!-- Add Sort Option -->
{{ $t('activity.addSort') }}

24
packages/nc-gui-v2/components/smartsheet/Form.vue

@ -357,7 +357,7 @@ onMounted(async () => {
<template>
<a-row v-if="submitted" class="h-full">
<a-col :span="24">
<div v-if="formViewData" class="align-center justify-center text-center mt-2">
<div v-if="formViewData" class="items-center justify-center text-center mt-2">
<a-alert type="success">
<template #message>
<div class="text-center">{{ formViewData.success_msg || 'Successfully submitted form data' }}</div>
@ -376,7 +376,7 @@ onMounted(async () => {
<a-col
v-if="isEditable"
:span="8"
class="bg-[#f7f7f7] shadow-md pa-5 h-full overflow-auto scrollbar-thin-primary nc-form-left-drawer"
class="bg-[#f7f7f7] shadow-md p-5 h-full overflow-auto scrollbar-thin-primary nc-form-left-drawer"
>
<div class="flex">
<div class="flex flex-row flex-1 text-lg">
@ -412,7 +412,7 @@ onMounted(async () => {
<template #item="{ element }">
<a-card
size="small"
class="ma-0 pa-0 cursor-pointer item mb-2"
class="m-0 p-0 cursor-pointer item mb-2"
@mousedown="moved = false"
@mousemove="moved = false"
@mouseup="handleMouseUp(element)"
@ -470,7 +470,7 @@ onMounted(async () => {
<!-- for future implementation of cover image -->
</div>
<a-card
class="h-full ma-0 rounded-b-0 pa-4 border-none"
class="h-full m-0 rounded-b-0 p-4 border-none"
:body-style="{
maxWidth: '700px',
margin: '0 auto',
@ -478,9 +478,9 @@ onMounted(async () => {
}"
>
<a-form ref="formRef" :model="formState" class="nc-form">
<a-card class="rounded ma-2 py-10 px-5">
<a-card class="rounded m-2 py-10 px-5">
<!-- Header -->
<a-form-item v-if="isEditable" class="ma-0 gap-0 pa-0">
<a-form-item v-if="isEditable" class="m-0 gap-0 p-0">
<a-input
v-model:value="formViewData.heading"
class="w-full text-bold text-h3"
@ -496,7 +496,7 @@ onMounted(async () => {
<div v-else class="ml-3 w-full text-bold text-h3">{{ formViewData.heading }}</div>
<!-- Sub Header -->
<a-form-item v-if="isEditable" class="ma-0 gap-0 pa-0">
<a-form-item v-if="isEditable" class="m-0 gap-0 p-0">
<a-input
v-model:value="formViewData.subheading"
class="w-full"
@ -518,7 +518,7 @@ onMounted(async () => {
item-key="fk_column_id"
draggable=".item"
group="form-inputs"
class="h-100"
class="h-full"
:move="onMoveCallback"
@change="onMove($event)"
@start="drag = true"
@ -526,7 +526,7 @@ onMounted(async () => {
>
<template #item="{ element, index }">
<div
class="nc-editable item cursor-pointer hover:bg-primary/10 pa-3"
class="nc-editable item cursor-pointer hover:(bg-primary bg-opacity-10) p-3"
:class="`nc-form-drag-${element.title.replaceAll(' ', '')}`"
@click="activeRow = element.title"
>
@ -555,7 +555,7 @@ onMounted(async () => {
<a-form-item
v-if="isVirtualCol(element)"
class="ma-0 gap-0 pa-0"
class="m-0 gap-0 p-0"
:name="element.title"
:rules="[{ required: element.required, message: `${element.title} is required` }]"
>
@ -570,7 +570,7 @@ onMounted(async () => {
<a-form-item
v-else
class="ma-0 gap-0 pa-0"
class="m-0 gap-0 p-0"
:name="element.title"
:rules="[{ required: element.required, message: `${element.title} is required` }]"
>
@ -710,7 +710,7 @@ onMounted(async () => {
}
.nc-input {
@apply w-full !bg-white rounded px-2 py-2 min-h-[40px] mt-2 mb-2 flex align-center border-solid border-1 border-primary;
@apply w-full !bg-white rounded px-2 py-2 min-h-[40px] mt-2 mb-2 flex items-center border-solid border-1 border-primary;
}
.form-meta-input::placeholder {

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

@ -309,7 +309,7 @@ const onNavigate = (dir: NavigateDir) => {
</script>
<template>
<div class="flex flex-col h-100 min-h-0 w-100">
<div class="flex flex-col h-full min-h-0 w-full">
<div class="nc-grid-wrapper min-h-0 flex-1 scrollbar-thin-dull">
<a-dropdown v-model:visible="contextMenu" :trigger="['contextmenu']">
<table
@ -325,7 +325,7 @@ const onNavigate = (dir: NavigateDir) => {
<div class="nc-no-label text-gray-500" :class="{ hidden: selectedAllRecords }">#</div>
<div
:class="{ hidden: !selectedAllRecords, flex: selectedAllRecords }"
class="nc-check-all w-full align-center"
class="nc-check-all w-full items-center"
>
<a-checkbox v-model:checked="selectedAllRecords" />
@ -360,7 +360,7 @@ const onNavigate = (dir: NavigateDir) => {
@click.stop="addColumnDropdown = true"
>
<a-dropdown v-model:visible="addColumnDropdown" :trigger="['click']">
<div class="h-full w-[60px] flex align-center justify-center">
<div class="h-full w-[60px] flex items-center justify-center">
<MdiPlus class="text-sm nc-column-add" />
</div>
@ -382,7 +382,7 @@ const onNavigate = (dir: NavigateDir) => {
<template #default="{ state }">
<tr class="nc-grid-row">
<td key="row-index" class="caption nc-grid-cell pl-5 pr-1">
<div class="align-center flex gap-1 min-w-[55px]">
<div class="items-center flex gap-1 min-w-[55px]">
<div
v-if="!readOnly && !isLocked"
class="nc-row-no text-xs text-gray-500"
@ -407,9 +407,12 @@ const onNavigate = (dir: NavigateDir) => {
>
{{ row.rowMeta.commentCount }}
</span>
<div v-else class="cursor-pointer flex items-center border-1 active:ring rounded p-1 hover:bg-primary/10">
<div
v-else
class="cursor-pointer flex items-center border-1 active:ring rounded p-1 hover:(bg-primary bg-opacity-10)"
>
<MdiArrowExpand
class="select-none transform hover:(text-pink-500 scale-120) nc-row-expand"
class="select-none transform hover:(text-accent scale-120) nc-row-expand"
@click="expandForm(row, state)"
/>
</div>
@ -540,7 +543,7 @@ const onNavigate = (dir: NavigateDir) => {
td:not(:first-child) > div {
overflow: hidden;
@apply flex align-center h-auto px-1;
@apply flex items-center h-auto px-1;
}
table,
@ -572,7 +575,7 @@ const onNavigate = (dir: NavigateDir) => {
}
td.active::before {
@apply bg-primary/5;
@apply bg-primary bg-opacity-5;
}
}

2
packages/nc-gui-v2/components/smartsheet/Pagination.vue

@ -34,7 +34,7 @@ const page = computed({
show-less-items
:show-size-changer="false"
/>
<div v-else class="mx-auto d-flex align-center mt-n1" style="max-width: 250px">
<div v-else class="mx-auto flex items-center mt-n1" style="max-width: 250px">
<span class="text-xs" style="white-space: nowrap"> Change page:</span>
<a-input :value="page" size="small" class="ml-1 !text-xs" type="number" @keydown.enter="changePage(page)">
<template #suffix>

9
packages/nc-gui-v2/components/smartsheet/expanded-form/Comments.vue

@ -24,13 +24,14 @@ watch(
<template>
<div class="h-full flex flex-col w-full bg-[#eceff1] p-2">
<div ref="commentsWrapperEl" class="flex-grow-1 min-h-[100px] overflow-y-auto scrollbar-thin-primary p-2 space-y-2">
<div ref="commentsWrapperEl" class="flex-1 min-h-[100px] overflow-y-auto scrollbar-thin-primary p-2 space-y-2">
<v-skeleton-loader v-if="isCommentsLoading && !commentsAndLogs" type="list-item-avatar-two-line@8" />
<template v-else>
<div v-for="log of commentsAndLogs" :key="log.id" class="flex gap-1 text-xs">
<MdiAccountCircle class="row-span-2" :class="isYou(log.user) ? 'text-pink-300' : 'text-blue-300 '" />
<div class="flex-grow">
<div class="flex-1">
<p class="mb-1 caption edited-text text-[10px] text-gray-500">
{{ isYou(log.user) ? 'You' : log.user == null ? 'Shared base' : log.user }}
{{ log.op_type === 'COMMENT' ? 'commented' : log.op_sub_type === 'INSERT' ? 'created' : 'edited' }}
@ -59,7 +60,7 @@ watch(
><span class="text-[11px] text-gray-500">Comments only</span>
</a-checkbox>
</div>
<div class="flex-shrink-1 mt-2 d-flex">
<div class="shrink mt-2 flex">
<a-input
v-model:value="comment"
class="!text-xs nc-comment-box"
@ -70,7 +71,7 @@ watch(
@keyup.enter.prevent="saveComment"
>
<template #addonBefore>
<div class="flex align-center">
<div class="flex items-center">
<mdi-account-circle class="text-lg text-pink-300" small @click="saveComment" />
</div>
</template>

6
packages/nc-gui-v2/components/smartsheet/expanded-form/Header.vue

@ -35,8 +35,8 @@ const iconColor = '#1890ff'
</script>
<template>
<div class="flex p-2 align-center gap-2 p-4">
<h5 class="text-lg font-weight-medium flex align-center gap-1 mb-0 min-w-0 overflow-x-hidden truncate">
<div class="flex p-2 items-center gap-2 p-4">
<h5 class="text-lg font-weight-medium flex items-center gap-1 mb-0 min-w-0 overflow-x-hidden truncate">
<mdi-table-arrow-right :style="{ color: iconColor }" />
<template v-if="meta">
@ -51,7 +51,7 @@ const iconColor = '#1890ff'
<template v-if="primaryValue">: {{ primaryValue }}</template>
</h5>
<div class="flex-grow" />
<div class="flex-1" />
<mdi-reload class="cursor-pointer select-none" />

4
packages/nc-gui-v2/components/smartsheet/expanded-form/index.vue

@ -109,13 +109,13 @@ export default {
<Header @cancel="onClose" />
<div class="!bg-gray-100 rounded">
<div class="flex h-full nc-form-wrapper items-stretch min-h-[70vh]">
<div class="flex-grow overflow-auto scrollbar-thin-primary">
<div class="flex-1 overflow-auto scrollbar-thin-primary">
<div class="w-[500px] mx-auto">
<div v-for="col of fields" :key="col.title" class="mt-2 py-2" :class="`nc-expand-col-${col.title}`">
<SmartsheetHeaderVirtualCell v-if="isVirtualCol(col)" :column="col" />
<SmartsheetHeaderCell v-else :column="col" />
<div class="!bg-white rounded px-1 min-h-[35px] flex align-center mt-2">
<div class="!bg-white rounded px-1 min-h-[35px] flex items-center mt-2">
<VirtualCell v-if="isVirtualCol(col)" v-model="row.row[col.title]" :row="row" :column="col" />
<Cell

4
packages/nc-gui-v2/components/smartsheet/sidebar/MenuTop.vue

@ -219,11 +219,11 @@ function onDeleted() {
}
.sortable-chosen {
@apply !bg-primary/25 text-primary;
@apply !bg-primary bg-opacity-25 text-primary;
}
.active {
@apply bg-primary/20 text-primary font-medium;
@apply bg-primary bg-opacity-25 text-primary font-medium;
}
}
</style>

2
packages/nc-gui-v2/components/smartsheet/sidebar/toolbar/AddRow.vue

@ -15,7 +15,7 @@ const onClick = () => {
<a-tooltip :placement="isOpen ? 'bottomRight' : 'left'">
<template #title> {{ $t('activity.addRow') }} </template>
<div
:class="{ 'hover:after:bg-primary/75 group': !isLocked, 'disabled-ring': isLocked }"
:class="{ 'hover:after:(bg-primary bg-opacity-75) group': !isLocked, 'disabled-ring': isLocked }"
class="nc-sidebar-right-item nc-sidebar-add-row"
>
<MdiPlusOutline :class="{ 'cursor-pointer group-hover:(!text-white)': !isLocked, 'disabled': isLocked }" @click="onClick" />

4
packages/nc-gui-v2/components/smartsheet/sidebar/toolbar/LockMenu.vue

@ -99,7 +99,7 @@ const Icon = computed(() => {
<style scoped>
.nc-menu-item > div {
@apply grid grid-cols-[30px,auto] gap-2 p-2 align-center;
@apply grid grid-cols-[30px,auto] gap-2 p-2 items-center;
}
.nc-menu-item > div > svg {
@ -107,7 +107,7 @@ const Icon = computed(() => {
}
.nc-menu-option > :first-child {
@apply align-self-center;
@apply self-center;
}
.nc-subtitle {

2
packages/nc-gui-v2/components/smartsheet/sidebar/toolbar/ToggleDrawer.vue

@ -7,7 +7,7 @@ const { isOpen, toggle } = useSidebar({ storageKey: 'nc-right-sidebar' })
<a-tooltip :placement="isOpen ? 'bottomRight' : 'left'" :mouse-enter-delay="0.8">
<template #title> Toggle sidebar</template>
<div class="nc-sidebar-right-item hover:after:bg-primary/75 group nc-sidebar-add-row">
<div class="nc-sidebar-right-item hover:after:(bg-primary bg-opacity-75) group nc-sidebar-add-row">
<MdiChevronDoubleLeft
class="cursor-pointer group-hover:(!text-white) transform transition-transform"
:class="{ 'rotate-180': isOpen }"

2
packages/nc-gui-v2/components/tabs/Auth.vue

@ -26,7 +26,7 @@ const selectedTab = $computed(() => tabsInfo[selectedTabKey])
<template>
<div>
<a-tabs v-model:active-key="selectedTabKey" :open-keys="[]" mode="horizontal" class="nc-auth-tabs mx-6">
<a-tabs v-model:active-key="selectedTabKey" :open-keys="[]" mode="horizontal" class="nc-auth-tabs !mx-6">
<a-tab-pane v-for="(tab, key) of tabsInfo" :key="key" class="select-none">
<template #tab>
<span>

2
packages/nc-gui-v2/components/tabs/Smartsheet.vue

@ -76,7 +76,7 @@ watch(isLocked, (nextValue) => (treeViewIsLockedInj.value = nextValue), { immedi
<template v-if="meta">
<div class="flex flex-1 min-h-0">
<div v-if="activeView" class="h-full flex-grow min-w-0 min-h-0">
<div v-if="activeView" class="h-full flex-1 min-w-0 min-h-0">
<SmartsheetGrid v-if="isGrid" :ref="el" />
<SmartsheetGallery v-else-if="isGallery" />

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

@ -700,7 +700,7 @@ onMounted(() => {
<a-button class="group" @click="addNewColumnRow(table, 'Number')">
<div class="flex items-center">
<mdi-numeric class="group-hover:!text-pink-500 flex text-lg" />
<mdi-numeric class="group-hover:!text-accent flex text-lg" />
</div>
</a-button>
</a-tooltip>
@ -713,7 +713,7 @@ onMounted(() => {
<a-button class="group" @click="addNewColumnRow(table, 'SingleLineText')">
<div class="flex items-center">
<mdi-alpha-a class="group-hover:!text-pink-500 text-lg" />
<mdi-alpha-a class="group-hover:!text-accent text-lg" />
</div>
</a-button>
</a-tooltip>
@ -726,7 +726,7 @@ onMounted(() => {
<a-button class="group" @click="addNewColumnRow(table, 'LongText')">
<div class="flex items-center">
<mdi-text class="group-hover:!text-pink-500 text-lg" />
<mdi-text class="group-hover:!text-accent text-lg" />
</div>
</a-button>
</a-tooltip>
@ -739,7 +739,7 @@ onMounted(() => {
<a-button class="group" @click="addNewColumnRow(table, 'SingleLineText')">
<div class="flex items-center gap-1">
<mdi-plus class="group-hover:!text-pink-500 text-lg" />
<mdi-plus class="group-hover:!text-accent text-lg" />
</div>
</a-button>
</a-tooltip>
@ -761,7 +761,7 @@ onMounted(() => {
@apply bg-white;
}
:deep(.template-form-row) > td {
@apply pa-0 mb-0;
@apply p-0 mb-0;
.ant-form-item {
@apply mb-0;
}

6
packages/nc-gui-v2/components/virtual-cell/BelongsTo.vue

@ -72,15 +72,15 @@ const unlinkRef = async (rec: Record<string, any>) => {
</script>
<template>
<div class="flex w-full chips-wrapper align-center" :class="{ active }">
<div class="chips d-flex align-center flex-grow">
<div class="flex w-full chips-wrapper items-center" :class="{ active }">
<div class="chips flex items-center flex-1">
<template v-if="value && relatedTablePrimaryValueProp">
<ItemChip :item="value" :value="value[relatedTablePrimaryValueProp]" @unlink="unlinkRef(value)" />
</template>
</div>
<div
v-if="!readOnly && !isLocked && isUIAllowed('xcDatatableEditable')"
class="flex-1 flex justify-end gap-1 min-h-[30px] align-center"
class="flex-1 flex justify-end gap-1 min-h-[30px] items-center"
>
<component
:is="addIcon"

2
packages/nc-gui-v2/components/virtual-cell/Formula.vue

@ -32,7 +32,7 @@ const urls = computed(() => replaceUrlsWithLink(result.value))
<span>ERR!</span>
</a-tooltip>
<div class="pa-2" @dblclick="showEditFormulaWarningMessage">
<div class="p-2" @dblclick="showEditFormulaWarningMessage">
<div v-if="urls" v-html="urls" />
<div v-else>{{ result }}</div>
<div v-if="showEditFormulaWarning" class="text-left text-wrap mt-2 text-[#e65100]">

6
packages/nc-gui-v2/components/virtual-cell/HasMany.vue

@ -85,9 +85,9 @@ const unlinkRef = async (rec: Record<string, any>) => {
</script>
<template>
<div class="flex align-center items-center gap-1 w-full chips-wrapper">
<div class="flex items-center items-center gap-1 w-full chips-wrapper">
<template v-if="!isForm">
<div class="chips flex align-center img-container flex-grow hm-items flex-nowrap min-w-0 overflow-hidden">
<div class="chips flex items-center img-container flex-1 hm-items flex-nowrap min-w-0 overflow-hidden">
<template v-if="cells">
<ItemChip v-for="(cell, i) of cells" :key="i" :item="cell.item" :value="cell.value" @unlink="unlinkRef(cell.item)" />
<span v-if="cellValue?.length === 10" class="caption pointer ml-1 grey--text" @click="childListDlg = true">
@ -97,7 +97,7 @@ const unlinkRef = async (rec: Record<string, any>) => {
</div>
<div
v-if="!isLocked && isUIAllowed('xcDatatableEditable')"
class="flex-grow flex justify-end gap-1 min-h-[30px] align-center"
class="flex-1 flex justify-end gap-1 min-h-[30px] items-center"
>
<MdiArrowExpand
class="select-none transform text-sm nc-action-icon text-gray-500/50 hover:text-gray-500 nc-arrow-expand"

6
packages/nc-gui-v2/components/virtual-cell/ManyToMany.vue

@ -84,9 +84,9 @@ const unlinkRef = async (rec: Record<string, any>) => {
</script>
<template>
<div class="flex align-center gap-1 w-full h-full chips-wrapper">
<div class="flex items-center gap-1 w-full h-full chips-wrapper">
<template v-if="!isForm">
<div class="chips flex align-center img-container flex-grow hm-items flex-nowrap min-w-0 overflow-hidden">
<div class="chips flex items-center img-container flex-1 hm-items flex-nowrap min-w-0 overflow-hidden">
<template v-if="cells">
<ItemChip v-for="(cell, i) of cells" :key="i" :item="cell.item" :value="cell.value" @unlink="unlinkRef(cell.item)" />
@ -94,7 +94,7 @@ const unlinkRef = async (rec: Record<string, any>) => {
</template>
</div>
<div v-if="!isLocked && isUIAllowed('xcDatatableEditable')" class="flex-1 flex justify-end gap-1 min-h-[30px] align-center">
<div v-if="!isLocked && isUIAllowed('xcDatatableEditable')" class="flex-1 flex justify-end gap-1 min-h-[30px] items-center">
<MdiArrowExpand
class="text-sm nc-action-icon text-gray-500/50 hover:text-gray-500 nc-arrow-expand"
@click="childListDlg = true"

4
packages/nc-gui-v2/components/virtual-cell/components/ItemChip.vue

@ -44,13 +44,13 @@ export default {
<template>
<div
class="group py-1 px-2 mr-1 my-1 flex align-center bg-blue-100/60 hover:bg-blue-100/40 rounded-[2px]"
class="group py-1 px-2 mr-1 my-1 flex items-center bg-blue-100/60 hover:bg-blue-100/40 rounded-[2px]"
:class="{ active }"
@click="expandedFormDlg = true"
>
<span class="name">{{ value }}</span>
<div v-show="active || isForm" v-if="!readOnly && !isLocked && isUIAllowed('xcDatatableEditable')" class="flex align-center">
<div v-show="active || isForm" v-if="!readOnly && !isLocked && isUIAllowed('xcDatatableEditable')" class="flex items-center">
<MdiCloseThick class="unlink-icon text-xs text-gray-500/50 group-hover:text-gray-500" @click.stop="emit('unlink')" />
</div>

10
packages/nc-gui-v2/components/virtual-cell/components/ListChildItems.vue

@ -83,13 +83,13 @@ const expandedFormRow = ref()
<template>
<component :is="container" v-model:visible="vModel" :footer="null" title="Child list">
<div class="max-h-[max(calc(100vh_-_300px)_,500px)] flex flex-col">
<div class="flex mb-4 align-center gap-2">
<div class="flex mb-4 items-center gap-2">
<div class="flex-1" />
<MdiReload v-if="!isForm" class="cursor-pointer text-gray-500" @click="loadChildrenList" />
<a-button v-if="!readonly" type="primary" ghost class="!text-xs" size="small" @click="emit('attachRecord')">
<div class="flex align-center gap-1">
<div class="flex items-center gap-1">
<MdiLinkVariantRemove class="text-xs" type="primary" @click="unlinkRow(row)" />
Link to '{{ meta.title }}'
</div>
@ -100,7 +100,7 @@ const expandedFormRow = ref()
<a-card
v-for="(row, i) of childrenList?.list ?? state?.[column?.title] ?? []"
:key="i"
class="ma-2 hover:(!bg-gray-200/50 shadow-md)"
class="m-2 hover:(!bg-gray-200/50 shadow-md)"
@click="
() => {
expandedFormRow = row
@ -108,8 +108,8 @@ const expandedFormRow = ref()
}
"
>
<div class="flex align-center">
<div class="flex-grow overflow-hidden min-w-0">
<div class="flex items-center">
<div class="flex-1 overflow-hidden min-w-0">
{{ row[relatedTablePrimaryValueProp] }}
<span class="text-gray-400 text-[11px] ml-1">(Primary key : {{ getRelatedTableRowId(row) }})</span>
</div>

4
packages/nc-gui-v2/components/virtual-cell/components/ListItems.vue

@ -99,7 +99,7 @@ const newRowState = computed(() => {
<template>
<a-modal v-model:visible="vModel" :footer="null" title="Link Record">
<div class="max-h-[max(calc(100vh_-_300px)_,500px)] flex flex-col">
<div class="flex mb-4 align-center gap-2">
<div class="flex mb-4 items-center gap-2">
<a-input
v-model:value="childrenExcludedListPagination.query"
placeholder="Filter query"
@ -115,7 +115,7 @@ const newRowState = computed(() => {
<a-card
v-for="(refRow, i) in childrenExcludedList?.list ?? []"
:key="i"
class="ma-2 cursor-pointer hover:(!bg-gray-200/50 shadow-md) group"
class="m-2 cursor-pointer hover:(!bg-gray-200/50 shadow-md) group"
@click="linkRow(refRow)"
>
{{ refRow[relatedTablePrimaryValueProp]

2
packages/nc-gui-v2/components/webhook/Drawer.vue

@ -35,7 +35,7 @@ async function editHook(hook: Record<string, any>) {
<WebhookEditor v-if="editOrAdd" ref="webhookEditorRef" @back-to-list="editOrAdd = false" />
<WebhookList v-else @edit="editHook" @add="editOrAdd = true" />
</div>
<div class="self-center flex flex-column flex-wrap gap-4 items-center mt-4 md:mx-8 md:justify-between justify-center">
<div class="self-center flex flex-col flex-wrap gap-4 items-center mt-4 md:mx-8 md:justify-between justify-center">
<a-button v-t="['e:hiring']" href="https://angel.co/company/nocodb" target="_blank" size="large">
🚀 We are Hiring! 🚀
</a-button>

4
packages/nc-gui-v2/components/webhook/List.vue

@ -64,7 +64,7 @@ onMounted(() => {
class="cursor-pointer max-h-[75vh] overflow-y-auto scrollbar-thin-primary"
>
<template #renderItem="{ item, index }">
<a-list-item class="pa-2" @click="emit('edit', item)">
<a-list-item class="p-2" @click="emit('edit', item)">
<a-list-item-meta>
<template #description>
<span class="uppercase"> {{ item.event }} {{ item.operation }}</span>
@ -93,7 +93,7 @@ onMounted(() => {
</template>
</a-list>
</div>
<div v-else class="pa-4 bg-gray-100 text-gray-600">
<div v-else class="p-4 bg-gray-100 text-gray-600">
Webhooks list is empty, create new webhook by clicking 'Create webhook' button.
</div>
</div>

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

@ -3,6 +3,7 @@ export * from './useDialog'
export * from './useGlobal'
export * from './useInjectionState'
export * from './useSidebar'
export * from './useTheme'
export * from './useUIPermission'
export * from './useAttachment'
export * from './useColors'

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

@ -0,0 +1,60 @@
import { ConfigProvider } from 'ant-design-vue'
import type { Theme as AntTheme } from 'ant-design-vue/es/config-provider'
import { useStorage } from '@vueuse/core'
import { NOCO, hexToRGB, themeV2Colors, toRefs, useCssVar, useInjectionState } from '#imports'
interface ThemeConfig extends AntTheme {
primaryColor: string
accentColor: string
}
const [setup, use] = useInjectionState((config?: Partial<ThemeConfig>) => {
const primaryColor = useCssVar('--color-primary', typeof document !== 'undefined' ? document.documentElement : null)
const accentColor = useCssVar('--color-accent', typeof document !== 'undefined' ? document.documentElement : null)
/** current theme config */
const currentTheme = useStorage<ThemeConfig>(
`${NOCO}db-theme`,
{
primaryColor: themeV2Colors['royal-blue'].DEFAULT,
accentColor: themeV2Colors.pink['500'],
},
localStorage,
{ mergeDefaults: true },
)
/** set initial config */
setTheme(config ?? currentTheme.value)
/** set theme (persists in localstorage) */
function setTheme(theme: Partial<ThemeConfig>) {
// convert hex colors to rgb values
if (theme.primaryColor) primaryColor.value = hexToRGB(theme.primaryColor)
if (theme.accentColor) accentColor.value = hexToRGB(theme.accentColor)
currentTheme.value = theme as ThemeConfig
ConfigProvider.config({
theme,
})
}
return {
theme: currentTheme,
setTheme,
}
})
export const provideTheme = setup
export function useTheme(config?: Partial<ThemeConfig>) {
const theme = use()
if (!theme) {
return setup(config)
} else {
if (config) theme.setTheme(config)
}
return theme
}

2
packages/nc-gui-v2/lang/nl.json

@ -447,7 +447,7 @@
"importZip": "Import project meta zip-bestand en start opnieuw op.",
"importText": "Import NocoDB-project door het uploaden van het metadata zip-bestand",
"metaNoChange": "Geen verandering gevonden",
"sqlMigration": "Schema-migraties worden automatisch gemaakt. Maak een tabel en vernieuw deze pagina.",
"sqlMigration": "Schem-migraties worden automatisch gemaakt. Maak een tabel en vernieuw deze pagina.",
"dbConnectionStatus": "Omgeving gevalideerd",
"dbConnected": "Succesvolle verbinding",
"notifications": {

22
packages/nc-gui-v2/layouts/base.vue

@ -36,7 +36,7 @@ const logout = () => {
<img width="35" alt="NocoDB" src="~/assets/img/icons/512x512-trans.png" />
</div>
<div class="flex justify-center">
<div class="!text-white flex justify-center">
<div v-show="isLoading" class="flex items-center gap-2 ml-3">
{{ $t('general.loading') }}
@ -53,20 +53,20 @@ const logout = () => {
<a-tooltip placement="bottom" :mouse-enter-delay="1">
<template #title> Switch language</template>
<div class="flex pr-4 items-center">
<GeneralLanguage class="cursor-pointer text-2xl hover:text-pink-500" />
<div class="flex pr-4 items-center text-white">
<GeneralLanguage class="cursor-pointer text-2xl hover:text-accent" />
</div>
</a-tooltip>
<template v-if="signedIn && !isSharedBase">
<a-dropdown :trigger="['click']">
<MdiDotsVertical class="md:text-xl cursor-pointer hover:text-pink-500 nc-menu-accounts" @click.prevent />
<MdiDotsVertical class="md:text-xl cursor-pointer hover:text-accent nc-menu-accounts text-white" @click.prevent />
<template #overlay>
<a-menu class="!py-0 dark:(!bg-gray-800) leading-8 !rounded">
<a-menu class="!py-0 leading-8 !rounded">
<a-menu-item key="0" class="!rounded-t">
<nuxt-link v-t="['c:navbar:user:email']" class="nc-project-menu-item group no-underline" to="/user">
<MdiAt class="mt-1 group-hover:text-pink-500" />&nbsp;
<nuxt-link v-t="['c:navbar:user:email']" class="nc-project-menu-item group !no-underline" to="/user">
<MdiAt class="mt-1 group-hover:text-accent" />&nbsp;
<span class="prose group-hover:text-primary"> {{ email }}</span>
</nuxt-link>
@ -76,7 +76,7 @@ const logout = () => {
<a-menu-item key="1" class="!rounded-b group">
<div v-t="['a:navbar:user:sign-out']" class="nc-project-menu-item group" @click="logout">
<MdiLogout class="group-hover:text-pink-500" />&nbsp;
<MdiLogout class="group-hover:text-accent" />&nbsp;
<span class="prose group-hover:text-primary">
{{ $t('general.signOut') }}
@ -109,7 +109,7 @@ const logout = () => {
<style lang="scss" scoped>
.nc-lang-btn {
@apply color-transition flex items-center justify-center fixed bottom-10 right-10 z-99 w-12 h-12 rounded-full shadow-md shadow-gray-500 p-2 !bg-primary text-white active:(ring ring-pink-500) hover:(ring ring-pink-500);
@apply color-transition flex items-center justify-center fixed bottom-10 right-10 z-99 w-12 h-12 rounded-full shadow-md shadow-gray-500 p-2 !bg-primary text-white active:(ring ring-accent) hover:(ring ring-accent);
&::after {
@apply rounded-full absolute top-0 left-0 right-0 bottom-0 transition-all duration-150 ease-in-out bg-primary;
@ -118,11 +118,11 @@ const logout = () => {
}
&:hover::after {
@apply transform scale-110 ring ring-pink-500;
@apply transform scale-110 ring ring-accent;
}
&:active::after {
@apply ring ring-pink-500;
@apply ring ring-accent;
}
}
</style>

1
packages/nc-gui-v2/nuxt-shim.d.ts vendored

@ -29,5 +29,6 @@ declare module 'vue-router' {
interface RouteMeta {
requiresAuth?: boolean
public?: boolean
hideHeader?: boolean
}
}

16
packages/nc-gui-v2/nuxt.config.ts

@ -6,7 +6,6 @@ import IconsResolver from 'unplugin-icons/resolver'
import Components from 'unplugin-vue-components/vite'
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'
import monacoEditorPlugin from 'vite-plugin-monaco-editor'
import { themeColors } from './utils/colorsUtils'
// https://v3.nuxtjs.org/api/configuration/nuxt.config
export default defineNuxtConfig({
@ -17,11 +16,9 @@ export default defineNuxtConfig({
css: [
'virtual:windi.css',
'virtual:windi-devtools',
'vuetify/lib/styles/main.sass',
'~/assets/style/fonts.css',
'~/assets/css/global.css',
'~/assets/style.css',
'~/assets/style-v2.scss',
'~/assets/style.scss',
],
meta: {
@ -55,14 +52,6 @@ export default defineNuxtConfig({
external: 'httpsnippet',
},
},
css: {
preprocessorOptions: {
less: {
modifyVars: { 'primary-color': themeColors.primary, 'text-color': 'rgba(61, 61, 61, 1)' },
javascriptEnabled: true,
},
},
},
plugins: [
vueI18n({
include: path.resolve(__dirname, './lang'),
@ -76,7 +65,8 @@ export default defineNuxtConfig({
Components({
resolvers: [
AntDesignVueResolver({
importStyle: 'less',
importStyle: false,
resolveIcons: false,
}),
IconsResolver({
prefix: false,

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

File diff suppressed because it is too large Load Diff

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

@ -16,7 +16,7 @@
"@vuelidate/validators": "^2.0.0-alpha.31",
"@vueuse/core": "^9.0.2",
"@vueuse/integrations": "^9.0.2",
"ant-design-vue": "^3.2.10",
"ant-design-vue": "^3.2.11",
"dayjs": "^1.11.3",
"file-saver": "^2.0.5",
"jsep": "^1.3.6",
@ -33,7 +33,6 @@
"vue-github-button": "^3.0.3",
"vue-i18n": "^9.1.10",
"vuedraggable": "^4.1.0",
"vuetify": "^3.0.0-alpha.13",
"xlsx": "^0.17.3"
},
"devDependencies": {
@ -66,13 +65,12 @@
"eslint-plugin-prettier": "^4.0.0",
"happy-dom": "^6.0.3",
"httpsnippet": "^2.0.0",
"less": "^4.1.3",
"nuxt": "3.0.0-rc.4",
"nuxt-windicss": "^2.5.0",
"prettier": "^2.7.1",
"sass": "^1.53.0",
"unplugin-icons": "^0.14.7",
"unplugin-vue-components": "^0.21.1",
"unplugin-vue-components": "^0.22.4",
"vite-plugin-monaco-editor": "^1.1.0",
"vitest": "^0.18.0",
"windicss": "^3.5.6"

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

@ -1,6 +1,9 @@
<script setup lang="ts">
import { Chrome } from '@ckpack/vue-color'
import { message } from 'ant-design-vue'
import {
computed,
definePageMeta,
navigateTo,
onKeyStroke,
openLink,
@ -12,10 +15,16 @@ import {
useProject,
useRoute,
useTabs,
useTheme,
useUIPermission,
watch,
} from '#imports'
import { TabType } from '~/composables'
definePageMeta({
hideHeader: true,
})
const route = useRoute()
const { appInfo, token, signOut, signedIn, user } = useGlobal()
@ -41,16 +50,31 @@ const openDialogKey = ref<string>()
const dropdownOpen = ref(false)
/** Sidebar ref */
const sidebar = ref()
const pickedColor = ref<any>('#ffffff')
let pickerActive = $ref<boolean | 'primary' | 'accent'>(false)
const email = computed(() => user.value?.email ?? '---')
const { setTheme, theme } = useTheme()
watch(pickedColor, (nextColor) => {
if (pickerActive && nextColor.hex) {
setTheme({
primaryColor: pickerActive === 'primary' ? nextColor.hex : theme.value.primaryColor,
accentColor: pickerActive === 'accent' ? nextColor.hex : theme.value.accentColor,
})
}
})
const logout = () => {
signOut()
navigateTo('/signin')
}
/** Sidebar ref */
const sidebar = ref()
onKeyStroke(
'Escape',
() => {
@ -102,9 +126,21 @@ const copyAuthToken = async () => {
}
}
definePageMeta({
hideHeader: true,
})
const openColorPicker = (type: 'primary' | 'accent') => {
if (!pickerActive || pickerActive !== type) {
pickedColor.value = type === 'primary' ? theme.value.primaryColor : theme.value.accentColor
pickerActive = type
} else {
pickerActive = false
}
}
const onMenuClose = (visible: boolean) => {
if (!visible) {
pickedColor.value = '#ffffff'
pickerActive = false
}
}
</script>
<template>
@ -152,7 +188,7 @@ definePageMeta({
</template>
</div>
<a-dropdown v-else class="h-full min-w-0 flex-1" :trigger="['click']" placement="bottom">
<a-dropdown v-else class="h-full min-w-0 flex-1" :trigger="['click']" placement="bottom" @visible-change="onMenuClose">
<div
:style="{ width: isOpen ? 'calc(100% - 40px) pr-2' : '100%' }"
:class="[isOpen ? '' : 'justify-center']"
@ -167,7 +203,7 @@ definePageMeta({
</a-tooltip>
<div v-else class="text-lg font-semibold truncate">{{ project.title }}</div>
<MdiChevronDown class="min-w-[28.5px] group-hover:text-pink-500 text-2xl" />
<MdiChevronDown class="min-w-[28.5px] group-hover:text-accent text-2xl" />
</template>
<template v-else>
@ -180,7 +216,7 @@ definePageMeta({
<a-menu-item-group>
<template #title>
<div class="group select-none flex items-center gap-4 py-1">
<MdiFolder class="group-hover:text-pink-500 text-xl" />
<MdiFolder class="group-hover:text-accent text-xl" />
<div class="flex flex-col">
<div class="text-lg group-hover:(!text-primary) font-semibold truncate">{{ project.title }}</div>
@ -188,7 +224,7 @@ definePageMeta({
<div class="flex items-center gap-1">
<div class="group-hover:(!text-primary)">ID:</div>
<div class="text-xs group-hover:text-pink-500 truncate font-italic">{{ project.id }}</div>
<div class="text-xs group-hover:text-accent truncate font-italic">{{ project.id }}</div>
</div>
</div>
</div>
@ -196,7 +232,7 @@ definePageMeta({
<a-menu-item key="copy">
<div class="nc-project-menu-item group" @click.stop="copyProjectInfo">
<MdiContentCopy class="group-hover:text-pink-500" />
<MdiContentCopy class="group-hover:text-accent" />
Copy Project Info
</div>
</a-menu-item>
@ -210,14 +246,14 @@ definePageMeta({
class="nc-project-menu-item group"
@click.stop="openLink(`/api/v1/db/meta/projects/${route.params.projectId}/swagger`, appInfo.ncSiteUrl)"
>
<MdiApi class="group-hover:text-pink-500" />
<MdiApi class="group-hover:text-accent" />
Swagger: Rest APIs
</div>
</a-menu-item>
<a-menu-item key="copy">
<div v-t="['a:navbar:user:copy-auth-token']" class="nc-project-menu-item group" @click.stop="copyAuthToken">
<MdiScriptTextKeyOutline class="group-hover:text-pink-500" />
<MdiScriptTextKeyOutline class="group-hover:text-accent" />
Copy Auth Token
</div>
</a-menu-item>
@ -231,7 +267,7 @@ definePageMeta({
class="nc-project-menu-item group"
@click="toggleDialog(true, 'teamAndAuth')"
>
<MdiCog class="group-hover:text-pink-500" />
<MdiCog class="group-hover:text-accent" />
Team & Settings
</div>
</a-menu-item>
@ -241,13 +277,13 @@ definePageMeta({
<a-sub-menu v-if="isUIAllowed('previewAs')" key="preview-as">
<template #title>
<div v-t="['c:navdraw:preview-as']" class="nc-project-menu-item group">
<MdiFileEyeOutline class="group-hover:text-pink-500" />
<MdiFileEyeOutline class="group-hover:text-accent" />
Preview Project As
<div class="flex-1" />
<MaterialSymbolsChevronRightRounded
class="transform group-hover:(scale-115 text-pink-500) text-xl text-gray-400"
class="transform group-hover:(scale-115 text-accent) text-xl text-gray-400"
/>
</div>
</template>
@ -260,12 +296,12 @@ definePageMeta({
<a-sub-menu key="language" class="lang-menu scrollbar-thin-dull min-w-50 max-h-90vh overflow-auto !py-0">
<template #title>
<div class="nc-project-menu-item group">
<MaterialSymbolsTranslate class="group-hover:text-pink-500 nc-language" />
<MaterialSymbolsTranslate class="group-hover:text-accent nc-language" />
Language
<div class="flex-1" />
<MaterialSymbolsChevronRightRounded
class="transform group-hover:(scale-115 text-pink-500) text-xl text-gray-400"
class="transform group-hover:(scale-115 text-accent) text-xl text-gray-400"
/>
</div>
</template>
@ -278,12 +314,12 @@ definePageMeta({
<a-sub-menu v-if="isUIAllowed('previewAs')" key="account">
<template #title>
<div class="nc-project-menu-item group">
<MdiAccount class="group-hover:text-pink-500" />
<MdiAccount class="group-hover:text-accent" />
Account
<div class="flex-1" />
<MaterialSymbolsChevronRightRounded
class="transform group-hover:(scale-115 text-pink-500) text-xl text-gray-400"
class="transform group-hover:(scale-115 text-accent) text-xl text-gray-400"
/>
</div>
</template>
@ -292,7 +328,7 @@ definePageMeta({
<a-menu-item key="0" class="!rounded-t">
<nuxt-link v-t="['c:navbar:user:email']" class="nc-project-menu-item group no-underline" to="/user">
<MdiAt class="mt-1 group-hover:text-pink-500" />&nbsp;
<MdiAt class="mt-1 group-hover:text-accent" />&nbsp;
<span class="prose-sm">{{ email }}</span>
</nuxt-link>
@ -300,7 +336,7 @@ definePageMeta({
<a-menu-item key="1" class="!rounded-b">
<div v-t="['a:navbar:user:sign-out']" class="nc-project-menu-item group" @click="logout">
<MdiLogout class="group-hover:(!text-pink-500)" />&nbsp;
<MdiLogout class="group-hover:(!text-accent)" />&nbsp;
<span class="prose-sm">
{{ $t('general.signOut') }}
@ -309,12 +345,53 @@ definePageMeta({
</a-menu-item>
</a-sub-menu>
</template>
<a-menu-divider />
<a-sub-menu>
<template #title>
<div class="nc-project-menu-item group">
<ClarityImageLine class="group-hover:text-accent" />
Theme
<div class="flex-1" />
<MaterialSymbolsChevronRightRounded
class="transform group-hover:(scale-115 text-accent) text-xl text-gray-400"
/>
</div>
</template>
<a-menu-item>
<div class="nc-project-menu-item group" @click.stop="openColorPicker('primary')">
<ClarityColorPickerSolid class="group-hover:text-accent" />
Primary Color
</div>
</a-menu-item>
<a-menu-item>
<div class="nc-project-menu-item group" @click.stop="openColorPicker('accent')">
<ClarityColorPickerSolid class="group-hover:text-accent" />
Accent Color
</div>
</a-menu-item>
</a-sub-menu>
<Chrome
v-if="pickerActive"
v-model="pickedColor"
class="z-99 absolute right-[-225px]"
@click.stop
@blur="onMenuClose(false)"
/>
</a-menu-item-group>
</a-menu>
</template>
</a-dropdown>
<div class="nc-sidebar-left-toggle-icon hover:after:bg-primary/75 group nc-sidebar-add-row flex align-center px-2">
<div
class="nc-sidebar-left-toggle-icon hover:after:(bg-primary bg-opacity-75) group nc-sidebar-add-row flex items-center px-2"
>
<MdiBackburger
class="cursor-pointer transform transition-transform duration-500"
:class="{ 'rotate-180': !isOpen }"

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

@ -29,8 +29,11 @@ const { isOpen, toggle } = useSidebar()
<template>
<div class="h-full w-full nc-container">
<div class="h-full w-full flex flex-col">
<div class="flex items-end !min-h-[50px] bg-primary/100">
<div v-if="!isOpen" class="nc-sidebar-left-toggle-icon hover:after:bg-primary/75 group nc-sidebar-add-row py-2 px-3">
<div class="flex items-end !min-h-[50px] !bg-primary">
<div
v-if="!isOpen"
class="nc-sidebar-left-toggle-icon hover:after:(bg-primary bg-opacity-75) group nc-sidebar-add-row py-2 px-3"
>
<MdiMenu
class="cursor-pointer transform transition-transform duration-500 text-white"
:class="{ 'rotate-180': !isOpen }"
@ -41,7 +44,7 @@ const { isOpen, toggle } = useSidebar()
<a-tabs v-model:activeKey="activeTabIndex" class="nc-root-tabs" type="editable-card" @edit="closeTab(activeTabIndex)">
<a-tab-pane v-for="(tab, i) in tabs" :key="i">
<template #tab>
<div class="flex align-center gap-2">
<div class="flex items-center gap-2">
<component :is="icon(tab)" class="text-sm" />
{{ tab.title }}
@ -49,8 +52,10 @@ const { isOpen, toggle } = useSidebar()
</template>
</a-tab-pane>
</a-tabs>
<span class="flex-1" />
<div class="flex justify-center align-self-center mr-2 min-w-[115px]">
<div class="flex justify-center self-center mr-2 min-w-[115px]">
<div v-show="isLoading" class="flex items-center gap-2 ml-3 text-white">
{{ $t('general.loading') }}

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

@ -121,7 +121,7 @@ function openQuickImportDialog(type: QuickImportTypes, file: File) {
class="text-3xl flex items-center justify-center gap-2 border-1 border-dashed rounded hover:border-primary"
@click="open"
>
<template v-if="isOverDropZone"> <MaterialSymbolsFileCopyOutline class="text-pink-500" /> Drop here </template>
<template v-if="isOverDropZone"> <MaterialSymbolsFileCopyOutline class="text-accent" /> Drop here </template>
</general-overlay>
<div class="flex flex-col gap-6 items-center justify-center md:w-1/2 mx-auto text-center">

74
packages/nc-gui-v2/pages/forgot-password.vue

@ -59,39 +59,30 @@ function resetError() {
<template>
<NuxtLayout>
<a-form
ref="formValidator"
layout="vertical"
:model="form"
class="bg-primary/5 forgot-password h-full flex justify-center items-center nc-form-signin"
@finish="resetPassword"
>
<div class="h-full w-full flex flex-col items-center justify-center pt-[50px]">
<div
class="color-transition bg-white dark:(!bg-gray-900 !text-white) relative flex flex-col justify-center gap-2 w-full max-w-[500px] mx-auto p-8 md:(rounded-lg border-1 border-gray-200 shadow-xl)"
>
<general-noco-icon
class="color-transition hover:(ring ring-pink-500)"
:class="[isLoading ? 'animated-bg-gradient' : '']"
/>
<div class="self-center flex flex-col justify-center items-center text-center gap-2">
<h1 class="prose-2xl font-bold my-4 w-full">{{ $t('title.resetPassword') }}</h1>
<template v-if="!success">
<div class="prose-sm">{{ $t('msg.info.passwordRecovery.message_1') }}</div>
<div class="prose-sm mb-4">{{ $t('msg.info.passwordRecovery.message_2') }}</div>
</template>
<template v-else>
<div class="prose-sm text-success flex items-center leading-8 gap-2">
{{ $t('msg.info.passwordRecovery.success') }} <ClaritySuccessLine />
</div>
<div class="md:bg-primary bg-opacity-5 forgot-password h-full min-h-[600px] flex flex-col justify-center items-center">
<div
class="bg-white mt-[60px] relative flex flex-col justify-center gap-2 w-full max-w-[500px] mx-auto p-8 md:(rounded-lg border-1 border-gray-200 shadow-xl)"
>
<general-noco-icon class="color-transition hover:(ring ring-accent)" :class="[isLoading ? 'animated-bg-gradient' : '']" />
<div class="self-center flex flex-col justify-center items-center text-center gap-2">
<h1 class="prose-2xl font-bold my-4 w-full">{{ $t('title.resetPassword') }}</h1>
<template v-if="!success">
<div class="prose-sm">{{ $t('msg.info.passwordRecovery.message_1') }}</div>
<div class="prose-sm mb-4">{{ $t('msg.info.passwordRecovery.message_2') }}</div>
</template>
<template v-else>
<div class="prose-sm text-success flex items-center leading-8 gap-2">
{{ $t('msg.info.passwordRecovery.success') }} <ClaritySuccessLine />
</div>
<nuxt-link to="/signin">{{ $t('general.signIn') }}</nuxt-link>
</template>
</div>
<nuxt-link to="/signin">{{ $t('general.signIn') }}</nuxt-link>
</template>
</div>
<a-form ref="formValidator" layout="vertical" :model="form" no-style @finish="resetPassword">
<Transition name="layout">
<div v-if="error" class="self-center mb-4 bg-red-500 text-white rounded-lg w-3/4 mx-auto p-1">
<div class="flex items-center gap-2 justify-center">
@ -102,12 +93,15 @@ function resetError() {
</Transition>
<a-form-item :label="$t('labels.email')" name="email" :rules="formRules.email">
<a-input v-model:value="form.email" size="large" :placeholder="$t('labels.email')" @focus="resetError" />
<a-input v-model:value="form.email" size="large" :placeholder="$t('msg.info.signUp.workEmail')" @focus="resetError" />
</a-form-item>
<div class="self-center flex flex-col gap-4 items-center justify-center w-full">
<button class="submit" type="submit">
<span class="flex items-center gap-2"><MdiLogin /> {{ $t('activity.sendEmail') }}</span>
<span class="flex items-center gap-2">
<MdiLogin />
{{ $t('activity.sendEmail') }}
</span>
</button>
<div class="text-end prose-sm">
@ -115,9 +109,9 @@ function resetError() {
<nuxt-link to="/signin">{{ $t('general.signIn') }}</nuxt-link>
</div>
</div>
</div>
</a-form>
</div>
</a-form>
</div>
</NuxtLayout>
</template>
@ -125,24 +119,24 @@ function resetError() {
.forgot-password {
.ant-input-affix-wrapper,
.ant-input {
@apply dark:(bg-gray-700 !text-white) !appearance-none my-1 border-1 border-solid border-primary/50 rounded;
@apply !appearance-none my-1 border-1 border-solid border-primary/50 rounded;
}
.submit {
@apply z-1 relative color-transition border border-gray-300 rounded-md p-3 text-white;
@apply z-1 relative color-transition rounded p-3 text-white shadow;
&::after {
@apply rounded-md absolute top-0 left-0 right-0 bottom-0 transition-all duration-150 ease-in-out bg-primary;
@apply rounded absolute top-0 left-0 right-0 bottom-0 transition-all duration-150 ease-in-out bg-primary;
content: '';
z-index: -1;
}
&:hover::after {
@apply transform scale-110 ring ring-pink-500;
@apply transform scale-110 ring ring-accent;
}
&:active::after {
@apply ring ring-pink-500;
@apply ring ring-accent;
}
}
}

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

@ -3,6 +3,7 @@ import { Modal, message } from 'ant-design-vue'
import type { ProjectType } from 'nocodb-sdk'
import {
computed,
definePageMeta,
extractSdkResponseErrorMsg,
navigateTo,
onMounted,
@ -13,6 +14,10 @@ import {
useUIPermission,
} from '#imports'
definePageMeta({
title: 'title.myProject',
})
const { $e } = useNuxtApp()
const { api, isLoading } = useApi()
@ -76,12 +81,12 @@ onMounted(() => {
<a-tooltip title="Reload projects">
<span
class="transition-all duration-200 h-full flex items-center group hover:ring active:(ring ring-pink-500) rounded-full mt-1"
class="transition-all duration-200 h-full flex items-center group hover:ring active:(ring ring-accent) rounded-full mt-1"
:class="isLoading ? 'animate-spin ring ring-gray-200' : ''"
>
<MdiRefresh
v-t="['a:project:refresh']"
class="text-xl text-gray-500 group-hover:text-pink-500 cursor-pointer"
class="text-xl text-gray-500 group-hover:text-accent cursor-pointer"
:class="isLoading ? '!text-primary' : ''"
@click="loadProjects"
/>
@ -96,7 +101,7 @@ onMounted(() => {
:placeholder="$t('activity.searchProject')"
/>
<div class="flex-grow" />
<div class="flex-1" />
<a-dropdown v-if="isUIAllowed('projectCreate', true)" :trigger="['click']">
<button class="nc-new-project-menu">
@ -160,7 +165,7 @@ onMounted(() => {
<a-table-column key="title" :title="$t('general.title')" data-index="title">
<template #default="{ text }">
<div
class="capitalize color-transition group-hover:text-pink-500 !w-[400px] overflow-hidden overflow-ellipsis whitespace-nowrap"
class="capitalize color-transition group-hover:text-accent !w-[400px] overflow-hidden overflow-ellipsis whitespace-nowrap"
>
{{ text }}
</div>
@ -199,7 +204,7 @@ onMounted(() => {
<style scoped>
.nc-action-btn {
@apply text-gray-500 hover:(text-pink-500 ring) active:(ring ring-pink-500) cursor-pointer p-2 w-[30px] h-[30px] hover:bg-gray-300/50 rounded-full;
@apply text-gray-500 hover:(text-accent ring) active:(ring ring-accent) cursor-pointer p-2 w-[30px] h-[30px] hover:bg-gray-300/50 rounded-full;
}
.nc-new-project-menu {
@ -212,11 +217,11 @@ onMounted(() => {
}
&:hover::after {
@apply transform scale-110 ring ring-pink-500;
@apply transform scale-110 ring ring-accent;
}
&:active::after {
@apply ring ring-pink-500;
@apply ring ring-accent;
}
}

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

@ -131,21 +131,17 @@ const resetError = () => {
.ant-input-affix-wrapper,
.ant-input {
@apply dark:(!bg-gray-700 !text-white) !appearance-none my-1 border-1 border-solid border-primary/50 rounded;
@apply !appearance-none my-1 border-1 border-solid border-primary/50 rounded;
}
.password {
input {
@apply !border-none;
}
.ant-input-password-icon {
@apply dark:!text-white;
}
}
.submit {
@apply ml-1 border border-gray-300 rounded-lg p-4 bg-gray-100/50 text-white bg-primary hover:bg-primary/75 dark:(!bg-secondary/75 hover:!bg-secondary/50);
@apply ml-1 border border-gray-300 rounded-lg p-4 bg-gray-100/50 text-white bg-primary hover:(bg-primary bg-opacity-75);
}
}
</style>

16
packages/nc-gui-v2/pages/projects/index.vue

@ -64,10 +64,10 @@ const deleteProject = (project: ProjectType) => {
<v-menu class="select-none">
<template #activator="{ props }">
<div
class="color-transition hover:(bg-gray-100 dark:bg-secondary/25) dark:(bg-secondary/50 !text-white shadow-gray-600) mr-auto select-none flex items-center gap-2 leading-8 cursor-pointer rounded-full border-1 border-gray-300 px-5 py-2 shadow prose-lg font-semibold"
class="color-transition hover:(bg-gray-100) mr-auto select-none flex items-center gap-2 leading-8 cursor-pointer rounded-full border-1 border-gray-300 px-5 py-2 shadow prose-lg font-semibold"
@click="props.onClick"
>
<MdiPlus class="text-primary dark:(!text-white) text-2xl" />
<MdiPlus class="text-primary text-2xl" />
{{ $t('title.newProj') }}
</div>
</template>
@ -91,7 +91,7 @@ const deleteProject = (project: ProjectType) => {
</v-menu>
</div>
<a-menu class="pr-4 dark:bg-gray-800 dark:text-white flex-1 border-0">
<a-menu class="pr-4 flex-1 border-0">
<a-menu-item
v-for="(option, index) in navDrawerOptions"
:key="index"
@ -114,20 +114,20 @@ const deleteProject = (project: ProjectType) => {
</div>
</template>
<v-container class="flex-1 mb-12">
<div class="flex-1 mb-12">
<div class="flex">
<div class="flex-1 text-2xl md:text-4xl font-bold text-gray-500 dark:text-white p-4">
<div class="flex-1 text-2xl md:text-4xl font-bold text-gray-500 p-4">
{{ activePage }}
</div>
<div class="self-end flex text-4xl mb-1">
<MaterialSymbolsGridView
:class="route.name === 'index-index' ? '!text-primary dark:(!text-secondary/75)' : ''"
:class="route.name === 'index-index' ? '!text-primary' : ''"
class="cursor-pointer p-2 hover:bg-gray-300/50 rounded-full"
@click="navigateTo('/')"
/>
<MaterialSymbolsFormatListBulletedRounded
:class="route.name === 'index-index-list' ? '!text-primary dark:(!text-secondary/75)' : ''"
:class="route.name === 'index-index-list' ? '!text-primary' : ''"
class="cursor-pointer p-2 hover:bg-gray-300/50 rounded-full"
@click="navigateTo('/list')"
/>
@ -137,7 +137,7 @@ const deleteProject = (project: ProjectType) => {
<a-divider class="!mb-4 lg:(!mb-8)" />
<NuxtPage :projects="projects" @delete-project="deleteProject" />
</v-container>
</div>
<a-modal></a-modal>
</NuxtLayout>

2
packages/nc-gui-v2/pages/projects/index/list.vue

@ -32,7 +32,7 @@ const openProject = async (project: ProjectType) => {
<template v-for="project of projects" :key="project.id">
<div
class="cursor-pointer grid grid-cols-3 gap-2 prose-md hover:(bg-gray-300/30 dark:bg-gray-500/30 shadow-sm) p-2 transition-color ease-in duration-100"
class="cursor-pointer grid grid-cols-3 gap-2 prose-md hover:(bg-gray-300/30) p-2 transition-color ease-in duration-100"
@click="openProject(project)"
>
<div class="font-semibold capitalize">{{ project.title || 'Untitled' }}</div>

56
packages/nc-gui-v2/pages/signin.vue

@ -80,24 +80,15 @@ function resetError() {
<template>
<NuxtLayout>
<a-form
ref="formValidator"
:model="form"
layout="vertical"
class="bg-primary/5 signin h-full flex justify-center items-center nc-form-signin"
@finish="signIn"
>
<div class="h-full w-full flex flex-col items-center justify-center pt-[50px]">
<div
class="bg-white dark:(!bg-gray-900 !text-white) relative flex flex-col justify-center gap-2 w-full max-w-[500px] mx-auto p-8 md:(rounded-lg border-1 border-gray-200 shadow-xl)"
>
<general-noco-icon
class="!rounded-full color-transition hover:(ring ring-pink-500)"
:class="[isLoading ? 'animated-bg-gradient' : '']"
/>
<h1 class="prose-2xl font-bold self-center my-4">{{ $t('general.signIn') }}</h1>
<div class="md:bg-primary bg-opacity-5 signin h-full min-h-[600px] flex flex-col justify-center items-center nc-form-signup">
<div
class="bg-white mt-[60px] relative flex flex-col justify-center gap-2 w-full max-w-[500px] mx-auto p-8 md:(rounded-lg border-1 border-gray-200 shadow-xl)"
>
<general-noco-icon class="color-transition hover:(ring ring-accent)" :class="[isLoading ? 'animated-bg-gradient' : '']" />
<h1 class="prose-2xl font-bold self-center my-4">{{ $t('general.signIn') }}</h1>
<a-form ref="formValidator" :model="form" layout="vertical" no-style @finish="signIn">
<Transition name="layout">
<div v-if="error" class="self-center mb-4 bg-red-500 text-white rounded-lg w-3/4 mx-auto p-1">
<div class="flex items-center gap-2 justify-center">
@ -108,7 +99,7 @@ function resetError() {
</Transition>
<a-form-item :label="$t('labels.email')" name="email" :rules="formRules.email">
<a-input v-model:value="form.email" size="large" :placeholder="$t('labels.email')" @focus="resetError" />
<a-input v-model:value="form.email" size="large" :placeholder="$t('msg.info.signUp.workEmail')" @focus="resetError" />
</a-form-item>
<a-form-item :label="$t('labels.password')" name="password" :rules="formRules.password">
@ -116,20 +107,23 @@ function resetError() {
v-model:value="form.password"
size="large"
class="password"
:placeholder="$t('labels.password')"
:placeholder="$t('msg.info.signUp.enterPassword')"
@focus="resetError"
/>
</a-form-item>
<div class="hidden md:block self-end">
<div class="hidden md:block text-right">
<nuxt-link class="prose-sm" to="/forgot-password">
{{ $t('msg.info.signUp.forgotPassword') }}
</nuxt-link>
</div>
<div class="self-center flex flex-col flex-wrap gap-4 items-center mt-4 justify-center">
<button class="submit" type="submit">
<span class="flex items-center gap-2"><MdiLogin /> {{ $t('general.signIn') }}</span>
<button class="submit group" type="submit">
<span class="flex items-center gap-2">
<MdiLogin />
{{ $t('general.signIn') }}
</span>
</button>
<div class="text-end prose-sm">
@ -143,9 +137,9 @@ function resetError() {
</nuxt-link>
</div>
</div>
</div>
</a-form>
</div>
</a-form>
</div>
</NuxtLayout>
</template>
@ -153,34 +147,30 @@ function resetError() {
.signin {
.ant-input-affix-wrapper,
.ant-input {
@apply dark:(bg-gray-700 !text-white) !appearance-none my-1 border-1 border-solid border-primary/50 rounded;
@apply !appearance-none my-1 border-1 border-solid border-primary/50 rounded;
}
.password {
input {
@apply !border-none;
}
.ant-input-password-icon {
@apply dark:!text-white;
}
}
.submit {
@apply z-1 relative color-transition border border-gray-300 rounded-md p-3 text-white;
@apply z-1 relative color-transition rounded p-3 text-white shadow-sm;
&::after {
@apply rounded-md absolute top-0 left-0 right-0 bottom-0 transition-all duration-150 ease-in-out bg-primary;
@apply rounded absolute top-0 left-0 right-0 bottom-0 transition-all duration-150 ease-in-out bg-primary;
content: '';
z-index: -1;
}
&:hover::after {
@apply transform scale-110 ring ring-pink-500;
@apply transform scale-110 ring ring-accent;
}
&:active::after {
@apply ring ring-pink-500;
@apply ring ring-accent;
}
}
}

37
packages/nc-gui-v2/pages/signup/[[token]].vue

@ -10,13 +10,17 @@ import {
useApi,
useGlobal,
useI18n,
useNuxtApp,
useRoute,
} from '#imports'
definePageMeta({
requiresAuth: false,
title: 'general.signUp',
})
const { $e } = useNuxtApp()
const route = useRoute()
const { appInfo, signIn } = useGlobal()
@ -84,6 +88,8 @@ async function signUp() {
signIn(token!)
await navigateTo('/')
$e('a:auth:sign-up')
})
.catch(async (err) => {
error = await extractSdkResponseErrorMsg(err)
@ -97,14 +103,11 @@ function resetError() {
<template>
<NuxtLayout>
<div class="bg-primary/5 signup h-full min-h-[600px] flex flex-col justify-center items-center nc-form-signup">
<div class="md:bg-primary bg-opacity-5 signup h-full min-h-[600px] flex flex-col justify-center items-center">
<div
class="bg-white dark:(!bg-gray-900 !text-white) mt-[60px] relative flex flex-col justify-center gap-2 w-full max-w-[500px] mx-auto p-8 md:(rounded-lg border-1 border-gray-200 shadow-xl)"
class="bg-white mt-[60px] relative flex flex-col justify-center gap-2 w-full max-w-[500px] mx-auto p-8 md:(rounded-lg border-1 border-gray-200 shadow-xl)"
>
<general-noco-icon
class="color-transition hover:(ring ring-pink-500)"
:class="[isLoading ? 'animated-bg-gradient' : '']"
/>
<general-noco-icon class="color-transition hover:(ring ring-accent)" :class="[isLoading ? 'animated-bg-gradient' : '']" />
<h1 class="prose-2xl font-bold self-center my-4">
{{ $t('general.signUp') }}
@ -127,7 +130,7 @@ function resetError() {
</Transition>
<a-form-item :label="$t('labels.email')" name="email" :rules="formRules.email">
<a-input v-model:value="form.email" size="large" :placeholder="$t('labels.email')" @focus="resetError" />
<a-input v-model:value="form.email" size="large" :placeholder="$t('msg.info.signUp.workEmail')" @focus="resetError" />
</a-form-item>
<a-form-item :label="$t('labels.password')" name="password" :rules="formRules.password">
@ -135,7 +138,7 @@ function resetError() {
v-model:value="form.password"
size="large"
class="password"
:placeholder="$t('labels.password')"
:placeholder="$t('msg.info.signUp.enterPassword')"
@focus="resetError"
/>
</a-form-item>
@ -153,7 +156,7 @@ function resetError() {
<a-switch
v-model:checked="subscribe"
size="small"
class="my-1 hover:(ring ring-pink-500) focus:(!ring !ring-pink-500)"
class="my-1 hover:(ring ring-accent) focus:(!ring !ring-accent)"
/>
<div class="prose-xs text-gray-500">Subscribe to our weekly newsletter</div>
</div>
@ -169,7 +172,7 @@ function resetError() {
<div class="prose-sm mt-4 text-gray-500">
By signing up, you agree to the
<a class="prose-sm text-gray-500 underline" target="_blank" href="https://nocodb.com/policy-nocodb">Terms of Service</a>
<a class="prose-sm !text-gray-500 underline" target="_blank" href="https://nocodb.com/policy-nocodb">Terms of Service</a>
</div>
</div>
</NuxtLayout>
@ -179,34 +182,30 @@ function resetError() {
.signup {
.ant-input-affix-wrapper,
.ant-input {
@apply dark:(bg-gray-700 !text-white) !appearance-none my-1 border-1 border-solid border-primary/50 rounded;
@apply !appearance-none my-1 border-1 border-solid border-primary/50 rounded;
}
.password {
input {
@apply !border-none;
}
.ant-input-password-icon {
@apply dark:!text-white;
}
}
.submit {
@apply z-1 relative color-transition border border-gray-300 rounded-md p-3 text-white;
@apply z-1 relative color-transition rounded p-3 text-white shadow;
&::after {
@apply rounded-md absolute top-0 left-0 right-0 bottom-0 transition-all duration-150 ease-in-out bg-primary;
@apply rounded absolute top-0 left-0 right-0 bottom-0 transition-all duration-150 ease-in-out bg-primary;
content: '';
z-index: -1;
}
&:hover::after {
@apply transform scale-110 ring ring-pink-500;
@apply transform scale-110 ring ring-accent;
}
&:active::after {
@apply ring ring-pink-500;
@apply ring ring-accent;
}
}
}

10
packages/nc-gui-v2/plugins/ant.ts

@ -1,12 +1,6 @@
import { Menu as AntMenu, ConfigProvider } from 'ant-design-vue'
import { defineNuxtPlugin, themeColors } from '#imports'
import { Menu as AntMenu } from 'ant-design-vue'
import { defineNuxtPlugin } from '#imports'
export default defineNuxtPlugin((nuxtApp) => {
ConfigProvider.config({
theme: {
primaryColor: themeColors.primary,
},
})
nuxtApp.vueApp.component(AntMenu.name, AntMenu)
})

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

@ -1,5 +1,4 @@
import { defineNuxtPlugin } from '#app'
import { useDark, useGlobal, watch } from '#imports'
import { defineNuxtPlugin, useApi, useGlobal } from '#imports'
/**
* Initialize global state and watches for changes
@ -15,25 +14,15 @@ import { useDark, useGlobal, watch } from '#imports'
*/
export default defineNuxtPlugin(async (nuxtApp) => {
const state = useGlobal()
const { $api } = useNuxtApp()
const darkMode = useDark()
const { api } = useApi()
/** set i18n locale to stored language */
nuxtApp.vueApp.i18n.locale.value = state.lang.value
try {
state.appInfo.value = await $api.utils.appInfo()
state.appInfo.value = await api.utils.appInfo()
} catch (e) {
console.error(e)
}
/** set current dark mode from storage */
watch(
state.darkMode,
(newMode) => {
darkMode.value = newMode
},
{ immediate: true },
)
})

29
packages/nc-gui-v2/plugins/vuetify.ts

@ -1,29 +0,0 @@
import type { ThemeDefinition } from 'vuetify'
import { createVuetify } from 'vuetify'
import { defineNuxtPlugin } from 'nuxt/app'
// todo: exclude unused components
// Import everything
import * as components from 'vuetify/components'
import { themeColors } from '~/utils/colorsUtils'
const ncLightTheme: ThemeDefinition = {
dark: false,
colors: themeColors,
}
export const createVuetifyPlugin = () =>
createVuetify({
components,
theme: {
defaultTheme: 'ncLightTheme',
themes: {
ncLightTheme,
},
},
})
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(createVuetifyPlugin())
})

42
packages/nc-gui-v2/utils/colorsUtils.ts

@ -1,3 +1,5 @@
import colors from 'windicss/colors'
export const theme = {
light: ['#ffdce5', '#fee2d5', '#ffeab6', '#d1f7c4', '#ede2fe', '#eee', '#cfdffe', '#d0f1fd', '#c2f5e8', '#ffdaf6'],
dark: [
@ -41,3 +43,43 @@ export const themeColors = {
'success': '#4CAF50',
'warning': '#FB8C00',
}
export const themeV2Colors = {
/** Primary shades */
'royal-blue': {
'DEFAULT': '#4351E8',
'50': '#E7E8FC',
'100': '#D4D8FA',
'200': '#B0B6F5',
'300': '#8C94F1',
'400': '#6773EC',
'500': '#4351E8',
'600': '#1A2BD8',
'700': '#1421A6',
'800': '#0E1774',
'900': '#080D42',
},
/** Accent shades */
'pink': colors.pink,
}
const isValidHex = (hex: string) => /^#([A-Fa-f0-9]{3,4}){1,2}$/.test(hex)
const getChunksFromString = (st: string, chunkSize: number) => st.match(new RegExp(`.{${chunkSize}}`, 'g'))
const convertHexUnitTo256 = (hexStr: string) => parseInt(hexStr.repeat(2 / hexStr.length), 16)
export const hexToRGB = (hex: string) => {
if (!isValidHex(hex)) {
throw new Error('Invalid HEX')
}
const chunkSize = Math.floor((hex.length - 1) / 3)
const hexArr = getChunksFromString(hex.slice(1), chunkSize)!
const [r, g, b] = hexArr.map(convertHexUnitTo256)
return `${r}, ${g}, ${b}`
}

31
packages/nc-gui-v2/windi.config.ts

@ -11,7 +11,7 @@ import animations from '@windicss/plugin-animations'
// @ts-expect-error no types for plugin-question-mark
import questionMark from '@windicss/plugin-question-mark'
import { theme as colors, themeColors } from './utils/colorsUtils'
import { theme as colors, themeColors, themeV2Colors } from './utils/colorsUtils'
export default defineConfig({
extract: {
@ -42,10 +42,8 @@ export default defineConfig({
shortcuts: {
'color-transition': 'transition-color duration-100 ease-in',
'scrollbar-thin-primary':
'scrollbar scrollbar-thin scrollbar-thumb-rounded scrollbar-thumb-primary scrollbar-track-white dark:(!scrollbar-track-black)',
'scrollbar-thin-dull':
'scrollbar scrollbar-thin scrollbar-thumb-rounded scrollbar-thumb-gray-300 scrollbar-track-white dark:(!scrollbar-track-black)',
'scrollbar-thin-primary': 'scrollbar scrollbar-thin scrollbar-thumb-rounded scrollbar-thumb-primary scrollbar-track-white',
'scrollbar-thin-dull': 'scrollbar scrollbar-thin scrollbar-thumb-rounded scrollbar-thumb-gray-300 scrollbar-track-white',
},
theme: {
@ -55,27 +53,16 @@ export default defineConfig({
mono: ['Roboto', 'mono'],
},
extend: {
typography: {
DEFAULT: {
css: {
'a': {
'color': '#1348ba',
'&:hover': {
color: 'rgba(19,72,186,0.75)',
},
},
'nuxt-link': {
'color': '#1348ba',
'&:hover': {
color: 'rgba(19,72,186,0.75)',
},
},
},
},
textColor: {
primary: 'rgba(var(--color-primary), var(--tw-text-opacity))',
accent: 'rgba(var(--color-accent), var(--tw-text-opacity))',
},
colors: {
...windiColors,
...themeColors,
...themeV2Colors,
primary: 'rgba(var(--color-primary), var(--tw-bg-opacity))',
accent: 'rgba(var(--color-accent), var(--tw-bg-opacity))',
dark: colors.dark,
light: colors.light,
},

Loading…
Cancel
Save