Browse Source

feat: revamp toolbar menu

pull/6340/head
DarkPhoenix2704 1 year ago
parent
commit
ae5438ce78
  1. 13
      packages/nc-gui/assets/nc-icons/download.svg
  2. 8
      packages/nc-gui/assets/nc-icons/fields.svg
  3. 4
      packages/nc-gui/assets/nc-icons/filter.svg
  4. 5
      packages/nc-gui/assets/nc-icons/group.svg
  5. 5
      packages/nc-gui/assets/nc-icons/sort.svg
  6. 5
      packages/nc-gui/assets/nc-icons/upload.svg
  7. 8
      packages/nc-gui/assets/nc-icons/users.svg
  8. 21
      packages/nc-gui/assets/style.scss
  9. 4
      packages/nc-gui/components/smartsheet/Toolbar.vue
  10. 3
      packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue
  11. 2
      packages/nc-gui/components/smartsheet/toolbar/ColumnFilterMenu.vue
  12. 4
      packages/nc-gui/components/smartsheet/toolbar/ExportSubActions.vue
  13. 95
      packages/nc-gui/components/smartsheet/toolbar/FieldsMenu.vue
  14. 16
      packages/nc-gui/components/smartsheet/toolbar/GroupByMenu.vue
  15. 35
      packages/nc-gui/components/smartsheet/toolbar/LockType.vue
  16. 2
      packages/nc-gui/components/smartsheet/toolbar/SearchData.vue
  17. 8
      packages/nc-gui/components/smartsheet/toolbar/SortListMenu.vue
  18. 87
      packages/nc-gui/components/smartsheet/toolbar/ViewActions.vue
  19. 2
      tests/playwright/pages/Dashboard/common/Toolbar/Fields.ts

13
packages/nc-gui/assets/nc-icons/download.svg

@ -1,10 +1,5 @@
<svg width="20" height="20" viewBox="0 0 41 40" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="download"> <path d="M14 10V12.6667C14 13.0203 13.8595 13.3594 13.6095 13.6095C13.3594 13.8595 13.0203 14 12.6667 14H3.33333C2.97971 14 2.64057 13.8595 2.39052 13.6095C2.14048 13.3594 2 13.0203 2 12.6667V10" stroke="#1F293A" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<mask id="mask0_101_20734" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="41" height="40"> <path d="M4.66675 6.66663L8.00008 9.99996L11.3334 6.66663" stroke="#1F293A" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<rect id="Bounding box" x="0.5" width="40" height="40" fill="currentColor"/> <path d="M8 10V2" stroke="#4A5268" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
</mask>
<g mask="url(#mask0_101_20734)">
<path id="download_2" d="M20.4948 25.6453C20.3202 25.6453 20.1595 25.6143 20.0128 25.5523C19.8661 25.4903 19.7247 25.3913 19.5886 25.2553L14.2628 19.9295C14.062 19.7287 13.9618 19.4828 13.9621 19.1918C13.9625 18.9009 14.0684 18.6496 14.28 18.4381C14.4915 18.2266 14.7415 18.1194 15.0299 18.1165C15.3184 18.1137 15.5684 18.218 15.7799 18.4295L19.453 22.1198V8.25854C19.453 7.96137 19.5537 7.71258 19.755 7.51217C19.9564 7.31178 20.2064 7.21158 20.505 7.21158C20.8036 7.21158 21.0519 7.31178 21.2499 7.51217C21.4479 7.71258 21.5469 7.96137 21.5469 8.25854V22.1198L25.2371 18.4295C25.438 18.2287 25.6853 18.127 25.9791 18.1245C26.2729 18.122 26.5256 18.2266 26.7371 18.4381C26.9486 18.6496 27.0544 18.8982 27.0544 19.1838C27.0544 19.4694 26.9486 19.718 26.7371 19.9295L21.4113 25.2553C21.2752 25.3913 21.1321 25.4903 20.982 25.5523C20.8319 25.6143 20.6695 25.6453 20.4948 25.6453ZM10.6068 32.5C9.8905 32.5 9.27694 32.2446 8.76617 31.7338C8.25539 31.223 8 30.6095 8 29.8931V25.985C8 25.6878 8.10069 25.439 8.30208 25.2386C8.50344 25.0382 8.75343 24.938 9.05204 24.938C9.35065 24.938 9.59896 25.0382 9.79696 25.2386C9.99496 25.439 10.094 25.6878 10.094 25.985V29.8931C10.094 30.0213 10.1474 30.1389 10.2542 30.2457C10.3611 30.3526 10.4786 30.406 10.6068 30.406H30.3931C30.5213 30.406 30.6388 30.3526 30.7457 30.2457C30.8525 30.1389 30.906 30.0213 30.906 29.8931V25.985C30.906 25.6878 31.0066 25.439 31.208 25.2386C31.4094 25.0382 31.6594 24.938 31.958 24.938C32.2566 24.938 32.5049 25.0382 32.7029 25.2386C32.9009 25.439 32.9999 25.6878 32.9999 25.985V29.8931C32.9999 30.6095 32.7445 31.223 32.2337 31.7338C31.723 32.2446 31.1094 32.5 30.3931 32.5H10.6068Z" fill="currentColor"/>
</g>
</g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 643 B

8
packages/nc-gui/assets/nc-icons/fields.svg

@ -0,0 +1,8 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.3335 12H14.0002" stroke="#374151" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2 12H2.00667" stroke="#374151" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.3335 8H14.0002" stroke="#4A5268" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2 8H2.00667" stroke="#4A5268" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M5.3335 4H14.0002" stroke="#4A5268" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M2 4H2.00667" stroke="#4A5268" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 792 B

4
packages/nc-gui/assets/nc-icons/filter.svg

@ -1,3 +1,3 @@
<svg width="16" height="14" viewBox="0 0 16 14" stroke="currentColor" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.6668 1H1.3335L6.66683 7.30667V11.6667L9.3335 13V7.30667L14.6668 1Z" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/> <path d="M14.6668 2H1.3335L6.66683 8.30667V12.6667L9.3335 14V8.30667L14.6668 2Z" stroke="#374151" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 300 B

After

Width:  |  Height:  |  Size: 273 B

5
packages/nc-gui/assets/nc-icons/group.svg

@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14 5.33331V14H2V5.33331" stroke="#374151" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6.6665 8H9.33317" stroke="#374151" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M15.3332 2H0.666504V5.33333H15.3332V2Z" stroke="#4A5268" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 482 B

5
packages/nc-gui/assets/nc-icons/sort.svg

@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 13.3334V10.6667" stroke="#374151" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8 13.3334V6.66669" stroke="#374151" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M12 13.3334V2.66669" stroke="#4A5268" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 458 B

5
packages/nc-gui/assets/nc-icons/upload.svg

@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14 10V12.6667C14 13.0203 13.8595 13.3594 13.6095 13.6095C13.3594 13.8595 13.0203 14 12.6667 14H3.33333C2.97971 14 2.64057 13.8595 2.39052 13.6095C2.14048 13.3594 2 13.0203 2 12.6667V10" stroke="#1F293A" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.3334 5.33333L8.00008 2L4.66675 5.33333" stroke="#1F293A" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M8 2V10" stroke="#4A5268" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 637 B

8
packages/nc-gui/assets/nc-icons/users.svg

@ -1,6 +1,6 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.3335 14.0002V12.6669C15.3331 12.0761 15.1364 11.5021 14.7744 11.0351C14.4124 10.5682 13.9056 10.2346 13.3335 10.0869" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/> <path d="M15.3333 14V12.6667C15.3328 12.0758 15.1362 11.5019 14.7742 11.0349C14.4122 10.5679 13.9053 10.2344 13.3333 10.0867" stroke="#1F293A" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11.3332 14V12.6667C11.3332 11.9594 11.0522 11.2811 10.5521 10.781C10.052 10.281 9.37375 10 8.6665 10H3.33317C2.62593 10 1.94765 10.281 1.44755 10.781C0.947456 11.2811 0.666504 11.9594 0.666504 12.6667V14" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/> <path d="M11.3334 14V12.6667C11.3334 11.9594 11.0525 11.2811 10.5524 10.781C10.0523 10.281 9.37399 10 8.66675 10H3.33341C2.62617 10 1.94789 10.281 1.4478 10.781C0.9477 11.2811 0.666748 11.9594 0.666748 12.6667V14" stroke="#1F293A" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M10.6665 2.08691C11.2401 2.23378 11.7485 2.56738 12.1116 3.03512C12.4747 3.50286 12.6717 4.07813 12.6717 4.67025C12.6717 5.26236 12.4747 5.83763 12.1116 6.30537C11.7485 6.77311 11.2401 7.10671 10.6665 7.25358" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/> <path d="M10.6667 2.08667C11.2404 2.23354 11.7488 2.56714 12.1118 3.03488C12.4749 3.50262 12.672 4.07789 12.672 4.67C12.672 5.26212 12.4749 5.83739 12.1118 6.30513C11.7488 6.77287 11.2404 7.10647 10.6667 7.25334" stroke="#4A5268" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M6.00016 7.33333C7.47292 7.33333 8.66683 6.13943 8.66683 4.66667C8.66683 3.19391 7.47292 2 6.00016 2C4.5274 2 3.3335 3.19391 3.3335 4.66667C3.3335 6.13943 4.5274 7.33333 6.00016 7.33333Z" stroke="currentColor" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/> <path d="M5.99992 7.33333C7.47268 7.33333 8.66659 6.13943 8.66659 4.66667C8.66659 3.19391 7.47268 2 5.99992 2C4.52716 2 3.33325 3.19391 3.33325 4.66667C3.33325 6.13943 4.52716 7.33333 5.99992 7.33333Z" stroke="#4A5268" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

21
packages/nc-gui/assets/style.scss

@ -7,7 +7,7 @@
--sidebar-bottom-height: 8.5rem; --sidebar-bottom-height: 8.5rem;
--new-header-height: 3.5rem; --new-header-height: 3.5rem;
--tw-text-opacity: 1; --tw-text-opacity: 1;
--navbar-bg: #FAFAFA; --navbar-bg: #fafafa;
--navbar-border: #e0e0e0; --navbar-border: #e0e0e0;
--nc-grid-bg: #fdfdfd; --nc-grid-bg: #fdfdfd;
} }
@ -135,7 +135,6 @@ main {
} }
} }
html { html {
overflow-y: auto !important; overflow-y: auto !important;
} }
@ -231,7 +230,6 @@ a {
// for highlighing toolbar kanban menu item // for highlighing toolbar kanban menu item
.nc-locked-overlay { .nc-locked-overlay {
@apply absolute h-full w-full z-2 top-0 left-0; @apply absolute h-full w-full z-2 top-0 left-0;
} }
@ -416,7 +414,7 @@ a {
} }
.nc-warning-info { .nc-warning-info {
@apply !shadow-none rounded ring-1 ring-red-600 @apply !shadow-none rounded ring-1 ring-red-600;
} }
.ant-modal { .ant-modal {
@ -454,22 +452,18 @@ a {
} }
} }
.nc-copy-failed-modal .ant-modal-confirm-content { .nc-copy-failed-modal .ant-modal-confirm-content {
@apply pt-4 overflow-auto; @apply pt-4 overflow-auto;
white-space: pre; white-space: pre;
user-select: auto; user-select: auto;
} }
.nc-drawer-expanded-form .ant-drawer-content-wrapper { .nc-drawer-expanded-form .ant-drawer-content-wrapper {
transition: width 0.3s !important; transition: width 0.3s !important;
} }
.nc-icon-transition { .nc-icon-transition {
@apply transform transition-transform !hover:(scale-115 text-shadow-sm) !active:(scale-100) @apply transform transition-transform !hover:(scale-115 text-shadow-sm) !active:(scale-100);
} }
.nc-animation-pulse { .nc-animation-pulse {
@ -488,17 +482,16 @@ a {
} }
} }
.nc-menu-item-wrapper { .nc-menu-item-wrapper {
@apply flex flex-row items-center py-3 gap-2; @apply flex flex-row items-center py-3 gap-2;
} }
.nc-click-transition { .nc-click-transition {
@apply transform transition-transform transition-color !text-gray-400 !hover:(scale-130 !text-gray-500) !active:(scale-100 !text-gray-500) @apply transform transition-transform transition-color !text-gray-400 !hover:(scale-130 !text-gray-500) !active:(scale-100 !text-gray-500);
} }
.nc-click-transition-1 { .nc-click-transition-1 {
@apply transform transition-transform !hover:(scale-120) !active:(scale-100) @apply transform transition-transform !hover:(scale-120) !active:(scale-100);
} }
@keyframes pop-in { @keyframes pop-in {
@ -518,10 +511,9 @@ a {
animation: pop-in 0.15s cubic-bezier(0, 0, 0.6, -0.13); animation: pop-in 0.15s cubic-bezier(0, 0, 0.6, -0.13);
} }
// button // button
.nc-btn { .nc-btn {
@apply rounded py-2 px-4 border-gray-200 h-auto @apply rounded py-2 px-4 border-gray-200 h-auto;
} }
.ant-popover-inner { .ant-popover-inner {
@ -535,7 +527,6 @@ a {
min-height: 0 !important; min-height: 0 !important;
} }
.ant-skeleton-input { .ant-skeleton-input {
@apply !h-full; @apply !h-full;
} }

4
packages/nc-gui/components/smartsheet/Toolbar.vue

@ -35,10 +35,10 @@ const isViewSidebarAvailable = computed(
<template v-else> <template v-else>
<LazySmartsheetToolbarMappedBy v-if="isMap" /> <LazySmartsheetToolbarMappedBy v-if="isMap" />
<LazySmartsheetToolbarStackedBy v-if="isKanban" />
<LazySmartsheetToolbarFieldsMenu v-if="isGrid || isGallery || isKanban || isMap" :show-system-fields="false" /> <LazySmartsheetToolbarFieldsMenu v-if="isGrid || isGallery || isKanban || isMap" :show-system-fields="false" />
<LazySmartsheetToolbarStackedBy v-if="isKanban" />
<LazySmartsheetToolbarColumnFilterMenu v-if="isGrid || isGallery || isKanban || isMap" /> <LazySmartsheetToolbarColumnFilterMenu v-if="isGrid || isGallery || isKanban || isMap" />
<LazySmartsheetToolbarGroupByMenu v-if="isGrid" /> <LazySmartsheetToolbarGroupByMenu v-if="isGrid" />

3
packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue

@ -340,8 +340,7 @@ onMounted(() => {
<component :is="iconMap.deleteListItem" /> <component :is="iconMap.deleteListItem" />
</NcButton> </NcButton>
</div> </div>
<div class="flex border-1 rounded-lg p-2 w-full" :class="nestedLevel % 2 !== 0 ? 'bg-white' : 'bg-gray-100'">
<div class="flex border-1 rounded-lg p-2 w-full">
<LazySmartsheetToolbarColumnFilter <LazySmartsheetToolbarColumnFilter
v-if="filter.id || filter.children" v-if="filter.id || filter.children"
:key="filter.id ?? i" :key="filter.id ?? i"

2
packages/nc-gui/components/smartsheet/toolbar/ColumnFilterMenu.vue

@ -69,7 +69,7 @@ useMenuCloseOnEsc(open)
<!-- Filter --> <!-- Filter -->
<span v-if="!isMobileMode" class="text-capitalize !text-sm font-medium">{{ $t('activity.filter') }}</span> <span v-if="!isMobileMode" class="text-capitalize !text-sm font-medium">{{ $t('activity.filter') }}</span>
<span v-if="filtersLength" class="nc-count-badge">{{ filtersLength }}</span> <span v-if="filtersLength" class="bg-brand-50 text-brand-500 py-1 px-2 text-md rounded-md">{{ filtersLength }}</span>
</div> </div>
</a-button> </a-button>
</div> </div>

4
packages/nc-gui/components/smartsheet/toolbar/ExportSubActions.vue

@ -101,7 +101,7 @@ const exportFile = async (exportType: ExportTypes) => {
<template> <template>
<a-menu-item> <a-menu-item>
<div v-e="['a:actions:download-csv']" class="nc-project-menu-item" @click="exportFile(ExportTypes.CSV)"> <div v-e="['a:actions:download-csv']" class="nc-project-menu-item" @click="exportFile(ExportTypes.CSV)">
<component :is="iconMap.csv" class="text-gray-500" /> <component :is="iconMap.csv" />
<!-- Download as CSV --> <!-- Download as CSV -->
{{ $t('activity.downloadCSV') }} {{ $t('activity.downloadCSV') }}
</div> </div>
@ -109,7 +109,7 @@ const exportFile = async (exportType: ExportTypes) => {
<a-menu-item> <a-menu-item>
<div v-e="['a:actions:download-excel']" class="nc-project-menu-item" @click="exportFile(ExportTypes.EXCEL)"> <div v-e="['a:actions:download-excel']" class="nc-project-menu-item" @click="exportFile(ExportTypes.EXCEL)">
<component :is="iconMap.excel" class="text-gray-500" /> <component :is="iconMap.excel" />
<!-- Download as XLSX --> <!-- Download as XLSX -->
{{ $t('activity.downloadExcel') }} {{ $t('activity.downloadExcel') }}
</div> </div>

95
packages/nc-gui/components/smartsheet/toolbar/FieldsMenu.vue

@ -3,6 +3,8 @@ import type { ColumnType, GalleryType, KanbanType } from 'nocodb-sdk'
import { UITypes, ViewTypes, isVirtualCol } from 'nocodb-sdk' import { UITypes, ViewTypes, isVirtualCol } from 'nocodb-sdk'
import Draggable from 'vuedraggable' import Draggable from 'vuedraggable'
import type { SelectProps } from 'ant-design-vue' import type { SelectProps } from 'ant-design-vue'
import FieldsIcon from '~icons/nc-icons/fields'
import { import {
ActiveViewInj, ActiveViewInj,
FieldsInj, FieldsInj,
@ -256,23 +258,29 @@ const getIcon = (c: ColumnType) =>
const open = ref(false) const open = ref(false)
const toggleSystemFields = (checked: boolean) => { const showSystemField = computed({
get: () => {
return showSystemFields.value
},
set: (val) => {
addUndo({ addUndo({
undo: { undo: {
fn: (v: boolean) => { fn: (v: boolean) => {
showSystemFields.value = !v showSystemFields.value = !v
}, },
args: [checked], args: [val],
}, },
redo: { redo: {
fn: (v: boolean) => { fn: (v: boolean) => {
showSystemFields.value = v showSystemFields.value = v
}, },
args: [checked], args: [val],
}, },
scope: defineViewScope({ view: activeView.value }), scope: defineViewScope({ view: activeView.value }),
}) })
} showSystemFields.value = val
},
})
useMenuCloseOnEsc(open) useMenuCloseOnEsc(open)
</script> </script>
@ -287,7 +295,7 @@ useMenuCloseOnEsc(open)
icon="creditCard" icon="creditCard"
class="h-4 w-4" class="h-4 w-4"
/> />
<GeneralIcon v-else icon="eye" class="h-4 w-4" /> <FieldsIcon v-else class="h-4 w-4" />
<!-- Fields --> <!-- Fields -->
<span v-if="!isMobileMode" class="text-capitalize text-sm font-medium"> <span v-if="!isMobileMode" class="text-capitalize text-sm font-medium">
@ -298,14 +306,15 @@ useMenuCloseOnEsc(open)
{{ $t('objects.fields') }} {{ $t('objects.fields') }}
</template> </template>
</span> </span>
<span v-if="numberOfHiddenFields" class="bg-brand-50 text-brand-500 py-1 px-2 text-md rounded-md">
<span v-if="numberOfHiddenFields" class="nc-count-badge">{{ numberOfHiddenFields }}</span> {{ numberOfHiddenFields }}
</span>
</div> </div>
</a-button> </a-button>
</div> </div>
<template #overlay> <template #overlay>
<div class="p-6 pr-0 bg-white w-90 rounded-2xl nc-table-toolbar-menu" data-testid="nc-fields-menu" @click.stop> <div class="p-4 pr-0 bg-white w-90 rounded-2xl nc-table-toolbar-menu" data-testid="nc-fields-menu" @click.stop>
<div <div
v-if="!filterQuery && (activeView?.type === ViewTypes.GALLERY || activeView?.type === ViewTypes.KANBAN)" v-if="!filterQuery && (activeView?.type === ViewTypes.GALLERY || activeView?.type === ViewTypes.KANBAN)"
class="flex flex-col gap-y-2 pr-6 mb-6" class="flex flex-col gap-y-2 pr-6 mb-6"
@ -328,49 +337,48 @@ useMenuCloseOnEsc(open)
></a-input> ></a-input>
</div> </div>
<div
v-if="!filterQuery"
class="pl-8 pr-2 mr-6 mt-3 py-2 justify-between flex flex-row items-center border-1 rounded-lg mb-2 border-gray-100 bg-gray-50"
>
<div class="ml-0.25 select-none">{{ $t('general.showAll') }} {{ $t('objects.fields').toLowerCase() }}</div>
<NcSwitch v-model:checked="showAllColumns" />
</div>
<div v-if="!filterQuery" class="pr-6"> <div v-if="!filterQuery" class="pr-6">
<div class="pt-0.25 w-full bg-gray-50"></div> <div class="pt-0.25 w-full bg-gray-50"></div>
</div> </div>
<div class="flex flex-col nc-scrollbar-md max-h-[35vh] pt-1 pr-5"> <div class="flex flex-col py-1 nc-scrollbar-md max-h-[45vh] pr-5">
<div class="nc-fields-list py-1"> <div class="nc-fields-list">
<div v-if="!fields?.filter((el) => el.title.includes(filterQuery)).length" class="px-0.5 py-2 text-gray-500"> <div
Empty v-if="!fields?.filter((el) => el.title.toLowerCase().includes(filterQuery.toLowerCase())).length"
class="px-0.5 py-2 text-gray-500"
>
No fields found
</div> </div>
<Draggable v-model="fields" item-key="id" @change="onMove($event)"> <Draggable v-model="fields" item-key="id" @change="onMove($event)">
<template #item="{ element: field, index: index }"> <template #item="{ element: field, index: index }">
<div <div
v-if=" v-if="
filteredFieldList filteredFieldList
.filter((el) => el !== gridDisplayValueField && el.title.includes(filterQuery)) .filter((el) => el !== gridDisplayValueField && el.title.toLowerCase().includes(filterQuery.toLowerCase()))
.includes(field) .includes(field)
" "
:key="field.id" :key="field.id"
class="px-2 py-2 flex flex-row items-center border-1 rounded-lg mb-2 border-gray-100" class="px-2 py-2 flex flex-row items-center first:border-t-1 border-b-1 border-x-1 first:rounded-t-md last:rounded-b-md border-gray-200"
:data-testid="`nc-fields-menu-${field.title}`" :data-testid="`nc-fields-menu-${field.title}`"
@click.stop @click.stop
> >
<component :is="iconMap.drag" class="cursor-move !h-3.75 text-gray-600 mr-1" /> <component :is="iconMap.drag" class="cursor-move !h-3.75 text-gray-600 mr-1" />
<div class="flex flex-row items-center justify-between w-full ml-1"> <div
v-e="['a:fields:show-hide']"
class="flex flex-row items-center justify-between w-full cursor-pointer ml-1"
@click="
() => {
field.show = !field.show
toggleFieldVisibility(field.show, field)
}
"
>
<div class="flex items-center -ml-0.75"> <div class="flex items-center -ml-0.75">
<component :is="getIcon(metaColumnById[field.fk_column_id])" /> <component :is="getIcon(metaColumnById[field.fk_column_id])" />
<div>{{ field.title }}</div> <span class="mt-0.65">{{ field.title }}</span>
</div> </div>
<NcSwitch <NcSwitch :checked="field.show" v-e="['a:fields:show-hide']" :disabled="field.isViewEssentialField" />
v-model:checked="field.show"
v-e="['a:fields:show-hide']"
:disabled="field.isViewEssentialField"
@change="toggleFieldVisibility($event, field)"
/>
</div> </div>
<div class="flex-1" /> <div class="flex-1" />
@ -380,12 +388,12 @@ useMenuCloseOnEsc(open)
<div <div
v-if="gridDisplayValueField && filteredFieldList[0].title.includes(filterQuery)" v-if="gridDisplayValueField && filteredFieldList[0].title.includes(filterQuery)"
:key="`pv-${gridDisplayValueField.id}`" :key="`pv-${gridDisplayValueField.id}`"
class="pl-7.5 pr-2.1 py-2 flex flex-row items-center border-1 rounded-lg mb-2 border-gray-100" class="pl-7.5 pr-2.1 py-1.9 flex flex-row items-center border-1 rounded-t-lg border-gray-200"
:data-testid="`nc-fields-menu-${gridDisplayValueField.title}`" :data-testid="`nc-fields-menu-${gridDisplayValueField.title}`"
@click.stop @click.stop
> >
<div class="flex flex-row items-center justify-between w-full"> <div class="flex flex-row items-center justify-between w-full">
<div class="flex"> <div class="flex items">
<a-tooltip placement="bottom"> <a-tooltip placement="bottom">
<template #title> <template #title>
<span class="text-sm">Display Value</span> <span class="text-sm">Display Value</span>
@ -403,12 +411,27 @@ useMenuCloseOnEsc(open)
</div> </div>
</template> </template>
</Draggable> </Draggable>
<div v-if="!isPublic && !filterQuery" class="mt-4 p-2 py-1 flex nc-fields-show-system-fields !text-base" @click.stop>
<NcCheckbox v-model:checked="showSystemFields" @change="toggleSystemFields">
<span class="select-none"> {{ $t('activity.showSystemFields') }}</span>
</NcCheckbox>
</div> </div>
</div> </div>
<div class="flex pr-6 mt-1 gap-2">
<NcButton
v-if="!filterQuery"
type="ghost"
size="sm"
class="nc-fields-show-all-fields !text-gray-500 !w-1/2"
@click="showAllColumns = !showAllColumns"
>
{{ showAllColumns ? 'Hide all' : $t('general.showAll') }} {{ $t('objects.fields').toLowerCase() }}
</NcButton>
<NcButton
v-if="!isPublic && !filterQuery"
type="ghost"
size="sm"
class="nc-fields-show-system-fields !text-gray-500 !w-1/2"
@click="showSystemField = !showSystemField"
>
{{ showSystemField ? 'Hide system fields' : $t('activity.showSystemFields') }}
</NcButton>
</div> </div>
</div> </div>
</template> </template>

16
packages/nc-gui/components/smartsheet/toolbar/GroupByMenu.vue

@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { type ColumnType, UITypes } from 'nocodb-sdk' import { type ColumnType, UITypes } from 'nocodb-sdk'
import MdiFormatListGroup from '~icons/mdi/format-list-group' import GroupIcon from '~icons/nc-icons/group'
import { import {
ActiveViewInj, ActiveViewInj,
IsLockedInj, IsLockedInj,
@ -148,15 +148,17 @@ watch(open, () => {
:trigger="['click']" :trigger="['click']"
overlay-class-name="nc-dropdown-group-by-menu nc-toolbar-dropdown" overlay-class-name="nc-dropdown-group-by-menu nc-toolbar-dropdown"
> >
<div :class="{ 'nc-badge nc-active-btn': groupedByColumnIds?.length }"> <div :class="{ 'nc-active-btn': groupedByColumnIds?.length }">
<a-button v-e="['c:group-by']" class="nc-group-by-menu-btn nc-toolbar-btn" :disabled="isLocked"> <a-button v-e="['c:group-by']" class="nc-group-by-menu-btn nc-toolbar-btn" :disabled="isLocked">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<MdiFormatListGroup class="h-4 w-4" /> <GroupIcon class="h-4 w-4" />
<!-- Group By --> <!-- Group By -->
<span v-if="!isMobileMode" class="text-capitalize !text-sm font-medium">{{ $t('activity.groupBy') }}</span> <span v-if="!isMobileMode" class="text-capitalize !text-sm font-medium">{{ $t('activity.groupBy') }}</span>
<span v-if="groupedByColumnIds?.length" class="nc-count-badge">{{ groupedByColumnIds.length }}</span> <span v-if="groupedByColumnIds?.length" class="bg-brand-50 text-brand-500 py-1 px-2 text-md rounded-md">{{
groupedByColumnIds.length
}}</span>
</div> </div>
</a-button> </a-button>
</div> </div>
@ -167,11 +169,7 @@ watch(open, () => {
data-testid="nc-group-by-menu" data-testid="nc-group-by-menu"
> >
<div class="group-by-grid pb-1 mb-2 max-h-100 nc-scrollbar-md pr-5" @click.stop> <div class="group-by-grid pb-1 mb-2 max-h-100 nc-scrollbar-md pr-5" @click.stop>
<template <template v-for="[i, group] of Object.entries(_groupBy)" :key="`grouped-by-${group.fk_column_id}`">
v-for="[i, group] of Object.entries(_groupBy)"
:key="`grouped-by-${group.fk_column_id}`"
class="nc-group-menu"
>
<LazySmartsheetToolbarFieldListAutoCompleteDropdown <LazySmartsheetToolbarFieldListAutoCompleteDropdown
v-model="group.fk_column_id" v-model="group.fk_column_id"
class="caption nc-sort-field-select" class="caption nc-sort-field-select"

35
packages/nc-gui/components/smartsheet/toolbar/LockType.vue

@ -1,5 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { ActiveViewInj, LockType, iconMap, inject } from '#imports' import { ActiveViewInj, LockType, iconMap, inject } from '#imports'
import UsersIcon from '~icons/nc-icons/users'
const { type, hideTick } = defineProps<{ hideTick?: boolean; type: LockType }>() const { type, hideTick } = defineProps<{ hideTick?: boolean; type: LockType }>()
@ -13,7 +14,7 @@ const types = {
}, },
[LockType.Collaborative]: { [LockType.Collaborative]: {
title: 'title.collabView', title: 'title.collabView',
icon: iconMap.users, icon: UsersIcon,
subtitle: 'msg.info.collabView', subtitle: 'msg.info.collabView',
}, },
[LockType.Locked]: { [LockType.Locked]: {
@ -27,38 +28,32 @@ const selectedView = inject(ActiveViewInj)
</script> </script>
<template> <template>
<div class="nc-locked-menu-item group-hover:text-accent" @click="emit('select', type)"> <div class="nc-locked-menu-item min-w-50" @click="emit('select', type)">
<div :class="{ 'show-tick': !hideTick }"> <div :class="{ 'show-tick': !hideTick }">
<template v-if="!hideTick"> <div class="flex items-center gap-2 flex-grow">
<GeneralIcon v-if="selectedView?.lock_type === type" icon="check" /> <component :is="types[type].icon" class="text-gray-800 !w-4 !h-4" />
<div class="flex flex-col">
<span v-else />
</template>
<div>
<div class="flex items-center gap-1">
<component :is="types[type].icon" class="text-gray-500 group-hover:text-accent" />
{{ $t(types[type].title) }} {{ $t(types[type].title) }}
</div> <div v-if="!hideTick" class="nc-subtitle max-w-108 text-sm text-gray-600 whitespace-normal">
<div class="nc-subtitle whitespace-normal">
{{ $t(types[type].subtitle) }} {{ $t(types[type].subtitle) }}
</div> </div>
</div> </div>
</div> </div>
<template v-if="!hideTick">
<GeneralIcon v-if="selectedView?.lock_type === type" icon="check" />
<span v-else />
</template>
</div>
</div> </div>
</template> </template>
<style scoped lang="scss"> <style scoped lang="scss">
.nc-locked-menu-item > div { .nc-locked-menu-item > div {
@apply p-2 items-center min-w-[350px] max-w-[350px]; @apply py-2 items-center;
&.show-tick { &.show-tick {
@apply grid gap-2 grid-cols-[30px,auto]; @apply flex gap-2;
}
.nc-subtitle {
@apply text-xs text-gray-500 font-weight-light;
} }
} }
</style> </style>

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

@ -124,7 +124,7 @@ watchDebounced(
:style="{ :style="{
width: '10rem', width: '10rem',
}" }"
:placeholder="$t('general.search')" :placeholder="`${$t('general.search')} in ${columns?.find((column) => column.value === search.field)?.label}`"
:bordered="false" :bordered="false"
data-testid="search-data-input" data-testid="search-data-input"
@press-enter="onPressEnter" @press-enter="onPressEnter"

8
packages/nc-gui/components/smartsheet/toolbar/SortListMenu.vue

@ -17,7 +17,7 @@ import {
watch, watch,
} from '#imports' } from '#imports'
import ListIcon from '~icons/nc-icons/list' import SortIcon from '~icons/nc-icons/sort'
const meta = inject(MetaInj, ref()) const meta = inject(MetaInj, ref())
const view = inject(ActiveViewInj, ref()) const view = inject(ActiveViewInj, ref())
@ -109,15 +109,15 @@ watch(open, () => {
<template> <template>
<NcDropdown v-model:visible="open" :trigger="['click']" overlay-class-name="nc-dropdown-sort-menu nc-toolbar-dropdown"> <NcDropdown v-model:visible="open" :trigger="['click']" overlay-class-name="nc-dropdown-sort-menu nc-toolbar-dropdown">
<div :class="{ 'nc-badge nc-active-btn': sorts?.length }"> <div :class="{ 'nc-active-btn': sorts?.length }">
<a-button v-e="['c:sort']" class="nc-sort-menu-btn nc-toolbar-btn" :disabled="isLocked"> <a-button v-e="['c:sort']" class="nc-sort-menu-btn nc-toolbar-btn" :disabled="isLocked">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<ListIcon class="h-4 w-4" /> <SortIcon class="h-4 w-4" />
<!-- Sort --> <!-- Sort -->
<span v-if="!isMobileMode" class="text-capitalize !text-sm font-medium">{{ $t('activity.sort') }}</span> <span v-if="!isMobileMode" class="text-capitalize !text-sm font-medium">{{ $t('activity.sort') }}</span>
<span v-if="sorts?.length" class="nc-count-badge">{{ sorts.length }}</span> <span v-if="sorts?.length" class="bg-brand-50 text-brand-500 py-1 px-2 text-md rounded-md">{{ sorts.length }}</span>
</div> </div>
</a-button> </a-button>
</div> </div>

87
packages/nc-gui/components/smartsheet/toolbar/ViewActions.vue

@ -1,5 +1,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import type { Ref } from '@vue/reactivity' import type { Ref } from '@vue/reactivity'
import UploadIcon from '~icons/nc-icons/upload'
import DownloadIcon from '~icons/nc-icons/download'
import { import {
ActiveViewInj, ActiveViewInj,
IsLockedInj, IsLockedInj,
@ -106,70 +108,23 @@ useMenuCloseOnEsc(open)
<template> <template>
<div> <div>
<a-dropdown v-model:visible="open" :trigger="['click']" overlay-class-name="nc-dropdown-actions-menu" placement="bottomLeft"> <a-dropdown v-model:visible="open" :trigger="['click']" overlay-class-name="nc-dropdown-actions-menu" placement="bottomRight">
<a-button v-e="['c:actions']" class="nc-actions-menu-btn nc-toolbar-btn !border-0 !rounded-md !py-1 !px-2"> <a-button v-e="['c:actions']" class="nc-actions-menu-btn nc-toolbar-btn !border-1 !border-gray-200 !rounded-md !py-1 !px-2">
<MdiDotsHorizontal class="!w-4 !h-4" /> <MdiDotsHorizontal class="!w-4 !h-4" />
</a-button> </a-button>
<template #overlay> <template #overlay>
<a-menu class="ml-6 !text-sm !px-0 !py-2 !rounded" data-testid="toolbar-actions" @click="open = false"> <a-menu class="!py-0 !rounded !text-gray-800 text-sm" data-testid="toolbar-actions" @click="open = false">
<a-menu-item-group> <a-menu-item-group>
<a-sub-menu
v-if="isUIAllowed('view-type') && false"
key="lock-type"
class="scrollbar-thin-dull min-w-50 max-h-90vh overflow-auto !py-0"
>
<template #title>
<div v-e="['c:navdraw:preview-as']" class="nc-project-menu-item group px-0 !py-0">
<LazySmartsheetToolbarLockType hide-tick :type="lockType" />
<component :is="iconMap.arrowRight" class="transform group-hover:(scale-115 ) text-gray-400" />
</div>
</template>
<template #expandIcon></template>
<a-menu-item @click="changeLockType(LockType.Collaborative)">
<LazySmartsheetToolbarLockType :type="LockType.Collaborative" />
</a-menu-item>
<a-menu-item @click="changeLockType(LockType.Locked)">
<LazySmartsheetToolbarLockType :type="LockType.Locked" />
</a-menu-item>
<a-menu-item @click="changeLockType(LockType.Personal)">
<LazySmartsheetToolbarLockType :type="LockType.Personal" />
</a-menu-item>
</a-sub-menu>
<a-menu-divider />
<a-sub-menu key="download">
<template #title>
<!-- Download -->
<div v-e="['c:navdraw:preview-as']" class="nc-project-menu-item group">
<component :is="iconMap.cloudDownload" class="text-gray-500 group-hover:text-accent" />
{{ $t('general.download') }}
<div class="flex-1" />
<component :is="iconMap.arrowRight" class="transform group-hover:(scale-115 ) text-gray-400" />
</div>
</template>
<template #expandIcon></template>
<LazySmartsheetToolbarExportSubActions />
</a-sub-menu>
<template v-if="isUIAllowed('csvImport') && !isView && !isPublicView && !isSqlView"> <template v-if="isUIAllowed('csvImport') && !isView && !isPublicView && !isSqlView">
<a-sub-menu key="upload"> <a-sub-menu key="upload">
<!-- Upload -->
<template #title> <template #title>
<div v-e="['c:navdraw:preview-as']" class="nc-project-menu-item group"> <div v-e="['c:navdraw:preview-as']" class="nc-project-menu-item group">
<component :is="iconMap.upload" class="text-gray-500 group-hover:text-accent" /> <UploadIcon class="w-4 h-4" />
{{ $t('general.upload') }} {{ $t('general.upload') }}
<div class="flex-1" /> <div class="flex-1" />
<component :is="iconMap.arrowRight" class="transform group-hover:(scale-115 ) text-gray-400" /> <component :is="iconMap.arrowRight" />
</div> </div>
</template> </template>
@ -182,26 +137,39 @@ useMenuCloseOnEsc(open)
:class="{ disabled: isLocked }" :class="{ disabled: isLocked }"
@click="!isLocked ? (dialog.value = true) : {}" @click="!isLocked ? (dialog.value = true) : {}"
> >
<component :is="iconMap.upload" class="text-gray-500" /> <component :is="iconMap.upload" />
{{ `${$t('general.upload')} ${type.toUpperCase()}` }} {{ `${$t('general.upload')} ${type.toUpperCase()}` }}
<div class="flex items-center text-gray-400"><MdiAlpha />version</div>
</div> </div>
</a-menu-item> </a-menu-item>
</template> </template>
</a-sub-menu> </a-sub-menu>
</template> </template>
</a-menu-item-group> <a-sub-menu key="download">
<template #title>
<div v-e="['c:navdraw:preview-as']" class="nc-project-menu-item group">
<DownloadIcon class="w-4 h-4" />
{{ $t('general.download') }}
<div class="flex-1" />
<component :is="iconMap.arrowRight" />
</div>
</template>
<template #expandIcon></template>
<LazySmartsheetToolbarExportSubActions />
</a-sub-menu>
<a-sub-menu <a-sub-menu
v-if="isUIAllowed('view-type')" v-if="isUIAllowed('view-type')"
key="lock-type" key="lock-type"
class="scrollbar-thin-dull min-w-50 max-h-90vh overflow-auto !py-0" class="scrollbar-thin-dull max-h-90vh overflow-auto !py-0"
> >
<template #title> <template #title>
<div v-e="['c:navdraw:preview-as']" class="nc-project-menu-item group px-0 !py-0"> <div v-e="['c:navdraw:preview-as']" class="nc-project-menu-item group px-0 !py-0">
<LazySmartsheetToolbarLockType hide-tick :type="lockType" /> <LazySmartsheetToolbarLockType hide-tick :type="lockType" />
<component :is="iconMap.arrowRight" class="transform group-hover:(scale-115 text-accent) text-gray-400" /> <component :is="iconMap.arrowRight" />
</div> </div>
</template> </template>
@ -214,10 +182,11 @@ useMenuCloseOnEsc(open)
<LazySmartsheetToolbarLockType :type="LockType.Locked" /> <LazySmartsheetToolbarLockType :type="LockType.Locked" />
</a-menu-item> </a-menu-item>
<a-menu-item @click="changeLockType(LockType.Personal)"> <!-- <a-menu-item @click="changeLockType(LockType.Personal)">
<LazySmartsheetToolbarLockType :type="LockType.Personal" /> <LazySmartsheetToolbarLockType :type="LockType.Personal" />
</a-menu-item> </a-menu-item> -->
</a-sub-menu> </a-sub-menu>
</a-menu-item-group>
</a-menu> </a-menu>
</template> </template>
</a-dropdown> </a-dropdown>

2
tests/playwright/pages/Dashboard/common/Toolbar/Fields.ts

@ -68,7 +68,7 @@ export class ToolbarFieldsPage extends BasePage {
async toggleShowAllFields({ isLocallySaved }: { isLocallySaved?: boolean } = {}) { async toggleShowAllFields({ isLocallySaved }: { isLocallySaved?: boolean } = {}) {
await this.toolbar.clickFields(); await this.toolbar.clickFields();
await this.waitForResponse({ await this.waitForResponse({
uiAction: () => this.get().locator(`button.nc-switch`).first().click(), uiAction: () => this.get().locator(`.nc-fields-show-all-fields`).click(),
requestUrlPathToMatch: isLocallySaved ? '/api/v1/db/public/' : '/api/v1/db/data/noco/', requestUrlPathToMatch: isLocallySaved ? '/api/v1/db/public/' : '/api/v1/db/data/noco/',
httpMethodsToMatch: ['GET'], httpMethodsToMatch: ['GET'],
}); });

Loading…
Cancel
Save