Browse Source

Merge branch 'develop' into refactor/webhooks

pull/5349/head
Wing-Kam Wong 2 years ago
parent
commit
a9ccf0122f
  1. 42
      packages/nc-gui/assets/style/fonts.css
  2. BIN
      packages/nc-gui/assets/style/material.woff2
  3. 23
      packages/nc-gui/components.d.ts
  4. 4
      packages/nc-gui/components/account/ResetPassword.vue
  5. 10
      packages/nc-gui/components/account/Token.vue
  6. 16
      packages/nc-gui/components/account/UserList.vue
  7. 7
      packages/nc-gui/components/account/UsersModal.vue
  8. 6
      packages/nc-gui/components/api-client/Headers.vue
  9. 6
      packages/nc-gui/components/api-client/Params.vue
  10. 23
      packages/nc-gui/components/cell/GeoData.vue
  11. 3
      packages/nc-gui/components/cell/MultiSelect.vue
  12. 3
      packages/nc-gui/components/cell/SingleSelect.vue
  13. 8
      packages/nc-gui/components/cell/attachment/Carousel.vue
  14. 2
      packages/nc-gui/components/cell/attachment/Image.vue
  15. 9
      packages/nc-gui/components/cell/attachment/Modal.vue
  16. 8
      packages/nc-gui/components/cell/attachment/index.vue
  17. 94
      packages/nc-gui/components/dashboard/TreeView.vue
  18. 6
      packages/nc-gui/components/dashboard/settings/AppStore.vue
  19. 4
      packages/nc-gui/components/dashboard/settings/AuditTab.vue
  20. 24
      packages/nc-gui/components/dashboard/settings/DataSources.vue
  21. 6
      packages/nc-gui/components/dashboard/settings/Metadata.vue
  22. 20
      packages/nc-gui/components/dashboard/settings/Modal.vue
  23. 7
      packages/nc-gui/components/dashboard/settings/UIAcl.vue
  24. 10
      packages/nc-gui/components/dashboard/settings/app-store/AppInstall.vue
  25. 9
      packages/nc-gui/components/dashboard/settings/data-sources/CreateBase.vue
  26. 11
      packages/nc-gui/components/dashboard/settings/data-sources/EditBase.vue
  27. 5
      packages/nc-gui/components/dlg/AirtableImport.vue
  28. 9
      packages/nc-gui/components/dlg/QuickImport.vue
  29. 18
      packages/nc-gui/components/dlg/TableCreate.vue
  30. 5
      packages/nc-gui/components/erd/HistogramPanel.vue
  31. 4
      packages/nc-gui/components/general/AddBaseButton.vue
  32. 4
      packages/nc-gui/components/general/HelpAndSupport.vue
  33. 11
      packages/nc-gui/components/general/Icon.vue
  34. 6
      packages/nc-gui/components/general/JoinCloud.vue
  35. 14
      packages/nc-gui/components/general/MiniSidebar.vue
  36. 19
      packages/nc-gui/components/general/PreviewAs.vue
  37. 4
      packages/nc-gui/components/general/ShareBaseButton.vue
  38. 26
      packages/nc-gui/components/general/Social.vue
  39. 18
      packages/nc-gui/components/general/SocialCard.vue
  40. 5
      packages/nc-gui/components/general/TableIcon.vue
  41. 6
      packages/nc-gui/components/smartsheet/Form.vue
  42. 3
      packages/nc-gui/components/smartsheet/Gallery.vue
  43. 8
      packages/nc-gui/components/smartsheet/Grid.vue
  44. 20
      packages/nc-gui/components/smartsheet/Kanban.vue
  45. 4
      packages/nc-gui/components/smartsheet/Map.vue
  46. 4
      packages/nc-gui/components/smartsheet/Pagination.vue
  47. 4
      packages/nc-gui/components/smartsheet/column/BarcodeOptions.vue
  48. 5
      packages/nc-gui/components/smartsheet/column/FormulaOptions.vue
  49. 10
      packages/nc-gui/components/smartsheet/column/SelectOptions.vue
  50. 12
      packages/nc-gui/components/smartsheet/expanded-form/Comments.vue
  51. 34
      packages/nc-gui/components/smartsheet/expanded-form/Header.vue
  52. 5
      packages/nc-gui/components/smartsheet/expanded-form/index.vue
  53. 74
      packages/nc-gui/components/smartsheet/header/CellIcon.ts
  54. 22
      packages/nc-gui/components/smartsheet/header/Menu.vue
  55. 60
      packages/nc-gui/components/smartsheet/header/VirtualCellIcon.ts
  56. 12
      packages/nc-gui/components/smartsheet/sidebar/MenuBottom.vue
  57. 24
      packages/nc-gui/components/smartsheet/sidebar/RenameableMenuItem.vue
  58. 4
      packages/nc-gui/components/smartsheet/sidebar/toolbar/DeleteCache.vue
  59. 4
      packages/nc-gui/components/smartsheet/sidebar/toolbar/DeleteTable.vue
  60. 4
      packages/nc-gui/components/smartsheet/sidebar/toolbar/ExportCache.vue
  61. 8
      packages/nc-gui/components/smartsheet/toolbar/AddRow.vue
  62. 11
      packages/nc-gui/components/smartsheet/toolbar/ColumnFilter.vue
  63. 5
      packages/nc-gui/components/smartsheet/toolbar/ColumnFilterMenu.vue
  64. 4
      packages/nc-gui/components/smartsheet/toolbar/Erd.vue
  65. 8
      packages/nc-gui/components/smartsheet/toolbar/Export.vue
  66. 5
      packages/nc-gui/components/smartsheet/toolbar/ExportSubActions.vue
  67. 9
      packages/nc-gui/components/smartsheet/toolbar/FieldsMenu.vue
  68. 5
      packages/nc-gui/components/smartsheet/toolbar/KanbanStackEditOrAdd.vue
  69. 16
      packages/nc-gui/components/smartsheet/toolbar/LockType.vue
  70. 3
      packages/nc-gui/components/smartsheet/toolbar/MappedBy.vue
  71. 4
      packages/nc-gui/components/smartsheet/toolbar/QrScannerButton.vue
  72. 9
      packages/nc-gui/components/smartsheet/toolbar/Reload.vue
  73. 15
      packages/nc-gui/components/smartsheet/toolbar/RowHeight.vue
  74. 5
      packages/nc-gui/components/smartsheet/toolbar/SearchData.vue
  75. 14
      packages/nc-gui/components/smartsheet/toolbar/ShareView.vue
  76. 5
      packages/nc-gui/components/smartsheet/toolbar/SharedViewList.vue
  77. 10
      packages/nc-gui/components/smartsheet/toolbar/SortListMenu.vue
  78. 3
      packages/nc-gui/components/smartsheet/toolbar/StackedBy.vue
  79. 38
      packages/nc-gui/components/smartsheet/toolbar/ViewActions.vue
  80. 20
      packages/nc-gui/components/tabs/auth/ApiTokenManagement.vue
  81. 17
      packages/nc-gui/components/tabs/auth/UserManagement.vue
  82. 11
      packages/nc-gui/components/tabs/auth/user-management/ShareBase.vue
  83. 7
      packages/nc-gui/components/tabs/auth/user-management/UsersModal.vue
  84. 29
      packages/nc-gui/components/template/Editor.vue
  85. 8
      packages/nc-gui/components/virtual-cell/BelongsTo.vue
  86. 6
      packages/nc-gui/components/virtual-cell/HasMany.vue
  87. 6
      packages/nc-gui/components/virtual-cell/ManyToMany.vue
  88. 7
      packages/nc-gui/components/virtual-cell/components/ItemChip.vue
  89. 12
      packages/nc-gui/components/virtual-cell/components/ListChildItems.vue
  90. 3
      packages/nc-gui/components/virtual-cell/components/ListItems.vue
  91. 6
      packages/nc-gui/components/webhook/Editor.vue
  92. 19
      packages/nc-gui/components/webhook/List.vue
  93. 11
      packages/nc-gui/layouts/base.vue
  94. 4
      packages/nc-gui/layouts/shared-view.vue
  95. 34
      packages/nc-gui/package-lock.json
  96. 3
      packages/nc-gui/package.json
  97. 31
      packages/nc-gui/pages/[projectType]/[projectId]/index.vue
  98. 13
      packages/nc-gui/pages/[projectType]/[projectId]/index/index.vue
  99. 4
      packages/nc-gui/pages/[projectType]/form/[viewId]/index/index.vue
  100. 15
      packages/nc-gui/pages/[projectType]/form/[viewId]/index/survey.vue
  101. Some files were not shown because too many files have changed in this diff Show More

42
packages/nc-gui/assets/style/fonts.css

@ -149,3 +149,45 @@
font-style: normal;
font-display: swap;
}
/* material-symbols-outlined-200 - latin */
@font-face {
font-family: 'Material Symbols Outlined';
font-style: normal;
font-weight: 200;
src: url('./material-symbols/material-symbols-outlined-v92-latin-200.eot'); /* IE9 Compat Modes */
src: url('./material-symbols/material-symbols-outlined-v92-latin-200.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('./material-symbols/material-symbols-outlined-v92-latin-200.woff2') format('woff2'), /* Super Modern Browsers */
url('./material-symbols/material-symbols-outlined-v92-latin-200.woff') format('woff'), /* Modern Browsers */
url('./material-symbols/material-symbols-outlined-v92-latin-200.ttf') format('truetype'), /* Safari, Android, iOS */
url('./material-symbols/material-symbols-outlined-v92-latin-200.svg#MaterialSymbolsOutlined') format('svg'); /* Legacy iOS */
}
/* // href: 'https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20,200,0,-25',
*/
/* fallback */
@font-face {
font-family: 'Material Symbols Outlined';
font-style: normal;
font-weight: 200;
src: url(./material.woff2) format('woff2');
}
.material-symbols-outlined {
font-family: 'Material Symbols Outlined';
font-weight: normal;
font-style: normal;
line-height: 1;
letter-spacing: normal;
text-transform: none;
display: inline-block;
white-space: nowrap;
word-wrap: normal;
direction: ltr;
-webkit-font-feature-settings: 'liga';
-webkit-font-smoothing: antialiased;
font-size: 22px !important;
user-select: none;
}

BIN
packages/nc-gui/assets/style/material.woff2

Binary file not shown.

23
packages/nc-gui/components.d.ts vendored

@ -259,10 +259,33 @@ declare module '@vue/runtime-core' {
NcIconsRowHeightMedium: typeof import('~icons/nc-icons/row-height-medium')['default']
NcIconsRowHeightShort: typeof import('~icons/nc-icons/row-height-short')['default']
NcIconsRowHeightTall: typeof import('~icons/nc-icons/row-height-tall')['default']
PhArrowClockwiseThin: typeof import('~icons/ph/arrow-clockwise-thin')['default']
PhAtThin: typeof import('~icons/ph/at-thin')['default']
PhBracketsAngleThin: typeof import('~icons/ph/brackets-angle-thin')['default']
PhBracketsCurlyThin: typeof import('~icons/ph/brackets-curly-thin')['default']
PhCaretDoubleLeftThin: typeof import('~icons/ph/caret-double-left-thin')['default']
PhCaretDoubleRightThin: typeof import('~icons/ph/caret-double-right-thin')['default']
PhCaretDoubleThin: typeof import('~icons/ph/caret-double-thin')['default']
PhCaretDownThin: typeof import('~icons/ph/caret-down-thin')['default']
PhChatTextThin: typeof import('~icons/ph/chat-text-thin')['default']
PhClockClockwiseThin: typeof import('~icons/ph/clock-clockwise-thin')['default']
PhCloudLightningDuotone: typeof import('~icons/ph/cloud-lightning-duotone')['default']
PhCloudLightningThin: typeof import('~icons/ph/cloud-lightning-thin')['default']
PhEyeThin: typeof import('~icons/ph/eye-thin')['default']
PhFileCsv: typeof import('~icons/ph/file-csv')['default']
PhFolderSimpleThin: typeof import('~icons/ph/folder-simple-thin')['default']
PhFolderThin: typeof import('~icons/ph/folder-thin')['default']
PhFunnelThin: typeof import('~icons/ph/funnel-thin')['default']
PhlistBulletsThin: typeof import('~icons/ph/list-bullets-thin')['default']
PhListBulletsThin: typeof import('~icons/ph/list-bullets-thin')['default']
PhMagnifyingGlassThin: typeof import('~icons/ph/magnifying-glass-thin')['default']
PhPlusThin: typeof import('~icons/ph/plus-thin')['default']
PhPresentationThin: typeof import('~icons/ph/presentation-thin')['default']
PhShareThin: typeof import('~icons/ph/share-thin')['default']
PhSignOutThin: typeof import('~icons/ph/sign-out-thin')['default']
PhSortAscendingThin: typeof import('~icons/ph/sort-ascending-thin')['default']
PhSplitVerticalThin: typeof import('~icons/ph/split-vertical-thin')['default']
PhTranslateThin: typeof import('~icons/ph/translate-thin')['default']
PhUserPlusThin: typeof import('~icons/ph/user-plus-thin')['default']
PhUsersThreeThin: typeof import('~icons/ph/users-three-thin')['default']
RiLineHeight: typeof import('~icons/ri/line-height')['default']

4
packages/nc-gui/components/account/ResetPassword.vue

@ -1,5 +1,5 @@
<script lang="ts" setup>
import { message, navigateTo, reactive, ref, useApi, useGlobal, useI18n } from '#imports'
import { iconMap, message, navigateTo, reactive, ref, useApi, useGlobal, useI18n } from '#imports'
const { api, error } = useApi({ useGlobalInstance: true })
@ -121,7 +121,7 @@ const resetError = () => {
<div class="text-center">
<button data-testid="nc-user-settings-form__submit" class="scaling-btn bg-opacity-100" type="submit">
<span class="flex items-center gap-2">
<MdiKeyChange />
<component :is="iconMap.passwordChange" />
{{ $t('activity.changePwd') }}
</span>
</button>

10
packages/nc-gui/components/account/Token.vue

@ -2,7 +2,7 @@
import type { VNodeRef } from '@vue/runtime-core'
import { Empty, Modal, message } from 'ant-design-vue'
import type { ApiTokenType, RequestParams, UserType } from 'nocodb-sdk'
import { extractSdkResponseErrorMsg, useApi, useCopy, useNuxtApp } from '#imports'
import { extractSdkResponseErrorMsg, iconMap, useApi, useCopy, useNuxtApp } from '#imports'
const { api, isLoading } = useApi()
@ -104,10 +104,10 @@ const descriptionInput: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
<div class="max-w-[900px] mx-auto p-4" data-testid="nc-token-list">
<div class="py-2 flex gap-4 items-center">
<div class="flex-grow"></div>
<MdiReload class="cursor-pointer" @click="loadTokens" />
<component :is="iconMap.reload" class="cursor-pointer" @click="loadTokens" />
<a-button data-testid="nc-token-create" size="small" type="primary" @click="showNewTokenModal = true">
<div class="flex items-center gap-1">
<MdiAdd />
<component :is="iconMap.plus" />
Add new token
</div>
</a-button>
@ -175,7 +175,7 @@ const descriptionInput: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
<a-button type="text" class="!rounded-md" @click="copyToken(record.token)">
<template #icon>
<MdiContentCopy class="flex mx-auto h-[1rem]" />
<component :is="iconMap.copy" class="flex mx-auto h-[1rem]" />
</template>
</a-button>
</a-tooltip>
@ -198,7 +198,7 @@ const descriptionInput: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
<a-menu data-testid="nc-token-row-action-icon">
<a-menu-item>
<div class="flex flex-row items-center py-3 h-[1rem] nc-delete-token" @click="deleteToken(record.token)">
<MdiDeleteOutline class="flex" />
<component :is="iconMap.delete" class="flex" />
<div class="text-xs pl-2">{{ $t('general.remove') }}</div>
</div>
</a-menu-item>

16
packages/nc-gui/components/account/UserList.vue

@ -1,7 +1,7 @@
<script lang="ts" setup>
import { Modal, message } from 'ant-design-vue'
import type { OrgUserReqType, RequestParams, UserType } from 'nocodb-sdk'
import { Role, extractSdkResponseErrorMsg, useApi, useCopy, useDashboard, useNuxtApp } from '#imports'
import { Role, extractSdkResponseErrorMsg, iconMap, useApi, useCopy, useDashboard, useNuxtApp } from '#imports'
import type { User } from '~/lib'
const { api, isLoading } = useApi()
@ -144,7 +144,7 @@ const copyPasswordResetUrl = async (user: User) => {
>
</a-input-search>
<div class="flex-grow"></div>
<MdiReload class="cursor-pointer" @click="loadUsers" />
<component :is="iconMap.reload" class="cursor-pointer" @click="loadUsers" />
<a-button
data-testid="nc-super-user-invite"
size="small"
@ -157,7 +157,7 @@ const copyPasswordResetUrl = async (user: User) => {
"
>
<div class="flex items-center gap-1">
<MdiAdd />
<component :is="iconMap.plus" />
Invite new user
</div>
</a-button>
@ -239,7 +239,7 @@ const copyPasswordResetUrl = async (user: User) => {
<div class="flex flex-row items-center">
<a-button type="text" class="!px-0">
<div class="flex flex-row items-center h-[1.2rem]">
<MdiDotsHorizontal class="nc-user-row-action" />
<component :is="iconMap.threeDotHorizontal" class="nc-user-row-action" />
</div>
</a-button>
</div>
@ -250,26 +250,26 @@ const copyPasswordResetUrl = async (user: User) => {
<a-menu-item>
<!-- Resend invite Email -->
<div class="flex flex-row items-center py-3" @click="resendInvite(record)">
<MdiEmailArrowRightOutline class="flex h-[1rem] text-gray-500" />
<component :is="iconMap.email" class="flex h-[1rem] text-gray-500" />
<div class="text-xs pl-2">{{ $t('activity.resendInvite') }}</div>
</div>
</a-menu-item>
<a-menu-item>
<div class="flex flex-row items-center py-3" @click="copyInviteUrl(record)">
<MdiContentCopy class="flex h-[1rem] text-gray-500" />
<component :is="iconMap.copy" class="flex h-[1rem] text-gray-500" />
<div class="text-xs pl-2">{{ $t('activity.copyInviteURL') }}</div>
</div>
</a-menu-item>
</template>
<a-menu-item>
<div class="flex flex-row items-center py-3" @click="copyPasswordResetUrl(record)">
<MdiContentCopy class="flex h-[1rem] text-gray-500" />
<component :is="iconMap.copy" class="flex h-[1rem] text-gray-500" />
<div class="text-xs pl-2">{{ $t('activity.copyPasswordResetURL') }}</div>
</div>
</a-menu-item>
<a-menu-item>
<div class="flex flex-row items-center py-3" @click="deleteUser(text)">
<MdiDeleteOutline data-testid="nc-super-user-delete" class="flex h-[1rem] text-gray-500" />
<component :is="iconMap.delete" data-testid="nc-super-user-delete" class="flex h-[1rem] text-gray-500" />
<div class="text-xs pl-2">{{ $t('general.delete') }}</div>
</div>
</a-menu-item>

7
packages/nc-gui/components/account/UsersModal.vue

@ -6,6 +6,7 @@ import {
computed,
emailValidator,
extractSdkResponseErrorMsg,
iconMap,
message,
ref,
useCopy,
@ -126,7 +127,7 @@ const emailInput: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
<template v-if="usersData.invitationToken">
<div class="flex flex-col mt-1 border-b-1 pb-5">
<div class="flex flex-row items-center pl-1.5 pb-1 h-[1.1rem]">
<MdiAccountOutline />
<component :is="iconMap.account" />
<div class="text-xs ml-0.5 mt-0.5">Copy Invite Token</div>
</div>
@ -139,7 +140,7 @@ const emailInput: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
<a-button type="text" class="!rounded-md -mt-0.5" @click="copyUrl">
<template #icon>
<MdiContentCopy class="flex mx-auto text-green-700 h-[1rem]" />
<component :is="iconMap.copy" class="flex mx-auto text-green-700 h-[1rem]" />
</template>
</a-button>
</div>
@ -165,7 +166,7 @@ const emailInput: VNodeRef = (el) => (el as HTMLInputElement)?.focus()
<div v-else class="flex flex-col pb-4">
<div class="flex flex-row items-center pl-2 pb-1 h-[1rem]">
<MdiAccountOutline />
<component :is="iconMap.account" />
<div class="text-xs ml-0.5 mt-0.5">{{ $t('activity.inviteUser') }}</div>
</div>

6
packages/nc-gui/components/api-client/Headers.vue

@ -1,5 +1,5 @@
<script setup lang="ts">
import { useVModel } from '#imports'
import { iconMap, useVModel } from '#imports'
const props = defineProps<{
modelValue: any[]
@ -116,7 +116,7 @@ const filterOption = (input: string, option: Option) => {
<td class="relative">
<div v-if="idx !== 0" class="absolute flex flex-col justify-start mt-2 -right-6 top-0">
<MdiDeleteOutline class="cursor-pointer" @click="deleteHeaderRow(idx)" />
<component :is="iconMap.delete" class="cursor-pointer" @click="deleteHeaderRow(idx)" />
</div>
</td>
</tr>
@ -125,7 +125,7 @@ const filterOption = (input: string, option: Option) => {
<td :colspan="12" class="text-center">
<a-button type="default" class="!bg-gray-100 rounded-md border-none mr-1" @click="addHeaderRow">
<template #icon>
<MdiPlus class="flex mx-auto" />
<component :is="iconMap.plus" class="flex mx-auto" />
</template>
</a-button>
</td>

6
packages/nc-gui/components/api-client/Params.vue

@ -1,5 +1,5 @@
<script setup lang="ts">
import { useVModel } from '#imports'
import { iconMap, useVModel } from '#imports'
const props = defineProps<{
modelValue: any[]
@ -59,7 +59,7 @@ const deleteParamRow = (i: number) => vModel.value.splice(i, 1)
<td class="relative">
<div v-if="idx !== 0" class="absolute flex flex-col justify-start mt-2 -right-6 top-0">
<MdiDeleteOutline class="cursor-pointer" @click="deleteParamRow(idx)" />
<component :is="iconMap.delete" class="cursor-pointer" @click="deleteParamRow(idx)" />
</div>
</td>
</tr>
@ -68,7 +68,7 @@ const deleteParamRow = (i: number) => vModel.value.splice(i, 1)
<td :colspan="12" class="text-center">
<a-button type="default" class="!bg-gray-100 rounded-md border-none mr-1 mb-3" @click="addParamRow">
<template #icon>
<MdiPlus class="flex mx-auto" />
<component :is="iconMap.plus" class="flex mx-auto" />
</template>
</a-button>
</td>

23
packages/nc-gui/components/cell/GeoData.vue

@ -1,6 +1,6 @@
<script lang="ts" setup>
import type { GeoLocationType } from 'nocodb-sdk'
import { Modal as AModal, latLongToJoinedString, useVModel } from '#imports'
import { Modal as AModal, iconMap, latLongToJoinedString, useVModel } from '#imports'
interface Props {
modelValue?: string | null
@ -89,7 +89,10 @@ const openInOSM = () => {
class="group cursor-pointer flex gap-1 items-center mx-auto max-w-64 justify-center active:(ring ring-accent ring-opacity-100) rounded border-1 p-1 shadow-sm hover:(bg-primary bg-opacity-10) dark:(!bg-slate-500)"
>
<div class="flex items-center gap-2" data-testid="nc-geo-data-set-location-button">
<MdiMapMarker class="transform dark:(!text-white) group-hover:(!text-accent scale-120) text-gray-500 text-[0.75rem]" />
<component
:is="iconMap.mapMarker"
class="transform dark:(!text-white) group-hover:(!text-accent scale-120) text-gray-500 text-[0.75rem]"
/>
<div class="group-hover:text-primary text-gray-500 dark:text-gray-200 dark:group-hover:!text-white text-xs">
{{ latLongStr }}
</div>
@ -135,16 +138,24 @@ const openInOSM = () => {
</a-form-item>
<a-form-item>
<div class="mr-2 flex flex-col items-end gap-1 text-left">
<MdiReload v-if="isLoading" :class="{ 'animate-infinite animate-spin text-gray-500': isLoading }" />
<component
:is="iconMap.reload"
v-if="isLoading"
:class="{ 'animate-infinite animate-spin text-gray-500': isLoading }"
/>
<a-button class="ml-2" @click="onClickSetCurrentLocation"
><MdiGpsFixed class="mr-2" />{{ $t('labels.currentLocation') }}</a-button
><component :is="iconMap.currentLocation" class="mr-2" />{{ $t('labels.currentLocation') }}</a-button
>
</div>
</a-form-item>
<a-form-item v-if="vModel">
<div class="mr-2 flex flex-row items-end gap-1 text-left">
<a-button @click="openInOSM"><MdiOpenInNew class="mr-2" />{{ $t('activity.map.openInOpenStreetMap') }}</a-button>
<a-button @click="openInGoogleMaps"><MdiOpenInNew class="mr-2" />{{ $t('activity.map.openInGoogleMaps') }}</a-button>
<a-button @click="openInOSM"
><component :is="iconMap.openInNew" class="mr-2" />{{ $t('activity.map.openInOpenStreetMap') }}</a-button
>
<a-button @click="openInGoogleMaps"
><component :is="iconMap.openInNew" class="mr-2" />{{ $t('activity.map.openInGoogleMaps') }}</a-button
>
</div>
</a-form-item>
<a-form-item>

3
packages/nc-gui/components/cell/MultiSelect.vue

@ -14,6 +14,7 @@ import {
enumColor,
extractSdkResponseErrorMsg,
h,
iconMap,
inject,
isDrawerOrModalExist,
onMounted,
@ -367,7 +368,7 @@ useEventListener(document, 'click', handleClose, true)
:value="searchVal"
>
<div class="flex gap-2 text-gray-500 items-center h-full">
<MdiPlusThick class="min-w-4" />
<component :is="iconMap.plusThick" class="min-w-4" />
<div class="text-xs whitespace-normal">
Create new option named <strong>{{ searchVal }}</strong>
</div>

3
packages/nc-gui/components/cell/SingleSelect.vue

@ -15,6 +15,7 @@ import {
computed,
enumColor,
extractSdkResponseErrorMsg,
iconMap,
inject,
isDrawerOrModalExist,
ref,
@ -295,7 +296,7 @@ useEventListener(document, 'click', handleClose, true)
:value="searchVal"
>
<div class="flex gap-2 text-gray-500 items-center h-full">
<MdiPlusThick class="min-w-4" />
<component :is="iconMap.plusThick" class="min-w-4" />
<div class="text-xs whitespace-normal">
Create new option named <strong>{{ searchVal }}</strong>
</div>

8
packages/nc-gui/components/cell/attachment/Carousel.vue

@ -1,7 +1,7 @@
<script lang="ts" setup>
import { onKeyDown } from '@vueuse/core'
import { useAttachmentCell } from './utils'
import { computed, isImage, onClickOutside, ref, useAttachment } from '#imports'
import { computed, iconMap, isImage, onClickOutside, ref, useAttachment } from '#imports'
const { selectedImage, visibleItems, downloadFile } = useAttachmentCell()!
@ -53,7 +53,11 @@ onClickOutside(carouselRef, () => {
<template v-if="selectedImage">
<div class="overflow-hidden p-12 text-center relative">
<div class="text-white group absolute top-5 right-5">
<MdiCloseCircle class="group-hover:text-red-500 cursor-pointer text-4xl" @click.stop="selectedImage = false" />
<component
:is="iconMap.closeCircle"
class="group-hover:text-red-500 cursor-pointer text-4xl"
@click.stop="selectedImage = false"
/>
</div>
<div

2
packages/nc-gui/components/cell/attachment/Image.vue

@ -21,5 +21,5 @@ const onError = () => index.value++
quality="75"
@error="onError"
/>
<MdiFileImageBox v-else />
<component :is="iconMap.imagePlaceholder" v-else />
</template>

9
packages/nc-gui/components/cell/attachment/Modal.vue

@ -2,7 +2,7 @@
import { onKeyDown } from '@vueuse/core'
import { useAttachmentCell } from './utils'
import { useSortable } from './sort'
import { isImage, ref, useAttachment, useDropZone, useUIPermission, watch } from '#imports'
import { iconMap, isImage, ref, useAttachment, useDropZone, useUIPermission, watch } from '#imports'
const { isUIAllowed } = useUIPermission()
@ -133,7 +133,8 @@ function onRemoveFileClick(title: any, i: number) {
<a-tooltip v-if="!readOnly">
<template #title> Remove File </template>
<MdiCloseCircle
<component
:is="iconMap.closeCircle"
v-if="isSharedForm || (isUIAllowed('tableAttachment') && !isPublic && !isLocked)"
class="nc-attachment-remove"
@click.stop="onRemoveFileClick(item.title, i)"
@ -144,7 +145,7 @@ function onRemoveFileClick(title: any, i: number) {
<template #title> Download File </template>
<div class="nc-attachment-download group-hover:(opacity-100)">
<MdiDownload @click.stop="downloadFile(item)" />
<component :is="iconMap.download" @click.stop="downloadFile(item)" />
</div>
</a-tooltip>
@ -155,7 +156,7 @@ function onRemoveFileClick(title: any, i: number) {
<template #title> Rename File </template>
<div class="nc-attachment-download group-hover:(opacity-100) mr-[35px]">
<MdiEditOutline @click.stop="renameFile(item, i)" />
<component :is="iconMap.edit" @click.stop="renameFile(item, i)" />
</div>
</a-tooltip>

8
packages/nc-gui/components/cell/attachment/index.vue

@ -7,6 +7,7 @@ import {
DropZoneRef,
IsGalleryInj,
IsKanbanInj,
iconMap,
inject,
isImage,
nextTick,
@ -188,7 +189,7 @@ useSelectedCellKeyupListener(inject(ActiveCellInj, ref(false)), (e) => {
data-testid="attachment-cell-file-picker-button"
@click.stop="open"
>
<MdiReload v-if="isLoading" :class="{ 'animate-infinite animate-spin': isLoading }" />
<component :is="iconMap.reload" v-if="isLoading" :class="{ 'animate-infinite animate-spin': isLoading }" />
<a-tooltip v-else placement="bottom">
<template #title> Click or drop a file into cell</template>
@ -238,12 +239,13 @@ useSelectedCellKeyupListener(inject(ActiveCellInj, ref(false)), (e) => {
<div
class="group cursor-pointer flex gap-1 items-center active:(ring ring-accent ring-opacity-100) rounded border-1 p-1 shadow-sm hover:(bg-primary bg-opacity-10) dark:(!bg-slate-500)"
>
<MdiReload v-if="isLoading" :class="{ 'animate-infinite animate-spin': isLoading }" />
<component :is="iconMap.reload" v-if="isLoading" :class="{ 'animate-infinite animate-spin': isLoading }" />
<a-tooltip v-else placement="bottom">
<template #title> View attachments</template>
<MdiArrowExpand
<component
:is="iconMap.expand"
class="transform dark:(!text-white) group-hover:(!text-accent scale-120) text-gray-500 text-[0.75rem]"
@click.stop="modalVisible = true"
/>

94
packages/nc-gui/components/dashboard/TreeView.vue

@ -12,6 +12,7 @@ import {
TabType,
computed,
extractSdkResponseErrorMsg,
iconMap,
isDrawerOrModalExist,
isMac,
parseProp,
@ -30,8 +31,6 @@ import {
useUIPermission,
watchEffect,
} from '#imports'
import MdiView from '~icons/mdi/eye-circle-outline'
import MdiTableLarge from '~icons/mdi/table-large'
const { isMobileMode } = useGlobal()
@ -152,10 +151,10 @@ watchEffect(() => {
const icon = (table: TableType) => {
if (table.type === 'table') {
return MdiTableLarge
return iconMap.table
}
if (table.type === 'view') {
return MdiView
return iconMap.view
}
}
@ -373,8 +372,13 @@ const setIcon = async (icon: string, table: TableType) => {
</Transition>
<Transition name="layout" mode="out-in">
<MdiClose v-if="searchActive" class="text-gray-500 text-lg mx-1 mt-0.5" @click="onSearchCloseIconClick" />
<IcRoundSearch v-else class="text-gray-500 text-lg mx-1 mt-0.5" @click="toggleSearchActive(true)" />
<GeneralIcon
v-if="searchActive"
icon="close"
class="text-gray-500 text-lg mx-1 mt-0.5"
@click="onSearchCloseIconClick"
/>
<GeneralIcon v-else icon="search" class="text-gray-500 text-lg mx-1 mt-0.5" @click="toggleSearchActive(true)" />
</Transition>
</div>
<div
@ -400,13 +404,18 @@ const setIcon = async (icon: string, table: TableType) => {
</Transition>
<Transition name="slide-right" mode="out-in">
<MdiClose v-if="searchActive" class="text-gray-500 text-lg mx-1 mt-0.5" @click="onSearchCloseIconClick" />
<IcRoundSearch v-else class="text-gray-500 text-lg mx-1 mt-0.5" @click="onSearchIconClick" />
<GeneralIcon
v-if="searchActive"
icon="close"
class="text-gray-500 text-lg mx-1 mt-0.5"
@click="onSearchCloseIconClick"
/>
<component :is="iconMap.search" v-else class="text-gray-500 text-lg mx-1 mt-0.5" @click="onSearchIconClick" />
</Transition>
<a-dropdown v-if="!isSharedBase" :trigger="['click']" overlay-class-name="nc-dropdown-import-menu" @click.stop>
<Transition name="slide-right" mode="out-in">
<MdiDotsVertical v-if="!searchActive" class="hover:text-accent outline-0" />
<GeneralIcon v-if="!searchActive" icon="threeDotVertical" class="hover:text-accent outline-0" />
</Transition>
<template #overlay>
@ -457,7 +466,7 @@ const setIcon = async (icon: string, table: TableType) => {
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-accent" />
<GeneralIcon icon="openInNew" class="group-hover:text-accent" />
<!-- Request a data source you need? -->
{{ $t('labels.requestDataSource') }}
</a>
@ -473,12 +482,15 @@ const setIcon = async (icon: string, table: TableType) => {
class="group flex items-center gap-2 pl-2 pr-3 py-2 text-primary/70 hover:(text-primary/100) cursor-pointer select-none"
@click="openTableCreateDialog(bases[0].id)"
>
<MdiPlus class="w-5" />
<GeneralIcon icon="plus" class="w-5" />
<span class="text-gray-500 group-hover:(text-primary/100) flex-1 nc-add-new-table">{{ $t('tooltip.addTable') }}</span>
<a-dropdown v-if="!isSharedBase" :trigger="['click']" overlay-class-name="nc-dropdown-import-menu" @click.stop>
<MdiDotsVertical class="transition-opacity opacity-0 group-hover:opacity-100 nc-import-menu outline-0" />
<GeneralIcon
icon="threeDotVertical"
class="transition-opacity opacity-0 group-hover:opacity-100 nc-import-menu outline-0"
/>
<template #overlay>
<a-menu class="!py-0 rounded text-sm">
@ -490,7 +502,7 @@ const setIcon = async (icon: string, table: TableType) => {
@click="openAirtableImportDialog(bases[0].id)"
>
<div class="color-transition nc-project-menu-item group">
<MdiTableLarge class="group-hover:text-accent" />
<GeneralIcon icon="table" class="group-hover:text-accent" />
Airtable
</div>
</a-menu-item>
@ -501,7 +513,7 @@ const setIcon = async (icon: string, table: TableType) => {
@click="openQuickImportDialog('csv', bases[0].id)"
>
<div class="color-transition nc-project-menu-item group">
<MdiFileDocumentOutline class="group-hover:text-accent" />
<GeneralIcon icon="csv" class="group-hover:text-accent" />
CSV file
</div>
</a-menu-item>
@ -512,7 +524,7 @@ const setIcon = async (icon: string, table: TableType) => {
@click="openQuickImportDialog('json', bases[0].id)"
>
<div class="color-transition nc-project-menu-item group">
<MdiCodeJson class="group-hover:text-accent" />
<GeneralIcon icon="code" class="group-hover:text-accent" />
JSON file
</div>
</a-menu-item>
@ -523,7 +535,7 @@ const setIcon = async (icon: string, table: TableType) => {
@click="openQuickImportDialog('excel', bases[0].id)"
>
<div class="color-transition nc-project-menu-item group">
<MdiFileExcel class="group-hover:text-accent" />
<GeneralIcon icon="excel" class="group-hover:text-accent" />
Microsoft Excel
</div>
</a-menu-item>
@ -577,7 +589,7 @@ const setIcon = async (icon: string, table: TableType) => {
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-accent" />
<GeneralIcon icon="openInNew" class="group-hover:text-accent" />
<!-- Request a data source you need? -->
{{ $t('labels.requestDataSource') }}
</a>
@ -658,7 +670,10 @@ const setIcon = async (icon: string, table: TableType) => {
:trigger="['click']"
@click.stop
>
<MdiDotsVertical class="transition-opacity opacity-0 group-hover:opacity-100 outline-0" />
<GeneralIcon
icon="threeDotVertical"
class="transition-opacity opacity-0 group-hover:opacity-100 outline-0"
/>
<template #overlay>
<a-menu class="!py-0 rounded text-sm">
@ -725,7 +740,7 @@ const setIcon = async (icon: string, table: TableType) => {
class="group flex items-center gap-2 pl-8 pr-3 py-2 text-primary/70 hover:(text-primary/100) cursor-pointer select-none"
@click="openTableCreateDialog(bases[0].id)"
>
<MdiPlus />
<component :is="iconMap.plus" />
<span class="text-gray-500 group-hover:(text-primary/100) flex-1 nc-add-new-table">{{
$t('tooltip.addTable')
@ -737,7 +752,10 @@ const setIcon = async (icon: string, table: TableType) => {
overlay-class-name="nc-dropdown-import-menu"
@click.stop
>
<MdiDotsVertical class="transition-opacity opacity-0 group-hover:opacity-100 nc-import-menu outline-0" />
<component
:is="iconMap.threeDotVertical"
class="transition-opacity opacity-0 group-hover:opacity-100 nc-import-menu outline-0"
/>
<template #overlay>
<a-menu class="!py-0 rounded text-sm">
@ -749,7 +767,7 @@ const setIcon = async (icon: string, table: TableType) => {
@click="openAirtableImportDialog(bases[0].id)"
>
<div class="color-transition nc-project-menu-item group">
<MdiTableLarge class="group-hover:text-accent" />
<component :is="iconMap.airtable" class="group-hover:text-accent" />
Airtable
</div>
</a-menu-item>
@ -760,7 +778,7 @@ const setIcon = async (icon: string, table: TableType) => {
@click="openQuickImportDialog('csv', bases[0].id)"
>
<div class="color-transition nc-project-menu-item group">
<MdiFileDocumentOutline class="group-hover:text-accent" />
<component :is="iconMap.csv" class="group-hover:text-accent" />
CSV file
</div>
</a-menu-item>
@ -771,7 +789,7 @@ const setIcon = async (icon: string, table: TableType) => {
@click="openQuickImportDialog('json', bases[0].id)"
>
<div class="color-transition nc-project-menu-item group">
<MdiCodeJson class="group-hover:text-accent" />
<component :is="iconMap.json" class="group-hover:text-accent" />
JSON file
</div>
</a-menu-item>
@ -782,7 +800,7 @@ const setIcon = async (icon: string, table: TableType) => {
@click="openQuickImportDialog('excel', bases[0].id)"
>
<div class="color-transition nc-project-menu-item group">
<MdiFileExcel class="group-hover:text-accent" />
<component :is="iconMap.excel" class="group-hover:text-accent" />
Microsoft Excel
</div>
</a-menu-item>
@ -797,7 +815,7 @@ const setIcon = async (icon: string, table: TableType) => {
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-accent" />
<component :is="iconMap.share" class="group-hover:text-accent" />
<!-- Request a data source you need? -->
{{ $t('labels.requestDataSource') }}
</a>
@ -811,7 +829,7 @@ const setIcon = async (icon: string, table: TableType) => {
class="group flex items-center gap-2 pl-8 pr-3 py-2 text-primary/70 hover:(text-primary/100) cursor-pointer select-none"
@click="openTableCreateDialog(base.id)"
>
<MdiPlus />
<component :is="iconMap.plus" />
<span class="text-gray-500 group-hover:(text-primary/100) flex-1 nc-add-new-table">{{
$t('tooltip.addTable')
@ -823,7 +841,10 @@ const setIcon = async (icon: string, table: TableType) => {
overlay-class-name="nc-dropdown-import-menu"
@click.stop
>
<MdiDotsVertical class="transition-opacity opacity-0 group-hover:opacity-100 nc-import-menu outline-0" />
<component
:is="iconMap.threeDotVertical"
class="transition-opacity opacity-0 group-hover:opacity-100 nc-import-menu outline-0"
/>
<template #overlay>
<a-menu class="!py-0 rounded text-sm">
@ -835,7 +856,7 @@ const setIcon = async (icon: string, table: TableType) => {
@click="openAirtableImportDialog(base.id)"
>
<div class="color-transition nc-project-menu-item group">
<MdiTableLarge class="group-hover:text-accent" />
<component :is="iconMap.airtable" class="group-hover:text-accent" />
Airtable
</div>
</a-menu-item>
@ -846,7 +867,7 @@ const setIcon = async (icon: string, table: TableType) => {
@click="openQuickImportDialog('csv', base.id)"
>
<div class="color-transition nc-project-menu-item group">
<MdiFileDocumentOutline class="group-hover:text-accent" />
<component :is="iconMap.csv" class="group-hover:text-accent" />
CSV file
</div>
</a-menu-item>
@ -857,7 +878,7 @@ const setIcon = async (icon: string, table: TableType) => {
@click="openQuickImportDialog('json', base.id)"
>
<div class="color-transition nc-project-menu-item group">
<MdiCodeJson class="group-hover:text-accent" />
<component :is="iconMap.json" class="group-hover:text-accent" />
JSON file
</div>
</a-menu-item>
@ -868,7 +889,7 @@ const setIcon = async (icon: string, table: TableType) => {
@click="openQuickImportDialog('excel', base.id)"
>
<div class="color-transition nc-project-menu-item group">
<MdiFileExcel class="group-hover:text-accent" />
<component :is="iconMap.excel" class="group-hover:text-accent" />
Microsoft Excel
</div>
</a-menu-item>
@ -883,7 +904,7 @@ const setIcon = async (icon: string, table: TableType) => {
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-accent" />
<component :is="iconMap.openInNew" class="group-hover:text-accent" />
<!-- Request a data source you need? -->
{{ $t('labels.requestDataSource') }}
</a>
@ -957,7 +978,10 @@ const setIcon = async (icon: string, table: TableType) => {
:trigger="['click']"
@click.stop
>
<MdiDotsVertical class="transition-opacity opacity-0 group-hover:opacity-100 outline-0" />
<component
:is="iconMap.threeDotVertical"
class="transition-opacity opacity-0 group-hover:opacity-100 outline-0"
/>
<template #overlay>
<a-menu class="!py-0 rounded text-sm">
@ -1027,9 +1051,7 @@ const setIcon = async (icon: string, table: TableType) => {
<a-divider class="!my-0" />
<div class="flex items-start flex-col justify-start px-2 py-3 gap-2">
<LazyGeneralAddBaseButton
class="color-transition py-1.5 px-2 text-primary font-bold cursor-pointer select-none hover:text-accent"
/>
<LazyGeneralAddBaseButton class="color-transition py-1.5 px-2 cursor-pointer select-none hover:text-primary" />
<LazyGeneralHelpAndSupport class="color-transition px-2 text-gray-500 cursor-pointer select-none hover:text-accent" />

6
packages/nc-gui/components/dashboard/settings/AppStore.vue

@ -1,5 +1,5 @@
<script setup lang="ts">
import { extractSdkResponseErrorMsg, message, onMounted, useI18n, useNuxtApp } from '#imports'
import { extractSdkResponseErrorMsg, iconMap, message, onMounted, useI18n, useNuxtApp } from '#imports'
const { t } = useI18n()
@ -125,14 +125,14 @@ onMounted(async () => {
<a-button v-if="app.parsedInput" size="small" outlined @click="showResetPluginModal(app)">
<div class="flex flex-row justify-center items-center caption capitalize nc-app-store-card-reset">
<MdiCloseCircleOutline />
<component :is="iconMap.closeCircle" />
<div class="flex ml-0.5">Reset</div>
</div>
</a-button>
<a-button v-else size="small" type="primary" @click="showInstallPluginModal(app)">
<div class="flex flex-row justify-center items-center caption capitalize nc-app-store-card-install">
<MdiPlus />
<component :is="iconMap.plus" />
Install
</div>
</a-button>

4
packages/nc-gui/components/dashboard/settings/AuditTab.vue

@ -1,7 +1,7 @@
<script setup lang="ts">
import { Tooltip as ATooltip, Empty } from 'ant-design-vue'
import type { AuditType } from 'nocodb-sdk'
import { h, onMounted, storeToRefs, timeAgo, useGlobal, useI18n, useNuxtApp, useProject } from '#imports'
import { h, iconMap, onMounted, storeToRefs, timeAgo, useGlobal, useI18n, useNuxtApp, useProject } from '#imports'
const { $api } = useNuxtApp()
@ -94,7 +94,7 @@ const columns = [
<a-button class="self-start" @click="loadAudits">
<!-- Reload -->
<div class="flex items-center gap-2 text-gray-600 font-light">
<MdiReload :class="{ 'animate-infinite animate-spin !text-success': isLoading }" />
<component :is="iconMap.reload" :class="{ 'animate-infinite animate-spin !text-success': isLoading }" />
{{ $t('general.reload') }}
</div>

24
packages/nc-gui/components/dashboard/settings/DataSources.vue

@ -255,9 +255,9 @@ watch(
<div class="flex items-center gap-2 text-gray-600 font-light">
<a-tooltip v-if="metadiffbases.includes(sources[0].id)">
<template #title>Out of sync</template>
<MdiDatabaseAlert class="text-lg group-hover:text-accent text-primary" />
<GeneralIcon icon="warning" class="group-hover:text-accent text-primary" />
</a-tooltip>
<MdiDatabaseSync v-else class="text-lg group-hover:text-accent" />
<GeneralIcon v-else icon="sync" class="group-hover:text-accent" />
Sync Metadata
</div>
</a-button>
@ -266,7 +266,7 @@ watch(
@click="baseAction(sources[0].id, DataSourcesSubTab.UIAcl)"
>
<div class="flex items-center gap-2 text-gray-600 font-light">
<MdiDatabaseLockOutline class="text-lg group-hover:text-accent" />
<GeneralIcon icon="acl" class="group-hover:text-accent" />
UI ACL
</div>
</a-button>
@ -275,7 +275,7 @@ watch(
@click="baseAction(sources[0].id, DataSourcesSubTab.ERD)"
>
<div class="flex items-center gap-2 text-gray-600 font-light">
<MdiGraphOutline class="text-lg group-hover:text-accent" />
<GeneralIcon icon="erd" class="group-hover:text-accent" />
ERD
</div>
</a-button>
@ -285,7 +285,7 @@ watch(
@click="baseAction(sources[0].id, DataSourcesSubTab.Edit)"
>
<div class="flex items-center gap-2 text-gray-600 font-light">
<MdiEditOutline class="text-lg group-hover:text-accent" />
<GeneralIcon icon="edit" class="group-hover:text-accent" />
Edit
</div>
</a-button>
@ -308,7 +308,7 @@ watch(
<template #item="{ element: base, index }">
<div v-if="index !== 0" class="ds-table-row border-gray-200">
<div class="ds-table-col ds-table-name">
<MdiDragVertical v-if="sources.length > 2" small class="ds-table-handle" />
<GeneralIcon v-if="sources.length > 2" icon="dragVertical" small class="ds-table-handle" />
<div class="flex items-center gap-1">
<GeneralBaseLogo :base-type="base.type" />
{{ base.is_meta ? 'BASE' : base.alias }} <span class="text-gray-400 text-xs">({{ base.type }})</span>
@ -324,9 +324,9 @@ watch(
<div class="flex items-center gap-2 text-gray-600 font-light">
<a-tooltip v-if="metadiffbases.includes(base.id)">
<template #title>Out of sync</template>
<MdiDatabaseAlert class="text-lg group-hover:text-accent text-primary" />
<GeneralIcon icon="warning" class="group-hover:text-accent text-primary" />
</a-tooltip>
<MdiDatabaseSync v-else class="text-lg group-hover:text-accent" />
<GeneralIcon v-else icon="sync" class="group-hover:text-accent" />
Sync Metadata
</div>
</a-button>
@ -335,13 +335,13 @@ watch(
@click="baseAction(base.id, DataSourcesSubTab.UIAcl)"
>
<div class="flex items-center gap-2 text-gray-600 font-light">
<MdiDatabaseLockOutline class="text-lg group-hover:text-accent" />
<GeneralIcon icon="acl" class="group-hover:text-accent" />
UI ACL
</div>
</a-button>
<a-button class="nc-action-btn cursor-pointer outline-0" @click="baseAction(base.id, DataSourcesSubTab.ERD)">
<div class="flex items-center gap-2 text-gray-600 font-light">
<MdiGraphOutline class="text-lg group-hover:text-accent" />
<GeneralIcon icon="erd" class="group-hover:text-accent" />
ERD
</div>
</a-button>
@ -351,13 +351,13 @@ watch(
@click="baseAction(base.id, DataSourcesSubTab.Edit)"
>
<div class="flex items-center gap-2 text-gray-600 font-light">
<MdiEditOutline class="text-lg group-hover:text-accent" />
<GeneralIcon icon="edit" class="group-hover:text-accent" />
Edit
</div>
</a-button>
<a-button v-if="!base.is_meta" class="nc-action-btn cursor-pointer outline-0" @click="deleteBase(base)">
<div class="flex items-center gap-2 text-red-500 font-light">
<MdiDeleteOutline class="text-lg group-hover:text-accent" />
<GeneralIcon icon="delete" class="group-hover:text-accent" />
Delete
</div>
</a-button>

6
packages/nc-gui/components/dashboard/settings/Metadata.vue

@ -1,5 +1,5 @@
<script setup lang="ts">
import { Empty, extractSdkResponseErrorMsg, h, message, storeToRefs, useI18n, useNuxtApp, useProject } from '#imports'
import { Empty, extractSdkResponseErrorMsg, h, iconMap, message, storeToRefs, useI18n, useNuxtApp, useProject } from '#imports'
const props = defineProps<{
baseId: string
@ -92,7 +92,7 @@ const columns = [
<!-- Reload -->
<a-button v-e="['a:proj-meta:meta-data:reload']" class="self-start nc-btn-metasync-reload" @click="loadMetaDiff">
<div class="flex items-center gap-2 text-gray-600 font-light">
<MdiReload :class="{ 'animate-infinite animate-spin !text-success': isLoading }" />
<component :is="iconMap.reload" :class="{ 'animate-infinite animate-spin !text-success': isLoading }" />
{{ $t('general.reload') }}
</div>
</a-button>
@ -135,7 +135,7 @@ const columns = [
<div v-if="isDifferent">
<a-button v-e="['a:proj-meta:meta-data:sync']" class="nc-btn-metasync-sync-now" type="primary" @click="syncMetaDiff">
<div class="flex items-center gap-2">
<MdiDatabaseSync />
<component :is="iconMap.databaseSync" />
{{ $t('activity.metaSync') }}
</div>
</a-button>

20
packages/nc-gui/components/dashboard/settings/Modal.vue

@ -2,11 +2,7 @@
import type { FunctionalComponent, SVGAttributes } from 'vue'
import DataSources from './DataSources.vue'
import Misc from './Misc.vue'
import { DataSourcesSubTab, useI18n, useNuxtApp, useUIPermission, useVModel, watch } from '#imports'
import TeamFillIcon from '~icons/ri/team-fill'
import MultipleTableIcon from '~icons/mdi/table-multiple'
import NotebookOutline from '~icons/mdi/notebook-outline'
import FolderCog from '~icons/mdi/folder-cog'
import { DataSourcesSubTab, iconMap, useI18n, useNuxtApp, useUIPermission, useVModel, watch } from '#imports'
interface Props {
modelValue: boolean
@ -54,7 +50,7 @@ const dataSourcesAwakened = ref(false)
const tabsInfo: TabGroup = {
teamAndAuth: {
title: t('title.teamAndAuth'),
icon: TeamFillIcon,
icon: iconMap.users,
subTabs: {
...(isUIAllowed('userMgmtTab')
? {
@ -82,7 +78,7 @@ const tabsInfo: TabGroup = {
dataSources: {
// Data Sources
title: 'Data Sources',
icon: MultipleTableIcon,
icon: iconMap.datasource,
subTabs: {
dataSources: {
title: 'Data Sources',
@ -97,7 +93,7 @@ const tabsInfo: TabGroup = {
audit: {
// Audit
title: t('title.audit'),
icon: NotebookOutline,
icon: iconMap.book,
subTabs: {
audit: {
// Audit
@ -112,7 +108,7 @@ const tabsInfo: TabGroup = {
projectSettings: {
// Project Settings
title: 'Project Settings',
icon: FolderCog,
icon: iconMap.settings,
subTabs: {
misc: {
// Misc
@ -174,7 +170,7 @@ watch(
data-testid="settings-modal-close-button"
@click="vModel = false"
>
<MdiClose class="cursor-pointer nc-modal-close w-4" />
<component :is="iconMap.close" class="cursor-pointer nc-modal-close w-4" />
</a-button>
</div>
@ -231,7 +227,7 @@ watch(
@click="vDataState = DataSourcesSubTab.New"
>
<div v-if="vDataState === ''" class="flex items-center gap-2 text-primary font-light">
<MdiDatabasePlusOutline class="text-lg group-hover:text-accent" />
<component :is="iconMap.plusCircle" class="text-lg group-hover:text-accent" />
New
</div>
</a-button>
@ -242,7 +238,7 @@ watch(
@click="dataSourcesReload = true"
>
<div class="flex items-center gap-2 text-gray-600 font-light">
<MdiReload :class="{ 'animate-infinite animate-spin !text-success': dataSourcesReload }" />
<component :is="iconMap.reload" :class="{ 'animate-infinite animate-spin !text-success': dataSourcesReload }" />
{{ $t('general.reload') }}
</div>
</a-button>

7
packages/nc-gui/components/dashboard/settings/UIAcl.vue

@ -4,6 +4,7 @@ import {
computed,
extractSdkResponseErrorMsg,
h,
iconMap,
message,
onMounted,
storeToRefs,
@ -120,20 +121,20 @@ const columns = [
<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>
<MdiMagnify />
<component :is="iconMap.search" />
</template>
</a-input>
<a-button class="self-start nc-acl-reload" @click="loadTableList">
<div class="flex items-center gap-2 text-gray-600 font-light">
<MdiReload :class="{ 'animate-infinite animate-spin !text-success': isLoading }" />
<component :is="iconMap.reload" :class="{ 'animate-infinite animate-spin !text-success': isLoading }" />
Reload
</div>
</a-button>
<a-button class="self-start nc-acl-save" @click="saveUIAcl">
<div class="flex items-center gap-2 text-gray-600 font-light">
<MdiContentSave />
<component :is="iconMap.save" />
Save
</div>
</a-button>

10
packages/nc-gui/components/dashboard/settings/app-store/AppInstall.vue

@ -1,6 +1,6 @@
<script setup lang="ts">
import type { PluginTestReqType, PluginType } from 'nocodb-sdk'
import { extractSdkResponseErrorMsg, message, onMounted, ref, useI18n, useNuxtApp } from '#imports'
import { extractSdkResponseErrorMsg, iconMap, message, onMounted, ref, useI18n, useNuxtApp } from '#imports'
const { id } = defineProps<{
id: string
@ -209,7 +209,11 @@ onMounted(async () => {
v-if="itemIndex !== 0 && columnIndex === plugin.formDetails.items.length - 1"
class="absolute flex flex-col justify-start mt-2 -right-6 top-0"
>
<MdiDeleteOutline class="hover:text-red-400 cursor-pointer" @click="deleteFormRow(itemIndex)" />
<component
:is="iconMap.delete"
class="hover:text-red-400 cursor-pointer"
@click="deleteFormRow(itemIndex)"
/>
</div>
</a-form-item>
</td>
@ -220,7 +224,7 @@ onMounted(async () => {
<td :colspan="plugin.formDetails.items.length" class="text-center">
<a-button type="default" class="!bg-gray-100 rounded-md border-none mr-1" @click="addSetting">
<template #icon>
<MdiPlus class="flex mx-auto" />
<component :is="iconMap.plus" class="flex mx-auto" />
</template>
</a-button>
</td>

9
packages/nc-gui/components/dashboard/settings/data-sources/CreateBase.vue

@ -15,6 +15,7 @@ import {
generateUniqueName,
getDefaultConnectionConfig,
getTestDatabaseName,
iconMap,
nextTick,
onMounted,
projectTitleValidator,
@ -565,11 +566,15 @@ watch(
<a-input v-model:value="item.value" />
<MdiClose :style="{ 'font-size': '1.5em', 'color': 'red' }" @click="removeParam(index)" />
<component
:is="iconMap.close"
:style="{ 'font-size': '1.5em', 'color': 'red' }"
@click="removeParam(index)"
/>
</div>
</div>
<a-button type="dashed" class="w-full caption mt-2" @click="addNewParam">
<div class="flex items-center justify-center"><MdiPlus /></div>
<div class="flex items-center justify-center"><component :is="iconMap.plus" /></div>
</a-button>
</a-card>
</a-form-item>

11
packages/nc-gui/components/dashboard/settings/data-sources/EditBase.vue

@ -15,6 +15,7 @@ import {
fieldRequiredValidator,
getDefaultConnectionConfig,
getTestDatabaseName,
iconMap,
onMounted,
projectTitleValidator,
readFile,
@ -537,11 +538,15 @@ onMounted(async () => {
<a-input v-model:value="item.value" />
<MdiClose :style="{ 'font-size': '1.5em', 'color': 'red' }" @click="removeParam(index)" />
<component
:is="iconMap.close"
:style="{ 'font-size': '1.5em', 'color': 'red' }"
@click="removeParam(index)"
/>
</div>
</div>
<a-button type="dashed" class="w-full caption mt-2" @click="addNewParam">
<div class="flex items-center justify-center"><MdiPlus /></div>
<div class="flex items-center justify-center"><component :is="iconMap.plus" /></div>
</a-button>
</a-card>
</a-form-item>
@ -588,7 +593,7 @@ onMounted(async () => {
</div>
</a-form-item>
<div class="w-full flex items-center mt-2 text-[#e65100]">
<MdiWarning class="mr-1" />
<component :is="iconMap.warning" class="mr-1" />
Please make sure database you are trying to connect is valid! This operation can cause schema loss!!
</div>
</a-form>

5
packages/nc-gui/components/dlg/AirtableImport.vue

@ -7,6 +7,7 @@ import {
computed,
extractSdkResponseErrorMsg,
fieldRequiredValidator,
iconMap,
message,
nextTick,
onBeforeUnmount,
@ -407,7 +408,7 @@ onBeforeUnmount(() => {
<a-card ref="logRef" :body-style="{ backgroundColor: '#000000', height: '400px', overflow: 'auto' }">
<div v-for="({ msg, status }, i) in progress" :key="i">
<div v-if="status === 'FAILED'" class="flex items-center">
<MdiCloseCircleOutline class="text-red-500" />
<component :is="iconMap.closeCircle" class="text-red-500" />
<span class="text-red-500 ml-2">{{ msg }}</span>
</div>
@ -428,7 +429,7 @@ onBeforeUnmount(() => {
class="flex items-center"
>
<!-- Importing -->
<MdiLoading class="text-green-500 animate-spin" />
<component :is="iconMap.loading" class="text-green-500 animate-spin" />
<span class="text-green-500 ml-2"> {{ $t('labels.importing') }}</span>
</div>
</a-card>

9
packages/nc-gui/components/dlg/QuickImport.vue

@ -12,6 +12,7 @@ import {
computed,
extractSdkResponseErrorMsg,
fieldRequiredValidator,
iconMap,
importCsvUrlValidator,
importExcelUrlValidator,
importUrlValidator,
@ -379,7 +380,7 @@ const beforeUpload = (file: UploadFile) => {
<template #tab>
<!-- Upload -->
<div class="flex items-center gap-2">
<MdiFileUploadOutline />
<component :is="iconMap.fileUpload" />
{{ $t('general.upload') }}
</div>
</template>
@ -398,7 +399,7 @@ const beforeUpload = (file: UploadFile) => {
@change="handleChange"
@reject="rejectDrop"
>
<MdiFilePlusOutline size="large" />
<component :is="iconMap.plusCircle" size="large" />
<!-- Click or drag file to this area to upload -->
<p class="ant-upload-text">{{ $t('msg.info.import.clickOrDrag') }}</p>
@ -413,7 +414,7 @@ const beforeUpload = (file: UploadFile) => {
<a-tab-pane v-if="isImportTypeJson" key="jsonEditorTab" :closable="false">
<template #tab>
<span class="flex items-center gap-2">
<MdiCodeJson />
<component :is="iconMap.json" />
JSON Editor
</span>
</template>
@ -426,7 +427,7 @@ const beforeUpload = (file: UploadFile) => {
<a-tab-pane v-else key="urlTab" :closable="false">
<template #tab>
<span class="flex items-center gap-2">
<MdiLinkVariant />
<component :is="iconMap.link" />
URL
</span>
</template>

18
packages/nc-gui/components/dlg/TableCreate.vue

@ -1,5 +1,17 @@
<script setup lang="ts">
import { Form, computed, nextTick, onMounted, ref, useProject, useTable, useTabs, useVModel, validateTableName } from '#imports'
import {
Form,
computed,
iconMap,
nextTick,
onMounted,
ref,
useProject,
useTable,
useTabs,
useVModel,
validateTableName,
} from '#imports'
import { TabType } from '~/lib'
const props = defineProps<{
@ -143,8 +155,8 @@ onMounted(() => {
<div class="pointer flex flex-row items-center gap-x-1" @click="isAdvanceOptVisible = !isAdvanceOptVisible">
{{ isAdvanceOptVisible ? $t('general.hideAll') : $t('general.showMore') }}
<MdiMinusCircleOutline v-if="isAdvanceOptVisible" class="text-gray-500" />
<MdiPlusCircleOutline v-else class="text-gray-500" />
<component :is="iconMap.minusCircle" v-if="isAdvanceOptVisible" class="text-gray-500" />
<component :is="iconMap.plusCircle" v-else class="text-gray-500" />
</div>
</div>
<div class="nc-table-advanced-options" :class="{ active: isAdvanceOptVisible }">

5
packages/nc-gui/components/erd/HistogramPanel.vue

@ -1,17 +1,18 @@
<script lang="ts" setup>
import { Panel, PanelPosition } from '@vue-flow/additional-components'
import { iconMap } from '#imports'
</script>
<template>
<Panel class="text-xs bg-white border-1 rounded border-gray-200 z-50 nc-erd-histogram" :position="PanelPosition.BottomRight">
<div class="flex flex-col divide-y-1">
<div class="flex items-center gap-1 p-2">
<MdiTableLarge class="text-primary" />
<component :is="iconMap.table" class="text-primary" />
<div>{{ $t('objects.table') }}</div>
</div>
<div class="flex items-center gap-1 p-2">
<MdiEyeCircleOutline class="text-primary" />
<component :is="iconMap.eye" class="text-primary" />
<div>{{ $t('objects.sqlVIew') }}</div>
</div>
</div>

4
packages/nc-gui/components/general/AddBaseButton.vue

@ -1,4 +1,6 @@
<script setup lang="ts">
import { iconMap } from '#imports'
const { isUIAllowed } = useUIPermission()
const { t } = useI18n()
@ -14,7 +16,7 @@ const toggleDialog = inject(ToggleDialogInj, () => {})
>
<div>
<div class="flex items-center space-x-1">
<RiTeamFill class="mr-1 nc-new-base" />
<component :is="iconMap.users" class="mr-1 nc-new-base" />
<div>{{ t('title.teamAndSettings') }}</div>
</div>
</div>

4
packages/nc-gui/components/general/HelpAndSupport.vue

@ -1,5 +1,5 @@
<script lang="ts" setup>
import { ref, storeToRefs, useGlobal, useProject, useRoute } from '#imports'
import { iconMap, ref, storeToRefs, useGlobal, useProject, useRoute } from '#imports'
const showDrawer = ref(false)
@ -19,7 +19,7 @@ const openSwaggerLink = () => {
class="flex items-center space-x-1 w-full cursor-pointer pl-3 py-1.5 hover:(text-primary bg-primary bg-opacity-5)"
@click="showDrawer = true"
>
<MdiCommentTextOutline class="mr-1" />
<component :is="iconMap.apiAndSupport" class="mr-1" />
<!-- APIs & Support -->
<div>{{ $t('title.APIsAndSupport') }}</div>

11
packages/nc-gui/components/general/Icon.vue

@ -0,0 +1,11 @@
<script lang="ts" setup>
import { iconMap } from '#imports'
const props = defineProps<{
icon: keyof typeof iconMap
}>()
</script>
<template>
<component :is="iconMap[props.icon]" />
</template>

6
packages/nc-gui/components/general/JoinCloud.vue

@ -1,3 +1,7 @@
<script lang="ts" setup>
import { iconMap } from '#imports'
</script>
<template>
<a
v-e="['c:navbar:join-cloud']"
@ -5,7 +9,7 @@
href="https://docs.google.com/forms/d/e/1FAIpQLSfKLe8Rcrq0uo2_jM5W1kbVBbzDiQ3IvlP8Iov61FTekVAvzA/viewform?usp=pp_url"
target="_blank"
>
<PhCloudLightningDuotone class="mr-1" />
<component :is="iconMap.cloud" class="mr-1" />
Join NocoDB Cloud
</a>
</template>

14
packages/nc-gui/components/general/MiniSidebar.vue

@ -1,5 +1,5 @@
<script lang="ts" setup>
import { computed, navigateTo, storeToRefs, useGlobal, useProject, useRoute, useSidebar } from '#imports'
import { computed, iconMap, navigateTo, storeToRefs, useGlobal, useProject, useRoute, useSidebar } from '#imports'
const { signOut, signedIn, user, currentVersion } = useGlobal()
@ -42,7 +42,7 @@ const logout = async () => {
<a-menu-item-group title="User Settings">
<a-menu-item key="email" class="!rounded-t">
<nuxt-link v-e="['c:navbar:user:email']" class="group flex items-center no-underline py-2" to="/user">
<MdiAt class="mt-1 group-hover:text-success" />
<component :is="iconMap.at" class="mt-1 group-hover:text-success" />
&nbsp;
<span class="prose group-hover:text-black nc-user-menu-email">{{ email }}</span>
</nuxt-link>
@ -52,7 +52,7 @@ const logout = async () => {
<a-menu-item key="signout" class="!rounded-b">
<div v-e="['a:navbar:user:sign-out']" class="group flex items-center py-2" @click="logout">
<MdiLogout class="group-hover:(!text-red-500)" />&nbsp;
<component :is="iconMap.signout" 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>
@ -66,7 +66,7 @@ const logout = async () => {
<div id="sidebar" ref="sidebar" class="text-white flex-auto flex flex-col items-center w-full">
<a-dropdown :trigger="['contextmenu']" placement="right" overlay-class-name="nc-dropdown">
<div :class="[route.name === 'index' ? 'active' : '']" class="nc-mini-sidebar-item" @click="navigateTo('/')">
<MdiFolder class="cursor-pointer transform hover:scale-105 text-2xl" />
<component :is="iconMap.folder" class="cursor-pointer transform hover:scale-105 text-2xl" />
</div>
<template #overlay>
@ -84,7 +84,7 @@ const logout = async () => {
class="group flex items-center gap-2 py-2 hover:text-primary"
@click="navigateTo('/project/create')"
>
<MdiPlus class="text-lg group-hover:text-accent" />
<component :is="iconMap.plus" class="text-lg group-hover:text-accent" />
{{ $t('activity.createProject') }}
</div>
</a-menu-item>
@ -95,7 +95,7 @@ const logout = async () => {
class="group flex items-center gap-2 py-2 hover:text-primary"
@click="navigateTo('/project/create-external')"
>
<MdiDatabaseOutline class="text-lg group-hover:text-accent" />
<component :is="iconMap.database" class="text-lg group-hover:text-accent" />
<div v-html="$t('activity.createProjectExtended.extDB')" />
</div>
</a-menu-item>
@ -112,7 +112,7 @@ const logout = async () => {
class="nc-mini-sidebar-item"
@click="navigateTo(`/${route.params.projectType}/${route.params.projectId}`)"
>
<MdiDatabase class="cursor-pointer transform hover:scale-105 text-2xl" />
<component :is="iconMap.database" class="cursor-pointer transform hover:scale-105 text-2xl" />
</div>
</a-tooltip>
</div>

19
packages/nc-gui/components/general/PreviewAs.vue

@ -1,10 +1,9 @@
<script lang="ts" setup>
import { onUnmounted, ref, useEventListener, useGlobal, useI18n, useNuxtApp, watch } from '#imports'
import { iconMap, onUnmounted, ref, useEventListener, useGlobal, useI18n, useNuxtApp, watch } from '#imports'
import MdiAccountStar from '~icons/mdi/account-star'
import MdiAccountHardHat from '~icons/mdi/account-hard-hat'
import MdiAccountEdit from '~icons/mdi/account-edit'
import MdiEyeOutline from '~icons/mdi/eye-outline'
import MdiCommentAccountOutline from '~icons/mdi/comment-account-outline'
import PhPencilCircleThin from '~icons/ph/pencil-circle-thin'
import PhChtTeardropTextThin from '~icons/ph/chat-teardrop-text-thin'
import { ProjectRole } from '~/lib'
const { float } = defineProps<{ float?: boolean }>()
@ -24,9 +23,9 @@ const roleList = [
const roleIcon = {
owner: MdiAccountStar,
creator: MdiAccountHardHat,
editor: MdiAccountEdit,
viewer: MdiEyeOutline,
commenter: MdiCommentAccountOutline,
editor: PhPencilCircleThin,
viewer: iconMap.eye,
commenter: PhChtTeardropTextThin,
}
const position = ref({
@ -65,7 +64,7 @@ watch(previewAs, (newRole) => {
class="floating-reset-btn nc-floating-preview-btn p-4"
:style="{ top: position.y, left: position.x }"
>
<MdiDrag class="cursor-move text-white" @mousedown="mouseDown" />
<component :is="iconMap.drag" class="cursor-move text-white" @mousedown="mouseDown" />
<div class="divider" />
@ -83,7 +82,7 @@ watch(previewAs, (newRole) => {
<!-- Close -->
<div class="flex items-center gap-2 cursor-pointer nc-preview-btn-exit-to-app" @click="previewAs = null">
<MdiExitToApp />
<component :is="iconMap.exit" />
{{ $t('general.close') }}
</div>
</div>
@ -105,7 +104,7 @@ watch(previewAs, (newRole) => {
<template v-if="previewAs">
<a-menu-item @click="previewAs = null">
<div class="nc-project-menu-item group">
<MdiClose class="group-hover:text-accent" />
<component :is="iconMap.close" class="group-hover:text-accent" />
<!-- Reset Preview -->
<span class="text-capitalize text-xs whitespace-nowrap">
{{ $t('activity.resetReview') }}

4
packages/nc-gui/components/general/ShareBaseButton.vue

@ -1,5 +1,5 @@
<script setup lang="ts">
import { isDrawerOrModalExist, isMac, useNuxtApp, useRoute, useUIPermission } from '#imports'
import { iconMap, isDrawerOrModalExist, isMac, useNuxtApp, useRoute, useUIPermission } from '#imports'
const route = useRoute()
@ -45,7 +45,7 @@ useEventListener(document, 'keydown', async (e: KeyboardEvent) => {
</template>
<a-button type="primary" class="!rounded-md mr-1" size="medium">
<div class="flex items-center space-x-1 cursor-pointer text-xs font-weight-bold">
<MdiAccountPlusOutline class="mr-1 nc-share-base hover:text-accent text-sm" />
<component :is="iconMap.accountPlus" class="mr-1 nc-share-base hover:text-accent text-sm" />
{{ $t('activity.share') }}
</div>
</a-button>

26
packages/nc-gui/components/general/Social.vue

@ -1,5 +1,5 @@
<script lang="ts" setup>
import { useI18n } from '#imports'
import { iconMap, useI18n } from '#imports'
const { locale } = useI18n()
@ -19,7 +19,12 @@ const isZhLang = $computed(() => locale.value.startsWith('zh'))
/>
<div v-else class="flex justify-between gap-1 w-full px-2">
<MdiDiscord v-e="['e:community:discord']" class="icon text-[#7289DA]" @click="open('https://discord.gg/5RgZmkW')" />
<component
:is="iconMap.discord"
v-e="['e:community:discord']"
class="icon text-[#7289DA]"
@click="open('https://discord.gg/5RgZmkW')"
/>
<div
v-e="['e:community:discourse']"
@ -29,11 +34,22 @@ const isZhLang = $computed(() => locale.value.startsWith('zh'))
<div class="discourse" />
</div>
<MdiReddit v-e="['e:community:reddit']" class="icon text-[#FF4600]" @click="open('https://www.reddit.com/r/NocoDB/')" />
<component
:is="iconMap.reddit"
v-e="['e:community:reddit']"
class="icon text-[#FF4600]"
@click="open('https://www.reddit.com/r/NocoDB/')"
/>
<MdiTwitter v-e="['e:community:twitter']" class="icon text-[#1DA1F2]" @click="open('https://twitter.com/NocoDB')" />
<component
:is="iconMap.twitter"
v-e="['e:community:twitter']"
class="icon text-[#1DA1F2]"
@click="open('https://twitter.com/NocoDB')"
/>
<MdiCalendarMonth
<component
:is="iconMap.calendar"
v-e="['e:community:book-demo']"
class="icon text-green-500"
@click="open('https://calendly.com/nocodb-meeting')"

18
packages/nc-gui/components/general/SocialCard.vue

@ -1,5 +1,5 @@
<script setup lang="ts">
import { enumColor as colors, useDialog, useGlobal, useNuxtApp } from '#imports'
import { enumColor as colors, iconMap, useDialog, useGlobal, useNuxtApp } from '#imports'
const { $e } = useNuxtApp()
@ -39,7 +39,7 @@ function openKeyboardShortcutDialog() {
to="https://docs.nocodb.com/"
>
<div class="ml-3 flex items-center text-sm">
<MdiBookOpenOutline class="text-lg text-accent" />
<component :is="iconMap.book" class="text-lg text-accent" />
<span class="ml-3">{{ $t('labels.documentation') }}</span>
</div>
</nuxt-link>
@ -55,7 +55,7 @@ function openKeyboardShortcutDialog() {
to="https://apis.nocodb.com/"
>
<div class="ml-3 flex items-center text-sm">
<MdiJson class="text-lg text-green-500" />
<component :is="iconMap.json" class="text-lg text-green-500" />
<!-- todo: i18n -->
<span class="ml-3">API {{ $t('labels.documentation') }}</span>
</div>
@ -72,7 +72,7 @@ function openKeyboardShortcutDialog() {
target="_blank"
>
<div class="flex items-center text-sm">
<mdi-github class="mx-3 text-lg" />
<component :is="iconMap.github" class="mx-3 text-lg" />
<div v-if="isRtlLang">
<!-- us on Github -->
{{ $t('labels.community.starUs2') }}
@ -101,7 +101,7 @@ function openKeyboardShortcutDialog() {
target="_blank"
>
<div class="flex items-center text-sm">
<mdi-calendar-month class="mx-3 text-lg" :color="colors.dark[3 % colors.dark.length]" />
<component :is="iconMap.calendar" class="mx-3 text-lg" :color="colors.dark[3 % colors.dark.length]" />
<!-- Book a Free DEMO -->
<div>
{{ $t('labels.community.bookDemo') }}
@ -120,7 +120,7 @@ function openKeyboardShortcutDialog() {
target="_blank"
>
<div class="flex items-center text-sm">
<mdi-discord class="mx-3 text-lg" :color="colors.dark[0 % colors.dark.length]" />
<component :is="iconMap.discord" class="mx-3 text-lg" :color="colors.dark[0 % colors.dark.length]" />
<!-- Get your questions answered -->
<div>
{{ $t('labels.community.getAnswered') }}
@ -139,7 +139,7 @@ function openKeyboardShortcutDialog() {
target="_blank"
>
<div class="flex items-center text-sm">
<mdi-twitter class="mx-3 text-lg" :color="colors.dark[1 % colors.dark.length]" />
<component :is="iconMap.twitter" class="mx-3 text-lg" :color="colors.dark[1 % colors.dark.length]" />
<!-- Follow NocoDB -->
<div>
{{ $t('labels.community.followNocodb') }}
@ -176,7 +176,7 @@ function openKeyboardShortcutDialog() {
to="https://www.reddit.com/r/NocoDB/"
>
<div class="ml-3 flex items-center text-sm">
<LogosRedditIcon />
<component :is="iconMap.reddit" color="red" />
<span class="ml-4">/r/NocoDB/</span>
</div>
</nuxt-link>
@ -184,7 +184,7 @@ function openKeyboardShortcutDialog() {
<a-list-item @click="openKeyboardShortcutDialog">
<div class="ml-3 flex items-center text-sm">
<MdiKeyboard class="text-lg text-primary" />
<component :is="iconMap.keyboard" class="text-lg text-primary" />
<span class="ml-4">{{ $t('title.keyboardShortcut') }}</span>
</div>
</a-list-item>

5
packages/nc-gui/components/general/TableIcon.vue

@ -1,6 +1,7 @@
<script lang="ts" setup>
import { Icon as IcIcon } from '@iconify/vue'
import type { TableType } from 'nocodb-sdk'
import { iconMap } from '#imports'
const { meta: tableMeta } = defineProps<{
meta: TableType
@ -15,6 +16,6 @@ const { meta: tableMeta } = defineProps<{
:icon="tableMeta.meta?.icon"
/>
<MdiEyeCircleOutline v-else-if="tableMeta?.type === 'view'" class="w-5" />
<MdiTableLarge v-else class="w-5" />
<component :is="iconMap.eye" v-else-if="tableMeta?.type === 'view'" class="w-5" />
<component :is="iconMap.table" v-else class="w-5" />
</template>

6
packages/nc-gui/components/smartsheet/Form.vue

@ -10,6 +10,7 @@ import {
computed,
createEventHook,
extractSdkResponseErrorMsg,
iconMap,
inject,
message,
onClickOutside,
@ -490,7 +491,7 @@ watch(view, (nextView) => {
<a-dropdown v-model:visible="showColumnDropdown" :trigger="['click']" overlay-class-name="nc-dropdown-form-add-column">
<button type="button" class="group w-full mt-2" @click.stop="showColumnDropdown = true">
<span class="flex items-center flex-wrap justify-center gap-2 prose-sm text-gray-400">
<MdiPlus class="color-transition transform group-hover:(text-accent scale-125)" />
<component :is="iconMap.plus" class="color-transition transform group-hover:(text-accent scale-125)" />
<!-- Add new field to this table -->
<span class="color-transition group-hover:text-primary break-words">
{{ $t('activity.addField') }}
@ -596,7 +597,8 @@ watch(view, (nextView) => {
v-if="isUIAllowed('editFormView') && !isRequired(element, element.required)"
class="absolute flex top-2 right-2"
>
<MdiEyeOffOutline
<component
:is="iconMap.eyeSlash"
class="opacity-0 nc-field-remove-icon"
data-testid="nc-field-remove-icon"
@click.stop="hideColumn(index)"

3
packages/nc-gui/components/smartsheet/Gallery.vue

@ -17,6 +17,7 @@ import {
computed,
createEventHook,
extractPkFromRow,
iconMap,
inject,
isImage,
isLTAR,
@ -262,7 +263,7 @@ watch(view, async (nextView) => {
</template>
</a-carousel>
<MdiFileImageBox v-else class="w-full h-48 my-4 text-cool-gray-200" />
<component :is="iconMap.imagePlaceholder" v-else class="w-full h-48 my-4 text-cool-gray-200" />
</template>
<div v-for="col in fieldsWithoutCover" :key="`record-${record.row.id}-${col.id}`">

8
packages/nc-gui/components/smartsheet/Grid.vue

@ -23,6 +23,7 @@ import {
createEventHook,
enumColor,
extractPkFromRow,
iconMap,
inject,
isColumnRequiredAndNull,
isDrawerOrModalExist,
@ -762,7 +763,7 @@ const closeAddColumnDropdown = () => {
overlay-class-name="nc-dropdown-grid-add-column"
>
<div class="h-full w-[60px] flex items-center justify-center">
<MdiPlus class="text-sm nc-column-add" />
<component :is="iconMap.plus" class="text-sm nc-column-add" />
</div>
<template #overlay>
@ -829,7 +830,8 @@ const closeAddColumnDropdown = () => {
v-else
class="cursor-pointer flex items-center border-1 active:ring rounded p-1 hover:(bg-primary bg-opacity-10)"
>
<MdiArrowExpand
<component
:is="iconMap.expand"
v-e="['c:row-expand']"
class="select-none transform hover:(text-accent scale-120) nc-row-expand"
@click="expandForm(row, state)"
@ -900,7 +902,7 @@ const closeAddColumnDropdown = () => {
@click="addEmptyRow()"
>
<div class="px-2 w-full flex items-center text-gray-500">
<MdiPlus class="text-pint-500 text-xs ml-2 text-primary" />
<component :is="iconMap.plus" class="text-pint-500 text-xs ml-2 text-primary" />
<span class="ml-1">
{{ $t('activity.addRow') }}

20
packages/nc-gui/components/smartsheet/Kanban.vue

@ -12,6 +12,7 @@ import {
IsPublicInj,
MetaInj,
OpenNewRecordFormHookInj,
iconMap,
inject,
isImage,
isLTAR,
@ -366,7 +367,7 @@ watch(view, async (nextView) => {
>
<LazyGeneralTruncateText>{{ stack.title ?? 'uncategorized' }}</LazyGeneralTruncateText>
<span v-if="!isLocked" class="w-full flex w-[15px]">
<mdi-menu-down class="text-grey text-lg ml-auto" />
<component :is="iconMap.arrowDown" class="text-grey text-lg ml-auto" />
</span>
</div>
<template v-if="!isLocked" #overlay>
@ -382,13 +383,13 @@ watch(view, async (nextView) => {
"
>
<div class="py-2 flex gap-2 items-center">
<mdi-plus class="text-gray-500" />
<component :is="iconMap.plus" class="text-gray-500" />
{{ $t('activity.addNewRecord') }}
</div>
</a-menu-item>
<a-menu-item v-e="['c:kanban:collapse-stack']" @click="handleCollapseStack(stackIdx)">
<div class="py-2 flex gap-2 items-center">
<mdi-arrow-collapse class="text-gray-500" />
<component :is="iconMap.arrowCollapse" class="text-gray-500" />
{{ $t('activity.kanban.collapseStack') }}
</div>
</a-menu-item>
@ -398,7 +399,7 @@ watch(view, async (nextView) => {
@click="handleDeleteStackClick(stack.title, stackIdx)"
>
<div class="py-2 flex gap-2 items-center">
<mdi-delete class="text-gray-500" />
<component :is="iconMap.delete" class="text-gray-500" />
{{ $t('activity.kanban.deleteStack') }}
</div>
</a-menu-item>
@ -470,7 +471,7 @@ watch(view, async (nextView) => {
</template>
</a-carousel>
<MdiFileImageBox v-else class="w-full h-48 my-4 text-cool-gray-200" />
<component :is="iconMap.imagePlaceholder" v-else class="w-full h-48 my-4 text-cool-gray-200" />
</template>
<div
v-for="col in fieldsWithoutCover"
@ -524,7 +525,8 @@ watch(view, async (nextView) => {
<a-layout-footer>
<div v-if="formattedData.get(stack.title) && countByStack.get(stack.title) >= 0" class="mt-5 text-center">
<!-- Stack Title -->
<mdi-plus
<component
:is="iconMap.plus"
v-if="!isPublic && !isLocked"
class="text-pint-500 text-lg text-primary cursor-pointer"
@click="
@ -570,7 +572,7 @@ watch(view, async (nextView) => {
:class="{ capitalize: stack.title === null }"
>
<LazyGeneralTruncateText>{{ stack.title ?? 'uncategorized' }}</LazyGeneralTruncateText>
<mdi-menu-down class="text-grey text-lg" />
<component :is="iconMap.arrowDown" class="text-grey text-lg" />
</div>
<!-- Record Count -->
{{ formattedData.get(stack.title).length }} / {{ countByStack.get(stack.title) }}
@ -586,7 +588,7 @@ watch(view, async (nextView) => {
<a-menu class="shadow !rounded !py-0" @click="contextMenu = false">
<a-menu-item v-if="contextMenuTarget" @click="expandForm(contextMenuTarget)">
<div v-e="['a:kanban:expand-record']" class="nc-project-menu-item nc-kanban-context-menu-item">
<MdiArrowExpand class="flex" />
<component :is="iconMap.expand" class="flex" />
<!-- Expand Record -->
{{ $t('activity.expandRecord') }}
</div>
@ -594,7 +596,7 @@ watch(view, async (nextView) => {
<a-divider class="!m-0 !p-0" />
<a-menu-item v-if="contextMenuTarget" @click="deleteRow(contextMenuTarget)">
<div v-e="['a:kanban:delete-record']" class="nc-project-menu-item nc-kanban-context-menu-item">
<MdiDeleteOutline class="flex" />
<component :is="iconMap.delete" class="flex" />
<!-- Delete Record -->
{{ $t('activity.deleteRecord') }}
</div>

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

@ -3,7 +3,7 @@ import 'leaflet/dist/leaflet.css'
import L, { LatLng } from 'leaflet'
import 'leaflet.markercluster'
import { ViewTypes } from 'nocodb-sdk'
import { IsPublicInj, OpenNewRecordFormHookInj, latLongToJoinedString, onMounted, provide, ref } from '#imports'
import { IsPublicInj, OpenNewRecordFormHookInj, iconMap, latLongToJoinedString, onMounted, provide, ref } from '#imports'
import type { Row } from '~/lib'
const route = useRoute()
@ -228,7 +228,7 @@ const count = computed(() => paginationData.value.totalRows)
<div v-if="count > 900" class="nc-warning-info flex min-w-32px h-32px items-center gap-1 px-2 bg-white">
<div>{{ count }} {{ $t('objects.records') }}</div>
<mdi-map-marker-alert />
<component :is="iconMap.markerAlert" />
</div>
</a-tooltip>
</div>

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

@ -1,5 +1,5 @@
<script setup lang="ts">
import { ChangePageInj, PaginationDataInj, computed, inject } from '#imports'
import { ChangePageInj, PaginationDataInj, computed, iconMap, inject } from '#imports'
const paginatedData = inject(PaginationDataInj)!
@ -39,7 +39,7 @@ const page = computed({
<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>
<MdiKeyboardReturn class="mt-1" @click="changePage(page)" />
<component :is="iconMap.returnKey" class="mt-1" @click="changePage(page)" />
</template>
</a-input>
</div>

4
packages/nc-gui/components/smartsheet/column/BarcodeOptions.vue

@ -2,7 +2,7 @@
import type { UITypes } from 'nocodb-sdk'
import { AllowedColumnTypesForQrAndBarcodes } from 'nocodb-sdk'
import type { SelectProps } from 'ant-design-vue'
import { onMounted, useVModel, watch } from '#imports'
import { iconMap, onMounted, useVModel, watch } from '#imports'
const props = defineProps<{
modelValue: any
@ -98,7 +98,7 @@ const showBarcodeValueColumnInfoIcon = computed(() => !columnsAllowedAsBarcodeVa
Decimal. Please create one first.
</span>
</template>
<mdi-information class="cursor-pointer" />
<component :is="iconMap.info" class="cursor-pointer" />
</a-tooltip>
</div>
</div>

5
packages/nc-gui/components/smartsheet/column/FormulaOptions.vue

@ -12,6 +12,7 @@ import {
formulas,
getUIDTIcon,
getWordUntilCaret,
iconMap,
insertAtCursor,
onMounted,
useColumnCreateStoreOrThrow,
@ -742,9 +743,9 @@ onMounted(() => {
</template>
<template #avatar>
<mdi-function v-if="item.type === 'function'" class="text-lg" />
<component :is="iconMap.function" v-if="item.type === 'function'" class="text-lg" />
<mdi-calculator v-if="item.type === 'op'" class="text-lg" />
<component :is="iconMap.calculator" v-if="item.type === 'op'" class="text-lg" />
<component :is="item.icon" v-if="item.type === 'column'" class="text-lg" />
</template>

10
packages/nc-gui/components/smartsheet/column/SelectOptions.vue

@ -1,7 +1,7 @@
<script setup lang="ts">
import Draggable from 'vuedraggable'
import { UITypes } from 'nocodb-sdk'
import { IsKanbanInj, enumColor, onMounted, useColumnCreateStoreOrThrow, useVModel, watch } from '#imports'
import { IsKanbanInj, enumColor, iconMap, onMounted, useColumnCreateStoreOrThrow, useVModel, watch } from '#imports'
interface Option {
color: string
@ -172,7 +172,8 @@ watch(inputs, () => {
:data-testid="`select-column-option-${index}`"
:class="{ removed: element.status === 'remove' }"
>
<MdiDragVertical
<component
:is="iconMap.dragVertical"
v-if="!isKanban"
small
class="nc-child-draggable-icon handle"
@ -208,7 +209,8 @@ watch(inputs, () => {
/>
</div>
<MdiClose
<component
:is="iconMap.close"
v-if="element.status !== 'remove'"
class="ml-2 hover:!text-black-500 text-gray-500 cursor-pointer"
:data-testid="`select-column-option-remove-${index}`"
@ -231,7 +233,7 @@ watch(inputs, () => {
</div>
<a-button type="dashed" class="w-full caption mt-2" @click="addNewOption()">
<div class="flex items-center">
<MdiPlus />
<component :is="iconMap.plus" />
<span class="flex-auto">Add option</span>
</div>
</a-button>

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

@ -1,7 +1,7 @@
<script setup lang="ts">
import type { VNodeRef } from '@vue/runtime-core'
import type { AuditType } from 'nocodb-sdk'
import { enumColor, ref, timeAgo, useCopy, useExpandedFormStoreOrThrow, useGlobal, useI18n, watch } from '#imports'
import { enumColor, iconMap, ref, timeAgo, useCopy, useExpandedFormStoreOrThrow, useGlobal, useI18n, watch } from '#imports'
const { loadCommentsAndLogs, commentsAndLogs, isCommentsLoading, commentsOnly, saveComment, isYou, comment, updateComment } =
useExpandedFormStoreOrThrow()
@ -133,7 +133,11 @@ watch(
<div v-for="(log, idx) of commentsAndLogs" :key="log.id">
<a-dropdown :trigger="['contextmenu']" :overlay-class-name="`nc-dropdown-comment-context-menu-${idx}`">
<div class="flex gap-1 text-xs">
<MdiAccountCircle class="row-span-2" :class="isYou(log.user) ? 'text-pink-300' : 'text-blue-300 '" />
<component
:is="iconMap.accountCircle"
class="row-span-2"
:class="isYou(log.user) ? 'text-pink-300' : 'text-blue-300 '"
/>
<div class="flex-1">
<p class="mb-1 caption edited-text text-[10px] text-gray-500">
@ -208,12 +212,12 @@ watch(
>
<template #addonBefore>
<div class="flex items-center">
<mdi-account-circle class="text-lg text-pink-300" small @click="saveComment" />
<component :is="iconMap.accountCircle" class="text-lg text-pink-300" small @click="saveComment" />
</div>
</template>
<template #suffix>
<mdi-keyboard-return v-if="comment" class="text-sm" small @click="saveComment" />
<component :is="iconMap.returnKey" v-if="comment" class="text-sm" small @click="saveComment" />
</template>
</a-input>
</div>

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

@ -3,6 +3,7 @@ import { message } from 'ant-design-vue'
import type { ViewType } from 'nocodb-sdk'
import {
ReloadRowDataHookInj,
iconMap,
isMac,
useExpandedFormStoreOrThrow,
useSmartsheetRowStoreOrThrow,
@ -106,7 +107,8 @@ const onConfirmDeleteRowClick = async () => {
<!-- todo: i18n -->
<div class="text-center w-full">Copy record URL</div>
</template>
<mdi-link
<component
:is="iconMap.link"
v-if="!isNew"
class="nc-icon-transition cursor-pointer select-none text-gray-500 mx-1 nc-copy-row-url min-w-4"
@click="copyRecordUrl"
@ -118,7 +120,8 @@ const onConfirmDeleteRowClick = async () => {
<template #title>
<div class="text-center w-full">{{ $t('activity.toggleCommentsDraw') }}</div>
</template>
<MdiCommentTextOutline
<component
:is="iconMap.comment"
v-if="isUIAllowed('rowComments') && !isNew"
v-e="['c:row-expand:comment-toggle']"
class="nc-icon-transition cursor-pointer select-none nc-toggle-comments text-gray-500 mx-1 min-w-4"
@ -127,59 +130,66 @@ const onConfirmDeleteRowClick = async () => {
</a-tooltip>
<a-dropdown-button class="nc-expand-form-save-btn" type="primary" :disabled="!isUIAllowed('tableRowUpdate')" @click="save">
<template #icon><MdiMenuDown /></template>
<template #icon><component :is="iconMap.arrowDown" /></template>
<template #overlay>
<a-menu class="nc-expand-form-save-dropdown-menu">
<a-menu-item key="0" class="!py-2 flex gap-2" @click="saveRowAndStay = 0">
<div class="flex items-center">
<MdiContentSave class="mr-1" />
<component :is="iconMap.contentSaveExit" class="mr-1" />
{{ $t('activity.saveAndExit') }}
</div>
</a-menu-item>
<a-menu-item key="1" class="!py-2 flex gap-2 items-center" @click="saveRowAndStay = 1">
<div class="flex items-center">
<MdiContentSaveEdit class="mr-1" />
<component :is="iconMap.contentSaveStay" class="mr-1" />
{{ $t('activity.saveAndStay') }}
</div>
</a-menu-item>
</a-menu>
</template>
<div v-if="saveRowAndStay === 0" class="flex items-center">
<MdiContentSave class="mr-1" />
<component :is="iconMap.contentSaveExit" class="mr-1" />
{{ $t('activity.saveAndExit') }}
</div>
<div v-if="saveRowAndStay === 1" class="flex items-center">
<MdiContentSaveEdit class="mr-1" />
<component :is="iconMap.contentSaveStay" class="mr-1" />
{{ $t('activity.saveAndStay') }}
</div>
</a-dropdown-button>
<a-dropdown>
<MdiDotsVertical class="nc-icon-transition" />
<component :is="iconMap.threeDotVertical" class="nc-icon-transition" />
<template #overlay>
<a-menu>
<a-menu-item v-if="!isNew" @click="loadRow">
<div v-e="['c:row-expand:reload']" class="py-2 flex gap-2 items-center">
<mdi-reload class="nc-icon-transition cursor-pointer select-none text-gray-500 mx-1 min-w-4" />
<component :is="iconMap.reload" class="nc-icon-transition cursor-pointer select-none text-gray-500 mx-1 min-w-4" />
{{ $t('general.reload') }}
</div>
</a-menu-item>
<a-menu-item v-if="isUIAllowed('xcDatatableEditable') && !isNew" @click="!isNew && emit('duplicateRow')">
<div v-e="['c:row-expand:duplicate']" class="py-2 flex gap-2 a">
<MdiContentCopy class="nc-icon-transition cursor-pointer select-none nc-duplicate-row text-gray-500 mx-1 min-w-4" />
<component
:is="iconMap.copy"
class="nc-icon-transition cursor-pointer select-none nc-duplicate-row text-gray-500 mx-1 min-w-4"
/>
{{ $t('activity.duplicateRow') }}
</div>
</a-menu-item>
<a-menu-item v-if="isUIAllowed('xcDatatableEditable') && !isNew" @click="!isNew && onDeleteRowClick()">
<div v-e="['c:row-expand:delete']" class="py-2 flex gap-2 items-center">
<MdiDeleteOutline class="nc-icon-transition cursor-pointer select-none nc-delete-row text-gray-500 mx-1 min-w-4" />
<component
:is="iconMap.delete"
class="nc-icon-transition cursor-pointer select-none nc-delete-row text-gray-500 mx-1 min-w-4"
/>
{{ $t('activity.deleteRow') }}
</div>
</a-menu-item>
<a-menu-item @click="emit('cancel')">
<div v-e="['c:row-expand:delete']" class="py-2 flex gap-2 items-center">
<MdiCloseCircleOutline
<component
:is="iconMap.closeCircle"
class="nc-icon-transition cursor-pointer select-none nc-delete-row text-gray-500 mx-1 min-w-4"
/>
{{ $t('general.close') }}

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

@ -11,6 +11,7 @@ import {
ReloadRowDataHookInj,
computedInject,
createEventHook,
iconMap,
inject,
message,
provide,
@ -301,7 +302,7 @@ export default {
<GeneralShortcutLabel class="justify-center" :keys="['Alt', '←']" />
</template>
<MdiChevronLeft class="cursor-pointer nc-prev-arrow" @click="$emit('prev')" />
<component :is="iconMap.chevronLeft" class="cursor-pointer nc-prev-arrow" @click="$emit('prev')" />
</a-tooltip>
<a-tooltip v-if="!props.lastRow" placement="bottom">
@ -309,7 +310,7 @@ export default {
{{ $t('labels.nextRow') }}
<GeneralShortcutLabel class="justify-center" :keys="['Alt', '→']" />
</template>
<MdiChevronRight class="cursor-pointer nc-next-arrow" @click="onNext" />
<component :is="iconMap.chevronRight" class="cursor-pointer nc-next-arrow" @click="onNext" />
</a-tooltip>
</template>
<div class="w-[500px] mx-auto">

74
packages/nc-gui/components/smartsheet/header/CellIcon.ts

@ -5,6 +5,7 @@ import {
computed,
defineComponent,
h,
iconMap,
inject,
isAttachment,
isBoolean,
@ -34,79 +35,56 @@ import {
toRef,
useProject,
} from '#imports'
import FilePhoneIcon from '~icons/mdi/file-phone'
import KeyIcon from '~icons/mdi/key-variant'
import JSONIcon from '~icons/mdi/code-json'
import ClockIcon from '~icons/mdi/clock-time-five'
import WebIcon from '~icons/mdi/web'
import TextAreaIcon from '~icons/mdi/card-text-outline'
import StringIcon from '~icons/mdi/alpha-a-box-outline'
import BooleanIcon from '~icons/mdi/check-box-outline'
import CalendarIcon from '~icons/mdi/calendar'
import SingleSelectIcon from '~icons/mdi/arrow-down-drop-circle'
import MultiSelectIcon from '~icons/mdi/format-list-bulleted-square'
import DatetimeIcon from '~icons/mdi/calendar-clock'
import GeoDataIcon from '~icons/mdi/map-marker'
import RatingIcon from '~icons/mdi/star'
import GenericIcon from '~icons/mdi/square-rounded'
import NumericIcon from '~icons/mdi/numeric'
import AttachmentIcon from '~icons/mdi/image-multiple-outline'
import EmailIcon from '~icons/mdi/email'
import CurrencyIcon from '~icons/mdi/currency-usd-circle-outline'
import PercentIcon from '~icons/mdi/percent-outline'
import DecimalIcon from '~icons/mdi/decimal'
import SpecificDBTypeIcon from '~icons/mdi/database-settings'
import DurationIcon from '~icons/mdi/timer-outline'
const renderIcon = (column: ColumnType, abstractType: any) => {
if (isPrimaryKey(column)) {
return KeyIcon
return iconMap.key
} else if (isSpecificDBType(column)) {
return SpecificDBTypeIcon
return iconMap.specificDbType
} else if (isJSON(column)) {
return JSONIcon
return iconMap.json
} else if (isDate(column, abstractType)) {
return CalendarIcon
return iconMap.calendar
} else if (isDateTime(column, abstractType)) {
return DatetimeIcon
return iconMap.datetime
} else if (isGeoData(column)) {
return GeoDataIcon
return iconMap.geoData
} else if (isSet(column)) {
return MultiSelectIcon
return iconMap.multiSelect
} else if (isSingleSelect(column)) {
return SingleSelectIcon
return iconMap.singleSelect
} else if (isBoolean(column, abstractType)) {
return BooleanIcon
return iconMap.boolean
} else if (isTextArea(column)) {
return TextAreaIcon
return iconMap.longText
} else if (isEmail(column)) {
return EmailIcon
return iconMap.email
} else if (isYear(column, abstractType)) {
return CalendarIcon
return iconMap.calendar
} else if (isTime(column, abstractType)) {
return ClockIcon
return iconMap.calendar
} else if (isRating(column)) {
return RatingIcon
return iconMap.rating
} else if (isAttachment(column)) {
return AttachmentIcon
return iconMap.image
} else if (isDecimal(column)) {
return DecimalIcon
return iconMap.decimal
} else if (isPhoneNumber(column)) {
return FilePhoneIcon
return iconMap.phone
} else if (isURL(column)) {
return WebIcon
return iconMap.web
} else if (isCurrency(column)) {
return CurrencyIcon
return iconMap.currency
} else if (isDuration(column)) {
return DurationIcon
return iconMap.duration
} else if (isPercent(column)) {
return PercentIcon
return iconMap.percent
} else if (isInt(column, abstractType) || isFloat(column, abstractType)) {
return NumericIcon
return iconMap.number
} else if (isString(column, abstractType)) {
return StringIcon
return iconMap.text
} else {
return GenericIcon
return iconMap.generic
}
}
@ -133,7 +111,7 @@ export default defineComponent({
return () => {
if (!column.value) return null
return h(renderIcon(column.value, abstractType.value), { class: 'text-grey mx-1 !text-xs' })
return h(renderIcon(column.value, abstractType.value), { class: 'text-gray-500 mx-1' })
}
},
})

22
packages/nc-gui/components/smartsheet/header/Menu.vue

@ -11,6 +11,7 @@ import {
SmartsheetStoreEvents,
extractSdkResponseErrorMsg,
getUniqueColumnName,
iconMap,
inject,
message,
useI18n,
@ -213,13 +214,12 @@ const hideField = async () => {
<template>
<a-dropdown v-if="!isLocked" placement="bottomRight" :trigger="['click']" overlay-class-name="nc-dropdown-column-operations">
<MdiMenuDown class="h-full text-grey nc-ui-dt-dropdown cursor-pointer outline-0" />
<div><GeneralIcon icon="arrowDown" class="text-grey h-full text-grey nc-ui-dt-dropdown cursor-pointer outline-0 mr-2" /></div>
<template #overlay>
<a-menu class="shadow bg-white">
<a-menu-item @click="emit('edit')">
<div class="nc-column-edit nc-header-menu-item">
<MdiPencil class="text-primary" />
<component :is="iconMap.edit" class="text-primary" />
<!-- Edit -->
{{ $t('general.edit') }}
</div>
@ -228,14 +228,14 @@ const hideField = async () => {
<a-divider class="!my-0" />
<a-menu-item @click="sortByColumn('asc')">
<div v-e="['a:field:sort', { dir: 'asc' }]" class="nc-column-insert-after nc-header-menu-item">
<MdiSortAscending class="text-primary" />
<component :is="iconMap.sortAsc" class="text-primary" />
<!-- Sort Ascending -->
{{ $t('general.sortAsc') }}
</div>
</a-menu-item>
<a-menu-item @click="sortByColumn('desc')">
<div v-e="['a:field:sort', { dir: 'desc' }]" class="nc-column-insert-before nc-header-menu-item">
<MdiSortDescending class="text-primary" />
<component :is="iconMap.sortDesc" class="text-primary" />
<!-- Sort Descending -->
{{ $t('general.sortDesc') }}
</div>
@ -244,7 +244,7 @@ const hideField = async () => {
<a-divider class="!my-0" />
<a-menu-item v-if="!column?.pv" @click="hideField">
<div v-e="['a:field:hide']" class="nc-column-insert-before nc-header-menu-item">
<MdiEyeOffOutline class="text-primary" />
<component :is="iconMap.eye" class="text-primary" />
<!-- Hide Field -->
{{ $t('general.hideField') }}
</div>
@ -257,21 +257,21 @@ const hideField = async () => {
@click="duplicateColumn"
>
<div v-e="['a:field:duplicate']" class="nc-column-duplicate nc-header-menu-item">
<MdiFileReplaceOutline class="text-primary" />
<component :is="iconMap.duplicate" class="text-primary" />
<!-- Duplicate -->
{{ t('general.duplicate') }}
</div>
</a-menu-item>
<a-menu-item @click="addColumn()">
<div v-e="['a:field:insert:after']" class="nc-column-insert-after nc-header-menu-item">
<MdiTableColumnPlusAfter class="text-primary" />
<component :is="iconMap.colInsertAfter" class="text-primary" />
<!-- Insert After -->
{{ t('general.insertAfter') }}
</div>
</a-menu-item>
<a-menu-item v-if="!column?.pv" @click="addColumn(true)">
<div v-e="['a:field:insert:before']" class="nc-column-insert-before nc-header-menu-item">
<MdiTableColumnPlusBefore class="text-primary" />
<component :is="iconMap.colInsertBefore" class="text-primary" />
<!-- Insert Before -->
{{ t('general.insertBefore') }}
</div>
@ -280,7 +280,7 @@ const hideField = async () => {
<a-menu-item v-if="(!virtual || column?.uidt === UITypes.Formula) && !column?.pv" @click="setAsDisplayValue">
<div class="nc-column-set-primary nc-header-menu-item">
<MdiStar class="text-primary" />
<GeneralIcon icon="star" class="text-primary" />
<!-- todo : tooltip -->
<!-- Set as Display value -->
@ -290,7 +290,7 @@ const hideField = async () => {
<a-menu-item v-if="!column?.pv" @click="deleteColumn">
<div class="nc-column-delete nc-header-menu-item">
<MdiDeleteOutline class="text-error" />
<component :is="iconMap.delete" class="text-error" />
<!-- Delete -->
{{ $t('general.delete') }}
</div>

60
packages/nc-gui/components/smartsheet/header/VirtualCellIcon.ts

@ -2,64 +2,68 @@ import type { PropType } from '@vue/runtime-core'
import type { ColumnType, LinkToAnotherRecordType, LookupType, RollupType } from 'nocodb-sdk'
import type { Ref } from 'vue'
import { RelationTypes, UITypes } from 'nocodb-sdk'
import { ColumnInj, MetaInj, defineComponent, h, inject, isBt, isHm, isLookup, isMm, isRollup, ref, toRef } from '#imports'
import GenericIcon from '~icons/mdi/square-rounded'
import HMIcon from '~icons/mdi/table-arrow-right'
import BTIcon from '~icons/mdi/table-arrow-left'
import MMIcon from '~icons/mdi/table-network'
import FormulaIcon from '~icons/mdi/math-integral'
import QrCodeScan from '~icons/mdi/qrcode-scan'
import BarcodeScan from '~icons/mdi/barcode-scan'
import RollupIcon from '~icons/mdi/movie-roll'
import {
ColumnInj,
MetaInj,
defineComponent,
h,
iconMap,
inject,
isBt,
isHm,
isLookup,
isMm,
isRollup,
ref,
toRef,
} from '#imports'
import CountIcon from '~icons/mdi/counter'
import SpecificDBTypeIcon from '~icons/mdi/database-settings'
import MdiTextSearchVariant from '~icons/mdi/text-search-variant'
const renderIcon = (column: ColumnType, relationColumn?: ColumnType) => {
switch (column.uidt) {
case UITypes.LinkToAnotherRecord:
switch ((column.colOptions as LinkToAnotherRecordType)?.type) {
case RelationTypes.MANY_TO_MANY:
return { icon: MMIcon, color: 'text-accent' }
return { icon: iconMap.mm, color: 'text-accent' }
case RelationTypes.HAS_MANY:
return { icon: HMIcon, color: 'text-yellow-500' }
return { icon: iconMap.hm, color: 'text-yellow-500' }
case RelationTypes.BELONGS_TO:
return { icon: BTIcon, color: 'text-sky-500' }
return { icon: iconMap.bt, color: 'text-sky-500' }
}
break
case UITypes.SpecificDBType:
return { icon: SpecificDBTypeIcon, color: 'text-grey' }
return { icon: iconMap.specificDbType, color: 'text-grey' }
case UITypes.Formula:
return { icon: FormulaIcon, color: 'text-grey' }
return { icon: iconMap.formula, color: 'text-grey' }
case UITypes.QrCode:
return { icon: QrCodeScan, color: 'text-grey' }
return { icon: iconMap.qrCode, color: 'text-grey' }
case UITypes.Barcode:
return { icon: BarcodeScan, color: 'text-grey' }
return { icon: iconMap.qrCode, color: 'text-grey' }
case UITypes.Lookup:
switch ((relationColumn?.colOptions as LinkToAnotherRecordType)?.type) {
case RelationTypes.MANY_TO_MANY:
return { icon: MdiTextSearchVariant, color: 'text-accent' }
return { icon: iconMap.lookup, color: 'text-accent' }
case RelationTypes.HAS_MANY:
return { icon: MdiTextSearchVariant, color: 'text-yellow-500' }
return { icon: iconMap.lookup, color: 'text-yellow-500' }
case RelationTypes.BELONGS_TO:
return { icon: MdiTextSearchVariant, color: 'text-sky-500' }
return { icon: iconMap.lookup, color: 'text-sky-500' }
}
return { icon: MdiTextSearchVariant, color: 'text-grey' }
return { icon: iconMap.lookup, color: 'text-grey' }
case UITypes.Rollup:
switch ((relationColumn?.colOptions as LinkToAnotherRecordType)?.type) {
case RelationTypes.MANY_TO_MANY:
return { icon: RollupIcon, color: 'text-accent' }
return { icon: iconMap, color: 'text-accent' }
case RelationTypes.HAS_MANY:
return { icon: RollupIcon, color: 'text-yellow-500' }
return { icon: iconMap, color: 'text-yellow-500' }
case RelationTypes.BELONGS_TO:
return { icon: RollupIcon, color: 'text-sky-500' }
return { icon: iconMap, color: 'text-sky-500' }
}
return { icon: RollupIcon, color: 'text-grey' }
return { icon: iconMap, color: 'text-grey' }
case UITypes.Count:
return { icon: CountIcon, color: 'text-grey' }
}
return { icon: GenericIcon, color: 'text-grey' }
return { icon: iconMap.generic, color: 'text-grey' }
}
export default defineComponent({
@ -91,7 +95,7 @@ export default defineComponent({
const { icon: Icon, color } = renderIcon(column.value, relationColumn)
return h(Icon, { class: `${color} mx-1 !text-xs` })
return h(Icon, { class: `${color} mx-1` })
}
},
})

12
packages/nc-gui/components/smartsheet/sidebar/MenuBottom.vue

@ -1,6 +1,6 @@
<script lang="ts" setup>
import { ViewTypes } from 'nocodb-sdk'
import { useNuxtApp, useSmartsheetStoreOrThrow, viewIcons } from '#imports'
import { iconMap, useNuxtApp, useSmartsheetStoreOrThrow, viewIcons } from '#imports'
const emits = defineEmits<Emits>()
@ -43,7 +43,7 @@ function onOpenModal(type: ViewTypes, title = '') {
<div class="flex-1" />
<mdi-plus class="group-hover:text-primary" />
<component :is="iconMap.plus" class="group-hover:text-primary" />
</div>
</a-tooltip>
</a-menu-item>
@ -65,7 +65,7 @@ function onOpenModal(type: ViewTypes, title = '') {
<div class="flex-1" />
<mdi-plus class="group-hover:text-primary" />
<component :is="iconMap.plus" class="group-hover:text-primary" />
</div>
</a-tooltip>
</a-menu-item>
@ -88,7 +88,7 @@ function onOpenModal(type: ViewTypes, title = '') {
<div class="flex-1" />
<mdi-plus class="group-hover:text-primary" />
<component :is="iconMap.plus" class="group-hover:text-primary" />
</div>
</a-tooltip>
</a-menu-item>
@ -110,7 +110,7 @@ function onOpenModal(type: ViewTypes, title = '') {
<div class="flex-1" />
<mdi-plus class="group-hover:text-primary" />
<component :is="iconMap.plus" class="group-hover:text-primary" />
</div>
</a-tooltip>
</a-menu-item>
@ -132,7 +132,7 @@ function onOpenModal(type: ViewTypes, title = '') {
<div class="flex-1" />
<mdi-plus class="group-hover:text-primary" />
<component :is="iconMap.plus" class="group-hover:text-primary" />
</div>
</a-tooltip>
</a-menu-item>

24
packages/nc-gui/components/smartsheet/sidebar/RenameableMenuItem.vue

@ -3,7 +3,17 @@ import type { VNodeRef } from '@vue/runtime-core'
import type { KanbanType, ViewType, ViewTypes } from 'nocodb-sdk'
import type { WritableComputedRef } from '@vue/reactivity'
import { Tooltip } from 'ant-design-vue'
import { IsLockedInj, inject, message, onKeyStroke, useDebounceFn, useNuxtApp, useUIPermission, useVModel } from '#imports'
import {
IsLockedInj,
iconMap,
inject,
message,
onKeyStroke,
useDebounceFn,
useNuxtApp,
useUIPermission,
useVModel,
} from '#imports'
interface Props {
view: ViewType
@ -199,7 +209,11 @@ function onStopEdit() {
{{ $t('activity.copyView') }}
</template>
<MdiContentCopy class="!hidden !group-hover:block text-gray-500 nc-view-copy-icon" @click.stop="onDuplicate" />
<component
:is="iconMap.copy"
class="!hidden !group-hover:block text-gray-500 nc-view-copy-icon"
@click.stop="onDuplicate"
/>
</a-tooltip>
<template v-if="!vModel.is_default">
@ -208,7 +222,11 @@ function onStopEdit() {
{{ $t('activity.deleteView') }}
</template>
<MdiTrashCan class="!hidden !group-hover:block text-red-500 nc-view-delete-icon" @click.stop="onDelete" />
<component
:is="iconMap.delete"
class="!hidden !group-hover:block text-red-500 nc-view-delete-icon"
@click.stop="onDelete"
/>
</a-tooltip>
</template>
</div>

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

@ -1,5 +1,5 @@
<script setup lang="ts">
import { message, useApi, useI18n } from '#imports'
import { iconMap, message, useApi, useI18n } from '#imports'
const { t } = useI18n()
@ -22,6 +22,6 @@ async function deleteCache() {
<span> Delete Cache </span>
</template>
<mdi-delete class="cursor-pointer" @click="deleteCache" />
<component :is="iconMap.delete" class="cursor-pointer" @click="deleteCache" />
</a-tooltip>
</template>

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

@ -1,5 +1,5 @@
<script setup lang="ts">
import { MetaInj, inject, useSidebar, useTable } from '#imports'
import { MetaInj, iconMap, inject, useSidebar, useTable } from '#imports'
const meta = inject(MetaInj)!
@ -13,7 +13,7 @@ const { isOpen } = useSidebar('nc-right-sidebar')
<template #title> {{ $t('activity.deleteTable') }} </template>
<div class="nc-sidebar-right-item hover:after:bg-red-500 group">
<MdiDeleteOutline class="cursor-pointer" @click="deleteTable(meta)" />
<component :is="iconMap.delete" class="cursor-pointer" @click="deleteTable(meta)" />
</div>
</a-tooltip>
</template>

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

@ -1,5 +1,5 @@
<script setup lang="ts">
import { message, useApi, useI18n } from '#imports'
import { iconMap, message, useApi, useI18n } from '#imports'
const { t } = useI18n()
@ -36,6 +36,6 @@ async function exportCache() {
<span> Export Cache </span>
</template>
<mdi-export class="cursor-pointer" @click="exportCache" />
<component :is="iconMap.export" class="cursor-pointer" @click="exportCache" />
</a-tooltip>
</template>

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

@ -1,5 +1,5 @@
<script setup lang="ts">
import { OpenNewRecordFormHookInj, inject } from '#imports'
import { IsLockedInj, OpenNewRecordFormHookInj, iconMap, inject } from '#imports'
const isLocked = inject(IsLockedInj)
@ -13,12 +13,14 @@ const onClick = () => {
<template>
<a-tooltip placement="bottom">
<template #title> {{ $t('activity.addRow') }} </template>
<IonImageOutline />
<div
v-e="['c:row:add:grid-top']"
:class="{ 'group': !isLocked, 'disabled-ring': isLocked }"
class="nc-add-new-row-btn nc-toolbar-btn flex min-w-32px w-32px h-32px items-center"
class="nc-add-new-row-btn nc-toolbar-btn flex min-w-32px w-32px h-32px items-center justify-center !px-0 select-none"
>
<MdiPlusOutline
<component
:is="iconMap.plus"
:class="{ 'cursor-pointer text-gray-500 group-hover:(text-primary)': !isLocked, 'disabled': isLocked }"
@click="onClick"
/>

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

@ -8,6 +8,7 @@ import {
comparisonOpList,
comparisonSubOpList,
computed,
iconMap,
inject,
ref,
useNuxtApp,
@ -213,7 +214,8 @@ defineExpose({
<template v-for="(filter, i) in filters" :key="i">
<template v-if="filter.status !== 'delete'">
<template v-if="filter.is_group">
<MdiCloseBox
<component
:is="iconMap.closeBox"
v-if="!filter.readOnly"
:key="i"
small
@ -251,7 +253,8 @@ defineExpose({
</div>
</template>
<template v-else>
<MdiCloseBox
<component
:is="iconMap.closeBox"
v-if="!filter.readOnly"
class="nc-filter-item-remove-btn text-grey self-center"
@click.stop="deleteFilter(filter, i)"
@ -363,7 +366,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 items-center gap-1">
<MdiPlus />
<component :is="iconMap.plus" />
<!-- Add Filter -->
{{ $t('activity.addFilter') }}
</div>
@ -372,7 +375,7 @@ defineExpose({
<a-button v-if="!webHook" class="text-capitalize !text-gray-500" @click.stop="addFilterGroup">
<div class="flex items-center gap-1">
<!-- Add Filter Group -->
<MdiPlus />
<component :is="iconMap.plus" />
{{ $t('activity.addFilterGroup') }}
</div>
</a-button>

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

@ -5,6 +5,7 @@ import {
IsLockedInj,
IsPublicInj,
computed,
iconMap,
inject,
ref,
useGlobal,
@ -74,10 +75,10 @@ useMenuCloseOnEsc(open)
<div :class="{ 'nc-active-btn': filtersLength }">
<a-button v-e="['c:filter']" class="nc-filter-menu-btn nc-toolbar-btn txt-sm" :disabled="isLocked">
<div class="flex items-center gap-1">
<MdiFilterOutline />
<component :is="iconMap.filter" />
<!-- Filter -->
<span v-if="!isMobileMode" class="text-capitalize !text-xs font-weight-normal">{{ $t('activity.filter') }}</span>
<MdiMenuDown class="text-grey" />
<component :is="iconMap.arrowDown" class="text-grey" />
<span v-if="filtersLength" class="nc-count-badge">{{ filtersLength }}</span>
</div>

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

@ -1,4 +1,6 @@
<script lang="ts" setup>
import { iconMap } from '#imports'
interface Props {
modelValue: boolean
}
@ -32,7 +34,7 @@ const selectedView = inject(ActiveViewInj)
</div>
<div class="flex h-full items-center justify-center rounded group" @click="vModel = false">
<MdiClose class="cursor-pointer mt-1 nc-modal-close group-hover:text-accent text-opacity-100" />
<component :is="iconMap.close" class="cursor-pointer mt-1 nc-modal-close group-hover:text-accent text-opacity-100" />
</div>
</div>

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

@ -1,10 +1,14 @@
<script lang="ts" setup>
import { iconMap } from '#imports'
</script>
<template>
<a-dropdown :trigger="['click']" overlay-class-name="nc-dropdown-actions-menu">
<a-button v-e="['c:actions']" class="nc-actions-menu-btn nc-toolbar-btn">
<div class="flex gap-2 items-center">
<MdiDownload class="group-hover:text-accent text-gray-500" />
<component :is="iconMap.download" class="group-hover:text-accent text-gray-500" />
<span class="text-capitalize !text-sm font-weight-normal">{{ $t('general.download') }}</span>
<MdiMenuDown class="text-grey" />
<component :is="iconMap.arrowDown" class="text-grey" />
</div>
</a-button>

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

@ -7,6 +7,7 @@ import {
IsPublicInj,
MetaInj,
extractSdkResponseErrorMsg,
iconMap,
inject,
message,
ref,
@ -99,7 +100,7 @@ const exportFile = async (exportType: ExportTypes) => {
<template>
<a-menu-item>
<div v-e="['a:actions:download-csv']" class="nc-project-menu-item" @click="exportFile(ExportTypes.CSV)">
<MdiDownloadOutline class="text-gray-500" />
<component :is="iconMap.csv" class="text-gray-500" />
<!-- Download as CSV -->
{{ $t('activity.downloadCSV') }}
</div>
@ -107,7 +108,7 @@ const exportFile = async (exportType: ExportTypes) => {
<a-menu-item>
<div v-e="['a:actions:download-excel']" class="nc-project-menu-item" @click="exportFile(ExportTypes.EXCEL)">
<MdiDownloadOutline class="text-gray-500" />
<component :is="iconMap.excel" class="text-gray-500" />
<!-- Download as XLSX -->
{{ $t('activity.downloadExcel') }}
</div>

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

@ -11,6 +11,7 @@ import {
MetaInj,
ReloadViewDataHookInj,
computed,
iconMap,
inject,
ref,
resolveComponent,
@ -156,12 +157,12 @@ useMenuCloseOnEsc(open)
<div :class="{ 'nc-active-btn': numberOfHiddenFields }">
<a-button v-e="['c:fields']" class="nc-fields-menu-btn nc-toolbar-btn" :disabled="isLocked">
<div class="flex items-center gap-1">
<MdiEyeOffOutline />
<component :is="iconMap.eye" />
<!-- Fields -->
<span v-if="!isMobileMode" class="text-capitalize !text-xs font-weight-normal">{{ $t('objects.fields') }}</span>
<MdiMenuDown class="text-grey" />
<component :is="iconMap.arrowDown" class="text-grey" />
<span v-if="numberOfHiddenFields" class="nc-count-badge">{{ numberOfHiddenFields }}</span>
</div>
@ -218,7 +219,7 @@ useMenuCloseOnEsc(open)
<div class="flex-1" />
<MdiDrag class="cursor-move" />
<component :is="iconMap.drag" class="cursor-move" />
</div>
</template>
<template v-if="activeView?.type === ViewTypes.GRID" #header>
@ -234,7 +235,7 @@ useMenuCloseOnEsc(open)
<span class="text-sm">Display Value</span>
</template>
<MdiTableKey class="text-xs" />
<component :is="iconMap.tableKey" class="text-xs" />
</a-tooltip>
<div class="flex items-center px-[8px]">

5
packages/nc-gui/components/smartsheet/toolbar/KanbanStackEditOrAdd.vue

@ -3,6 +3,7 @@ import {
IsKanbanInj,
IsLockedInj,
IsPublicInj,
iconMap,
inject,
provide,
ref,
@ -44,11 +45,11 @@ provide(IsKanbanInj, ref(true))
:disabled="isLocked"
>
<div class="flex items-center gap-1">
<MdiPlusCircleOutline />
<component :is="iconMap.plusCircle" />
<span class="text-capitalize !text-sm font-weight-normal">
{{ $t('activity.kanban.addOrEditStack') }}
</span>
<MdiMenuDown class="text-grey" />
<component :is="iconMap.arrowDown" class="text-grey" />
</div>
</a-button>
</div>

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

@ -1,9 +1,6 @@
<script setup lang="ts">
import MdiLockOutlineIcon from '~icons/mdi/lock-outline'
import MdiAccountIcon from '~icons/mdi/account'
import MdiAccountGroupIcon from '~icons/mdi/account-group'
import { LockType } from '~/lib'
import { ActiveViewInj, inject } from '#imports'
import { ActiveViewInj, iconMap, inject } from '#imports'
const { type, hideTick } = defineProps<{ hideTick?: boolean; type: LockType }>()
@ -12,17 +9,17 @@ const emit = defineEmits(['select'])
const types = {
[LockType.Personal]: {
title: 'title.personalView',
icon: MdiAccountIcon,
icon: iconMap.account,
subtitle: 'msg.info.personalView',
},
[LockType.Collaborative]: {
title: 'title.collabView',
icon: MdiAccountGroupIcon,
icon: iconMap.users,
subtitle: 'msg.info.collabView',
},
[LockType.Locked]: {
title: 'title.lockedView',
icon: MdiLockOutlineIcon,
icon: iconMap.lock,
subtitle: 'msg.info.lockedView',
},
}
@ -34,16 +31,17 @@ const selectedView = inject(ActiveViewInj)
<div class="nc-locked-menu-item" @click="emit('select', type)">
<div :class="{ 'show-tick': !hideTick }">
<template v-if="!hideTick">
<MdiCheck v-if="selectedView?.lock_type === type" />
<GeneralIcon v-if="selectedView?.lock_type === type" icon="check" />
<span v-else />
</template>
<div>
<div class="flex items-center gap-1">
<component :is="types[type].icon" class="text-gray-500" />
{{ $t(types[type].title) }}
</div>
<div class="nc-subtitle whitespace-normal">
{{ $t(types[type].subtitle) }}
</div>

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

@ -9,6 +9,7 @@ import {
MetaInj,
ReloadViewDataHookInj,
computed,
iconMap,
inject,
ref,
useViewColumns,
@ -84,7 +85,7 @@ const handleChange = () => {
{{ $t('activity.map.mappedBy') }}
<span class="font-bold">{{ geoDataFieldColumn?.title }}</span>
</span>
<MdiMenuDown class="text-grey" />
<component :is="iconMap.arrowDown" class="text-grey" />
</div>
</a-button>
</div>

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

@ -4,7 +4,7 @@ import { ref } from 'vue'
import { StreamBarcodeReader } from 'vue-barcode-reader'
import type { ColumnType } from 'nocodb-sdk'
import { UITypes } from 'nocodb-sdk'
import { NOCO, storeToRefs } from '#imports'
import { NOCO, iconMap, storeToRefs } from '#imports'
const meta = inject(MetaInj, ref())
@ -125,7 +125,7 @@ const onDecode = async (codeValue: string) => {
<div>
<a-button class="nc-btn-find-row-by-scan nc-toolbar-btn" @click="showCodeScannerOverlay = true">
<div class="flex items-center gap-1">
<MdiQrcodeScan />
<component :is="iconMap.qrCode" />
<span v-if="!isMobileMode" class="!text-xs font-weight-normal"> {{ $t('activity.findRowByCodeScan') }}</span>
</div>
</a-button>

9
packages/nc-gui/components/smartsheet/toolbar/Reload.vue

@ -1,5 +1,5 @@
<script setup lang="ts">
import { ReloadViewDataHookInj, inject, ref, useNuxtApp, watch } from '#imports'
import { ReloadViewDataHookInj, iconMap, inject, ref, useNuxtApp, watch } from '#imports'
const { $e, $state } = useNuxtApp()
@ -25,9 +25,10 @@ const onClick = () => {
<a-tooltip placement="bottom">
<template #title> {{ $t('general.reload') }} </template>
<div class="nc-toolbar-btn flex min-w-32px w-32px h-32px items-center">
<MdiReload
class="w-full h-full cursor-pointer text-gray-500 group-hover:(text-primary) nc-toolbar-reload-btn"
<div class="nc-toolbar-btn flex min-w-32px w-32px h-32px items-center justify-center select-none">
<component
:is="iconMap.reload"
class="cursor-pointer text-gray-500 group-hover:(text-primary) nc-toolbar-reload-btn"
:class="isReloading ? 'animate-spin' : ''"
@click="onClick"
/>

15
packages/nc-gui/components/smartsheet/toolbar/RowHeight.vue

@ -1,6 +1,6 @@
<script setup lang="ts">
import type { GridType } from 'nocodb-sdk'
import { ActiveViewInj, IsLockedInj, inject, ref, storeToRefs, useMenuCloseOnEsc } from '#imports'
import { ActiveViewInj, IsLockedInj, iconMap, inject, ref, storeToRefs, useMenuCloseOnEsc } from '#imports'
const { isSharedBase } = storeToRefs(useProject())
@ -43,10 +43,9 @@ useMenuCloseOnEsc(open)
<div>
<a-button v-e="['c:row-height']" class="nc-height-menu-btn nc-toolbar-btn" :disabled="isLocked">
<div class="flex items-center gap-1">
<RiLineHeight />
<component :is="iconMap.rowHeight" />
<!-- Row Height -->
<MdiMenuDown class="text-grey" />
<component :is="iconMap.arrowDown" class="text-grey" />
</div>
</a-button>
</div>
@ -55,19 +54,19 @@ useMenuCloseOnEsc(open)
<div class="text-gray-500 !text-xs px-4 py-2">Select a row height</div>
<div class="flex flex-col w-full text-sm" @click.stop>
<div class="nc-row-height-option" @click="updateRowHeight(0)">
<NcIconsRowHeightShort class="nc-row-height-icon" />
<GeneralIcon icon="heightShort" class="nc-row-height-icon" />
Short
</div>
<div class="nc-row-height-option" @click="updateRowHeight(1)">
<NcIconsRowHeightMedium class="nc-row-height-icon" />
<GeneralIcon icon="heightMedium" class="nc-row-height-icon" />
Medium
</div>
<div class="nc-row-height-option" @click="updateRowHeight(2)">
<NcIconsRowHeightTall class="nc-row-height-icon" />
<GeneralIcon icon="heightTall" class="nc-row-height-icon" />
Tall
</div>
<div class="nc-row-height-option" @click="updateRowHeight(3)">
<NcIconsRowHeightExtraTall class="nc-row-height-icon" />
<GeneralIcon icon="heightExtra" class="nc-row-height-icon" />
Extra
</div>
</div>

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

@ -4,6 +4,7 @@ import {
ActiveViewInj,
ReloadViewDataHookInj,
computed,
iconMap,
inject,
onClickOutside,
ref,
@ -55,9 +56,9 @@ function onPressEnter() {
:class="{ '!bg-gray-100 ': isDropdownOpen }"
@click="isDropdownOpen = !isDropdownOpen"
>
<MdiMagnify class="text-grey" />
<component :is="iconMap.search" class="text-grey" />
<MdiMenuDown class="text-grey" />
<component :is="iconMap.arrowDown" class="text-grey" />
<a-select
v-model:value="search.field"

14
packages/nc-gui/components/smartsheet/toolbar/ShareView.vue

@ -5,6 +5,7 @@ import tinycolor from 'tinycolor2'
import {
computed,
extractSdkResponseErrorMsg,
iconMap,
isRtlLang,
message,
projectThemeColors,
@ -249,7 +250,7 @@ const copyIframeCode = async () => {
@click="genShareLink"
>
<div class="flex items-center gap-1">
<MdiOpenInNew />
<component :is="iconMap.share" />
<!-- Share View -->
<span v-if="!isMobileMode" class="!text-xs font-weight-normal"> {{ $t('activity.shareView') }}</span>
</div>
@ -272,17 +273,22 @@ const copyIframeCode = async () => {
<div class="flex-1 h-min text-xs text-gray-500">{{ sharedViewUrl }}</div>
<a v-e="['c:view:share:open-url']" :href="sharedViewUrl" target="_blank">
<MdiOpenInNew class="text-sm text-gray-500" />
<component :is="iconMap.share" class="text-sm text-gray-500" />
</a>
<MdiContentCopy v-e="['c:view:share:copy-url']" class="text-gray-500 text-sm cursor-pointer" @click="copyLink" />
<component
:is="iconMap.copy"
v-e="['c:view:share:copy-url']"
class="text-gray-500 text-sm cursor-pointer"
@click="copyLink"
/>
</div>
<div
class="flex gap-1 items-center pb-1 text-gray-500 cursor-pointer font-weight-medium mb-2 mt-4 pl-1"
@click="copyIframeCode"
>
<MdiCodeTags class="text-gray-500" />
<component :is="iconMap.embed" class="text-gray-500" />
Embed this view in your site
</div>

5
packages/nc-gui/components/smartsheet/toolbar/SharedViewList.vue

@ -3,6 +3,7 @@ import { ViewTypes } from 'nocodb-sdk'
import {
Empty,
extractSdkResponseErrorMsg,
iconMap,
message,
onMounted,
parseProp,
@ -162,8 +163,8 @@ const deleteLink = async (id: string) => {
<a-table-column key="id" :title="$t('labels.actions')" data-index="title">
<template #default="{ record }">
<div class="text-sm flex gap-2" :title="text">
<MdiContentCopy class="cursor-pointer" @click="copyLink(record)" />
<MdiDeleteOutline class="cursor-pointer" @click="deleteLink(record.id)" />
<component :is="iconMap.copy" class="cursor-pointer" @click="copyLink(record)" />
<component :is="iconMap.delete" class="cursor-pointer" @click="deleteLink(record.id)" />
</div>
</template>
</a-table-column>

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

@ -8,6 +8,7 @@ import {
ReloadViewDataHookInj,
computed,
getSortDirectionOptions,
iconMap,
inject,
ref,
useMenuCloseOnEsc,
@ -75,11 +76,11 @@ useMenuCloseOnEsc(open)
<div :class="{ 'nc-badge nc-active-btn': sorts?.length }">
<a-button v-e="['c:sort']" class="nc-sort-menu-btn nc-toolbar-btn" :disabled="isLocked">
<div class="flex items-center gap-1">
<MdiSort />
<component :is="iconMap.sort" />
<!-- Sort -->
<span v-if="!isMobileMode" class="text-capitalize !text-xs font-weight-normal">{{ $t('activity.sort') }}</span>
<MdiMenuDown class="text-grey" />
<component :is="iconMap.arrowDown" class="text-grey" />
<span v-if="sorts?.length" class="nc-count-badge">{{ sorts.length }}</span>
</div>
@ -93,7 +94,8 @@ useMenuCloseOnEsc(open)
>
<div v-if="sorts?.length" class="sort-grid mb-2 max-h-420px overflow-y-auto" @click.stop>
<template v-for="(sort, i) of sorts" :key="i">
<MdiCloseBox
<component
:is="iconMap.closeBox"
ref="removeIcon"
class="nc-sort-item-remove-btn text-grey self-center"
small
@ -131,7 +133,7 @@ useMenuCloseOnEsc(open)
<a-button class="text-capitalize mb-1 mt-4" type="primary" ghost @click.stop="addSort">
<div class="flex gap-1 items-center">
<MdiPlus />
<component :is="iconMap.plus" />
<!-- Add Sort Option -->
{{ $t('activity.addSort') }}
</div>

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

@ -9,6 +9,7 @@ import {
MetaInj,
ReloadViewDataHookInj,
computed,
iconMap,
inject,
ref,
useKanbanViewStoreOrThrow,
@ -94,7 +95,7 @@ const handleChange = () => {
{{ $t('activity.kanban.stackedBy') }}
<span class="font-bold">{{ groupingField }}</span>
</span>
<MdiMenuDown class="text-grey" />
<component :is="iconMap.arrowDown" class="text-grey" />
</div>
</a-button>
</div>

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

@ -6,6 +6,7 @@ import {
IsPublicInj,
MetaInj,
extractSdkResponseErrorMsg,
iconMap,
inject,
message,
ref,
@ -18,9 +19,6 @@ import {
useUIPermission,
} from '#imports'
import { LockType } from '~/lib'
import MdiLockOutlineIcon from '~icons/mdi/lock-outline'
import MdiAccountIcon from '~icons/mdi/account'
import MdiAccountGroupIcon from '~icons/mdi/account-group'
const { t } = useI18n()
@ -70,12 +68,12 @@ const currentBaseId = computed(() => meta.value?.base_id)
const Icon = computed(() => {
switch (selectedView.value?.lock_type) {
case LockType.Personal:
return MdiAccountIcon
return iconMap.account
case LockType.Locked:
return MdiLockOutlineIcon
return iconMap.lock
case LockType.Collaborative:
default:
return MdiAccountGroupIcon
return iconMap.users
}
})
@ -120,7 +118,7 @@ useMenuCloseOnEsc(open)
<component :is="Icon" class="text-gray-500" :class="`nc-icon-${selectedView?.lock_type}`" />
<MdiMenuDown class="text-grey" />
<component :is="iconMap.arrowDown" class="text-grey" />
</div>
</a-button>
@ -136,9 +134,7 @@ useMenuCloseOnEsc(open)
<div v-e="['c:navdraw:preview-as']" class="nc-project-menu-item group px-0 !py-0">
<LazySmartsheetToolbarLockType hide-tick :type="lockType" />
<MaterialSymbolsChevronRightRounded
class="transform group-hover:(scale-115 text-accent) text-xl text-gray-400"
/>
<component :is="iconMap.arrowRight" class="transform group-hover:(scale-115 text-accent) text-gray-400" />
</div>
</template>
@ -162,13 +158,11 @@ useMenuCloseOnEsc(open)
<template #title>
<!-- Download -->
<div v-e="['c:navdraw:preview-as']" class="nc-project-menu-item group">
<MdiDownload class="group-hover:text-accent text-gray-500" />
<component :is="iconMap.download" class="group-hover:text-accent text-gray-500" />
{{ $t('general.download') }}
<div class="flex-1" />
<MaterialSymbolsChevronRightRounded
class="transform group-hover:(scale-115 text-accent) text-xl text-gray-400"
/>
<component :is="iconMap.arrowRight" class="transform group-hover:(scale-115 text-accent) text-gray-400" />
</div>
</template>
@ -182,13 +176,11 @@ useMenuCloseOnEsc(open)
<!-- Upload -->
<template #title>
<div v-e="['c:navdraw:preview-as']" class="nc-project-menu-item group">
<MdiUpload class="group-hover:text-accent text-gray-500" />
<component :is="iconMap.upload" class="group-hover:text-accent text-gray-500" />
{{ $t('general.upload') }}
<div class="flex-1" />
<MaterialSymbolsChevronRightRounded
class="transform group-hover:(scale-115 text-accent) text-xl text-gray-400"
/>
<component :is="iconMap.arrowRight" class="transform group-hover:(scale-115 text-accent) text-gray-400" />
</div>
</template>
@ -201,7 +193,7 @@ useMenuCloseOnEsc(open)
:class="{ disabled: isLocked }"
@click="!isLocked ? (dialog.value = true) : {}"
>
<MdiUploadOutline class="text-gray-500" />
<component :is="iconMap.upload" class="text-gray-500" />
{{ `${$t('general.upload')} ${type.toUpperCase()}` }}
<div class="flex items-center text-gray-400"><MdiAlpha />version</div>
</div>
@ -214,7 +206,7 @@ useMenuCloseOnEsc(open)
<a-menu-item v-if="isUIAllowed('SharedViewList') && !isView && !isPublicView">
<div v-e="['a:actions:shared-view-list']" class="py-2 flex gap-2 items-center" @click="sharedViewListDlg = true">
<MdiViewListOutline class="text-gray-500" />
<component :is="iconMap.list" class="text-gray-500" />
<!-- Shared View List -->
{{ $t('activity.listSharedView') }}
</div>
@ -227,14 +219,14 @@ useMenuCloseOnEsc(open)
class="py-2 flex gap-2 items-center"
@click="showWebhookDrawer = true"
>
<MdiHook class="text-gray-500" />
<component :is="iconMap.hook" class="text-gray-500" />
{{ $t('objects.webhooks') }}
</div>
</a-menu-item>
<a-menu-item v-if="!isSharedBase && !isPublicView && !isMobileMode">
<div v-e="['c:snippet:open']" class="py-2 flex gap-2 items-center" @click="showApiSnippetDrawer = true">
<MdiXml class="text-gray-500" />
<component :is="iconMap.snippet" class="text-gray-500" />
<!-- Get API Snippet -->
{{ $t('activity.getApiSnippet') }}
</div>
@ -247,7 +239,7 @@ useMenuCloseOnEsc(open)
class="py-2 flex gap-2 items-center nc-view-action-erd"
@click="showErd = true"
>
<MaterialSymbolsAccountTreeRounded class="text-gray-500" />
<component :is="iconMap.erd" class="text-gray-500" />
{{ $t('title.erdView') }}
</div>
</a-menu-item>

20
packages/nc-gui/components/tabs/auth/ApiTokenManagement.vue

@ -1,6 +1,16 @@
<script setup lang="ts">
import type { ApiTokenType } from 'nocodb-sdk'
import { extractSdkResponseErrorMsg, message, onMounted, storeToRefs, useCopy, useI18n, useNuxtApp, useProject } from '#imports'
import {
extractSdkResponseErrorMsg,
iconMap,
message,
onMounted,
storeToRefs,
useCopy,
useI18n,
useNuxtApp,
useProject,
} from '#imports'
interface ApiToken extends ApiTokenType {
show?: boolean
@ -159,7 +169,7 @@ onMounted(() => {
<div class="flex flex-row space-x-1">
<a-button size="middle" type="text" @click="loadApiTokens()">
<div class="flex flex-row justify-center items-center caption capitalize space-x-1">
<MdiReload class="text-gray-500" />
<component :is="iconMap.reload" class="text-gray-500" />
<div class="text-gray-500">{{ $t('general.reload') }}</div>
</div>
</a-button>
@ -167,7 +177,7 @@ onMounted(() => {
<!-- Add New Token -->
<a-button size="middle" type="primary" ghost @click="openNewTokenModal">
<div class="flex flex-row justify-center items-center caption capitalize space-x-1">
<MdiPlus />
<component :is="iconMap.plus" />
<div>{{ $t('activity.newToken') }}</div>
</div>
</a-button>
@ -212,7 +222,7 @@ onMounted(() => {
<a-button type="text" class="!rounded-md" @click="copyToken(item.token)">
<template #icon>
<MdiContentCopy class="flex mx-auto h-[1rem]" />
<component :is="iconMap.copy" class="flex mx-auto h-[1rem]" />
</template>
</a-button>
</a-tooltip>
@ -235,7 +245,7 @@ onMounted(() => {
<a-menu>
<a-menu-item>
<div class="flex flex-row items-center py-3 h-[1rem]" @click="openDeleteModal(item)">
<MdiDeleteOutline class="flex" />
<component :is="iconMap.delete" class="flex" />
<div class="text-xs pl-2">{{ $t('general.remove') }}</div>
</div>
</a-menu-item>

17
packages/nc-gui/components/tabs/auth/UserManagement.vue

@ -3,6 +3,7 @@ import { OrgUserRoles } from 'nocodb-sdk'
import type { ProjectUserReqType, RequestParams } from 'nocodb-sdk'
import {
extractSdkResponseErrorMsg,
iconMap,
message,
onBeforeMount,
projectRoleTagColors,
@ -229,7 +230,7 @@ const isSuperAdmin = (user: { main_roles?: string }) => {
<div class="flex flex-row space-x-1">
<a-button v-e="['a:user:reload']" size="middle" type="text" @click="loadUsers()">
<div class="flex flex-row justify-center items-center caption capitalize space-x-1">
<MdiReload class="text-gray-500" />
<component :is="iconMap.reload" class="text-gray-500" />
<div class="text-gray-500">{{ $t('general.reload') }}</div>
</div>
</a-button>
@ -244,7 +245,7 @@ const isSuperAdmin = (user: { main_roles?: string }) => {
@click="onInvite"
>
<div class="flex flex-row justify-center items-center caption capitalize space-x-1">
<MdiAccountPlusOutline class="mr-1" />
<component :is="iconMap.accountPlus" class="mr-1" />
<div>{{ $t('activity.inviteTeam') }}</div>
</div>
</a-button>
@ -254,12 +255,12 @@ const isSuperAdmin = (user: { main_roles?: string }) => {
<div class="px-5">
<div class="flex flex-row border-b-1 pb-2 px-2">
<div class="flex flex-row w-4/6 space-x-1 items-center pl-1">
<EvaEmailOutline class="flex text-gray-500 -mt-0.5" />
<component :is="iconMap.email" class="flex text-gray-500 -mt-0.5" />
<div class="text-gray-600 text-xs space-x-1">{{ $t('labels.email') }}</div>
</div>
<div class="flex flex-row justify-center w-1/6 space-x-1 items-center pl-1">
<MdiDramaMasks class="flex text-gray-500 -mt-0.5" />
<component :is="iconMap.role" class="flex text-gray-500 -mt-0.5" />
<div class="text-gray-600 text-xs">{{ $t('objects.role') }}</div>
</div>
@ -311,7 +312,7 @@ const isSuperAdmin = (user: { main_roles?: string }) => {
<a-button type="text" class="!rounded-md nc-user-invite" @click="inviteUser(user)">
<template #icon>
<MdiPlus class="flex mx-auto h-[1.1rem] text-gray-500" />
<component :is="iconMap.plus" class="flex mx-auto h-[1.1rem] text-gray-500" />
</template>
</a-button>
</a-tooltip>
@ -324,7 +325,7 @@ const isSuperAdmin = (user: { main_roles?: string }) => {
<a-button v-e="['c:user:delete']" type="text" class="!rounded-md nc-user-delete" @click="onDelete(user)">
<template #icon>
<MdiDeleteOutline class="flex mx-auto h-[1.1rem] text-gray-500" />
<component :is="iconMap.delete" class="flex mx-auto h-[1.1rem] text-gray-500" />
</template>
</a-button>
</a-tooltip>
@ -349,13 +350,13 @@ const isSuperAdmin = (user: { main_roles?: string }) => {
<a-menu-item>
<!-- Resend invite Email -->
<div class="flex flex-row items-center py-3" @click="resendInvite(user)">
<MdiEmailArrowRightOutline class="flex h-[1rem] text-gray-500" />
<component :is="iconMap.sendEmail" class="flex h-[1rem] text-gray-500" />
<div class="text-xs pl-2">{{ $t('activity.resendInvite') }}</div>
</div>
</a-menu-item>
<a-menu-item>
<div class="flex flex-row items-center py-3" @click="copyInviteUrl(user)">
<MdiContentCopy class="flex h-[1rem] text-gray-500" />
<component :is="iconMap.copy" class="flex h-[1rem] text-gray-500" />
<div class="text-xs pl-2">{{ $t('activity.copyInviteURL') }}</div>
</div>
</a-menu-item>

11
packages/nc-gui/components/tabs/auth/user-management/ShareBase.vue

@ -1,6 +1,7 @@
<script setup lang="ts">
import {
extractSdkResponseErrorMsg,
iconMap,
message,
onMounted,
storeToRefs,
@ -153,7 +154,7 @@ onMounted(() => {
<template>
<div class="flex flex-col w-full" data-testid="nc-share-base-sub-modal">
<div class="flex flex-row items-center space-x-0.5 pl-2 h-[0.8rem]">
<MdiOpenInNew />
<component :is="iconMap.share" />
<div class="text-xs">{{ $t('activity.shareBase.link') }}</div>
</div>
@ -169,7 +170,7 @@ onMounted(() => {
<a-button type="text" class="!rounded-md mr-1 -mt-1.5 h-[1rem]" @click="recreate">
<template #icon>
<MdiReload class="flex mx-auto text-gray-600" />
<component :is="iconMap.reload" class="flex mx-auto text-gray-600" />
</template>
</a-button>
</a-tooltip>
@ -181,7 +182,7 @@ onMounted(() => {
<a-button type="text" class="!rounded-md mr-1 -mt-1.5 h-[1rem]" @click="copyUrl">
<template #icon>
<MdiContentCopy class="flex mx-auto text-gray-600" />
<component :is="iconMap.copy" class="flex mx-auto text-gray-600" />
</template>
</a-button>
</a-tooltip>
@ -193,7 +194,7 @@ onMounted(() => {
<a-button type="text" class="!rounded-md mr-1 -mt-1.5 h-[1rem]" @click="navigateToSharedBase">
<template #icon>
<MdiOpenInNew class="flex mx-auto text-gray-600" />
<component :is="iconMap.share" class="flex mx-auto text-gray-600" />
</template>
</a-button>
</a-tooltip>
@ -205,7 +206,7 @@ onMounted(() => {
<a-button type="text" class="!rounded-md mr-1 -mt-1.5 h-[1rem]" @click="generateEmbeddableIframe">
<template #icon>
<MdiXml class="flex mx-auto text-gray-600" />
<component :is="iconMap.xml" class="flex mx-auto text-gray-600" />
</template>
</a-button>
</a-tooltip>

7
packages/nc-gui/components/tabs/auth/user-management/UsersModal.vue

@ -6,6 +6,7 @@ import {
computed,
emailValidator,
extractSdkResponseErrorMsg,
iconMap,
message,
onMounted,
projectRoleTagColors,
@ -191,7 +192,7 @@ watch(
<template v-if="usersData.invitationToken">
<div class="flex flex-col mt-1 border-b-1 pb-5">
<div class="flex flex-row items-center pl-1.5 pb-1 h-[1.1rem]">
<MdiAccountOutline />
<component :is="iconMap.account" />
<div class="text-xs ml-0.5 mt-0.5">Copy Invite Token</div>
</div>
@ -204,7 +205,7 @@ watch(
<a-button type="text" class="!rounded-md -mt-0.5" @click="copyUrl">
<template #icon>
<MdiContentCopy class="flex mx-auto text-green-700 h-[1rem]" />
<component :is="iconMap.copy" class="flex mx-auto text-green-700 h-[1rem]" />
</template>
</a-button>
</div>
@ -230,7 +231,7 @@ watch(
<div v-else class="flex flex-col pb-4">
<div class="flex flex-row items-center pl-2 pb-1 h-[1rem]">
<MdiAccountOutline />
<component :is="iconMap.account" />
<div class="text-xs ml-0.5 mt-0.5">{{ selectedUser ? $t('activity.editUser') : $t('activity.inviteTeam') }}</div>
</div>

29
packages/nc-gui/components/template/Editor.vue

@ -17,6 +17,7 @@ import {
getDateFormat,
getDateTimeFormat,
getUIDTIcon,
iconMap,
inject,
message,
nextTick,
@ -635,7 +636,7 @@ function isSelectDisabled(uidt: string, disableSelect = false) {
<a-collapse-panel v-for="(table, tableIdx) of data.tables" :key="tableIdx">
<template #header>
<span class="font-weight-bold text-lg flex items-center gap-2">
<mdi-table class="text-primary" />
<component :is="iconMap.table" class="text-primary" />
{{ table.table_name }}
</span>
</template>
@ -646,7 +647,12 @@ function isSelectDisabled(uidt: string, disableSelect = false) {
<!-- TODO: i18n -->
<span>Delete Table</span>
</template>
<mdi-delete-outline v-if="data.tables.length > 1" class="text-lg mr-8" @click.stop="deleteTable(tableIdx)" />
<component
:is="iconMap.delete"
v-if="data.tables.length > 1"
class="text-lg mr-8"
@click.stop="deleteTable(tableIdx)"
/>
</a-tooltip>
</template>
@ -726,7 +732,7 @@ function isSelectDisabled(uidt: string, disableSelect = false) {
/>
</a-form-item>
<span v-else class="font-weight-bold text-lg flex items-center gap-2" @click="setEditableTn(tableIdx, true)">
<mdi-table class="text-primary" />
<component :is="iconMap.table" class="text-primary" />
{{ table.table_name }}
</span>
</template>
@ -737,7 +743,12 @@ function isSelectDisabled(uidt: string, disableSelect = false) {
<!-- TODO: i18n -->
<span>Delete Table</span>
</template>
<mdi-delete-outline v-if="data.tables.length > 1" class="text-lg mr-8" @click.stop="deleteTable(tableIdx)" />
<component
:is="iconMap.delete"
v-if="data.tables.length > 1"
class="text-lg mr-8"
@click.stop="deleteTable(tableIdx)"
/>
</a-tooltip>
</template>
<a-table
@ -832,7 +843,7 @@ function isSelectDisabled(uidt: string, disableSelect = false) {
<a-button type="text" @click="deleteTableColumn(tableIdx, record.key)">
<div class="flex items-center">
<mdi-delete-outline class="text-lg" />
<component :is="iconMap.delete" class="text-lg" />
</div>
</a-button>
</a-tooltip>
@ -849,7 +860,7 @@ function isSelectDisabled(uidt: string, disableSelect = false) {
<a-button class="group" @click="addNewColumnRow(tableIdx, 'Number')">
<div class="flex items-center">
<mdi-numeric class="group-hover:!text-accent flex text-lg" />
<component :is="iconMap.number" class="group-hover:!text-accent flex text-lg" />
</div>
</a-button>
</a-tooltip>
@ -862,7 +873,7 @@ function isSelectDisabled(uidt: string, disableSelect = false) {
<a-button class="group" @click="addNewColumnRow(tableIdx, 'SingleLineText')">
<div class="flex items-center">
<mdi-alpha-a class="group-hover:!text-accent text-lg" />
<component :is="iconMap.text" class="group-hover:!text-accent text-lg" />
</div>
</a-button>
</a-tooltip>
@ -875,7 +886,7 @@ function isSelectDisabled(uidt: string, disableSelect = false) {
<a-button class="group" @click="addNewColumnRow(tableIdx, 'LongText')">
<div class="flex items-center">
<mdi-text class="group-hover:!text-accent text-lg" />
<component :is="iconMap.longText" class="group-hover:!text-accent text-lg" />
</div>
</a-button>
</a-tooltip>
@ -888,7 +899,7 @@ function isSelectDisabled(uidt: string, disableSelect = false) {
<a-button class="group" @click="addNewColumnRow(tableIdx, 'SingleLineText')">
<div class="flex items-center gap-1">
<mdi-plus class="group-hover:!text-accent text-lg" />
<component :is="iconMap.plus" class="group-hover:!text-accent text-lg" />
</div>
</a-button>
</a-tooltip>

8
packages/nc-gui/components/virtual-cell/BelongsTo.vue

@ -19,8 +19,6 @@ import {
useSmartsheetRowStoreOrThrow,
useUIPermission,
} from '#imports'
import MdiArrowExpand from '~icons/mdi/arrow-expand'
import MdiPlus from '~icons/mdi/plus'
const column = inject(ColumnInj)!
@ -53,7 +51,7 @@ const { loadRelatedTableMeta, relatedTableDisplayValueProp, unlink } = useProvid
await loadRelatedTableMeta()
const addIcon = computed(() => (cellValue?.value ? MdiArrowExpand : MdiPlus))
const addIcon = computed(() => (cellValue?.value ? 'expand' : 'plus'))
const value = computed(() => {
if (cellValue?.value) {
@ -94,8 +92,8 @@ useSelectedCellKeyupListener(active, (e: KeyboardEvent) => {
v-if="!readOnly && !isLocked && (isUIAllowed('xcDatatableEditable') || isForm)"
class="flex justify-end gap-1 min-h-[30px] items-center"
>
<component
:is="addIcon"
<GeneralIcon
:icon="addIcon"
class="text-sm nc-action-icon text-gray-500/50 hover:text-gray-500 select-none group-hover:(text-gray-500) nc-plus"
@click.stop="listItemsDlg = true"
/>

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

@ -113,13 +113,15 @@ useSelectedCellKeyupListener(inject(ActiveCellInj, ref(false)), (e: KeyboardEven
</div>
<div v-if="!isLocked" class="flex justify-end gap-1 min-h-[30px] items-center">
<MdiArrowExpand
<GeneralIcon
icon="expand"
class="select-none transform text-sm nc-action-icon text-gray-500/50 hover:text-gray-500 nc-arrow-expand"
@click.stop="childListDlg = true"
/>
<MdiPlus
<GeneralIcon
v-if="!readOnly && isUIAllowed('xcDatatableEditable')"
icon="plus"
class="select-none text-sm nc-action-icon text-gray-500/50 hover:text-gray-500 nc-plus"
@click.stop="listItemsDlg = true"
/>

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

@ -115,13 +115,15 @@ useSelectedCellKeyupListener(inject(ActiveCellInj, ref(false)), (e: KeyboardEven
</div>
<div v-if="!isLocked" class="flex justify-end gap-1 min-h-[30px] items-center">
<MdiArrowExpand
<GeneralIcon
icon="expand"
class="text-sm nc-action-icon text-gray-500/50 hover:text-gray-500 nc-arrow-expand"
@click.stop="childListDlg = true"
/>
<MdiPlus
<GeneralIcon
v-if="!readOnly && isUIAllowed('xcDatatableEditable')"
icon="plus"
class="text-sm nc-action-icon text-gray-500/50 hover:text-gray-500 nc-plus"
@click.stop="listItemsDlg = true"
/>

7
packages/nc-gui/components/virtual-cell/components/ItemChip.vue

@ -4,6 +4,7 @@ import {
IsFormInj,
IsLockedInj,
ReadonlyInj,
iconMap,
inject,
ref,
useExpandedFormDetached,
@ -62,7 +63,11 @@ export default {
<span class="name">{{ value }}</span>
<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')" />
<component
:is="iconMap.closeThick"
class="unlink-icon text-xs text-gray-500/50 group-hover:text-gray-500"
@click.stop="emit('unlink')"
/>
</div>
</div>
</template>

12
packages/nc-gui/components/virtual-cell/components/ListChildItems.vue

@ -10,6 +10,7 @@ import {
ReadonlyInj,
computed,
h,
iconMap,
inject,
ref,
useLTARStoreOrThrow,
@ -111,7 +112,8 @@ const onClick = (row: Row) => {
<div class="max-h-[max(calc(100vh_-_300px)_,500px)] flex flex-col py-6">
<div class="flex mb-4 items-center gap-2 px-12">
<div class="flex-1" />
<MdiReload
<component
:is="iconMap.reload"
v-if="!isForm"
class="cursor-pointer text-gray-500"
data-testid="nc-child-list-reload"
@ -128,7 +130,7 @@ const onClick = (row: Row) => {
@click="emit('attachRecord')"
>
<div class="flex items-center gap-1">
<MdiLinkVariant class="text-xs" type="primary" />
<component :is="iconMap.link" class="text-xs" type="primary" />
Link to '
<GeneralTableIcon :meta="relatedTableMeta" class="-mx-1 w-5" />
{{ relatedTableMeta.title }}'
@ -151,12 +153,14 @@ const onClick = (row: Row) => {
</div>
<div v-if="!readonly" class="flex gap-2">
<MdiLinkVariantRemove
<component
:is="iconMap.linkRemove"
class="text-xs text-grey hover:(!text-red-500) cursor-pointer"
data-testid="nc-child-list-icon-unlink"
@click.stop="unlinkRow(row)"
/>
<MdiDeleteOutline
<component
:is="iconMap.delete"
v-if="!readonly && !isPublic"
class="text-xs text-grey hover:(!text-red-500) cursor-pointer"
data-testid="nc-child-list-icon-delete"

3
packages/nc-gui/components/virtual-cell/components/ListItems.vue

@ -8,6 +8,7 @@ import {
IsPublicInj,
SaveRowInj,
computed,
iconMap,
inject,
isDrawerExist,
ref,
@ -210,7 +211,7 @@ watch(vModel, (nextVal) => {
<div class="flex-1" />
<MdiReload class="cursor-pointer text-gray-500 nc-reload" @click="loadChildrenExcludedList" />
<component :is="iconMap.reload" class="cursor-pointer text-gray-500 nc-reload" @click="loadChildrenExcludedList" />
<!-- Add new record -->
<a-button v-if="!isPublic" type="primary" size="small" @click="expandedFormDlg = true">

6
packages/nc-gui/components/webhook/Editor.vue

@ -453,7 +453,7 @@ onMounted(async () => {
<a-button class="nc-btn-webhook-save" type="primary" size="large" @click.prevent="saveHooks">
<div class="flex items-center">
<MdiContentSave class="mr-2" />
<component :is="iconMap.save" class="mr-2" />
<!-- Save -->
{{ $t('general.save') }}
</div>
@ -530,9 +530,9 @@ onMounted(async () => {
>
<a-select-option v-for="(notificationOption, i) in notificationList" :key="i" :value="notificationOption.type">
<div class="flex items-center">
<MdiLink v-if="notificationOption.type === 'URL'" class="mr-2" />
<component :is="iconMap.link" v-if="notificationOption.type === 'URL'" class="mr-2" />
<MdiEmail v-if="notificationOption.type === 'Email'" class="mr-2" />
<component :is="iconMap.email" v-if="notificationOption.type === 'Email'" class="mr-2" />
<MdiSlack v-if="notificationOption.type === 'Slack'" class="mr-2" />

19
packages/nc-gui/components/webhook/List.vue

@ -1,6 +1,17 @@
<script setup lang="ts">
import type { FilterReqType, HookReqType, HookType } from 'nocodb-sdk'
import { MetaInj, extractSdkResponseErrorMsg, inject, message, onMounted, parseProp, ref, useI18n, useNuxtApp } from '#imports'
import {
MetaInj,
extractSdkResponseErrorMsg,
iconMap,
inject,
message,
onMounted,
parseProp,
ref,
useI18n,
useNuxtApp,
} from '#imports'
const emit = defineEmits(['edit', 'add'])
@ -125,7 +136,7 @@ onMounted(() => {
<template #avatar>
<div class="my-1 px-2">
<MdiHook class="text-xl" />
<component :is="iconMap.hook" class="text-xl" />
</div>
<div class="px-2 text-white rounded" :class="{ 'bg-green-500': item.active, 'bg-gray-500': !item.active }">
{{ item.active ? 'ON' : 'OFF' }}
@ -143,14 +154,14 @@ onMounted(() => {
<template #title>
{{ $t('activity.copyWebhook') }}
</template>
<MdiContentCopy class="text-xl nc-hook-copy-icon" @click.stop="copyHook(item)" />
<component :is="iconMap.copy" class="text-xl nc-hook-copy-icon" @click.stop="copyHook(item)" />
</a-tooltip>
<a-tooltip placement="left">
<template #title>
{{ $t('activity.deleteWebhook') }}
</template>
<MdiDeleteOutline class="text-xl nc-hook-delete-icon" @click.stop="deleteHook(item, index)" />
<component :is="iconMap.delete" class="text-xl nc-hook-delete-icon" @click.stop="deleteHook(item, index)" />
</a-tooltip>
</div>
</div>

11
packages/nc-gui/layouts/base.vue

@ -1,5 +1,5 @@
<script lang="ts" setup>
import { computed, navigateTo, ref, useGlobal, useNuxtApp, useRoute, useSidebar } from '#imports'
import { computed, iconMap, navigateTo, ref, useGlobal, useNuxtApp, useRoute, useSidebar } from '#imports'
const { signOut, signedIn, isLoading, user, currentVersion } = useGlobal()
@ -60,7 +60,7 @@ hooks.hook('page:finish', () => {
<div v-show="isLoading" class="flex items-center gap-2 ml-3" data-testid="nc-loading">
{{ $t('general.loading') }}
<MdiReload :class="{ 'animate-infinite animate-spin': isLoading }" />
<component :is="iconMap.reload" :class="{ 'animate-infinite animate-spin': isLoading }" />
</div>
</div>
@ -78,7 +78,8 @@ hooks.hook('page:finish', () => {
<template v-if="signedIn">
<a-dropdown :trigger="['click']" overlay-class-name="nc-dropdown-user-accounts-menu">
<MdiDotsVertical
<component
:is="iconMap.threeDotVertical"
data-testid="nc-menu-accounts"
class="md:text-xl cursor-pointer hover:text-accent nc-menu-accounts"
@click.prevent
@ -88,7 +89,7 @@ hooks.hook('page:finish', () => {
<a-menu class="!py-0 leading-8 !rounded">
<a-menu-item key="0" data-testid="nc-menu-accounts__user-settings" class="!rounded-t">
<nuxt-link v-e="['c:navbar:user:email']" class="nc-project-menu-item group !no-underline" to="/account/users">
<MdiAccountCircleOutline class="mt-1 group-hover:text-accent" />&nbsp;
<component :is="iconMap.accountCircle" class="mt-1 group-hover:text-accent" />&nbsp;
<div class="prose group-hover:text-primary">
<div>Account</div>
<div class="text-xs text-gray-500">{{ email }}</div>
@ -114,7 +115,7 @@ hooks.hook('page:finish', () => {
<a-menu-item key="1" class="!rounded-b group">
<div v-e="['a:navbar:user:sign-out']" class="nc-project-menu-item group" @click="logout">
<MdiLogout class="group-hover:text-accent" />&nbsp;
<component :is="iconMap.signout" class="group-hover:text-accent" />&nbsp;
<span class="prose group-hover:text-primary">
{{ $t('general.signOut') }}

4
packages/nc-gui/layouts/shared-view.vue

@ -1,5 +1,5 @@
<script lang="ts" setup>
import { navigateTo, useEventListener, useRouter } from '#imports'
import { iconMap, navigateTo, useEventListener, useRouter } from '#imports'
const { isLoading, appInfo } = useGlobal()
@ -65,7 +65,7 @@ export default {
<template v-if="isLoading">
<span class="text-white" data-testid="nc-loading">{{ $t('general.loading') }}</span>
<MdiReload :class="{ 'animate-infinite animate-spin ': isLoading }" />
<component :is="iconMap.reload" :class="{ 'animate-infinite animate-spin ': isLoading }" />
</template>
<div v-else class="text-xl font-semibold truncate text-white nc-shared-view-title flex gap-2 items-center">

34
packages/nc-gui/package-lock.json generated

@ -11,7 +11,6 @@
"@ckpack/vue-color": "^1.2.0",
"@iconify/vue": "^4.0.1",
"@pinia/nuxt": "^0.4.7",
"@types/file-saver": "^2.0.5",
"@vue-flow/additional-components": "^1.2.0",
"@vue-flow/core": "^1.3.0",
"@vuelidate/core": "^2.0.0-alpha.44",
@ -61,13 +60,14 @@
"@iconify-json/clarity": "^1.1.4",
"@iconify-json/eva": "^1.1.2",
"@iconify-json/ic": "^1.1.7",
"@iconify-json/ion": "^1.1.8",
"@iconify-json/la": "^1.1.2",
"@iconify-json/logos": "^1.1.14",
"@iconify-json/lucide": "^1.1.36",
"@iconify-json/material-symbols": "^1.1.8",
"@iconify-json/mdi": "^1.1.25",
"@iconify-json/mi": "^1.1.2",
"@iconify-json/ph": "^1.1.2",
"@iconify-json/ph": "^1.1.5",
"@iconify-json/ri": "^1.1.3",
"@iconify-json/simple-icons": "^1.1.29",
"@iconify-json/system-uicons": "^1.1.4",
@ -1116,6 +1116,15 @@
"@iconify/types": "*"
}
},
"node_modules/@iconify-json/ion": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/@iconify-json/ion/-/ion-1.1.8.tgz",
"integrity": "sha512-RRyuDG3Sf3ggpK3MV/uSfMXAWlQUpquvt/jHtDg8Duprb/zE8NNXHhEFoSVGVRu7533nNEzUzN4KmYRdxJ790A==",
"dev": true,
"dependencies": {
"@iconify/types": "*"
}
},
"node_modules/@iconify-json/la": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@iconify-json/la/-/la-1.1.2.tgz",
@ -1171,9 +1180,9 @@
}
},
"node_modules/@iconify-json/ph": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@iconify-json/ph/-/ph-1.1.2.tgz",
"integrity": "sha512-NuTdtt/UmuxIHS4hfdyv3BP5JiWikNkr81hFHXDScXlH0GUMdRSY/B5T9vDvbXDY/esMLFnIAXoFVDLsGinhpw==",
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/@iconify-json/ph/-/ph-1.1.5.tgz",
"integrity": "sha512-iLXq3nohfGge22gL2tZmQ2WHBKkKkIbGWrkuyC8arckS4PWaONyh4A+uDPtSek9QbYDvi9AE2+NxWkNZhANotA==",
"dev": true,
"dependencies": {
"@iconify/types": "*"
@ -19077,6 +19086,15 @@
"@iconify/types": "*"
}
},
"@iconify-json/ion": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/@iconify-json/ion/-/ion-1.1.8.tgz",
"integrity": "sha512-RRyuDG3Sf3ggpK3MV/uSfMXAWlQUpquvt/jHtDg8Duprb/zE8NNXHhEFoSVGVRu7533nNEzUzN4KmYRdxJ790A==",
"dev": true,
"requires": {
"@iconify/types": "*"
}
},
"@iconify-json/la": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@iconify-json/la/-/la-1.1.2.tgz",
@ -19132,9 +19150,9 @@
}
},
"@iconify-json/ph": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@iconify-json/ph/-/ph-1.1.2.tgz",
"integrity": "sha512-NuTdtt/UmuxIHS4hfdyv3BP5JiWikNkr81hFHXDScXlH0GUMdRSY/B5T9vDvbXDY/esMLFnIAXoFVDLsGinhpw==",
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/@iconify-json/ph/-/ph-1.1.5.tgz",
"integrity": "sha512-iLXq3nohfGge22gL2tZmQ2WHBKkKkIbGWrkuyC8arckS4PWaONyh4A+uDPtSek9QbYDvi9AE2+NxWkNZhANotA==",
"dev": true,
"requires": {
"@iconify/types": "*"

3
packages/nc-gui/package.json

@ -84,13 +84,14 @@
"@iconify-json/clarity": "^1.1.4",
"@iconify-json/eva": "^1.1.2",
"@iconify-json/ic": "^1.1.7",
"@iconify-json/ion": "^1.1.8",
"@iconify-json/la": "^1.1.2",
"@iconify-json/logos": "^1.1.14",
"@iconify-json/lucide": "^1.1.36",
"@iconify-json/material-symbols": "^1.1.8",
"@iconify-json/mdi": "^1.1.25",
"@iconify-json/mi": "^1.1.2",
"@iconify-json/ph": "^1.1.2",
"@iconify-json/ph": "^1.1.5",
"@iconify-json/ri": "^1.1.3",
"@iconify-json/simple-icons": "^1.1.29",
"@iconify-json/system-uicons": "^1.1.4",

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

@ -5,6 +5,7 @@ import {
computed,
definePageMeta,
extractSdkResponseErrorMsg,
iconMap,
isDrawerOrModalExist,
isMac,
message,
@ -315,12 +316,11 @@ useEventListener(document, 'keydown', async (e: KeyboardEvent) => {
</template>
</a-tooltip>
<div v-else class="text-md font-semibold truncate capitalize">{{ project.title }}</div>
<MdiChevronDown class="min-w-[17px] group-hover:text-accent text-md" />
<component :is="iconMap.arrowDown" class="min-w-[17px] group-hover:text-accent text-md" />
</template>
<template v-else>
<MdiFolder class="text-primary cursor-pointer transform hover:scale-105 text-2xl" />
<component :is="iconMap.folder" class="text-primary cursor-pointer transform hover:scale-105 text-2xl" />
</template>
</div>
@ -329,7 +329,7 @@ useEventListener(document, 'keydown', async (e: KeyboardEvent) => {
<a-menu-item-group>
<template #title>
<div class="group select-none flex items-center gap-4 py-1">
<MdiFolder class="group-hover:text-accent text-xl" />
<component :is="iconMap.folder" class="group-hover:text-accent text-xl" />
<div class="flex flex-col">
<div class="text-lg group-hover:(!text-primary) font-semibold capitalize">
@ -352,7 +352,7 @@ useEventListener(document, 'keydown', async (e: KeyboardEvent) => {
class="nc-project-menu-item group"
@click.stop="copyProjectInfo"
>
<MdiContentCopy class="group-hover:text-accent" />
<component :is="iconMap.copy" class="group-hover:text-accent" />
{{ $t('activity.account.projInfo') }}
</div>
</a-menu-item>
@ -367,7 +367,7 @@ useEventListener(document, 'keydown', async (e: KeyboardEvent) => {
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-accent" />
<component :is="iconMap.json" class="group-hover:text-accent" />
{{ $t('activity.account.swagger') }}
</div>
</a-menu-item>
@ -380,7 +380,7 @@ useEventListener(document, 'keydown', async (e: KeyboardEvent) => {
class="nc-project-menu-item group"
@click.stop="copyAuthToken"
>
<MdiScriptTextKeyOutline class="group-hover:text-accent" />
<component :is="iconMap.copy" class="group-hover:text-accent" />
{{ $t('activity.account.authToken') }}
</div>
</a-menu-item>
@ -395,7 +395,7 @@ useEventListener(document, 'keydown', async (e: KeyboardEvent) => {
class="nc-project-menu-item group"
@click="toggleDialog(true, 'teamAndAuth')"
>
<MdiCog class="group-hover:text-accent" />
<component :is="iconMap.settings" class="group-hover:text-accent" />
{{ $t('title.teamAndSettings') }}
</div>
</a-menu-item>
@ -417,7 +417,7 @@ useEventListener(document, 'keydown', async (e: KeyboardEvent) => {
<a-sub-menu key="theme">
<template #title>
<div class="nc-project-menu-item group">
<ClarityImageLine class="group-hover:text-accent" />
<component :is="iconMap.image" class="group-hover:text-accent" />
{{ $t('activity.account.themes') }}
<div class="flex-1" />
@ -492,7 +492,7 @@ useEventListener(document, 'keydown', async (e: KeyboardEvent) => {
<a-sub-menu v-if="isUIAllowed('previewAs') && !isMobileMode" key="preview-as">
<template #title>
<div v-e="['c:navdraw:preview-as']" class="nc-project-menu-item group">
<MdiFileEyeOutline class="group-hover:text-accent" />
<component :is="iconMap.preview" class="group-hover:text-accent" />
{{ $t('activity.previewAs') }}
<div class="flex-1" />
@ -516,7 +516,7 @@ useEventListener(document, 'keydown', async (e: KeyboardEvent) => {
>
<template #title>
<div class="nc-project-menu-item group">
<MaterialSymbolsTranslate class="group-hover:text-accent nc-language" />
<component :is="iconMap.translate" class="group-hover:text-accent nc-language" />
{{ $t('labels.language') }}
<div class="flex items-center text-gray-400 text-xs">(Community Translated)</div>
<div class="flex-1" />
@ -537,7 +537,7 @@ useEventListener(document, 'keydown', async (e: KeyboardEvent) => {
<a-sub-menu key="account">
<template #title>
<div class="nc-project-menu-item group">
<MdiAccount class="group-hover:text-accent" />
<component :is="iconMap.account" class="group-hover:text-accent" />
{{ $t('labels.account') }}
<div class="flex-1" />
@ -555,7 +555,7 @@ useEventListener(document, 'keydown', async (e: KeyboardEvent) => {
class="nc-project-menu-item group !no-underline"
to="/account/users"
>
<MdiAt class="mt-1 group-hover:text-accent" />&nbsp;
<component :is="iconMap.at" class="mt-1 group-hover:text-accent" />&nbsp;
<div class="prose-sm group-hover:text-primary">
<div>Account</div>
<div class="text-xs text-gray-500">{{ email }}</div>
@ -565,7 +565,7 @@ useEventListener(document, 'keydown', async (e: KeyboardEvent) => {
<a-menu-item key="1" class="!rounded-b">
<div v-e="['a:navbar:user:sign-out']" class="nc-project-menu-item group" @click="logout">
<MdiLogout class="group-hover:(!text-accent)" />&nbsp;
<component :is="iconMap.signout" class="group-hover:(!text-accent)" />&nbsp;
<span class="prose-sm nc-user-menu-signout">
{{ $t('general.signOut') }}
@ -582,7 +582,8 @@ useEventListener(document, 'keydown', async (e: KeyboardEvent) => {
<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
<component
:is="iconMap.sidebarMinimise"
v-e="['c:grid:toggle-navdraw']"
class="cursor-pointer transform transition-transform duration-500"
:class="{ 'rotate-180': !isOpen }"

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

@ -15,11 +15,11 @@ provide(TabMetaInj, activeTab)
const icon = (tab: TabItem) => {
switch (tab.type) {
case TabType.TABLE:
return iconMap['mdi-table-large']
return iconMap.table
case TabType.VIEW:
return iconMap['mdi-eye-circle-outline']
return iconMap.eye
case TabType.AUTH:
return iconMap['mdi-account-group']
return iconMap.users
}
}
@ -42,9 +42,10 @@ const hideSidebarOnClickOrTouchIfMobileMode = () => {
<div class="flex items-end !min-h-[var(--header-height)] !bg-white-500 nc-tab-bar">
<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 mb-1"
class="nc-sidebar-left-toggle-icon hover:after:(bg-primary bg-opacity-75) group nc-sidebar-add-row py-2 px-3"
>
<MdiMenu
<component
:is="iconMap.sidebarMinimise"
v-e="['c:grid:toggle-navdraw']"
class="cursor-pointer transform transition-transform duration-500 text-gray-500/80 hover:text-gray-500"
:class="{ 'rotate-180': !isOpen }"
@ -82,7 +83,7 @@ const hideSidebarOnClickOrTouchIfMobileMode = () => {
<div v-if="isLoading" class="flex items-center gap-2 ml-3 text-gray-200" data-testid="nc-loading">
{{ $t('general.loading') }}
<MdiLoading class="animate-infinite animate-spin" />
<component :is="iconMap.loading" class="animate-infinite animate-spin" />
</div>
</div>

4
packages/nc-gui/pages/[projectType]/form/[viewId]/index/index.vue

@ -3,7 +3,7 @@ import type { ColumnType } from 'nocodb-sdk'
import { RelationTypes, UITypes, isVirtualCol } from 'nocodb-sdk'
import { ref } from 'vue'
import { StreamBarcodeReader } from 'vue-barcode-reader'
import { useSharedFormStoreOrThrow } from '#imports'
import { iconMap, useSharedFormStoreOrThrow } from '#imports'
const { sharedFormView, submitForm, v$, formState, notFound, formColumns, submitted, secondsRemain, isLoading } =
useSharedFormStoreOrThrow()
@ -175,7 +175,7 @@ const onDecode = async (scannedCodeValue: string) => {
@click="showCodeScannerForFieldTitle(field.title)"
>
<div class="flex items-center gap-1">
<mdi-qrcode-scan class="h-5 w-5" />
<component :is="iconMap.qrCodeScan" class="h-5 w-5" />
</div>
</a-button>
</div>

15
packages/nc-gui/pages/[projectType]/form/[viewId]/index/survey.vue

@ -6,6 +6,7 @@ import {
DropZoneRef,
IsSurveyFormInj,
computed,
iconMap,
isValidURL,
onKeyStroke,
onMounted,
@ -365,11 +366,12 @@ onMounted(() => {
</Transition>
<Transition name="slide-right" mode="out-in">
<MdiCloseCircleOutline
<component
:is="iconMap.closeCircle"
v-if="v$.localState[field.title]?.$error || columnValidationError"
class="text-red-500 md:text-md"
/>
<MdiCheck v-else class="text-white md:text-md" />
<component :is="iconMap.check" v-else class="text-white md:text-md" />
</Transition>
</button>
</a-tooltip>
@ -443,7 +445,11 @@ onMounted(() => {
data-testid="nc-survey-form__icon-prev"
@click="goPrevious()"
>
<MdiChevronLeft :class="isFirst ? 'text-gray-300' : 'group-hover:text-accent'" class="text-2xl md:text-md" />
<component
:is="iconMap.chevronLeft"
:class="isFirst ? 'text-gray-300' : 'group-hover:text-accent'"
class="text-2xl md:text-md"
/>
</button>
</a-tooltip>
@ -462,7 +468,8 @@ onMounted(() => {
data-testid="nc-survey-form__icon-next"
@click="goNext()"
>
<MdiChevronRight
<component
:is="iconMap.chevronRight"
:class="[isLast || v$.localState[field.title]?.$error ? 'text-gray-300' : 'group-hover:text-accent']"
class="text-2xl md:text-md"
/>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save