Browse Source

Merge branch 'develop' into nc-fix/title-len

pull/7092/head
աɨռɢӄաօռɢ 1 year ago
parent
commit
117451285e
  1. 12
      packages/nc-gui/components/cell/TextArea.vue
  2. 6
      packages/nc-gui/components/dashboard/TreeView/ProjectNode.vue
  3. 21
      packages/nc-gui/components/dashboard/TreeView/TableNode.vue
  4. 7
      packages/nc-gui/components/dashboard/TreeView/ViewsNode.vue
  5. 17
      packages/nc-gui/components/dashboard/settings/UIAcl.vue
  6. 2
      packages/nc-gui/components/erd/TableNode.vue
  7. 2
      packages/nc-gui/components/nc/Switch.vue
  8. 9
      packages/nc-gui/components/project/View.vue
  9. 1
      packages/nc-gui/components/smartsheet/Cell.vue
  10. 13
      packages/nc-gui/components/smartsheet/Toolbar.vue
  11. 8
      packages/nc-gui/components/smartsheet/column/LinkedToAnotherRecordOptions.vue
  12. 19
      packages/nc-gui/components/smartsheet/details/Fields.vue
  13. 12
      packages/nc-gui/components/smartsheet/expanded-form/index.vue
  14. 12
      packages/nc-gui/components/smartsheet/header/Cell.vue
  15. 11
      packages/nc-gui/components/smartsheet/header/VirtualCell.vue
  16. 9
      packages/nc-gui/components/smartsheet/toolbar/CreateSort.vue
  17. 10
      packages/nc-gui/components/smartsheet/toolbar/FieldListAutoCompleteDropdown.vue
  18. 27
      packages/nc-gui/components/smartsheet/toolbar/FieldsMenu.vue
  19. 6
      packages/nc-gui/components/smartsheet/toolbar/OpenedViewAction.vue
  20. 5
      packages/nc-gui/components/smartsheet/toolbar/SearchData.vue
  21. 2
      packages/nc-gui/components/smartsheet/toolbar/SortListMenu.vue
  22. 4
      packages/nc-gui/composables/useData.ts
  23. 4
      packages/nc-gui/composables/useViewData.ts
  24. 88
      packages/nc-gui/lang/es.json
  25. 364
      packages/nc-gui/lang/zh-Hans.json
  26. 50
      packages/nc-gui/package.json
  27. 2
      packages/nc-gui/pages/signin.vue
  28. 2
      packages/nc-lib-gui/package.json
  29. 378
      packages/noco-docs/docs/070.fields/040.field-types/060.formula/020.numeric-functions.md
  30. 228
      packages/noco-docs/docs/070.fields/040.field-types/060.formula/030.string-functions.md
  31. 101
      packages/noco-docs/docs/070.fields/040.field-types/060.formula/040.date-functions.md
  32. 76
      packages/noco-docs/docs/070.fields/040.field-types/060.formula/050.conditional-expressions.md
  33. 12
      packages/nocodb-sdk/package.json
  34. 2837
      packages/nocodb-sdk/pnpm-lock.yaml
  35. 6
      packages/nocodb/package.json
  36. 8
      packages/nocodb/src/controllers/data-table.controller.ts
  37. 42
      packages/nocodb/src/db/BaseModelSqlv2.ts
  38. 9
      packages/nocodb/src/db/sortV2.ts
  39. 2
      packages/nocodb/src/helpers/getAst.ts
  40. 2
      packages/nocodb/src/models/Store.ts
  41. 8
      packages/nocodb/src/services/data-table.service.ts
  42. 1144
      pnpm-lock.yaml
  43. 2
      scripts/pkg-executable/package.json

12
packages/nc-gui/components/cell/TextArea.vue

@ -1,16 +1,21 @@
<script setup lang="ts">
import type { VNodeRef } from '@vue/runtime-core'
import {
ActiveCellInj,
ColumnInj,
EditColumnInj,
EditModeInj,
IsExpandedFormOpenInj,
IsFormInj,
ReadonlyInj,
RowHeightInj,
computed,
iconMap,
inject,
onClickOutside,
ref,
useGlobal,
useVModel,
watch,
} from '#imports'
const props = defineProps<{
@ -61,9 +66,8 @@ const height = computed(() => {
const isVisible = ref(false)
const inputWrapperRef = ref<HTMLElement | null>(null)
const inputRef = ref<HTMLTextAreaElement | null>(null)
const active = inject(ActiveCellInj, ref(false))
const inputRef = ref<HTMLTextAreaElement | null>(null)
const readOnly = inject(ReadonlyInj)

6
packages/nc-gui/components/dashboard/TreeView/ProjectNode.vue

@ -424,15 +424,17 @@ function projectDelete() {
@keyup.esc="updateProjectTitle"
@blur="updateProjectTitle"
/>
<span
<NcTooltip
v-else
class="nc-sidebar-node-title capitalize text-ellipsis overflow-hidden select-none"
:style="{ wordBreak: 'keep-all', whiteSpace: 'nowrap', display: 'inline' }"
:class="{ 'text-black font-semibold': activeProjectId === base.id && baseViewOpen }"
@click="onProjectClick(base)"
>
<template #title>{{ base.title }}</template>
<span @click="onProjectClick(base)">
{{ base.title }}
</span>
</NcTooltip>
<div :class="{ 'flex flex-grow h-full': !editMode }" @click="onProjectClick(base)"></div>
<NcDropdown v-if="!isSharedBase" v-model:visible="isOptionsOpen" :trigger="['click']">

21
packages/nc-gui/components/dashboard/TreeView/TableNode.vue

@ -149,20 +149,15 @@ const isTableOpened = computed(() => {
:class="[`nc-base-tree-tbl nc-base-tree-tbl-${table.title}`]"
:data-active="openedTableId === table.id"
>
<GeneralTooltip
class="nc-tree-item-inner nc-sidebar-node pl-11 pr-0.75 mb-0.25 rounded-md h-7.1 w-full group cursor-pointer hover:bg-gray-200"
<div
v-e="['a:table:open']"
class="table-context flex items-center gap-1 h-full nc-tree-item-inner nc-sidebar-node pl-11 pr-0.75 mb-0.25 rounded-md h-7.1 w-full group cursor-pointer hover:bg-gray-200"
:class="{
'hover:bg-gray-200': openedTableId !== table.id,
'pl-12 xs:(pl-14)': sourceIndex !== 0,
'pl-6.5': sourceIndex === 0,
'!bg-primary-selected': isTableOpened,
}"
modifier-key="Alt"
>
<template #title>{{ table.table_name }}</template>
<div
v-e="['a:table:open']"
class="table-context flex items-center gap-1 h-full"
:data-testid="`nc-tbl-side-node-${table.title}`"
@contextmenu="setMenuContext('table', table)"
@click="onOpenTable"
@ -236,9 +231,9 @@ const isTableOpened = computed(() => {
</div>
</div>
</div>
<NcTooltip class="nc-tbl-title nc-sidebar-node-title text-ellipsis w-full overflow-hidden select-none">
<template #title>{{ table.title }}</template>
<span
class="nc-tbl-title nc-sidebar-node-title text-ellipsis overflow-hidden select-none"
:class="{
'text-black !font-medium': isTableOpened,
}"
@ -247,12 +242,12 @@ const isTableOpened = computed(() => {
>
{{ table.title }}
</span>
</NcTooltip>
<div class="flex flex-grow h-full"></div>
<div class="flex flex-row items-center">
<div
v-if="
!isSharedBase &&
(isUIAllowed('tableRename', { roles: baseRole }) || isUIAllowed('tableDelete', { roles: baseRole }))
!isSharedBase && (isUIAllowed('tableRename', { roles: baseRole }) || isUIAllowed('tableDelete', { roles: baseRole }))
"
v-e="['c:table:option']"
>
@ -313,7 +308,7 @@ const isTableOpened = computed(() => {
:table-id="table.id"
:base-id="base.id"
/>
</GeneralTooltip>
<DashboardTreeViewViewsList v-if="isExpanded" :table-id="table.id" :base-id="base.id" />
</div>
</template>

7
packages/nc-gui/components/dashboard/TreeView/ViewsNode.vue

@ -232,10 +232,9 @@ watch(isDropdownOpen, async () => {
@blur="onRename"
@keydown.stop="onKeyDown($event)"
/>
<NcTooltip v-else class="nc-sidebar-node-title text-ellipsis overflow-hidden select-none w-full">
<template #title> {{ vModel.alias || vModel.title }}</template>
<div
v-else
class="nc-sidebar-node-title text-ellipsis overflow-hidden select-none w-full"
data-testid="sidebar-view-title"
:class="{
'font-medium': activeView?.id === vModel.id,
@ -244,7 +243,7 @@ watch(isDropdownOpen, async () => {
>
{{ vModel.alias || vModel.title }}
</div>
</NcTooltip>
<div class="flex-1" />
<template v-if="!isEditing && !isLocked && isUIAllowed('viewCreateOrEdit')">

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

@ -147,7 +147,10 @@ const toggleSelectAll = (role: Role) => {
<template>
<div class="flex flex-row w-full items-center justify-center">
<div class="flex flex-col w-[900px]">
<span class="mb-4 first-letter:capital font-bold"> UI ACL : {{ base.title }} </span>
<NcTooltip class="mb-4 first-letter:capital font-bold max-w-100 truncate">
<template #title>{{ base.title }}</template>
<span> UI ACL : {{ base.title }} </span>
</NcTooltip>
<div class="flex flex-row items-center w-full mb-4 gap-2 justify-between">
<a-input v-model:value="searchInput" :placeholder="$t('placeholder.searchModels')" class="nc-acl-search !w-[400px]">
<template #prefix>
@ -208,9 +211,10 @@ const toggleSelectAll = (role: Role) => {
<div class="min-w-5 flex items-center justify-center">
<GeneralTableIcon :meta="{ meta: record.table_meta, type: record.ptype }" class="text-gray-500" />
</div>
<GeneralTruncateText>
<span class="overflow-ellipsis min-w-0 shrink-1">{{ record._ptn }}</span>
</GeneralTruncateText>
<NcTooltip class="overflow-ellipsis min-w-0 shrink-1 truncate">
<template #title>{{ record._ptn }}</template>
<span>{{ record._ptn }}</span>
</NcTooltip>
</div>
</div>
@ -219,7 +223,10 @@ const toggleSelectAll = (role: Role) => {
<div class="min-w-5 flex items-center justify-center">
<GeneralViewIcon :meta="record" class="text-gray-500"></GeneralViewIcon>
</div>
<span class="overflow-ellipsis min-w-0 shrink-1">{{ record.title }}</span>
<NcTooltip class="overflow-ellipsis min-w-0 shrink-1 truncate">
<template #title>{{ record.title }}</template>
<span>{{ record.title }}</span>
</NcTooltip>
</div>
</div>

2
packages/nc-gui/components/erd/TableNode.vue

@ -54,7 +54,7 @@ watch(
</template>
<div
class="relative h-full flex flex-col justify-center bg-white min-w-16 min-h-8 rounded-lg nc-erd-table-node"
class="relative h-full max-w-76 flex flex-col justify-center bg-white min-w-16 min-h-8 rounded-lg nc-erd-table-node"
:class="[
`nc-erd-table-node-${table.table_name}`,
showSkeleton ? 'cursor-pointer items-center min-h-200px min-w-300px' : '',

2
packages/nc-gui/components/nc/Switch.vue

@ -12,7 +12,7 @@ const onChange = (e: boolean) => {
<template>
<a-switch v-model:checked="checked" :disabled="props.disabled" class="nc-switch" size="small" @change="onChange"> </a-switch>
<span class="cursor-pointer pl-2" @click="checked = !checked">
<span v-if="$slots.default" class="cursor-pointer pl-2" @click="checked = !checked">
<slot />
</span>
</template>

9
packages/nc-gui/components/project/View.vue

@ -68,16 +68,19 @@ watch(
<template>
<div class="h-full nc-base-view">
<div
class="flex flex-row pl-2 pr-2 border-b-1 border-gray-200 justify-between w-full"
class="flex flex-row pl-2 pr-2 gap-1 border-b-1 border-gray-200 justify-between w-full"
:class="{ 'nc-table-toolbar-mobile': isMobileMode, 'h-[var(--topbar-height)]': !isMobileMode }"
>
<div class="flex flex-row items-center gap-x-3">
<GeneralOpenLeftSidebarBtn />
<div class="flex flex-row items-center h-full gap-x-2.5">
<GeneralProjectIcon :type="openedProject?.type" />
<div class="flex font-medium text-sm capitalize">
<NcTooltip class="flex font-medium text-sm capitalize truncate max-w-150">
<template #title> {{ openedProject?.title }}</template>
<span class="truncate">
{{ openedProject?.title }}
</div>
</span>
</NcTooltip>
</div>
</div>
<LazyGeneralShareProject />

1
packages/nc-gui/components/smartsheet/Cell.vue

@ -8,7 +8,6 @@ import {
EditModeInj,
IsExpandedFormOpenInj,
IsFormInj,
IsLockedInj,
IsPublicInj,
IsSurveyFormInj,
NavigateDir,

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

@ -1,5 +1,14 @@
<script setup lang="ts">
import { IsPublicInj, inject, ref, useRoles, useSharedView, useSmartsheetStoreOrThrow, useViewsStore } from '#imports'
import {
IsPublicInj,
inject,
ref,
storeToRefs,
useGlobal,
useSharedView,
useSmartsheetStoreOrThrow,
useViewsStore,
} from '#imports'
const { isGrid, isGallery, isKanban, isMap } = useSmartsheetStoreOrThrow()
@ -9,8 +18,6 @@ const { isViewsLoading } = storeToRefs(useViewsStore())
const { isMobileMode } = useGlobal()
const { isUIAllowed } = useRoles()
const { allowCSVDownload } = useSharedView()
</script>

8
packages/nc-gui/components/smartsheet/column/LinkedToAnotherRecordOptions.vue

@ -78,12 +78,14 @@ const isLinks = computed(() => vModel.value.uidt === UITypes.Links)
@change="onDataTypeChange"
>
<a-select-option v-for="table of refTables" :key="table.title" :value="table.id">
<div class="flex items-center gap-2">
<div class="flex w-full items-center gap-2">
<div class="min-w-5 flex items-center justify-center">
<GeneralTableIcon :meta="table" class="text-gray-500" />
</div>
<span class="overflow-ellipsis min-w-0 shrink-1">{{ table.title }}</span>
<NcTooltip class="flex-1 truncate">
<template #title>{{ table.title }}</template>
<span>{{ table.title }}</span>
</NcTooltip>
</div>
</a-select-option>
</a-select>

19
packages/nc-gui/components/smartsheet/details/Fields.vue

@ -769,8 +769,8 @@ const onFieldOptionUpdate = () => {
</NcTooltip>
</div>
</div>
<div class="flex flex-row rounded-lg border-1 border-gray-200">
<div ref="fieldsListWrapperDomRef" class="nc-scrollbar-md !overflow-auto w-full flex-grow-1 nc-fields-height">
<div class="flex flex-row rounded-lg border-1 overflow-clip border-gray-200">
<div ref="fieldsListWrapperDomRef" class="nc-scrollbar-md !overflow-auto flex-1 flex-grow-1 nc-fields-height">
<Draggable v-model="fields" :disabled="isLocked" item-key="id" @change="onMove($event)">
<template #item="{ element: field }">
<div
@ -814,14 +814,17 @@ const onFieldOptionUpdate = () => {
'text-brand-500': compareCols(field, activeField),
}"
/>
<span
<NcTooltip
:class="{
'text-brand-500': compareCols(field, activeField),
}"
class="truncate max-w-64"
class="truncate flex-1"
>
<template #title> {{ fieldState(field)?.title || field.title }} </template>
<span>
{{ fieldState(field)?.title || field.title }}
</span>
</NcTooltip>
</div>
<div class="flex items-center justify-end gap-1">
<div class="flex items-center">
@ -970,13 +973,17 @@ const onFieldOptionUpdate = () => {
'text-brand-500': compareCols(displayColumn, activeField),
}"
/>
<span
<NcTooltip
class="truncate flex-1"
:class="{
'text-brand-500': compareCols(displayColumn, activeField),
}"
>
<template #title> {{ fieldState(displayColumn)?.title || displayColumn.title }} </template>
<span>
{{ fieldState(displayColumn)?.title || displayColumn.title }}
</span>
</NcTooltip>
</div>
<div class="flex items-center justify-end gap-1">
<div class="flex items-center">
@ -1072,7 +1079,7 @@ const onFieldOptionUpdate = () => {
</Draggable>
</div>
<Transition v-if="!changingField" name="slide-fade">
<div class="border-gray-200 border-l-1 rounded-r-xl h-[calc(100vh-(var(--topbar-height)*3.85))]">
<div class="border-gray-200 border-l-1 nc-scrollbar-md nc-fields-height !overflow-y-auto">
<SmartsheetColumnEditOrAddProvider
v-if="activeField"
class="p-4 w-[25rem]"

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

@ -645,11 +645,11 @@ export default {
<template v-if="isLoading">
<div
v-if="isMobileMode"
class="!h-8.5 !xs:h-12 !xs:bg-white !sm:mr-21 !w-60 mt-0.75 !rounded-lg !overflow-hidden"
class="!h-8.5 !xs:h-12 !xs:bg-white sm:mr-21 w-60 mt-0.75 !rounded-lg !overflow-hidden"
></div>
<a-skeleton-input
v-else
class="!h-8.5 !xs:h-9.5 !xs:bg-white !sm:mr-21 !w-60 mt-0.75 !rounded-lg !overflow-hidden"
class="!h-8.5 !xs:h-9.5 !xs:bg-white sm:mr-21 !w-60 mt-0.75 !rounded-lg !overflow-hidden"
active
size="small"
/>
@ -658,7 +658,7 @@ export default {
<SmartsheetDivDataCell
v-if="col.title"
:ref="i ? null : (el: any) => (cellWrapperEl = el)"
class="bg-white rounded-lg !w-[20rem] !xs:w-full border-1 border-gray-200 overflow-hidden px-1 sm:min-h-[35px] xs:min-h-13 flex items-center relative"
class="bg-white rounded-lg w-80 xs:w-full border-1 border-gray-200 overflow-hidden px-1 sm:min-h-[35px] xs:min-h-13 flex items-center relative"
:class="{
'!bg-gray-50 !px-0 !select-text': isReadOnlyVirtualCell(col),
}"
@ -720,11 +720,11 @@ export default {
<template v-if="isLoading">
<div
v-if="isMobileMode"
class="!h-8.5 !xs:h-9.5 !xs:bg-white !sm:mr-21 !w-60 mt-0.75 !rounded-lg !overflow-hidden"
class="!h-8.5 !xs:h-9.5 !xs:bg-white sm:mr-21 w-60 mt-0.75 !rounded-lg !overflow-hidden"
></div>
<a-skeleton-input
v-else
class="!h-8.5 !xs:h-12 !xs:bg-white !sm:mr-21 !w-60 mt-0.75 !rounded-lg !overflow-hidden"
class="!h-8.5 !xs:h-12 !xs:bg-white sm:mr-21 w-60 mt-0.75 !rounded-lg !overflow-hidden"
active
size="small"
/>
@ -733,7 +733,7 @@ export default {
<LazySmartsheetDivDataCell
v-if="col.title"
:ref="i ? null : (el: any) => (cellWrapperEl = el)"
class="!bg-white rounded-lg !w-[20rem] border-1 overflow-hidden border-gray-200 px-1 sm:min-h-[35px] xs:min-h-13 flex items-center relative"
class="bg-white rounded-lg w-80 border-1 overflow-hidden border-gray-200 px-1 sm:min-h-[35px] xs:min-h-13 flex items-center relative"
>
<LazySmartsheetVirtualCell
v-if="isVirtualCol(col)"

12
packages/nc-gui/components/smartsheet/header/Cell.vue

@ -90,18 +90,22 @@ const onClick = (e: Event) => {
'self-start': isForm || isSurveyForm,
}"
/>
<div
<NcTooltip
v-if="column"
class="name pl-1"
:class="{
'cursor-pointer pt-0.25': !isForm && isUIAllowed('fieldEdit') && !hideMenu && !isExpandedForm,
'cursor-default': isForm || !isUIAllowed('fieldEdit') || hideMenu,
'!truncate': !isForm,
'truncate': !isForm,
}"
:data-test-id="column.title"
class="name pl-1"
placement="bottom"
>
<template #title> {{ column.title }} </template>
<div :class="{ truncate: !isForm }" :data-test-id="column.title">
{{ column.title }}
</div>
</NcTooltip>
<span v-if="(column.rqd && !column.cdf) || required" class="text-red-500">&nbsp;*</span>

11
packages/nc-gui/components/smartsheet/header/VirtualCell.vue

@ -92,6 +92,7 @@ const tooltipMsg = computed(() => {
if (!column.value) {
return ''
}
if (isHm(column.value)) {
return `'${tableTile.value}' ${t('labels.hasMany')} '${relatedTableTitle.value}'`
} else if (isMm(column.value)) {
@ -110,7 +111,7 @@ const tooltipMsg = computed(() => {
} else if (isRollup(column.value)) {
return `'${childColumn.value.title}' of '${relatedTableTitle.value}' (${childColumn.value.uidt})`
}
return ''
return column?.value?.title || ''
})
const columnOrder = ref<Pick<ColumnReqType, 'column_order'> | null>(null)
@ -153,14 +154,14 @@ const openDropDown = (e: Event) => {
>
<LazySmartsheetHeaderVirtualCellIcon v-if="column && !props.hideIcon" />
<a-tooltip placement="bottom">
<template v-if="!isForm && !isExpandedForm" #title>
<NcTooltip placement="bottom" class="truncate name pl-1">
<template #title>
{{ tooltipMsg }}
</template>
<span class="name truncate pl-1" :class="{ truncate: !isForm }" :data-test-id="column.title">
<span :data-test-id="column.title">
{{ column.title }}
</span>
</a-tooltip>
</NcTooltip>
<span v-if="isVirtualColRequired(column, meta?.columns || []) || required" class="text-red-500">&nbsp;*</span>

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

@ -91,7 +91,7 @@ const onArrowUp = () => {
<div class="flex pb-3 px-4 border-b-1 border-gray-100">
<input ref="inputRef" v-model="search" class="w-full focus:outline-none" :placeholder="$t('msg.selectFieldToSort')" />
</div>
<div class="flex-col w-full max-h-100 nc-scrollbar-md !overflow-y-auto">
<div class="flex-col w-full max-h-100 max-w-76 nc-scrollbar-md !overflow-y-auto">
<div v-if="!options.length" class="flex text-gray-500 px-4 py-2.25">{{ $t('general.empty') }}</div>
<div
v-for="(option, index) in options"
@ -104,9 +104,12 @@ const onArrowUp = () => {
@click="onClick(option)"
>
<SmartsheetHeaderIcon :column="option" />
<div>
<NcTooltip class="truncate">
<template #title> {{ option.title }}</template>
<span>
{{ option.title }}
</div>
</span>
</NcTooltip>
</div>
</div>
</div>

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

@ -82,13 +82,15 @@ if (!localValue.value && allowEmpty !== true) {
<a-select-option v-for="option in options" :key="option.value" :label="option.label" :value="option.value">
<div class="flex gap-2 items-center items-center h-full">
<component :is="option.icon" class="min-w-5 !mx-0" />
<div
class="min-w-0 text-ellipsis overflow-hidden select-none"
<NcTooltip
:style="{ wordBreak: 'keep-all', whiteSpace: 'nowrap', display: 'inline' }"
class="max-w-[15rem] truncate select-none"
>
<template #title> {{ option.label }}</template>
<span>
{{ option.label }}
</div>
</span>
</NcTooltip>
</div>
</a-select-option>
</NcSelect>

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

@ -371,7 +371,7 @@ useMenuCloseOnEsc(open)
<component :is="iconMap.drag" class="cursor-move !h-3.75 text-gray-600 mr-1" />
<div
v-e="['a:fields:show-hide']"
class="flex flex-row items-center justify-between w-full cursor-pointer ml-1"
class="flex flex-row items-center w-full truncate cursor-pointer ml-1"
@click="
() => {
field.show = !field.show
@ -379,15 +379,13 @@ useMenuCloseOnEsc(open)
}
"
>
<div class="flex items-center -ml-0.75">
<component :is="getIcon(metaColumnById[field.fk_column_id])" />
<NcTooltip :disabled="field.title.length < 30">
<NcTooltip :disabled="field.title.length < 30" class="flex-1 px-1 truncate">
<template #title>
{{ field.title }}
</template>
<span class="mx-0.65 break-all line-clamp-1">{{ field.title }}</span>
<span>{{ field.title }}</span>
</NcTooltip>
</div>
<NcSwitch v-e="['a:fields:show-hide']" :checked="field.show" :disabled="field.isViewEssentialField" />
</div>
@ -399,7 +397,7 @@ useMenuCloseOnEsc(open)
<div
v-if="gridDisplayValueField && filteredFieldList[0].title.toLowerCase().includes(filterQuery.toLowerCase())"
:key="`pv-${gridDisplayValueField.id}`"
class="pl-7.5 pr-2.1 py-2 flex flex-row items-center border-1 border-gray-200"
class="pl-7.4 pr-2 py-2 flex flex-row items-center border-1 border-gray-200"
:class="{
'rounded-t-lg': filteredFieldList.length > 1,
'rounded-lg': filteredFieldList.length === 1,
@ -407,23 +405,14 @@ useMenuCloseOnEsc(open)
:data-testid="`nc-fields-menu-${gridDisplayValueField.title}`"
@click.stop
>
<div class="flex flex-row items-center justify-between w-full">
<div class="flex items">
<a-tooltip placement="bottom">
<template #title>
<span class="text-sm">$t('title.displayValue') </span>
</template>
</a-tooltip>
<div class="flex items-center">
<component :is="getIcon(metaColumnById[filteredFieldList[0].fk_column_id as string])" />
<NcTooltip :disabled="filteredFieldList?.[0]?.title?.length < 30" class="px-1 flex-1 truncate">
<template #title>{{ filteredFieldList[0].title }}</template>
<span>{{ filteredFieldList[0].title }}</span>
</div>
</div>
</NcTooltip>
<NcSwitch v-e="['a:fields:show-hide']" :checked="true" :disabled="true" />
</div>
</div>
</template>
</Draggable>
</div>

6
packages/nc-gui/components/smartsheet/toolbar/OpenedViewAction.vue

@ -173,7 +173,7 @@ function openDeleteDialog() {
>
<div
v-e="['c:breadcrumb:view-actions']"
class="truncate nc-active-view-title !hover:(bg-gray-100 text-gray-800) ml-0.25 pl-1 pr-0.25 rounded-md py-1 cursor-pointer"
class="truncate nc-active-view-title flex gap-0.5 items-center !hover:(bg-gray-100 text-gray-800) ml-1 pl-1 pr-0.25 rounded-md py-1 cursor-pointer"
:class="{
'max-w-2/5': !isSharedBase && !isMobileMode && activeView?.is_default,
'max-w-3/5': !isSharedBase && !isMobileMode && !activeView?.is_default,
@ -182,14 +182,16 @@ function openDeleteDialog() {
'text-gray-800 font-medium': !activeView?.is_default,
}"
>
<NcTooltip class="truncate xs:pl-1.25 flex-1 text-inherit">
<template #title>{{ activeView?.is_default ? $t('title.defaultView') : activeView?.title }} </template>
<span
class="truncate xs:pl-1.25 text-inherit"
:class="{
'max-w-28/100': !isMobileMode,
}"
>
{{ activeView?.is_default ? $t('title.defaultView') : activeView?.title }}
</span>
</NcTooltip>
<GeneralIcon icon="arrowDown" class="ml-1" />
</div>
<template #overlay>

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

@ -114,7 +114,10 @@ watch(columns, () => {
<a-select-option v-for="op of columns" :key="op.value" v-e="['c:search:field:select']" :value="op.value">
<div class="text-[0.75rem] flex items-center -ml-1 gap-2">
<SmartsheetHeaderIcon class="text-sm" :column="op.column" />
{{ op.label }}
<NcTooltip class="truncate" placement="top">
<template #title>{{ op.label }}</template>
<span>{{ op.label }}</span>
</NcTooltip>
</div>
</a-select-option>
</a-select>

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

@ -138,7 +138,7 @@ onMounted(() => {
<template v-for="(sort, i) of sorts" :key="i">
<SmartsheetToolbarFieldListAutoCompleteDropdown
v-model="sort.fk_column_id"
class="flex caption nc-sort-field-select min-w-40 flex-grow"
class="flex caption nc-sort-field-select w-44 flex-grow"
:columns="columns"
is-sort
@click.stop

4
packages/nc-gui/composables/useData.ts

@ -34,10 +34,6 @@ export function useData(args: {
}) {
const { meta, viewMeta, formattedData, paginationData, callbacks } = args
if (!meta) {
throw new Error('Table meta is not available')
}
const { t } = useI18n()
const { getMeta, metas } = useMetas()

4
packages/nc-gui/composables/useViewData.ts

@ -42,10 +42,6 @@ export function useViewData(
const meta = computed(() => _meta.value || activeTable.value)
const metaId = computed(() => _meta.value?.id || activeTableId.value)
if (!meta.value) {
throw new Error('Table meta is not available')
}
const { t } = useI18n()
const optimisedQuery = useState('optimisedQuery', () => true)

88
packages/nc-gui/lang/es.json

@ -1,45 +1,45 @@
{
"dashboards": {
"create_new_dashboard_project": "Create New Interface",
"connect_data_sources": "Connect data sources",
"alert": "Alert",
"alert-message": "No databases have been connected. Connect database bases to build interfaces. Skip this step and add databases from the base home page later.",
"select_database_projects_that_you_want_to_link_to_this_dashboard_projects": "Select Database Bases that you want to link to this Interface.",
"create_interface": "Create interface",
"project_name": "Base Name",
"connect": "Connect",
"create_new_dashboard_project": "Crear nueva interfaz",
"connect_data_sources": "Conectar bases de datos",
"alert": "Alerta",
"alert-message": "No se ha conectado ninguna base de datos. Conecte las bases de datos para crear interfaces. Omita este paso y luego añada bases de datos desde la página de inicio de la base.",
"select_database_projects_that_you_want_to_link_to_this_dashboard_projects": "Seleccione las bases de datos que desea vincular a esta interfaz.",
"create_interface": "Crear interfaz",
"project_name": "Nombre de la base",
"connect": "Conectar",
"buttonActionTypes": {
"open_external_url": "Open external link",
"delete_record": "Delete record",
"update_record": "Update record",
"open_layout": "Open layout"
"open_external_url": "Abrir enlace externo",
"delete_record": "Borrar fila",
"update_record": "Actualizar fila",
"open_layout": "Abrir diagrama"
},
"widgets": {
"static_text": "Text",
"chart": "Chart",
"table": "Table",
"image": "Image",
"map": "Map",
"button": "Button",
"number": "Number",
"bar_chart": "Bar Chart",
"line_chart": "Line Chart",
"area_chart": "Area Chart",
"pie_chart": "Pie Chart",
"donut_chart": "Donut Chart",
"scatter_plot": "Scatter Plot",
"bubble_chart": "Bubble Chart",
"radar_chart": "Radar Chart",
"polar_area_chart": "Polar Area Chart",
"radial_bar_chart": "Radial Bar Chart",
"heatmap_chart": "Heatmap Chart",
"treemap_chart": "Treemap Chart",
"box_plot_chart": "Box Plot Chart",
"candlestick_chart": "Candlestick Chart"
"static_text": "Texto",
"chart": "Gráfico",
"table": "Tabla",
"image": "Imagen",
"map": "Mapa",
"button": "Botón",
"number": "Número",
"bar_chart": "Gráfico de barras",
"line_chart": "Gráfico de Líneas",
"area_chart": "Gráfico de área",
"pie_chart": "Gráfico de tarta",
"donut_chart": "Gráfico de dona",
"scatter_plot": "Gráfico de dispersión",
"bubble_chart": "Gráfico de burbujas",
"radar_chart": "Gráfico de radar",
"polar_area_chart": "Gráfico de área polar",
"radial_bar_chart": "Gráfico de barra radial",
"heatmap_chart": "Mapa de calor",
"treemap_chart": "Gráfico de árbol",
"box_plot_chart": "Gráfico de cajas",
"candlestick_chart": "Gráfico de velas"
}
},
"general": {
"quit": "Quit",
"quit": "Salir",
"home": "Inicio",
"load": "Cargar",
"open": "Abrir",
@ -47,24 +47,24 @@
"yes": "Sí",
"no": "No",
"ok": "Ok",
"back": "Back",
"back": "Volver",
"and": "Y",
"or": "O",
"add": "Agregar",
"edit": "Editar",
"link": "Link",
"links": "Links",
"link": "Enlace",
"links": "Enlaces",
"remove": "Eliminar",
"import": "Import",
"logout": "Log Out",
"import": "Importar",
"logout": "Cerrar sesión",
"empty": "Empty",
"changeIcon": "Change Icon",
"changeIcon": "Cambiar icono",
"save": "Salvar",
"available": "Available",
"abort": "Abort",
"saving": "Saving",
"available": "Disponible",
"abort": "Cancelar",
"saving": "Guardando",
"cancel": "Cancelar",
"null": "Null",
"null": "Nulo",
"escape": "Escape",
"hex": "Hex",
"clear": "Clear",

364
packages/nc-gui/lang/zh-Hans.json

@ -1,27 +1,27 @@
{
"dashboards": {
"create_new_dashboard_project": "Create New Interface",
"connect_data_sources": "Connect data sources",
"alert": "Alert",
"alert-message": "No databases have been connected. Connect database bases to build interfaces. Skip this step and add databases from the base home page later.",
"select_database_projects_that_you_want_to_link_to_this_dashboard_projects": "Select Database Bases that you want to link to this Interface.",
"create_interface": "Create interface",
"project_name": "Base Name",
"connect": "Connect",
"create_new_dashboard_project": "创建新界面",
"connect_data_sources": "连接数据源",
"alert": "警告",
"alert-message": "尚未连接数据库,连接数据库以构建界面。跳过此步骤,稍后从数据库主页添加数据库。",
"select_database_projects_that_you_want_to_link_to_this_dashboard_projects": "选择要链接到此界面的数据库。",
"create_interface": "创建界面",
"project_name": "项目名",
"connect": "连接",
"buttonActionTypes": {
"open_external_url": "Open external link",
"delete_record": "Delete record",
"update_record": "Update record",
"open_external_url": "打开外部链接",
"delete_record": "删除行",
"update_record": "更新记录",
"open_layout": "Open layout"
},
"widgets": {
"static_text": "Text",
"chart": "Chart",
"table": "Table",
"image": "Image",
"map": "Map",
"button": "Button",
"number": "Number",
"static_text": "文本",
"chart": "图表",
"table": "表格",
"image": "图片",
"map": "地图",
"button": "按钮",
"number": "数字",
"bar_chart": "Bar Chart",
"line_chart": "Line Chart",
"area_chart": "Area Chart",
@ -39,7 +39,7 @@
}
},
"general": {
"quit": "Quit",
"quit": "退出",
"home": "首页",
"load": "加载",
"open": "打开",
@ -47,16 +47,16 @@
"yes": "是",
"no": "否",
"ok": "行",
"back": "Back",
"back": "返回",
"and": "与",
"or": "或",
"add": "添加",
"edit": "编辑",
"link": "Link",
"links": "Links",
"link": "链接",
"links": "链接",
"remove": "移除",
"import": "Import",
"logout": "Log Out",
"import": "导入",
"logout": "退出登录",
"empty": "Empty",
"changeIcon": "Change Icon",
"save": "保存",
@ -69,7 +69,7 @@
"hex": "Hex",
"clear": "Clear",
"slack": "Slack",
"comment": "Comment",
"comment": "评论",
"microsoftTeams": "Microsoft Teams",
"discord": "Discord",
"matterMost": "Mattermost",
@ -78,12 +78,12 @@
"quote": "Quote",
"submit": "提交",
"create": "创建",
"createEntity": "Create {entity}",
"createEntity": "新建{entity}",
"creating": "Creating",
"creatingEntity": "Creating {entity}",
"details": "Details",
"skip": "Skip",
"code": "Code",
"skip": "跳过",
"code": "代码",
"duplicate": "复制",
"duplicating": "Duplicating",
"activate": "Activate",
@ -107,7 +107,7 @@
"deprecated": "Deprecated",
"showAll": "显示全部",
"hideAll": "隐藏全部",
"notFound": "Not found",
"notFound": "未找到",
"showMore": "显示更多",
"showOptions": "显示选项",
"hideOptions": "隐藏选项",
@ -127,8 +127,8 @@
"upload": "上传",
"download": "下载",
"default": "默认",
"base": "Source",
"datasource": "Data Source",
"base": "",
"datasource": "数据源",
"more": "更多的",
"less": "较少的",
"event": "事件",
@ -160,39 +160,39 @@
"hideField": "隐藏字段",
"sortAsc": "升序",
"sortDesc": "降序",
"move": "Move",
"move": "移动",
"geoDataField": "地理数据字段",
"type": "Type",
"name": "Name",
"changes": "Changes",
"new": "New",
"old": "Old",
"data": "Data",
"source": "Source",
"type": "类型",
"name": "名称",
"changes": "更新",
"new": "新的",
"old": "旧的",
"data": "数据",
"source": "",
"destination": "Destination",
"active": "Active",
"inactive": "Inactive",
"linked": "linked",
"finish": "Finish",
"min": "Min",
"max": "Max",
"avg": "Avg",
"sum": "Sum",
"count": "Count",
"min": "最小",
"max": "最大",
"avg": "平均值",
"sum": "求和",
"count": "总计",
"countDistinct": "Count Distinct",
"sumDistinct": "Sum Distinct",
"avgDistinct": "Avg Distinct",
"join": "Join",
"options": "Options",
"options": "选项",
"primaryValue": "Primary Value",
"useSurveyMode": "Use Survey Mode",
"shift": "Shift",
"enter": "Enter",
"seconds": "Seconds"
"shift": "Shift",
"enter": "回车键",
"seconds": ""
},
"objects": {
"workspace": "Workspace",
"workspaces": "Workspaces",
"workspace": "工作区",
"workspaces": "工作区",
"project": "项目",
"projects": "项目",
"table": "表格",
@ -209,7 +209,7 @@
"webhooks": "Webhooks.",
"view": "视图",
"views": "视图",
"sidebar": "Sidebar",
"sidebar": "侧边栏",
"viewType": {
"grid": "表格",
"gallery": "画廊",
@ -222,7 +222,7 @@
"users": "用户",
"role": "角色",
"roles": "角色",
"developer": "Developer",
"developer": "开发人员",
"roleType": {
"owner": "所有者",
"creator": "创造者",
@ -240,9 +240,9 @@
"short": "Short",
"medium": "Medium",
"tall": "Tall",
"extra": "Extra"
"extra": "备注"
},
"externalDb": "External Database"
"externalDb": "外部数据库"
},
"datatype": {
"ID": "ID",
@ -297,8 +297,8 @@
"isNotNull": "有值(Not Null)"
},
"title": {
"docs": "Docs",
"forum": "Forum",
"docs": "文档",
"forum": "论坛",
"parameter": "Parameter",
"headers": "Headers",
"parameterName": "Parameter Name",
@ -311,39 +311,39 @@
"inDesktop": "in Desktop",
"rowData": "Record data",
"creator": "Creator",
"qrCode": "QR Code",
"termsOfService": "Terms of Service",
"updateSelectedRows": "Update Selected Records",
"noFiltersAdded": "No filters added",
"editCards": "Edit Cards",
"noFieldsFound": "No fields found",
"displayValue": "Display Value",
"qrCode": "二维码",
"termsOfService": "服务条款",
"updateSelectedRows": "更新选定记录",
"noFiltersAdded": "未添加过滤器",
"editCards": "编辑卡片",
"noFieldsFound": "无可用字段",
"displayValue": "显示值",
"expand": "Expand",
"hideAll": "Hide all",
"hideSystemFields": "Hide system fields",
"removeFile": "Remove File",
"hasMany": "Has Many",
"manyToMany": "Many to Many",
"hideAll": "隐藏全部",
"hideSystemFields": "隐藏系统字段",
"removeFile": "删除文件",
"hasMany": "单对多",
"manyToMany": "多对多",
"virtualRelation": "Virtual Relation",
"linkMore": "Link More",
"linkMoreRecords": "Link more records",
"downloadFile": "Download File",
"downloadFile": "下载文件",
"renameTable": "Rename Table",
"renamingTable": "Renaming Table",
"renamingWs": "Renaming Workspace",
"renameWs": "Rename Workspace",
"deleteWs": "Delete Workspace",
"deletingWs": "Deleting Workspace",
"copyAuthToken": "Copy Auth Token",
"copyAuthToken": "复制 Auth 令牌",
"copiedAuthToken": "Copied Auth Token",
"copyInviteToken": "Copy Invite Token",
"showSidebar": "Show Sidebar",
"hideSidebar": "Hide Sidebar",
"creatingTable": "Creating Table",
"erdView": "ERD 视图",
"newBase": "New Data Source",
"newBase": "新建数据源",
"newProj": "创建新项目",
"createBase": "Create Base",
"createBase": "新建项目",
"myProject": "我的项目",
"formTitle": "表单标题",
"collaborative": "Collaborative",
@ -353,7 +353,7 @@
"teamAndAuth": "团队和认证",
"rolesUserMgmt": "角色和用户管理",
"userMgmt": "用户管理",
"apiTokens": "API Tokens",
"apiTokens": "API 令牌",
"apiTokenMgmt": "API Tokens 管理",
"rolesMgmt": "角色管理",
"projMeta": "项目基础信息",
@ -380,11 +380,11 @@
"swaggerDocumentation": "Swagger 文档",
"quickImportFrom": "快速导入自",
"quickImport": "快速导入",
"quickImportAirtable": "Quick Import - Airtable",
"quickImportCSV": "Quick Import - CSV",
"quickImportExcel": "Quick Import - Excel",
"quickImportJSON": "Quick Import - JSON",
"jsonEditor": "JSON Editor",
"quickImportAirtable": "快速导入 - Airtable",
"quickImportCSV": "快速导入 - CSV",
"quickImportExcel": "快速导入 - Excel",
"quickImportJSON": "快速导入 - JSON",
"jsonEditor": "JSON编辑器",
"comingSoon": "Coming Soon",
"advancedSettings": "高级设置",
"codeSnippet": "代码片段",
@ -392,17 +392,17 @@
"generateRandomName": "随机名称",
"findRowByScanningCode": "通过扫描二维码或条码查找行",
"tokenManagement": "Token Management",
"addNewToken": "Add new token",
"accountSettings": "Account Settings",
"resetPasswordMenu": "Reset Password",
"tokens": "Tokens",
"addNewToken": "添加新令牌(Token)",
"accountSettings": "账户设置",
"resetPasswordMenu": "密码重置",
"tokens": "令牌",
"userManagement": "User Management",
"accountManagement": "Account management",
"licence": "Licence",
"licence": "许可证",
"allowAllMimeTypes": "Allow All Mime Types",
"defaultView": "Default View",
"defaultView": "默认视图",
"relations": "Relations",
"switchLanguage": "Switch Language",
"switchLanguage": "切换语言",
"renameFile": "Rename File",
"links": {
"noAction": "No Action",
@ -413,30 +413,30 @@
}
},
"labels": {
"heading1": "Heading 1",
"heading2": "Heading 2",
"heading3": "Heading 3",
"bold": "Bold",
"italic": "Italic",
"underline": "Underline",
"strike": "Strike",
"taskList": "Task List",
"bulletList": "Bullet List",
"numberedList": "Numbered List",
"downloadData": "Download Data",
"heading1": "一级标题",
"heading2": "二级标题",
"heading3": "三级标题",
"bold": "粗体",
"italic": "斜体",
"underline": "下划线",
"strike": "删除线",
"taskList": "任务列表",
"bulletList": "无序列表",
"numberedList": "有序列表",
"downloadData": "下载数据",
"blockQuote": "Block Quote",
"noToken": "No Token",
"tokenLimit": "Only one token per user is allowed",
"duplicateAttachment": "File with name {filename} already attached",
"viewIdColon": "VIEW ID: {viewId}",
"noToken": "无令牌",
"tokenLimit": "每个用户只能使用一个令牌",
"duplicateAttachment": "文件{filename} 已附加",
"viewIdColon": "查看 ID:{viewId}",
"toAddress": "To Address",
"subject": "Subject",
"body": "Body",
"commaSeparatedMobileNumber": "Comma separated Mobile #",
"headerName": "Header Name",
"icon": "Icon",
"max": "Max",
"enableRichText": "Enable Rich Text",
"icon": "图标",
"max": "最大",
"enableRichText": "启用富文本",
"idColon": "Id:",
"copiedRecordURL": "Copied Record URL",
"copyRecordURL": "Copy Record URL",
@ -446,33 +446,33 @@
"examples": "Examples",
"durationInfo": "A duration of time in minutes or seconds (e.g. 1:23).",
"addHeader": "Add Header",
"enterDefaultUrlOptional": "Enter default URL (Optional)",
"enterDefaultUrlOptional": "输入默认 URL(可选)",
"negative": "Negative",
"discard": "Discard",
"default": "Default",
"defaultNumberPercent": "Default Number (%)",
"durationFormat": "Duration Format",
"dateFormat": "Date Format",
"dateFormat": "日期格式",
"timeFormat": "Time Format",
"singularLabel": "Singular Label",
"pluralLabel": "Plural Label",
"optional": "(Optional)",
"clickToMake": "Click to make",
"optional": "(可选)",
"clickToMake": "点击创建",
"visibleForRole": "visible for role:",
"inUI": "in UI Dashboard",
"projectSettings": "Base Settings",
"clickToHide": "Click to hide",
"clickToDownload": "Click to download",
"projectSettings": "基础设置",
"clickToHide": "点击隐藏",
"clickToDownload": "点击下载",
"forRole": "for role",
"clickToCopyViewID": "Click to copy View ID",
"viewMode": "View Mode",
"searchUsers": "Search Users",
"superAdmin": "Super Admin",
"allTables": "All Tables",
"clickToCopyViewID": "点击复制视图ID",
"viewMode": "视图模式",
"searchUsers": "搜索用户",
"superAdmin": "超级管理员",
"allTables": "所有表格",
"members": "Members",
"dataSources": "Data Sources",
"dataSources": "数据源",
"connectDataSource": "Connect a Data Source",
"searchProjects": "Search Bases",
"searchProjects": "搜索项目",
"createdBy": "创建自",
"viewingAttachmentsOf": "Viewing Attachments of",
"readOnly": "Readonly",
@ -491,8 +491,8 @@
"createView": "Create a View",
"creatingView": "Creating View",
"duplicateView": "Duplicate View",
"duplicateGridView": "Duplicate Grid View",
"createGridView": "Create Grid View",
"duplicateGridView": "复制视图",
"createGridView": "新建视图",
"duplicateGalleryView": "Duplicate Gallery View",
"createGalleryView": "Create Gallery View",
"duplicateFormView": "Duplicate Form View",
@ -528,12 +528,12 @@
"where": "在哪里",
"cache": "缓存",
"chat": "聊天",
"showOrHide": "Show or Hide",
"showOrHide": "显示或隐藏",
"airtable": "Airtable",
"csv": "CSV",
"csvFile": "CSV File",
"csvFile": "CSV文件",
"json": "JSON",
"jsonFile": "JSON File",
"jsonFile": "JSON 文件",
"excel": "Excel",
"microsoftExcel": "Microsoft Excel",
"email": "电子邮件",
@ -544,7 +544,7 @@
"created": "创建了",
"sqlOutput": "输出 SQL",
"addOption": "添加选项",
"interfaceColor": "Interface Color",
"interfaceColor": "界面颜色",
"qrCodeValueColumn": "二维码要显示的数据",
"barcodeValueColumn": "条形码要显示的数据",
"barcodeFormat": "条形码码制",
@ -575,14 +575,14 @@
"followNocodb": "关注 NocoDB",
"communityTranslated": "(Community Translated)"
},
"twitter": "Twitter",
"twitter": "推特(Twitter",
"docReference": "参考文档",
"selectUserRole": "选择用户角色",
"childTable": "子表",
"childColumn": "子列",
"childField": "Child field",
"linkToAnotherRecord": "关联到其他表",
"links": "Links",
"links": "链接",
"onUpdate": "更新时",
"onDelete": "删除时",
"account": "帐户",
@ -593,7 +593,7 @@
"requestDataSource": "请求您需要的数据源?",
"apiKey": "API 密钥",
"personalAccessToken": "Personal Access Token",
"sharedBaseUrl": "Shared Base URL",
"sharedBaseUrl": "分享项目链接",
"importData": "导入数据",
"importSecondaryViews": "导入次要视图",
"importRollupColumns": "导入聚合列",
@ -620,15 +620,15 @@
"subscribeNewsletter": "订阅我们的每周新闻",
"signUpWithProvider": "使用 {provider} 注册",
"signInWithProvider": "使用 {provider} 登录",
"agreeToTos": "注册即表您同意服务条款",
"agreeToTos": "注册即表您同意服务条款",
"welcomeToNc": "欢迎来到NocoDB!",
"inviteOnlySignup": "只能通过邀请链接注册账户",
"nextRow": "下一行",
"prevRow": "上一行",
"addRowGrid": "Manually add data in grid view",
"addRowForm": "Enter record data through a form",
"noAccess": "No access",
"restApis": "Rest APIs",
"noAccess": "无权访问",
"restApis": "重置API",
"apis": "APIs",
"includeData": "Include Data",
"includeView": "Include View",
@ -649,37 +649,37 @@
"attachFile": "Attach File",
"viewAttachment": "View Attachments",
"attachmentDrop": "Click or drop a file into cell",
"addFiles": "Add File(s)",
"addFiles": "添加文件(一个或多个)",
"hideInUI": "Hide in UI",
"addBase": "Add Base",
"addBase": "添加项目",
"addParameter": "Add Parameter",
"submitAnotherForm": "Submit Another Form",
"dragAndDropFieldsHereToAdd": "Drag and drop fields here to add",
"editSource": "Edit Data Source",
"enterText": "Enter text",
"enterText": "输入文本",
"okEditBase": "Ok & Edit Base",
"showInUI": "Show in UI",
"outOfSync": "Out of sync",
"newSource": "New Data Source",
"newWebhook": "New Webhook",
"enablePublicAccess": "Enable Public Access",
"doYouWantToSaveTheChanges": "Do you want to save the changes ?",
"newSource": "新建数据源",
"newWebhook": "新建 Webhook",
"enablePublicAccess": "启用公共访问",
"doYouWantToSaveTheChanges": "您要保存这些更改吗?",
"editingAccess": "Editing access",
"enabledPublicViewing": "Enable public viewing",
"restrictAccessWithPassword": "Restrict access with password",
"manageProjectAccess": "Manage Base Access",
"allowDownload": "Allow Download",
"enabledPublicViewing": "启用公开查看",
"restrictAccessWithPassword": "使用密码限制访问",
"manageProjectAccess": "管理项目访问",
"allowDownload": "允许下载",
"surveyMode": "Survey Mode",
"rtlOrientation": "RTL Orientation",
"useTheme": "Use Theme",
"copyLink": "Copy Link",
"useTheme": "使用主题",
"copyLink": "复制链接",
"copiedLink": "Link Copied",
"copyInviteLink": "Copy invite link",
"copiedInviteLink": "Copied invite link",
"copyUrl": "复制链接",
"moreColors": "More Colors",
"moreColors": "更多颜色",
"moveProject": "Move Base",
"createProject": "建项目",
"createProject": "建项目",
"importProject": "导入项目",
"searchProject": "搜索项目",
"editProject": "编辑项目",
@ -689,7 +689,7 @@
"deleteProject": "删除项目",
"refreshProject": "刷新项目",
"saveProject": "保存项目",
"saveAndQuit": "Save & Quit",
"saveAndQuit": "保存并退出",
"deleteKanbanStack": "是否删除此类别?",
"createProjectExtended": {
"extDB": "新建 <br>从外部数据库",
@ -715,10 +715,10 @@
"filter": "筛选",
"addFilter": "添加过滤器",
"share": "分享",
"groupBy": "Group By",
"addSubGroup": "Add subgroup",
"groupBy": "分组",
"addSubGroup": "添加子分组",
"shareBase": {
"label": "Share base",
"label": "分享项目",
"disable": "停止分享项目",
"enable": "任何有链接的人",
"link": "分享项目链接"
@ -729,7 +729,7 @@
"inviteUser": "邀请用户",
"inviteToken": "邀请令牌",
"linkedRecords": "Linked Records",
"addNewLink": "Add New Link",
"addNewLink": "添加新的链接",
"newUser": "创建用户",
"editUser": "编辑用户",
"deleteUser": "从项目中踢出用户",
@ -782,7 +782,7 @@
"ListView": "视图列表",
"copyView": "复制视图",
"renameView": "重命名视图",
"uploadData": "Upload Data",
"uploadData": "上传数据",
"deleteView": "删除视图",
"createGrid": "创建网格视图",
"createGallery": "创建画廊视图",
@ -815,7 +815,7 @@
"addFilterGroup": "添加筛选器组",
"linkRecord": "关联到其他记录",
"addNewRecord": "新增记录",
"newRecord": "New record",
"newRecord": "新增记录",
"tableNameCreateNewRecord": "{tableName}: Create new record",
"gotSavedLinkedSuccessfully": "{tableName} '{recordTitle}' got saved & linked successfully",
"recordCreatedLinked": "Record Created & Linked",
@ -823,15 +823,15 @@
"toggleCommentsDraw": "切换评论视图",
"expandRecord": "展开记录",
"deleteRecord": "删除记录",
"fullWidth": "Full width",
"exitFullWidth": "Exit full width",
"fullWidth": "使用宽屏",
"exitFullWidth": "退出宽屏",
"markAllAsRead": "Mark all as read",
"column": {
"delete": "Delete Field",
"addNumber": "Add Number Field",
"addSingleLineText": "Add SingleLineText Field",
"addLongText": "Add LongText Field",
"addOther": "Add Other Field"
"delete": "删除字段",
"addNumber": "添加数字字段",
"addSingleLineText": "添加单行文本字段",
"addLongText": "添加长文本字段",
"addOther": "添加其它字段"
},
"erd": {
"showColumns": "显示列",
@ -854,7 +854,7 @@
"openInOpenStreetMap": "OSM"
},
"toggleMobileMode": "切换移动模式",
"startCommenting": "Start commenting!"
"startCommenting": "开始评论"
},
"tooltip": {
"reachedSourceLimit": "Limited to only one data source for the moment",
@ -891,13 +891,13 @@
"selectTeamsChannels": "Select Microsoft Teams channels",
"selectDiscordChannels": "Select Discord channels",
"selectMattermostChannels": "Select Mattermost channels",
"webhookTitle": "Webhook Title",
"webhookTitle": "Webhook 标题",
"barcodeColumn": "Select a field for the Barcode value",
"notFoundContent": "No valid field Type can be found.",
"selectBarcodeFormat": "Select a Barcode format",
"projName": "输入项目名称",
"selectGroupField": "Select a Grouping Field",
"selectGroupFieldNotFound": "No Single Select Field can be found. Please create one first.",
"selectGroupField": "选择分组字段",
"selectGroupFieldNotFound": "没有可用的单选字段,请先创建。",
"selectGeoField": "Select a GeoData Field",
"selectGeoFieldNotFound": "No GeoData Field can be found. Please create one first.",
"password": {
@ -928,14 +928,14 @@
"decimal6": "1.000000",
"decimal7": "1.0000000",
"decimal8": "1.00000000",
"value": "Value",
"key": "Key"
"value": "",
"key": ""
},
"msg": {
"clickToCopyFieldId": "Click to copy Field Id",
"enterPassword": "Enter password",
"bySigningUp": "By signing up, you agree to the",
"subscribeToOurWeeklyNewsletter": "Subscribe to our weekly newsletter",
"clickToCopyFieldId": "点击复制字段ID",
"enterPassword": "输入密码",
"bySigningUp": "注册即表示您同意服务条款",
"subscribeToOurWeeklyNewsletter": "订阅我们的每周的新闻",
"verifyingPassword": "Verifying Password",
"thisSharedViewIsProtected": "This shared view is protected",
"successfullySubmittedFormData": "Successfully submitted form data",
@ -1048,7 +1048,7 @@
},
"info": {
"disabledAsViewLocked": "Disabled as View is locked",
"basesMigrated": "Bases are migrated. Please try again.",
"basesMigrated": "项目已迁移。请重试。",
"pasteNotSupported": "处于活动状态的单元格不能粘贴内容",
"roles": {
"orgCreator": "创始人可以创建新项目,访问受邀项目。",
@ -1203,29 +1203,29 @@
"userConfirmation": "I understand that this action is irreversible",
"pageNotFound": "Page Not Found",
"makeLineBreak": "to make a line break",
"goToPrevious": "Go to previous",
"goToNext": "Go to next",
"thankYou": "Thank you!",
"submittedFormData": "You have successfully submitted the form data."
"goToPrevious": "转到上一个",
"goToNext": "转到下一个",
"thankYou": "谢谢你!",
"submittedFormData": "您已成功提交表单数据。"
},
"error": {
"nameRequired": "Name Required",
"nameMinLength": "Name must be at least 2 characters long",
"nameMaxLength": "Name must be at most 60 characters long",
"nameMaxLength": "名称长度不得超过 60 个字符",
"viewNameRequired": "View name is required",
"nameMaxLength256": "Name must be at most 256 characters long",
"viewNameUnique": "View name should be unique",
"nameMaxLength256": "名称长度不得超过 256 个字符",
"viewNameUnique": "视图名称应该是唯一的",
"searchProject": "搜索: {search} 没有发现匹配的结果",
"invalidChar": "文件夹路径中的字符无效。",
"invalidDbCredentials": "无效的数据库凭据。",
"unableToConnectToDb": "无法连接到数据库,请检查您的数据库是否已启动。",
"invalidYear": "Invalid year",
"invalidYear": "年份无效",
"userDoesntHaveSufficientPermission": "用户不存在或无权创建模式。",
"dbConnectionStatus": "数据库参数无效",
"dbConnectionFailed": "连接失败:",
"nullFilterExists": "Null filter exists. Please remove them",
"nullFilterExists": "存在空过滤器。请删除它们",
"signUpRules": {
"emailRequired": "Email is required",
"emailRequired": "电子邮件为必填",
"emailInvalid": "邮箱格式无效",
"passwdRequired": "密码必填",
"passwdLength": "您的密码必须至少为8个字符",
@ -1236,8 +1236,8 @@
"atLeastOneNumber": "一个数字",
"atLeastOneSpecialChar": "一个特殊字符",
"allowedSpecialCharList": "允许的特殊字符列表",
"invalidEmails": "Invalid emails",
"invalidEmail": "Invalid Email"
"invalidEmails": "邮箱格式错误",
"invalidEmail": "无效的电子邮件"
},
"invalidURL": "无效的 URL",
"invalidEmail": "无效的电子邮件",
@ -1302,7 +1302,7 @@
"futureRelease": "即将推出!"
},
"success": {
"licenseKeyUpdated": "License Key Updated",
"licenseKeyUpdated": "更新许可证密钥",
"columnDuplicated": "此列的副本创建成功",
"rowDuplicatedWithoutSavedYet": "该行已复制(未保存)",
"updatedUIACL": "已成功更新表的 UI ACL",

50
packages/nc-gui/package.json

@ -73,7 +73,7 @@
"rfdc": "^1.3.0",
"showdown": "^2.1.0",
"socket.io-client": "^4.7.2",
"sortablejs": "^1.15.0",
"sortablejs": "^1.15.1",
"splitpanes": "^3.1.5",
"tinycolor2": "^1.4.2",
"unique-names-generator": "^4.7.1",
@ -93,10 +93,10 @@
"xlsx": "^0.18.5",
"@tiptap/extension-link": "2.0.4",
"@tiptap/extension-task-list": "2.0.4",
"@tiptap/extension-underline": "^2.1.12",
"@tiptap/extension-underline": "^2.1.13",
"@tiptap/html": "2.0.4",
"@tiptap/pm": "^2.1.12",
"@tiptap/starter-kit": "^2.1.12",
"@tiptap/pm": "^2.1.13",
"@tiptap/starter-kit": "^2.1.13",
"marked": "^4.3.0",
"turndown": "^7.1.2",
"@tiptap/vue-3": "2.0.4"
@ -104,34 +104,34 @@
"devDependencies": {
"@antfu/eslint-config": "^0.26.3",
"@esbuild-plugins/node-modules-polyfill": "^0.2.2",
"@iconify-json/ant-design": "^1.1.10",
"@iconify-json/ant-design": "^1.1.12",
"@iconify-json/bi": "^1.1.21",
"@iconify-json/carbon": "^1.1.21",
"@iconify-json/cil": "^1.1.5",
"@iconify-json/clarity": "^1.1.9",
"@iconify-json/eva": "^1.1.7",
"@iconify-json/ic": "^1.1.14",
"@iconify-json/ion": "^1.1.12",
"@iconify-json/la": "^1.1.5",
"@iconify-json/logos": "^1.1.38",
"@iconify-json/lucide": "^1.1.141",
"@iconify-json/material-symbols": "^1.1.63",
"@iconify-json/mdi": "^1.1.55",
"@iconify-json/mi": "^1.1.5",
"@iconify-json/ph": "^1.1.6",
"@iconify-json/ri": "^1.1.12",
"@iconify-json/simple-icons": "^1.1.79",
"@iconify-json/system-uicons": "^1.1.9",
"@iconify-json/tabler": "^1.1.98",
"@iconify-json/vscode-icons": "^1.1.29",
"@iconify-json/carbon": "^1.1.23",
"@iconify-json/cil": "^1.1.7",
"@iconify-json/clarity": "^1.1.11",
"@iconify-json/eva": "^1.1.9",
"@iconify-json/ic": "^1.1.16",
"@iconify-json/ion": "^1.1.14",
"@iconify-json/la": "^1.1.7",
"@iconify-json/logos": "^1.1.40",
"@iconify-json/lucide": "^1.1.144",
"@iconify-json/material-symbols": "^1.1.65",
"@iconify-json/mdi": "^1.1.57",
"@iconify-json/mi": "^1.1.7",
"@iconify-json/ph": "^1.1.8",
"@iconify-json/ri": "^1.1.15",
"@iconify-json/simple-icons": "^1.1.81",
"@iconify-json/system-uicons": "^1.1.11",
"@iconify-json/tabler": "^1.1.100",
"@iconify-json/vscode-icons": "^1.1.31",
"@intlify/unplugin-vue-i18n": "^0.12.3",
"@nuxt/image-edge": "1.1.0-28346300.6030589",
"@nuxt/image-edge": "1.1.0-28355789.b3279fe",
"@types/d3-scale": "^4.0.8",
"@types/dagre": "^0.7.52",
"@types/file-saver": "^2.0.7",
"@types/leaflet": "^1.9.8",
"@types/leaflet.markercluster": "^1.5.4",
"@types/papaparse": "^5.3.13",
"@types/papaparse": "^5.3.14",
"@types/parse-github-url": "^1.0.3",
"@types/qrcode": "^1.5.5",
"@types/showdown": "^2.0.6",

2
packages/nc-gui/pages/signin.vue

@ -184,7 +184,7 @@ function navigateForgotPassword() {
</a>
</div>
<div class="text-end prose-sm" v-if="!appInfo.inviteOnlySignup">
<div v-if="!appInfo.inviteOnlySignup" class="text-end prose-sm">
{{ $t('msg.info.signUp.dontHaveAccount') }}
<nuxt-link @click="navigateSignUp">{{ $t('general.signUp') }}</nuxt-link>
</div>

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

@ -1,6 +1,6 @@
{
"name": "nc-lib-gui",
"version": "0.202.8",
"version": "0.202.9",
"description": "NocoDB GUI",
"author": {
"name": "NocoDB",

378
packages/noco-docs/docs/070.fields/040.field-types/060.formula/020.numeric-functions.md

@ -5,34 +5,360 @@ tags: ['Fields', 'Field types', 'Formula']
keywords: ['Fields', 'Field types', 'Formula', 'Create formula field', 'Numeric functions']
---
This cheat sheet provides a quick reference guide for various mathematical functions commonly used in data analysis and programming. Each function is accompanied by its syntax, a sample usage, and a brief description.
### Numeric functions
| Name | Syntax | Sample | Output |
|-------------|----------------------------|----------------------------------------|-------------------------------------------------------------------------------------------------------------|
| **ABS** | `ABS(value)` | `ABS({field})` | Absolute value of the input parameter |
| **ADD** | `ADD(value1,[value2,...])` | `ADD({field1}, {field2})` | Sum of input parameters |
| **AVG** | `AVG(value1,[value2,...])` | `AVG({field1}, {field2})` | Average of input parameters |
| **CEILING** | `CEILING(value)` | `CEILING({field})` | Rounded next largest integer value of input parameter |
| **EXP** | `EXP(value)` | `EXP({field})` | Exponential value of input parameter (`e^x`) |
| **FLOOR** | `FLOOR(value)` | `FLOOR({field})` | Rounded largest integer less than or equal to input parameter |
| **INT** | `INT(value)` | `INT({field})` | Integer value of input parameter |
| **LOG** | `LOG([base], value)` | `LOG(10, {field})` | Logarithm of input parameter to the base (default = e) specified |
| **MAX** | `MAX(value1,[value2,...])` | `MAX({field1}, {field2}, {field3})` | Maximum value amongst input parameters |
| **MIN** | `MIN(value1,[value2,...])` | `MIN({field1}, {field2}, {field3})` | Minimum value amongst input parameters |
| **MOD** | `MOD(value1, value2)` | `MOD({field}, 2)` | Remainder after integer division of input parameters |
| **POWER** | `POWER(base, exponent)` | `POWER({field}, 3)` | `base` to the `exponent` power, as in `base ^ exponent` |
| **ROUND** | `ROUND(value, precision)` | `ROUND({field}, 3)` | Round input `value` to decimal place specified by `precision` (Nearest integer if `precision` not provided) |
| **SQRT** | `SQRT(value)` | `SQRT({field})` | Square root of the input parameter |
| **COUNT** | `COUNT(value1,[value2,...])`| `COUNT({field1},{field2},{field3})` | Count the number of arguments that are numbers |
| **COUNTA** | `COUNTA(value1,[value2,...])`| `COUNTA({field1},{field2},{field3})`| Counts the number of non-empty arguments |
| **COUNTALL** | `COUNTALL(value1,[value2,...])`|`COUNTALL({field1},{field2},{field3})`| Counts the number of arguments |
| **EVEN** | `EVEN(value)` | `EVEN({field})` | Returns the nearest even integer that is greater than or equal to the specified value |
| **ODD** | `ODD(value)` | `ODD({field})` | Returns the nearest odd integer that is greater than or equal to the specified value |
| **VALUE** | `VALUE(value)` | `VALUE({field})` | Extract the numeric value from a string, if `%` or `-` is present, it will handle it accordingly and return the numeric value|
| **ROUNDDOWN** | `ROUNDDOWN(value, [precision])` | `ROUNDDOWN({field}, 2)` | Round down the value after the decimal point to the number of decimal places given by "precision"(default is 0) |
| **ROUNDUP** | `ROUNDUP(value, [precision])` | `ROUNDUP({field}, 2)` | Round up the value after the decimal point to the number of decimal places given by "precision"(default is 0) |
------------
## ABS
The ABS function returns the distance of the number from zero on the number line, ensuring that the result is non-negative.
#### Syntax
```plaintext
ABS(number)
```
#### Sample
```plaintext
ABS(10.35) => 10.35
ABS(-15) => 15
```
------------
## ADD
The ADD function computes the total of multiple numbers provided as arguments.
#### Syntax
```plaintext
ADD(number1, [number2, ...])
```
#### Sample
```plaintext
ADD(5, 7) => 12
ADD(-10, 15, 20) => 25
```
------------
## AVG
The AVG function calculates the mean of a set of numerical values.
#### Syntax
```plaintext
AVG(number1, [number2, ...])
```
#### Sample
```plaintext
AVG(10, 20, 30) => 20
AVG(-5, 5) => 0
```
------------
## CEILING
The CEILING function rounds a number up to the nearest integer greater than or equal to the input.
#### Syntax
```plaintext
CEILING(number)
```
#### Sample
```plaintext
CEILING(8.75) => 9
CEILING(-15.25) => -15
```
------------
## COUNT
The COUNT function calculates the number of numeric arguments provided.
#### Syntax
```plaintext
COUNT(number1, [number2, ...])
```
#### Sample
```plaintext
COUNT(1, 2, "abc", 3) => 3
COUNT(-5, 0, "$abc", 5) => 3
```
------------
## COUNTA
The COUNTA function counts the number of non-empty arguments provided.
#### Syntax
```plaintext
COUNTA(value1, [value2, ...])
```
#### Sample
```plaintext
COUNTA(1, "", "text") => 2
COUNTA("one", "two", "three") => 3
```
------------
## COUNTALL
The COUNTALL function calculates the total number of arguments, both numeric and non-numeric.
#### Syntax
```plaintext
COUNTALL(value1, [value2, ...])
```
#### Sample
```plaintext
COUNTALL(1, "", "text") => 3
COUNTALL("one", "two", "three") => 3
```
------------
## EVEN
The EVEN function rounds positive values up to the nearest even number and negative values down to the nearest even number.
#### Syntax
```plaintext
EVEN(number)
```
#### Sample
```plaintext
EVEN(7) => 8
EVEN(-5) => -6
```
------------
## EXP
The EXP function returns 'e' raised to the power of a given number.
#### Syntax
```plaintext
EXP(number)
```
#### Sample
```plaintext
EXP(2) => 7.38905609893065
EXP(-1) => 0.36787944117144233
```
------------
## FLOOR
The FLOOR function rounds a number down to the nearest integer.
#### Syntax
```plaintext
FLOOR(number)
```
#### Sample
```plaintext
FLOOR(8.75) => 8
FLOOR(-15.25) => -16
```
------------
## INT
The INT function truncates the decimal part, returning the integer portion of a number.
#### Syntax
```plaintext
INT(number)
```
#### Sample
```plaintext
INT(8.75) => 8
INT(-15.25) => -15
```
------------
## LOG
The LOG function computes the logarithm of a number to a specified base. (default = e).
#### Syntax
```plaintext
LOG([base], number)
```
#### Sample
```plaintext
LOG(10, 100) => 2
LOG(2, 8) => 3
```
------------
## MAX
The MAX function identifies the highest value from a set of numbers.
#### Syntax
```plaintext
MAX(number1, [number2, ...])
```
#### Sample
```plaintext
MAX(5, 10, 3) => 10
MAX(-10, -5, -20) => -5
```
------------
## MIN
The MIN function identifies the lowest value from a set of numbers.
#### Syntax
```plaintext
MIN(number1, [number2, ...])
```
#### Sample
```plaintext
MIN(5, 10, 3) => 3
MIN(-10, -5, -20) => -20
```
------------
## MOD
The MOD function calculates the remainder when dividing (integer division) one number by another.
#### Syntax
```plaintext
MOD(number1, number2)
```
#### Sample
```plaintext
MOD(10, 3) => 1
MOD(-15, 4) => -3
```
------------
## ODD
The ODD function rounds positive values up to the nearest odd number and negative values down to the nearest odd number.
#### Syntax
```plaintext
ODD(number)
```
#### Sample
```plaintext
ODD(6) => 7
ODD(-5.5) => -7
```
------------
## POWER
The POWER function raises a given base to a specified exponent.
#### Syntax
```plaintext
POWER(base, exponent)
```
#### Sample
```plaintext
POWER(2, 3) => 8
POWER(10, -2) => 0.01
```
------------
## ROUND
The ROUND function is used to round a number to a specified number of decimal places (precision). Default value for precision is 0.
#### Syntax
```plaintext
ROUND(number, [precision])
```
#### Sample
```plaintext
ROUND(8.765, 2) => 8.77
ROUND(-15.123, 1) => -15.1
```
------------
## ROUNDDOWN
The ROUNDDOWN function rounds a number down to a specified number of decimal places (precision). Default value for precision is 0.
#### Syntax
```plaintext
ROUNDDOWN(number, [precision])
```
#### Sample
```plaintext
ROUNDDOWN(8.765, 2) => 8.76
ROUNDDOWN(-15.123, 1) => -15.2
```
------------
## ROUNDUP
The ROUNDUP function rounds a number up to a specified number of decimal places (precision). Default value for precision is 0.
#### Syntax
```plaintext
ROUNDUP(number, [precision])
```
#### Sample
```plaintext
ROUNDUP(8.765, 2) => 8.77
ROUNDUP(-15.123, 1) => -15.1
```
------------
## SQRT
The SQRT function calculates the square root of a given number.
#### Syntax
```plaintext
SQRT(number)
```
#### Sample
```plaintext
SQRT(25) => 5
SQRT(2) => 1.4142135623730951
```
------------
## VALUE
The VALUE function is used to extract the numeric value from a string (after handling `%` or `-` accordingly).
#### Syntax
```plaintext
VALUE(text)
```
#### Sample
```plaintext
VALUE("123$") => 123
VALUE("USD -45.67") => -45.67
```
------------
## Related Articles
- [Numeric and Logical Operators](015.operators.md)

228
packages/noco-docs/docs/070.fields/040.field-types/060.formula/030.string-functions.md

@ -5,28 +5,216 @@ tags: ['Fields', 'Field types', 'Formula']
keywords: ['Fields', 'Field types', 'Formula', 'Create formula field', 'String functions']
---
This cheat sheet provides a quick reference guide for various string based functions commonly used in data analysis and programming. Each function is accompanied by its syntax, a sample usage, and a brief description.
### String functions
| Name | Syntax | Sample | Output |
|-------------|----------------------------------|-------------------------------------|---------------------------------------------------------------------------|
| **CONCAT** | `CONCAT(str1, [str2,...])` | `CONCAT({field1}, ' ', {field2})` | Concatenated string of input parameters |
| **LEFT** | `LEFT(str1, n)` | `LEFT({field}, 3)` | `n` characters from the beginning of input parameter |
| **LEN** | `LEN(str)` | `LEN({field})` | Input parameter character length |
| **LOWER** | `LOWER(str)` | `LOWER({field})` | Lower case converted string of input parameter |
| **MID** | `MID(str, position, [count])` | `MID({field}, 3, 2)` | Alias for `SUBSTR` |
| **REPEAT** | `REPEAT(str, count)` | `REPEAT({field}, 2)` | Specified copies of the input parameter string concatenated together |
| **REPLACE** | `REPLACE(str, srchStr, rplcStr)` | `REPLACE({field}, 'int', 'num')` | String, after replacing all occurrences of `srchStr` with `rplcStr` |
| **RIGHT** | `RIGHT(str, n)` | `RIGHT({field}, 3)` | `n` characters from the end of input parameter |
| **SEARCH** | `SEARCH(str, srchStr)` | `SEARCH({field}, 'str')` | Index of `srchStr` specified if found, 0 otherwise |
| **SUBSTR** | `SUBTR(str, position, [count])` | `SUBSTR({field}, 3, 2)` | Substring of length 'count' of input string, from the postition specified |
| **TRIM** | `TRIM(str)` | `TRIM({field})` | Remove trailing and leading whitespaces from input parameter |
| **UPPER** | `UPPER(str)` | `UPPER({field})` | Upper case converted string of input parameter |
| **URL** | `URL(str)` | `URL({field})` | Convert to a hyperlink if it is a valid URL |
| **REGEX_MATCH** | `REGEX_MATCH(str, pattern)` | `REGEX_MATCH({field}, 'a.*')` | Returns 1 if the input text matches a regular expression or 0 if it does not |
| **REGEX_EXTRACT** | `REGEX_EXTRACT(str, pattern)` | `REGEX_MATCH({field}, 'a.*')` | Returns the first match of a regular expression in a string |
| **REGEX_REPLACE** | `REGEX_REPLACE(str, pattern, replacer)` | `REGEX_MATCH({field}, 'a.*', '---')` | Replaces all matches of a regular expression in a string with a replacement string |
## CONCAT
The CONCAT function concatenates one or more strings into a single string.
#### Syntax
```plaintext
CONCAT(text, [text,...])
```
#### Sample
```plaintext
CONCAT('John', ' ', 'Doe') => 'John Doe'
```
## LEFT
The LEFT function retrieves the first 'n' characters specified from the beginning of the input string.
#### Syntax
```plaintext
LEFT(text, count)
```
#### Sample
```plaintext
LEFT('123-456-7890', 3) => '123'
```
## LEN
The LEN function calculates and returns the total number of characters present in the provided string.
#### Syntax
```plaintext
LEN(text)
```
#### Sample
```plaintext
LEN('Product Description') => 19
```
## LOWER
The LOWER function transforms all characters in the input string to lowercase
#### Syntax
```plaintext
LOWER(text)
```
#### Sample
```plaintext
LOWER('User INPUT') => 'user input'
```
## MID
The MID function retrieves a substring from the input string starting at the specified position and extending for the specified count of characters.
#### Syntax
```plaintext
MID(text, position, [count])
```
#### Sample
```plaintext
MID('This is a sentence', 5, 3) => 'is '
```
## REGEX_EXTRACT
The REGEX_EXTRACT function searches the input string for the first occurrence of the specified regular expression pattern and returns the matched substring.
#### Syntax
```plaintext
REGEX_EXTRACT(text, pattern)
```
#### Sample
```plaintext
REGEX_EXTRACT('Error: Something went wrong', 'Error: (.*)') => 'Something went wrong'
```
## REGEX_MATCH
The REGEX_MATCH function evaluates whether the input string matches the specified regular expression pattern, returning 1 if there is a match and 0 if there is no match.
#### Syntax
```plaintext
REGEX_MATCH(text, pattern)
```
#### Sample
```plaintext
REGEX_MATCH('123-45-6789', '\d{3}-\d{2}-\d{4}') => 1
```
## REGEX_REPLACE
The REGEX_REPLACE function identifies all occurrences of the specified regular expression pattern in the input string and substitutes them with the provided replacement string.
#### Syntax
```plaintext
REGEX_REPLACE(text, pattern, replacer)
```
#### Sample
```plaintext
REGEX_REPLACE('Replace all bugs', 'bug', 'feature') => 'Replace all features'
```
## REPEAT
The REPEAT function duplicates the provided string the specified number of times, facilitating the creation of repeated patterns or sequences.
#### Syntax
```plaintext
REPEAT(text, count)
```
#### Sample
```plaintext
REPEAT('😃', 3) => '😃😃😃'
```
## REPLACE
The REPLACE function identifies all instances of a particular substring within the given string and substitutes them with another specified substring.
#### Syntax
```plaintext
REPLACE(text, srchStr, rplcStr)
```
#### Sample
```plaintext
REPLACE('Replace old text', 'old', 'new') => 'Replace new text'
```
## RIGHT
The RIGHT function retrieves the last 'n' characters from the end of the input string, allowing you to extract a substring starting from the right.
#### Syntax
```plaintext
RIGHT(text, n)
```
#### Sample
```plaintext
RIGHT('file_name.txt', 3) => 'txt'
```
## SEARCH
The SEARCH function identifies the position of the specified substring within the input string, returning the index if found, and 0 otherwise.
#### Syntax
```plaintext
SEARCH(text, srchStr)
```
#### Sample
```plaintext
SEARCH('user@example.com', '@') => 5
```
## SUBSTR
The SUBSTR function extracts a substring from the input string, starting at the specified position and optionally extending for the specified count of characters.
#### Syntax
```plaintext
SUBSTR(text, position, [count])
```
#### Sample
```plaintext
SUBSTR('Extract this text', 9, 4) => 'this'
```
## TRIM
The TRIM function eliminates any leading or trailing whitespaces from the input string.
#### Syntax
```plaintext
TRIM(text)
```
#### Sample
```plaintext
TRIM(' Trim this ') => 'Trim this'
```
## UPPER
The UPPER function transforms all characters in the input string to uppercase.
#### Syntax
```plaintext
UPPER(text)
```
#### Sample
```plaintext
UPPER('title') => 'TITLE'
```
## URL
The URL function checks if the input string is a valid URL and converts it into a hyperlink
#### Syntax
```plaintext
URL(text)
```
#### Sample
```plaintext
URL('https://www.example.com') => a clickable link for https://www.example.com
```
## Related Articles

101
packages/noco-docs/docs/070.fields/040.field-types/060.formula/040.date-functions.md

@ -5,22 +5,95 @@ tags: ['Fields', 'Field types', 'Formula', 'Date & Time']
keywords: ['Fields', 'Field types', 'Formula', 'Date & Time', 'Create formula field', 'Date functions']
---
This cheat sheet provides a quick reference guide for various date functions commonly used in data analysis and programming. Each function is accompanied by its syntax, a sample usage, and a brief description.
| Name | Syntax | Sample | Output | Remark |
|-------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------|---------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| **NOW** | `NOW()` | `NOW()` | 2022-05-19 17:20:43 | Returns the current time and day |
| | `IF(NOW() < {DATE_COL}`, "true", "false")` | `IF(NOW() < date, "true", "false")` | If current date is less than `{DATE_COL}`, it returns true. Otherwise, it returns false. | DateTime fields and negative values are supported. |
| **DATEADD** | `DATEADD(date \| datetime, value, ["day" \| "week" \| "month" \| "year"])` | `DATEADD(date, 1, 'day')` | Supposing `{DATE_COL}` is 2022-03-14. The result is 2022-03-15. | DateTime fields and negative values are supported. Example: `DATEADD(DATE_TIME_COL, -1, 'day')` |
| | | `DATEADD(date, 1, 'week')` | Supposing `{DATE_COL}` is 2022-03-14 03:14. The result is 2022-03-21 03:14. | DateTime fields and negative values are supported. Example: `DATEADD(DATE_TIME_COL, -1, 'week')` |
| | | `DATEADD(date, 1, 'month')` | Supposing `{DATE_COL}` is 2022-03-14 03:14. The result is 2022-04-14 03:14. | DateTime fields and negative values are supported. Example: `DATEADD(DATE_TIME_COL, -1, 'month')` |
| | | `DATEADD(date, 1, 'year')` | Supposing `{DATE_COL}` is 2022-03-14 03:14. The result is 2023-03-14 03:14. | DateTime fields and negative values are supported. Example: `DATEADD(DATE_TIME_COL, -1, 'year')` |
| | | `IF(NOW() < DATEADD(date,10,'day'), "true", "false")` | If the current date is less than `{DATE_COL}` plus 10 days, it returns true. Otherwise, it returns false. | DateTime fields and negative values are supported. |
| | | `IF(NOW() < DATEADD(date,10,'day'), "true", "false")` | If the current date is less than `{DATE_COL}` plus 10 days, it returns true. Otherwise, it returns false. | DateTime fields and negative values are supported. |
| **DATETIME_DIFF** | `DATETIME_DIFF(date, date, ["milliseconds" \| "ms" \| "seconds" \| "s" \| "minutes" \| "m" \| "hours" \| "h" \| "days" \| "d" \| "weeks" \| "w" \| "months" \| "M" \| "quarters" \| "Q" \| "years" \| "y"])` | `DATETIME_DIFF("2022/10/14", "2022/10/15", "second")` | Supposing `{DATE_COL_1}` is 2017-08-25 and `{DATE_COL_2}` is 2011-08-25. The result is 86400. | Compares two dates and returns the difference in the unit specified. Positive integers indicate the second date being in the past compared to the first and vice versa for negative ones. |
| | | `WEEKDAY(NOW(), "sunday")` | If today is Monday, it returns 1 | Get the week day of NOW() with the first day set as sunday |
| **WEEKDAY** | `WEEKDAY(date, [startDayOfWeek])` | `WEEKDAY(NOW())` | If today is Monday, it returns 0 | Returns the day of the week as an integer between 0 and 6 inclusive starting from Monday by default. You can optionally change the start day of the week by specifying in the second argument |
| | | `WEEKDAY(NOW(), "sunday")` | If today is Monday, it returns 1 | Get the week day of NOW() with the first day set as sunday |
## DATETIME_DIFF
The DATETIME_DIFF function calculates the difference between two dates in various units.
#### Syntax
```plaintext
DATETIME_DIFF(date1, date2, ["milliseconds" | "ms" | "seconds" | "s" | "minutes" | "m" | "hours" | "h" | "days" | "d" | "weeks" | "w" | "months" | "M" | "quarters" | "Q" | "years" | "y"])
```
#### Sample
```plaintext
DATETIME_DIFF("2022/10/14", "2022/10/15", "seconds") => -86400
```
#### Remark
This function compares two dates and returns the difference in the specified unit. Positive integers indicate that second date is in the past compared to first, and vice versa for negative values.
---
## DATEADD
The DATEADD function adds a specified value to a date or datetime.
#### Syntax
```plaintext
DATEADD(date | datetime, value, ["day" | "week" | "month" | "year"])
```
#### Sample
```plaintext
DATEADD('2022-03-14', 1, 'day') => 2022-03-15
DATEADD('2022-03-14', 1, 'week') => 2022-03-21
DATEADD('2022-03-14', 1, 'month') => 2022-04-14
DATEADD('2022-03-14', 1, 'year') => 2023-03-14
```
#### Conditional Example
```plaintext
IF(NOW() < DATEADD(date, 10, 'day'), "true", "false") => If the current date is less than the specified date plus 10 days, it returns true. Otherwise, it returns false.
```
#### Remark
This function supports date and datetime fields and can handle negative values.
---
## NOW
The NOW function returns the current time and day.
#### Syntax
```plaintext
NOW()
```
#### Sample
```plaintext
NOW() => 2022-05-19 17:20:43 (current date & time)
```
#### Conditional Example
```plaintext
IF(NOW() < date, "true", "false") => If the current date is less than the specified date, it returns true. Otherwise, it returns false.
```
#### Remark
This function provides the current time and day, supporting datetime fields and negative values.
---
## WEEKDAY
The WEEKDAY function returns the day of the week as an integer.
#### Syntax
```plaintext
WEEKDAY(date, [startDayOfWeek])
```
#### Sample
```plaintext
WEEKDAY(NOW()) => If today is Monday, it returns 0.
WEEKDAY(NOW(), "sunday") => If today is Monday, it returns 1.
```
#### Remark
Returns the day of the week as an integer between 0 and 6 (inclusive), with Monday as the default start day. The start day of the week can be optionally changed by specifying it as the second argument.
---
## Related Articles
- [Numeric and Logical Operators](015.operators.md)

76
packages/noco-docs/docs/070.fields/040.field-types/060.formula/050.conditional-expressions.md

@ -1,19 +1,79 @@
---
title: 'Conditional expressions'
title: 'Conditional Expressions'
description: 'This article explains various conditional expressions that can be used in formula fields.'
tags: ['Fields', 'Field types', 'Formula']
keywords: ['Fields', 'Field types', 'Formula', 'Create formula field', 'Conditional expressions']
---
This cheat sheet provides a quick reference guide for various conditional expressions commonly used in data analysis and programming. Each expression is accompanied by its syntax, a sample usage, and a brief description.
### Conditional expressions
## IF
The IF function in programming and spreadsheet formulas provides a way to perform conditional operations. It evaluates a condition and returns a value if the condition is `TRUE`, or another value if the condition is `FALSE`.
| Name | Syntax | Sample | Output |
|------------|------------------------------------------------|------------------------------------------------|-------------------------------------------------------------|
| **IF** | `IF(expr, successCase, elseCase)` | `IF({field} > 1, Value1, Value2)` | successCase if `expr` evaluates to TRUE, elseCase otherwise |
| **SWITCH** | `SWITCH(expr, [pattern, value, ..., default])` | `SWITCH({field}, 1, 'One', 2, 'Two', '--')` | Switch case value based on `expr` output |
| **AND** | `AND(expr1, [expr2,...])` | `AND({field} > 2, {field} < 10)` | TRUE if all `expr` evaluate to TRUE |
| **OR** | `OR(expr1, [expr2,...])` | `OR({field} > 2, {field} < 10)` | TRUE if at least one `expr` evaluates to TRUE |
#### Syntax
```markdown
IF(expr, successCase, elseCase)
```
#### Sample
```markdown
IF({field} > 1, Value1, Value2)
Output
- `Value1` if `{field} > 1` evaluates to TRUE
- `Value2` otherwise
```
## SWITCH
The SWITCH function is a versatile tool for handling multiple cases. It evaluates the given expression (expr) against a series of patterns and returns the corresponding value of the first matching pattern. If none match, it returns the default value.
#### Syntax
```markdown
SWITCH(expr, [pattern, value, ..., default])
```
#### Sample
```markdown
SWITCH({field}, 1, 'One', 2, 'Two', '--')
Output
Switch case value based on the output of `{field}`:
- `'One'` if `{field} = 1`
- `'Two'` if `{field} = 2`
- `'--'` for the default case
```
## AND
The AND function is a logical operator that returns TRUE only if all its conditions are true.
#### Syntax
```markdown
AND(expr1, [expr2,...])
```
#### Sample
```markdown
AND({field} > 2, {field} < 10)
Output
TRUE if both `{field} > 2` and `{field} < 10` evaluate to TRUE
```
## OR
The OR function is another logical operator, returning TRUE if at least one of its conditions is true.
#### Syntax
```markdown
OR(expr1, [expr2,...])
```
#### Sample
```markdown
OR({field} > 2, {field} < 10)
Output
TRUE if at least one of the conditions `{field} > 2` or `{field} < 10` evaluates to TRUE
```
:::tip
Logical operators, along with Numerical operators can be used to build conditional `expressions`.

12
packages/nocodb-sdk/package.json

@ -1,6 +1,6 @@
{
"name": "nocodb-sdk",
"version": "0.202.8",
"version": "0.202.9",
"description": "NocoDB SDK",
"main": "build/main/index.js",
"typings": "build/main/index.d.ts",
@ -42,14 +42,14 @@
"jsep": "^1.3.8"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^6.1.0",
"@typescript-eslint/parser": "^6.1.0",
"@typescript-eslint/eslint-plugin": "^6.13.1",
"@typescript-eslint/parser": "^6.13.1",
"cspell": "^4.2.8",
"eslint": "^8.33.0",
"eslint-config-prettier": "^8.8.0",
"eslint": "^8.54.0",
"eslint-config-prettier": "^8.10.0",
"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-functional": "^5.0.8",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-import": "^2.29.0",
"eslint-plugin-prettier": "^4.2.1",
"npm-run-all": "^4.1.5",
"prettier": "^2.8.8",

2837
packages/nocodb-sdk/pnpm-lock.yaml

File diff suppressed because it is too large Load Diff

6
packages/nocodb/package.json

@ -1,6 +1,6 @@
{
"name": "nocodb",
"version": "0.202.8",
"version": "0.202.9",
"description": "NocoDB Backend",
"main": "dist/bundle.js",
"author": {
@ -132,7 +132,7 @@
"mysql2": "^3.6.5",
"nanoid": "^3.1.20",
"nc-help": "0.3.1",
"nc-lib-gui": "0.202.8",
"nc-lib-gui": "0.202.9",
"nc-plugin": "^0.1.3",
"ncp": "^2.0.0",
"nestjs-kafka": "^1.0.6",
@ -165,7 +165,7 @@
"socket.io": "^4.4.1",
"sql-query-identifier": "^2.5.0",
"sqlite3": "^5.1.6",
"tedious": "^16.6.0",
"tedious": "^16.6.1",
"tinycolor2": "^1.4.2",
"twilio": "^3.55.1",
"unique-names-generator": "^4.7.1",

8
packages/nocodb/src/controllers/data-table.controller.ts

@ -153,7 +153,13 @@ export class DataTableController {
@Param('columnId') columnId: string,
@Param('rowId') rowId: string,
@Body()
refRowIds: string | string[] | number | number[] | Record<string, any>,
refRowIds:
| string
| string[]
| number
| number[]
| Record<string, any>
| Record<string, any>[],
) {
return await this.dataTableService.nestedLink({
modelId,

42
packages/nocodb/src/db/BaseModelSqlv2.ts

@ -4869,7 +4869,9 @@ class BaseModelSqlv2 {
);
NcError.unprocessableEntity(
`Child record with id [${missingIds.join(', ')}] not found`,
`Child record with id [${extractIdsString(
missingIds,
)}] not found`,
);
}
@ -4878,8 +4880,11 @@ class BaseModelSqlv2 {
.filter((childRow) => !childRow[vChildCol.column_name])
// generate insert data for new links
.map((childRow) => ({
[vParentCol.column_name]: childRow[parentColumn.column_name],
[vChildCol.column_name]: row[childColumn.column_name],
[vParentCol.column_name]:
childRow[parentColumn.title] ??
childRow[parentColumn.column_name],
[vChildCol.column_name]:
row[childColumn.title] ?? row[childColumn.column_name],
}));
// if no new links, return true
@ -4930,7 +4935,9 @@ class BaseModelSqlv2 {
);
NcError.unprocessableEntity(
`Child record with id [${missingIds.join(', ')}] not found`,
`Child record with id [${extractIdsString(
missingIds,
)}] not found`,
);
}
}
@ -4980,7 +4987,10 @@ class BaseModelSqlv2 {
if (!childRow) {
NcError.unprocessableEntity(
`Child record with id [${childIds[0]}] not found`,
`Child record with id [${extractIdsString(
childIds,
true,
)}] not found`,
);
}
}
@ -5106,7 +5116,9 @@ class BaseModelSqlv2 {
);
NcError.unprocessableEntity(
`Child record with id [${missingIds.join(', ')}] not found`,
`Child record with id [${extractIdsString(
missingIds,
)}] not found`,
);
}
}
@ -5154,7 +5166,9 @@ class BaseModelSqlv2 {
);
NcError.unprocessableEntity(
`Child record with id [${missingIds.join(', ')}] not found`,
`Child record with id [${extractIdsString(
missingIds,
)}] not found`,
);
}
}
@ -5208,7 +5222,10 @@ class BaseModelSqlv2 {
if (!childRow) {
NcError.unprocessableEntity(
`Child record with id [${childIds[0]}] not found`,
`Child record with id [${extractIdsString(
childIds,
true,
)}] not found`,
);
}
}
@ -5674,4 +5691,13 @@ export function getListArgs(
return obj;
}
function extractIdsString(
childIds: (string | number | Record<string, any>)[],
isBt = false,
) {
return (isBt ? childIds.slice(0, 1) : childIds)
.map((r) => (typeof r === 'object' ? JSON.stringify(r) : r))
.join(', ');
}
export { BaseModelSqlv2 };

9
packages/nocodb/src/db/sortV2.ts

@ -1,12 +1,7 @@
import { RelationTypes, UITypes } from 'nocodb-sdk';
import { UITypes } from 'nocodb-sdk';
import type { BaseModelSqlv2 } from '~/db/BaseModelSqlv2';
import type { Knex } from 'knex';
import type {
FormulaColumn,
LinkToAnotherRecordColumn,
LookupColumn,
RollupColumn,
} from '~/models';
import type { FormulaColumn, RollupColumn } from '~/models';
import { NcError } from '~/helpers/catchError';
import formulaQueryBuilderv2 from '~/db/formulav2/formulaQueryBuilderv2';
import genRollupSelectv2 from '~/db/genRollupSelectv2';

2
packages/nocodb/src/helpers/getAst.ts

@ -68,7 +68,7 @@ const getAst = async ({
fields = Array.isArray(fields) ? fields : fields.split(',');
if (throwErrorIfInvalidParams) {
const colAliasMap = await model.getColAliasMapping();
const aliasColMap = await model.getAliasColMapping();
const aliasColMap = await model.getAliasColObjMap();
const invalidFields = fields.filter(
(f) => !colAliasMap[f] && !aliasColMap[f],
);

2
packages/nocodb/src/models/Store.ts

@ -58,7 +58,7 @@ export default class Store {
'tag',
]);
const existing = await Store.get(store.key,false, ncMeta);
const existing = await Store.get(store.key, false, ncMeta);
if (existing) {
await ncMeta.metaUpdate(null, null, MetaTable.STORE, insertObj, {
key: store.key,

8
packages/nocodb/src/services/data-table.service.ts

@ -371,7 +371,13 @@ export class DataTableService {
modelId: string;
columnId: string;
query: any;
refRowIds: string | string[] | number | number[] | Record<string, any>;
refRowIds:
| string
| string[]
| number
| number[]
| Record<string, any>
| Record<string, any>[];
rowId: string;
}) {
this.validateIds(param.refRowIds);

1144
pnpm-lock.yaml

File diff suppressed because it is too large Load Diff

2
scripts/pkg-executable/package.json

@ -28,7 +28,7 @@
"license": "ISC",
"dependencies": {
"express": "^4.17.3",
"nocodb": "0.202.8",
"nocodb": "0.202.9",
"@nestjs/common": "^10.2.10",
"@nestjs/core": "^10.2.10"
}

Loading…
Cancel
Save