Browse Source

Nc fix: UI improvements (#8223)

* fix(nc-gui): add background color for row on hover in grid view

* fix(nc-gui): reduce width of index column

* fix(nc-gui): on hover grid row bg opacity issue

* fix(nc-gui): reduce font size and grid cell height

* fix(nc-gui): reduce font size

* fix(nc-gui): set column default width to 180px

* fix(nc-gui): revert sidebar changes

* fix(nc-gui): disable grid row hover effect if fillMode is true

* fix(nc-gui): display checkbox & row expand icon if it is active row

* fix(nc-gui): increase the weight of display value cell

* fix(nc-gui): grid cell should have constant padding

* fix(nc-gui): att cell row height auto increase issue

* fix(nc-gui): att cell center align issue

* fix(nc-gui): percent cell spacing issue

* fix(nc-gui): text overflow ellipsis

* fix(nc-gui): single select auto increase height issue if rowHeight is short

* fix(nc-gui): grayed out header text color

* fix(nc-gui): reduce header icon size

* fix(nc-gui): set 12px grid header horizontal padding

* fix(nc-gui): center align expand icon for short row height

* fix(nc-gui): rich text and select type field auto row height increase issue

* fix(nc-gui): lookup cell auto increase height issue

* fix(nc-gui): small changes

* fix(nc-gui): truncate text after no. of lines base on rowHeight

* fix(nc-gui): barcode & qrcode cell row height issue

* chore(nc-gui): lint

* fix(test): update row height pw test

* fix(test): grid toolbar rowHeight test update

* fix(nc-gui): ai review changes

* fix(test): multiselect pw test update

* fix(test): verify multiselct options test update

* fix(nc-gui): merge conflicts

* fix(nc-gui): small changes

* fix(nc-gui): small changes

* fix(nc-gui): display value should be in blue color
pull/8300/head
Ramesh Mane 7 months ago committed by GitHub
parent
commit
e9aa37e284
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 7
      packages/nc-gui/components/cell/Checkbox.vue
  2. 5
      packages/nc-gui/components/cell/ClampedText.vue
  3. 2
      packages/nc-gui/components/cell/Currency.vue
  4. 4
      packages/nc-gui/components/cell/Decimal.vue
  5. 4
      packages/nc-gui/components/cell/Email.vue
  6. 4
      packages/nc-gui/components/cell/Float.vue
  7. 4
      packages/nc-gui/components/cell/Integer.vue
  8. 43
      packages/nc-gui/components/cell/MultiSelect.vue
  9. 6
      packages/nc-gui/components/cell/Percent.vue
  10. 4
      packages/nc-gui/components/cell/PhoneNumber.vue
  11. 32
      packages/nc-gui/components/cell/RichText.vue
  12. 20
      packages/nc-gui/components/cell/SingleSelect.vue
  13. 46
      packages/nc-gui/components/cell/TextArea.vue
  14. 6
      packages/nc-gui/components/cell/Url.vue
  15. 55
      packages/nc-gui/components/cell/User.vue
  16. 38
      packages/nc-gui/components/cell/attachment/index.vue
  17. 44
      packages/nc-gui/components/smartsheet/Cell.vue
  18. 10
      packages/nc-gui/components/smartsheet/VirtualCell.vue
  19. 2
      packages/nc-gui/components/smartsheet/column/DefaultValue.vue
  20. 170
      packages/nc-gui/components/smartsheet/grid/Table.vue
  21. 2
      packages/nc-gui/components/smartsheet/header/VirtualCell.vue
  22. 9
      packages/nc-gui/components/virtual-cell/Lookup.vue
  23. 6
      packages/nc-gui/components/virtual-cell/QrCode.vue
  24. 6
      packages/nc-gui/components/virtual-cell/barcode/Barcode.vue
  25. 2
      packages/nc-gui/composables/useViewColumns.ts
  26. 20
      packages/nc-gui/utils/cell.ts
  27. 4
      packages/nc-gui/windi.config.ts
  28. 4
      packages/nocodb/src/models/GridViewColumn.ts
  29. 2
      tests/playwright/pages/Dashboard/Grid/Row.ts
  30. 32
      tests/playwright/pages/Dashboard/common/Cell/SelectOptionCell.ts
  31. 10
      tests/playwright/tests/db/features/undo-redo.spec.ts
  32. 8
      tests/playwright/tests/db/general/toolbarOperations.spec.ts

7
packages/nc-gui/components/cell/Checkbox.vue

@ -9,6 +9,7 @@ import {
getMdiIcon, getMdiIcon,
inject, inject,
parseProp, parseProp,
rowHeightInPx,
useBase, useBase,
useSelectedCellKeyupListener, useSelectedCellKeyupListener,
} from '#imports' } from '#imports'
@ -47,6 +48,8 @@ const rowHeight = inject(RowHeightInj, ref())
const isSurveyForm = inject(IsSurveyFormInj, ref(false)) const isSurveyForm = inject(IsSurveyFormInj, ref(false))
const isGrid = inject(IsGridInj, ref(false))
const checkboxMeta = computed(() => { const checkboxMeta = computed(() => {
return { return {
icon: { icon: {
@ -110,7 +113,9 @@ useSelectedCellKeyupListener(active, (e) => {
}" }"
:style="{ :style="{
height: height:
isForm || isExpandedFormOpen || isGallery || isEditColumnMenu ? undefined : `max(${(rowHeight || 1) * 1.8}rem, 41px)`, isGrid && !isForm && !isExpandedFormOpen && !isEditColumnMenu
? `${!rowHeight || rowHeight === 1 ? rowHeightInPx['1'] - 4 : rowHeightInPx[`${rowHeight}`] - 20}px`
: undefined,
}" }"
:tabindex="readOnly ? -1 : 0" :tabindex="readOnly ? -1 : 0"
@click="onClick(false, $event)" @click="onClick(false, $event)"

5
packages/nc-gui/components/cell/ClampedText.vue

@ -6,7 +6,12 @@ const props = defineProps<{
</script> </script>
<template> <template>
<div v-if="!props.lines || props.lines === 1" class="text-ellipsis overflow-hidden">
<span :style="{ 'word-break': 'keep-all', 'white-space': 'nowrap' }">{{ props.value || '' }}</span>
</div>
<div <div
v-else
:style="{ :style="{
'display': '-webkit-box', 'display': '-webkit-box',
'max-width': '100%', 'max-width': '100%',

2
packages/nc-gui/components/cell/Currency.vue

@ -116,7 +116,7 @@ onMounted(() => {
:ref="focus" :ref="focus"
v-model="vModel" v-model="vModel"
type="number" type="number"
class="nc-cell-field h-full text-sm border-none rounded-md py-1 outline-none focus:outline-none focus:ring-0" class="nc-cell-field h-full border-none rounded-md py-1 outline-none focus:outline-none focus:ring-0"
:class="isForm && !isEditColumn ? 'flex flex-1' : 'w-full'" :class="isForm && !isEditColumn ? 'flex flex-1' : 'w-full'"
:placeholder="isEditColumn ? $t('labels.optional') : ''" :placeholder="isEditColumn ? $t('labels.optional') : ''"
:disabled="readOnly" :disabled="readOnly"

4
packages/nc-gui/components/cell/Decimal.vue

@ -99,7 +99,7 @@ watch(isExpandedFormOpen, () => {
v-if="!readOnly && editEnabled" v-if="!readOnly && editEnabled"
:ref="focus" :ref="focus"
v-model="vModel" v-model="vModel"
class="nc-cell-field outline-none py-1 border-none rounded-md w-full h-full !text-sm" class="nc-cell-field outline-none py-1 border-none rounded-md w-full h-full"
type="number" type="number"
:step="precision" :step="precision"
:placeholder="isEditColumn ? $t('labels.optional') : ''" :placeholder="isEditColumn ? $t('labels.optional') : ''"
@ -114,7 +114,7 @@ watch(isExpandedFormOpen, () => {
@mousedown.stop @mousedown.stop
/> />
<span v-else-if="vModel === null && showNull" class="nc-cell-field nc-null uppercase">{{ $t('general.null') }}</span> <span v-else-if="vModel === null && showNull" class="nc-cell-field nc-null uppercase">{{ $t('general.null') }}</span>
<span v-else class="nc-cell-field text-sm">{{ displayValue }}</span> <span v-else class="nc-cell-field">{{ displayValue }}</span>
</template> </template>
<style scoped lang="scss"> <style scoped lang="scss">

4
packages/nc-gui/components/cell/Email.vue

@ -91,7 +91,7 @@ watch(
v-if="!readOnly && editEnabled" v-if="!readOnly && editEnabled"
:ref="focus" :ref="focus"
v-model="vModel" v-model="vModel"
class="nc-cell-field w-full outline-none text-sm py-1" class="nc-cell-field w-full outline-none py-1"
:placeholder="isEditColumn ? $t('labels.optional') : ''" :placeholder="isEditColumn ? $t('labels.optional') : ''"
@blur="editEnabled = false" @blur="editEnabled = false"
@keydown.down.stop @keydown.down.stop
@ -109,7 +109,7 @@ watch(
<nuxt-link <nuxt-link
v-else-if="validEmail" v-else-if="validEmail"
no-ref no-ref
class="py-1 text-sm underline hover:opacity-75 inline-block" class="py-1 underline hover:opacity-75 inline-block nc-cell-field-link"
:href="`mailto:${vModel}`" :href="`mailto:${vModel}`"
target="_blank" target="_blank"
:tabindex="readOnly ? -1 : 0" :tabindex="readOnly ? -1 : 0"

4
packages/nc-gui/components/cell/Float.vue

@ -51,7 +51,7 @@ const focus: VNodeRef = (el) =>
v-if="editEnabled" v-if="editEnabled"
:ref="focus" :ref="focus"
v-model="vModel" v-model="vModel"
class="nc-cell-field outline-none px-1 border-none w-full h-full text-sm" class="nc-cell-field outline-none px-1 border-none w-full h-full"
type="number" type="number"
step="0.1" step="0.1"
:placeholder="isEditColumn ? $t('labels.optional') : ''" :placeholder="isEditColumn ? $t('labels.optional') : ''"
@ -65,7 +65,7 @@ const focus: VNodeRef = (el) =>
@mousedown.stop @mousedown.stop
/> />
<span v-else-if="vModel === null && showNull" class="nc-cell-field nc-null uppercase">{{ $t('general.null') }}</span> <span v-else-if="vModel === null && showNull" class="nc-cell-field nc-null uppercase">{{ $t('general.null') }}</span>
<span v-else class="nc-cell-field text-sm">{{ vModel }}</span> <span v-else class="nc-cell-field">{{ vModel }}</span>
</template> </template>
<style scoped lang="scss"> <style scoped lang="scss">

4
packages/nc-gui/components/cell/Integer.vue

@ -94,7 +94,7 @@ function onKeyDown(e: any) {
v-if="!readOnly && editEnabled" v-if="!readOnly && editEnabled"
:ref="focus" :ref="focus"
v-model="vModel" v-model="vModel"
class="nc-cell-field outline-none py-1 border-none w-full h-full text-sm" class="nc-cell-field outline-none py-1 border-none w-full h-full"
:type="inputType" :type="inputType"
style="letter-spacing: 0.06rem" style="letter-spacing: 0.06rem"
:placeholder="isEditColumn ? $t('labels.optional') : ''" :placeholder="isEditColumn ? $t('labels.optional') : ''"
@ -109,7 +109,7 @@ function onKeyDown(e: any) {
@mousedown.stop @mousedown.stop
/> />
<span v-else-if="vModel === null && showNull" class="nc-cell-field nc-null uppercase">{{ $t('general.null') }}</span> <span v-else-if="vModel === null && showNull" class="nc-cell-field nc-null uppercase">{{ $t('general.null') }}</span>
<span v-else class="nc-cell-field text-sm">{{ displayValue }}</span> <span v-else class="nc-cell-field">{{ displayValue }}</span>
</template> </template>
<style scoped lang="scss"> <style scoped lang="scss">

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

@ -23,6 +23,7 @@ import {
onMounted, onMounted,
reactive, reactive,
ref, ref,
rowHeightTruncateLines,
useBase, useBase,
useEventListener, useEventListener,
useMetas, useMetas,
@ -413,11 +414,11 @@ const onFocus = () => {
<a-tag class="rounded-tag max-w-full" :color="op.color"> <a-tag class="rounded-tag max-w-full" :color="op.color">
<span <span
:style="{ :style="{
'color': tinycolor.isReadable(op.color || '#ccc', '#fff', { level: 'AA', size: 'large' }) color: tinycolor.isReadable(op.color || '#ccc', '#fff', { level: 'AA', size: 'large' })
? '#fff' ? '#fff'
: tinycolor.mostReadable(op.color || '#ccc', ['#0b1d05', '#fff']).toHex8String(), : tinycolor.mostReadable(op.color || '#ccc', ['#0b1d05', '#fff']).toHex8String(),
'font-size': '13px',
}" }"
class="text-small"
> >
<NcTooltip class="truncate max-w-full" show-on-truncate-only> <NcTooltip class="truncate max-w-full" show-on-truncate-only>
<template #title> <template #title>
@ -446,21 +447,26 @@ const onFocus = () => {
:style="{ :style="{
'display': '-webkit-box', 'display': '-webkit-box',
'max-width': '100%', 'max-width': '100%',
'-webkit-line-clamp': rowHeight || 1, '-webkit-line-clamp': rowHeightTruncateLines(rowHeight),
'-webkit-box-orient': 'vertical', '-webkit-box-orient': 'vertical',
'overflow': 'hidden', 'overflow': 'hidden',
}" }"
> >
<template v-for="selectedOpt of selectedOpts" :key="selectedOpt.value"> <template v-for="selectedOpt of selectedOpts" :key="selectedOpt.value">
<a-tag class="rounded-tag max-w-full" :color="selectedOpt.color"> <a-tag
class="rounded-tag max-w-full"
:class="{
'!my-0': !rowHeight || rowHeight === 1,
}"
:color="selectedOpt.color"
>
<span <span
:style="{ :style="{
'color': tinycolor.isReadable(selectedOpt.color || '#ccc', '#fff', { level: 'AA', size: 'large' }) color: tinycolor.isReadable(selectedOpt.color || '#ccc', '#fff', { level: 'AA', size: 'large' })
? '#fff' ? '#fff'
: tinycolor.mostReadable(selectedOpt.color || '#ccc', ['#0b1d05', '#fff']).toHex8String(), : tinycolor.mostReadable(selectedOpt.color || '#ccc', ['#0b1d05', '#fff']).toHex8String(),
'font-size': '13px',
}" }"
:class="{ 'text-sm': isKanban }" :class="{ 'text-sm': isKanban, 'text-small': !isKanban }"
> >
<NcTooltip class="truncate max-w-full" show-on-truncate-only> <NcTooltip class="truncate max-w-full" show-on-truncate-only>
<template #title> <template #title>
@ -496,7 +502,7 @@ const onFocus = () => {
:open="isOpen && editAllowed" :open="isOpen && editAllowed"
:disabled="readOnly || !editAllowed" :disabled="readOnly || !editAllowed"
:class="{ 'caret-transparent': !hasEditRoles }" :class="{ 'caret-transparent': !hasEditRoles }"
:dropdown-class-name="`nc-dropdown-multi-select-cell !min-w-200px ${isOpen ? 'active' : ''}`" :dropdown-class-name="`nc-dropdown-multi-select-cell !min-w-156px ${isOpen ? 'active' : ''}`"
@search="search" @search="search"
@keydown="onKeyDown" @keydown="onKeyDown"
@focus="onFocus" @focus="onFocus"
@ -516,12 +522,11 @@ const onFocus = () => {
<a-tag class="rounded-tag max-w-full" :color="op.color"> <a-tag class="rounded-tag max-w-full" :color="op.color">
<span <span
:style="{ :style="{
'color': tinycolor.isReadable(op.color || '#ccc', '#fff', { level: 'AA', size: 'large' }) color: tinycolor.isReadable(op.color || '#ccc', '#fff', { level: 'AA', size: 'large' })
? '#fff' ? '#fff'
: tinycolor.mostReadable(op.color || '#ccc', ['#0b1d05', '#fff']).toHex8String(), : tinycolor.mostReadable(op.color || '#ccc', ['#0b1d05', '#fff']).toHex8String(),
'font-size': '13px',
}" }"
:class="{ 'text-sm': isKanban }" :class="{ 'text-sm': isKanban, 'text-small': !isKanban }"
> >
<NcTooltip class="truncate max-w-full" show-on-truncate-only> <NcTooltip class="truncate max-w-full" show-on-truncate-only>
<template #title> <template #title>
@ -559,6 +564,9 @@ const onFocus = () => {
<a-tag <a-tag
v-if="options.find((el) => el.title === val)" v-if="options.find((el) => el.title === val)"
class="rounded-tag nc-selected-option" class="rounded-tag nc-selected-option"
:class="{
'!my-0': !rowHeight || rowHeight === 1,
}"
:style="{ display: 'flex', alignItems: 'center' }" :style="{ display: 'flex', alignItems: 'center' }"
:color="options.find((el) => el.title === val)?.color" :color="options.find((el) => el.title === val)?.color"
:closable="editAllowed && (vModel.length > 1 || !column?.rqd)" :closable="editAllowed && (vModel.length > 1 || !column?.rqd)"
@ -568,7 +576,7 @@ const onFocus = () => {
> >
<span <span
:style="{ :style="{
'color': tinycolor.isReadable(options.find((el) => el.title === val)?.color || '#ccc', '#fff', { color: tinycolor.isReadable(options.find((el) => el.title === val)?.color || '#ccc', '#fff', {
level: 'AA', level: 'AA',
size: 'large', size: 'large',
}) })
@ -576,9 +584,8 @@ const onFocus = () => {
: tinycolor : tinycolor
.mostReadable(options.find((el) => el.title === val)?.color || '#ccc', ['#0b1d05', '#fff']) .mostReadable(options.find((el) => el.title === val)?.color || '#ccc', ['#0b1d05', '#fff'])
.toHex8String(), .toHex8String(),
'font-size': '13px',
}" }"
:class="{ 'text-sm': isKanban }" :class="{ 'text-sm': isKanban, 'text-small': !isKanban }"
> >
{{ val }} {{ val }}
</span> </span>
@ -622,11 +629,11 @@ const onFocus = () => {
} }
.rounded-tag { .rounded-tag {
@apply py-0 px-[12px] rounded-[12px]; @apply py-[0.5px] px-2 rounded-[12px];
} }
:deep(.ant-tag) { :deep(.ant-tag) {
@apply "rounded-tag" my-[2px]; @apply "rounded-tag" my-[1px];
} }
:deep(.ant-tag-close-icon) { :deep(.ant-tag-close-icon) {
@ -638,7 +645,7 @@ const onFocus = () => {
} }
:deep(.ant-select-selection-overflow) { :deep(.ant-select-selection-overflow) {
@apply flex-nowrap overflow-hidden; @apply flex-nowrap overflow-hidden max-w-[fit-content];
} }
.nc-multi-select:not(.read-only) { .nc-multi-select:not(.read-only) {
@ -649,7 +656,7 @@ const onFocus = () => {
} }
:deep(.ant-select-selector) { :deep(.ant-select-selector) {
@apply !pl-0; @apply !pl-0 flex-nowrap;
} }
:deep(.ant-select-selection-search-input) { :deep(.ant-select-selection-search-input) {

6
packages/nc-gui/components/cell/Percent.vue

@ -140,10 +140,10 @@ const onTabPress = (e: KeyboardEvent) => {
@focus="onWrapperFocus" @focus="onWrapperFocus"
> >
<input <input
v-if="!readOnly && editEnabled && (isExpandedFormOpen ? expandedEditEnabled : true)" v-if="!readOnly && editEnabled && (isExpandedFormOpen ? expandedEditEnabled || !percentMeta.is_progress : true)"
:ref="focus" :ref="focus"
v-model="vModel" v-model="vModel"
class="nc-cell-field w-full !text-sm !border-none !outline-none focus:ring-0 text-base py-1" class="nc-cell-field w-full !border-none !outline-none focus:ring-0 py-1"
:type="inputType" :type="inputType"
:placeholder="isEditColumn ? $t('labels.optional') : ''" :placeholder="isEditColumn ? $t('labels.optional') : ''"
@blur="onBlur" @blur="onBlur"
@ -169,7 +169,7 @@ const onTabPress = (e: KeyboardEvent) => {
/> />
</div> </div>
<!-- nbsp to keep height even if vModel is zero length --> <!-- nbsp to keep height even if vModel is zero length -->
<span v-else class="nc-cell-field">{{ vModel }}&nbsp;</span> <span v-else class="nc-cell-field">{{ vModel }} {{ !vModel ? '&nbsp;' : '' }}</span>
</div> </div>
</template> </template>

4
packages/nc-gui/components/cell/PhoneNumber.vue

@ -67,7 +67,7 @@ watch(
v-if="!readOnly && editEnabled" v-if="!readOnly && editEnabled"
:ref="focus" :ref="focus"
v-model="vModel" v-model="vModel"
class="nc-cell-field w-full outline-none text-sm py-1" class="nc-cell-field w-full outline-none py-1"
:placeholder="isEditColumn ? $t('labels.optional') : ''" :placeholder="isEditColumn ? $t('labels.optional') : ''"
@blur="editEnabled = false" @blur="editEnabled = false"
@keydown.down.stop @keydown.down.stop
@ -83,7 +83,7 @@ watch(
<a <a
v-else-if="validPhoneNumber" v-else-if="validPhoneNumber"
class="py-1 text-sm underline hover:opacity-75 inline-block" class="py-1 underline hover:opacity-75 inline-block nc-cell-field-link"
:href="`tel:${vModel}`" :href="`tel:${vModel}`"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"

32
packages/nc-gui/components/cell/RichText.vue

@ -10,7 +10,15 @@ import Placeholder from '@tiptap/extension-placeholder'
import { TaskItem } from '@/helpers/dbTiptapExtensions/task-item' import { TaskItem } from '@/helpers/dbTiptapExtensions/task-item'
import { Link } from '@/helpers/dbTiptapExtensions/links' import { Link } from '@/helpers/dbTiptapExtensions/links'
import type { RichTextBubbleMenuOptions } from '#imports' import type { RichTextBubbleMenuOptions } from '#imports'
import { IsExpandedFormOpenInj, IsFormInj, IsGridInj, IsSurveyFormInj, ReadonlyInj, RowHeightInj } from '#imports' import {
IsExpandedFormOpenInj,
IsFormInj,
IsGridInj,
IsSurveyFormInj,
ReadonlyInj,
RowHeightInj,
rowHeightTruncateLines,
} from '#imports'
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
@ -283,7 +291,7 @@ useEventListener(
'mt-2.5 flex-grow': fullMode, 'mt-2.5 flex-grow': fullMode,
'scrollbar-thin scrollbar-thumb-gray-200 scrollbar-track-transparent': !fullMode || (!fullMode && isExpandedFormOpen), 'scrollbar-thin scrollbar-thumb-gray-200 scrollbar-track-transparent': !fullMode || (!fullMode && isExpandedFormOpen),
'flex-grow': isExpandedFormOpen, 'flex-grow': isExpandedFormOpen,
[`!overflow-hidden children:line-clamp-${rowHeight}`]: [`!overflow-hidden nc-truncate nc-line-clamp-${rowHeightTruncateLines(rowHeight)}`]:
!fullMode && readOnly && rowHeight && !isExpandedFormOpen && !isForm, !fullMode && readOnly && rowHeight && !isExpandedFormOpen && !isForm,
}" }"
@keydown.alt.enter.stop @keydown.alt.enter.stop
@ -379,6 +387,26 @@ useEventListener(
} }
.nc-textarea-rich-editor { .nc-textarea-rich-editor {
&.nc-truncate {
.tiptap.ProseMirror {
display: -webkit-box;
max-width: 100%;
-webkit-box-orient: vertical;
word-break: break-word;
}
&.nc-line-clamp-1 .tiptap.ProseMirror {
-webkit-line-clamp: 1;
}
&.nc-line-clamp-2 .tiptap.ProseMirror {
-webkit-line-clamp: 2;
}
&.nc-line-clamp-3 .tiptap.ProseMirror {
-webkit-line-clamp: 3;
}
&.nc-line-clamp-4 .tiptap.ProseMirror {
-webkit-line-clamp: 4;
}
}
.tiptap p.is-editor-empty:first-child::before { .tiptap p.is-editor-empty:first-child::before {
color: #9aa2af; color: #9aa2af;
content: attr(data-placeholder); content: attr(data-placeholder);

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

@ -340,11 +340,11 @@ const onFocus = () => {
<a-tag class="rounded-tag max-w-full" :color="op.color"> <a-tag class="rounded-tag max-w-full" :color="op.color">
<span <span
:style="{ :style="{
'color': tinycolor.isReadable(op.color || '#ccc', '#fff', { level: 'AA', size: 'large' }) color: tinycolor.isReadable(op.color || '#ccc', '#fff', { level: 'AA', size: 'large' })
? '#fff' ? '#fff'
: tinycolor.mostReadable(op.color || '#ccc', ['#0b1d05', '#fff']).toHex8String(), : tinycolor.mostReadable(op.color || '#ccc', ['#0b1d05', '#fff']).toHex8String(),
'font-size': '13px',
}" }"
class="text-small"
> >
<NcTooltip class="truncate max-w-full" show-on-truncate-only> <NcTooltip class="truncate max-w-full" show-on-truncate-only>
<template #title> <template #title>
@ -378,12 +378,11 @@ const onFocus = () => {
<a-tag v-if="selectedOpt" class="rounded-tag max-w-full" :color="selectedOpt.color"> <a-tag v-if="selectedOpt" class="rounded-tag max-w-full" :color="selectedOpt.color">
<span <span
:style="{ :style="{
'color': tinycolor.isReadable(selectedOpt.color || '#ccc', '#fff', { level: 'AA', size: 'large' }) color: tinycolor.isReadable(selectedOpt.color || '#ccc', '#fff', { level: 'AA', size: 'large' })
? '#fff' ? '#fff'
: tinycolor.mostReadable(selectedOpt.color || '#ccc', ['#0b1d05', '#fff']).toHex8String(), : tinycolor.mostReadable(selectedOpt.color || '#ccc', ['#0b1d05', '#fff']).toHex8String(),
'font-size': '13px',
}" }"
:class="{ 'text-sm': isKanban }" :class="{ 'text-sm': isKanban, 'text-small': !isKanban }"
> >
<NcTooltip class="truncate max-w-full" show-on-truncate-only> <NcTooltip class="truncate max-w-full" show-on-truncate-only>
<template #title> <template #title>
@ -417,7 +416,7 @@ const onFocus = () => {
:disabled="readOnly || !editAllowed" :disabled="readOnly || !editAllowed"
:show-search="!isMobileMode && isOpen && active" :show-search="!isMobileMode && isOpen && active"
:show-arrow="hasEditRoles && !readOnly && active && (vModel === null || vModel === undefined)" :show-arrow="hasEditRoles && !readOnly && active && (vModel === null || vModel === undefined)"
:dropdown-class-name="`nc-dropdown-single-select-cell !min-w-200px ${isOpen && active ? 'active' : ''}`" :dropdown-class-name="`nc-dropdown-single-select-cell !min-w-156px ${isOpen && active ? 'active' : ''}`"
:dropdown-match-select-width="true" :dropdown-match-select-width="true"
@select="onSelect" @select="onSelect"
@keydown="onKeydown($event)" @keydown="onKeydown($event)"
@ -436,12 +435,11 @@ const onFocus = () => {
<a-tag class="rounded-tag max-w-full" :color="op.color"> <a-tag class="rounded-tag max-w-full" :color="op.color">
<span <span
:style="{ :style="{
'color': tinycolor.isReadable(op.color || '#ccc', '#fff', { level: 'AA', size: 'large' }) color: tinycolor.isReadable(op.color || '#ccc', '#fff', { level: 'AA', size: 'large' })
? '#fff' ? '#fff'
: tinycolor.mostReadable(op.color || '#ccc', ['#0b1d05', '#fff']).toHex8String(), : tinycolor.mostReadable(op.color || '#ccc', ['#0b1d05', '#fff']).toHex8String(),
'font-size': '13px',
}" }"
:class="{ 'text-sm': isKanban }" :class="{ 'text-sm': isKanban, 'text-small': !isKanban }"
> >
<NcTooltip class="truncate max-w-full" show-on-truncate-only> <NcTooltip class="truncate max-w-full" show-on-truncate-only>
<template #title> <template #title>
@ -476,11 +474,11 @@ const onFocus = () => {
<style scoped lang="scss"> <style scoped lang="scss">
.rounded-tag { .rounded-tag {
@apply py-0 px-[12px] rounded-[12px]; @apply py-[1px] px-2 rounded-[12px];
} }
:deep(.ant-tag) { :deep(.ant-tag) {
@apply "rounded-tag" my-[2px]; @apply "rounded-tag";
} }
:deep(.ant-select-clear) { :deep(.ant-select-clear) {

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

@ -6,6 +6,7 @@ import {
EditModeInj, EditModeInj,
IsExpandedFormOpenInj, IsExpandedFormOpenInj,
IsFormInj, IsFormInj,
IsGridInj,
ReadonlyInj, ReadonlyInj,
RowHeightInj, RowHeightInj,
computed, computed,
@ -36,6 +37,8 @@ const rowHeight = inject(RowHeightInj, ref(1 as const))
const isForm = inject(IsFormInj, ref(false)) const isForm = inject(IsFormInj, ref(false))
const isGrid = inject(IsGridInj, ref(false))
const readOnly = inject(ReadonlyInj, ref(false)) const readOnly = inject(ReadonlyInj, ref(false))
const { showNull } = useGlobal() const { showNull } = useGlobal()
@ -208,7 +211,7 @@ watch(inputWrapperRef, () => {
class="flex flex-row w-full long-text-wrapper" class="flex flex-row w-full long-text-wrapper"
:class="{ :class="{
'min-h-10': rowHeight !== 1 || isExpandedFormOpen, 'min-h-10': rowHeight !== 1 || isExpandedFormOpen,
'min-h-9': rowHeight === 1 && !isExpandedFormOpen, 'min-h-5.5': rowHeight === 1 && !isExpandedFormOpen,
'h-full w-full': isForm, 'h-full w-full': isForm,
}" }"
> >
@ -240,8 +243,8 @@ watch(inputWrapperRef, () => {
'nc-readonly-rich-text-sort-height': rowHeight === 1 && !isExpandedFormOpen && !isForm, 'nc-readonly-rich-text-sort-height': rowHeight === 1 && !isExpandedFormOpen && !isForm,
}" }"
:style="{ :style="{
maxHeight: isForm ? undefined : isExpandedFormOpen ? `${height}px` : `${25 * (rowHeight || 1)}px`, maxHeight: isForm ? undefined : isExpandedFormOpen ? `${height}px` : `${21 * rowHeightTruncateLines(rowHeight)}px`,
minHeight: isForm ? undefined : isExpandedFormOpen ? `${height}px` : `${25 * (rowHeight || 1)}px`, minHeight: isForm ? undefined : isExpandedFormOpen ? `${height}px` : `${21 * rowHeightTruncateLines(rowHeight)}px`,
}" }"
@dblclick="onExpand" @dblclick="onExpand"
@keydown.enter="onExpand" @keydown.enter="onExpand"
@ -281,11 +284,12 @@ watch(inputWrapperRef, () => {
<LazyCellClampedText <LazyCellClampedText
v-else-if="rowHeight" v-else-if="rowHeight"
:value="vModel" :value="vModel"
:lines="rowHeight" :lines="rowHeightTruncateLines(rowHeight)"
class="nc-text-area-clamped-text my-auto" class="nc-text-area-clamped-text"
:style="{ :style="{
'word-break': 'break-word', 'word-break': 'break-word',
'max-height': `${25 * (rowHeight || 1)}px`, 'max-height': `${25 * rowHeightTruncateLines(rowHeight)}px`,
'my-auto': rowHeightTruncateLines(rowHeight) === 1,
}" }"
@click="onTextClick" @click="onTextClick"
/> />
@ -295,12 +299,28 @@ watch(inputWrapperRef, () => {
<NcTooltip <NcTooltip
v-if="!isVisible && !isForm" v-if="!isVisible && !isForm"
placement="bottom" placement="bottom"
class="!absolute top-1 hidden nc-text-area-expand-btn group-hover:block z-3" class="!absolute !hidden nc-text-area-expand-btn group-hover:block z-3"
:class="isForm ? 'right-1' : 'right-0'" :class="{
'right-1': isForm,
'right-0': !isForm,
'top-0': isGrid && !isExpandedFormOpen && !isForm && !(!rowHeight || rowHeight === 1),
'top-1': !(isGrid && !isExpandedFormOpen && !isForm),
}"
:style="
isGrid && !isExpandedFormOpen && !isForm && (!rowHeight || rowHeight === 1)
? { top: '50%', transform: 'translateY(-50%)' }
: undefined
"
> >
<template #title>{{ $t('title.expand') }}</template> <template #title>{{ $t('title.expand') }}</template>
<NcButton type="secondary" size="xsmall" data-testid="attachment-cell-file-picker-button" @click.stop="onExpand"> <NcButton
<component :is="iconMap.expand" class="transform group-hover:(!text-grey-800 ) scale-120 text-gray-700 text-xs" /> type="secondary"
size="xsmall"
data-testid="attachment-cell-file-picker-button"
class="!p-0 !w-5 !h-5 !min-w-[fit-content]"
@click.stop="onExpand"
>
<component :is="iconMap.expand" class="transform group-hover:(!text-grey-800) text-gray-700 text-xs" />
</NcButton> </NcButton>
</NcTooltip> </NcTooltip>
</div> </div>
@ -381,9 +401,9 @@ textarea:focus {
:deep(.ProseMirror) { :deep(.ProseMirror) {
@apply !pt-0; @apply !pt-0;
} }
&.nc-readonly-rich-text-sort-height { // &.nc-readonly-rich-text-sort-height {
@apply mt-2; // @apply mt-1;
} // }
} }
} }
</style> </style>

6
packages/nc-gui/components/cell/Url.vue

@ -104,7 +104,7 @@ watch(
:ref="focus" :ref="focus"
v-model="vModel" v-model="vModel"
:placeholder="isEditColumn ? $t('labels.enterDefaultUrlOptional') : ''" :placeholder="isEditColumn ? $t('labels.enterDefaultUrlOptional') : ''"
class="nc-cell-field outline-none text-sm w-full py-1 bg-transparent h-full" class="nc-cell-field outline-none w-full py-1 bg-transparent h-full"
@blur="editEnabled = false" @blur="editEnabled = false"
@keydown.down.stop @keydown.down.stop
@keydown.left.stop @keydown.left.stop
@ -121,7 +121,7 @@ watch(
v-else-if="isValid && !cellUrlOptions?.overlay" v-else-if="isValid && !cellUrlOptions?.overlay"
no-prefetch no-prefetch
no-rel no-rel
class="py-1 z-3 text-sm underline hover:opacity-75" class="py-1 z-3 underline hover:opacity-75 nc-cell-field-link"
:to="url" :to="url"
:target="cellUrlOptions?.behavior === 'replace' ? undefined : '_blank'" :target="cellUrlOptions?.behavior === 'replace' ? undefined : '_blank'"
:tabindex="readOnly ? -1 : 0" :tabindex="readOnly ? -1 : 0"
@ -133,7 +133,7 @@ watch(
v-else-if="isValid && !disableOverlay && cellUrlOptions?.overlay" v-else-if="isValid && !disableOverlay && cellUrlOptions?.overlay"
no-prefetch no-prefetch
no-rel no-rel
class="py-1 z-3 w-full h-full text-center !no-underline hover:opacity-75" class="py-1 z-3 w-full h-full text-center !no-underline hover:opacity-75 nc-cell-field-link"
:to="url" :to="url"
:target="cellUrlOptions?.behavior === 'replace' ? undefined : '_blank'" :target="cellUrlOptions?.behavior === 'replace' ? undefined : '_blank'"
:tabindex="readOnly ? -1 : 0" :tabindex="readOnly ? -1 : 0"

55
packages/nc-gui/components/cell/User.vue

@ -20,6 +20,7 @@ import {
isDrawerOrModalExist, isDrawerOrModalExist,
onMounted, onMounted,
ref, ref,
rowHeightTruncateLines,
useEventListener, useEventListener,
useRoles, useRoles,
useSelectedCellKeyupListener, useSelectedCellKeyupListener,
@ -343,12 +344,11 @@ const filterOption = (input: string, option: any) => {
<a-tag class="rounded-tag max-w-full !pl-0" color="'#ccc'"> <a-tag class="rounded-tag max-w-full !pl-0" color="'#ccc'">
<span <span
:style="{ :style="{
'color': tinycolor.isReadable('#ccc' || '#ccc', '#fff', { level: 'AA', size: 'large' }) color: tinycolor.isReadable('#ccc' || '#ccc', '#fff', { level: 'AA', size: 'large' })
? '#fff' ? '#fff'
: tinycolor.mostReadable('#ccc' || '#ccc', ['#0b1d05', '#fff']).toHex8String(), : tinycolor.mostReadable('#ccc' || '#ccc', ['#0b1d05', '#fff']).toHex8String(),
'font-size': '13px',
}" }"
class="flex items-stretch gap-2" class="flex items-stretch gap-2 text-small"
> >
<div> <div>
<GeneralUserIcon <GeneralUserIcon
@ -393,22 +393,27 @@ const filterOption = (input: string, option: any) => {
:style="{ :style="{
'display': '-webkit-box', 'display': '-webkit-box',
'max-width': '100%', 'max-width': '100%',
'-webkit-line-clamp': rowHeight || 1, '-webkit-line-clamp': rowHeightTruncateLines(rowHeight),
'-webkit-box-orient': 'vertical', '-webkit-box-orient': 'vertical',
'overflow': 'hidden', 'overflow': 'hidden',
}" }"
> >
<template v-for="selectedOpt of vModel" :key="selectedOpt.value"> <template v-for="selectedOpt of vModel" :key="selectedOpt.value">
<a-tag class="rounded-tag max-w-full !pl-0" color="'#ccc'"> <a-tag
class="rounded-tag max-w-full !pl-0"
:class="{
'!my-0': !rowHeight || rowHeight === 1,
}"
color="'#ccc'"
>
<span <span
:style="{ :style="{
'color': tinycolor.isReadable('#ccc' || '#ccc', '#fff', { level: 'AA', size: 'large' }) color: tinycolor.isReadable('#ccc' || '#ccc', '#fff', { level: 'AA', size: 'large' })
? '#fff' ? '#fff'
: tinycolor.mostReadable('#ccc' || '#ccc', ['#0b1d05', '#fff']).toHex8String(), : tinycolor.mostReadable('#ccc' || '#ccc', ['#0b1d05', '#fff']).toHex8String(),
'font-size': '13px',
}" }"
class="flex items-stretch gap-2" class="flex items-stretch gap-2"
:class="{ 'text-sm': isKanban }" :class="{ 'text-sm': isKanban, 'text-small': !isKanban }"
> >
<div class="flex-none"> <div class="flex-none">
<GeneralUserIcon <GeneralUserIcon
@ -452,7 +457,7 @@ const filterOption = (input: string, option: any) => {
:open="isOpen && editAllowed" :open="isOpen && editAllowed"
:disabled="readOnly || !editAllowed" :disabled="readOnly || !editAllowed"
:class="{ 'caret-transparent': !hasEditRoles }" :class="{ 'caret-transparent': !hasEditRoles }"
:dropdown-class-name="`nc-dropdown-user-select-cell !min-w-200px ${isOpen ? 'active' : ''}`" :dropdown-class-name="`nc-dropdown-user-select-cell !min-w-156px ${isOpen ? 'active' : ''}`"
:filter-option="filterOption" :filter-option="filterOption"
@search="search" @search="search"
@keydown.stop @keydown.stop
@ -468,16 +473,21 @@ const filterOption = (input: string, option: any) => {
:class="`nc-select-option-${column.title}-${op.email}`" :class="`nc-select-option-${column.title}-${op.email}`"
@click.stop @click.stop
> >
<a-tag class="rounded-tag max-w-full !pl-0" color="'#ccc'"> <a-tag
class="rounded-tag max-w-full !pl-0"
:class="{
'!my-0': !rowHeight || rowHeight === 1,
}"
color="'#ccc'"
>
<span <span
:style="{ :style="{
'color': tinycolor.isReadable('#ccc' || '#ccc', '#fff', { level: 'AA', size: 'large' }) color: tinycolor.isReadable('#ccc' || '#ccc', '#fff', { level: 'AA', size: 'large' })
? '#fff' ? '#fff'
: tinycolor.mostReadable('#ccc' || '#ccc', ['#0b1d05', '#fff']).toHex8String(), : tinycolor.mostReadable('#ccc' || '#ccc', ['#0b1d05', '#fff']).toHex8String(),
'font-size': '13px',
}" }"
class="flex items-stretch gap-2" class="flex items-stretch gap-2"
:class="{ 'text-sm': isKanban }" :class="{ 'text-sm': isKanban, 'text-small': !isKanban }"
> >
<div> <div>
<GeneralUserIcon <GeneralUserIcon
@ -511,6 +521,9 @@ const filterOption = (input: string, option: any) => {
<a-tag <a-tag
v-if="options.find((el) => el.id === val)" v-if="options.find((el) => el.id === val)"
class="rounded-tag nc-selected-option !pl-0" class="rounded-tag nc-selected-option !pl-0"
:class="{
'!my-0': !rowHeight || rowHeight === 1,
}"
:style="{ display: 'flex', alignItems: 'center' }" :style="{ display: 'flex', alignItems: 'center' }"
color="'#ccc'" color="'#ccc'"
:closable="editAllowed && ((vModel?.length ?? 0) > 1 || !column?.rqd)" :closable="editAllowed && ((vModel?.length ?? 0) > 1 || !column?.rqd)"
@ -520,16 +533,15 @@ const filterOption = (input: string, option: any) => {
> >
<span <span
:style="{ :style="{
'color': tinycolor.isReadable('#ccc' || '#ccc', '#fff', { color: tinycolor.isReadable('#ccc' || '#ccc', '#fff', {
level: 'AA', level: 'AA',
size: 'large', size: 'large',
}) })
? '#fff' ? '#fff'
: tinycolor.mostReadable('#ccc' || '#ccc', ['#0b1d05', '#fff']).toHex8String(), : tinycolor.mostReadable('#ccc' || '#ccc', ['#0b1d05', '#fff']).toHex8String(),
'font-size': '13px',
}" }"
class="flex items-stretch gap-2" class="flex items-stretch gap-2"
:class="{ 'text-sm': isKanban }" :class="{ 'text-sm': isKanban, 'text-small': !isKanban }"
> >
<div> <div>
<GeneralUserIcon <GeneralUserIcon
@ -581,11 +593,11 @@ const filterOption = (input: string, option: any) => {
} }
.rounded-tag { .rounded-tag {
@apply bg-gray-200 py-0 px-[12px] rounded-[12px]; @apply bg-gray-200 px-2 rounded-[12px];
} }
:deep(.ant-tag) { :deep(.ant-tag) {
@apply "rounded-tag" my-[2px]; @apply "rounded-tag" my-[1px];
} }
:deep(.ant-tag-close-icon) { :deep(.ant-tag-close-icon) {
@ -597,7 +609,7 @@ const filterOption = (input: string, option: any) => {
} }
:deep(.ant-select-selection-overflow) { :deep(.ant-select-selection-overflow) {
@apply flex-nowrap overflow-hidden; @apply flex-nowrap overflow-hidden max-w-[fit-content];
} }
.nc-user-select:not(.read-only) { .nc-user-select:not(.read-only) {
@ -608,10 +620,13 @@ const filterOption = (input: string, option: any) => {
} }
:deep(.ant-select-selector) { :deep(.ant-select-selector) {
@apply !pl-0; @apply !pl-0 flex-nowrap;
} }
:deep(.ant-select-selection-search-input) { :deep(.ant-select-selection-search-input) {
@apply !text-xs; @apply !text-xs;
} }
:deep(.nc-user-avatar) {
@apply min-h-4.2;
}
</style> </style>

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

@ -8,6 +8,7 @@ import {
DropZoneRef, DropZoneRef,
IsExpandedFormOpenInj, IsExpandedFormOpenInj,
IsGalleryInj, IsGalleryInj,
IsGridInj,
IsKanbanInj, IsKanbanInj,
IsSurveyFormInj, IsSurveyFormInj,
RowHeightInj, RowHeightInj,
@ -52,6 +53,8 @@ const isExpandedForm = inject(IsExpandedFormOpenInj, ref(false))
const isSurveyForm = inject(IsSurveyFormInj, ref(false)) const isSurveyForm = inject(IsSurveyFormInj, ref(false))
const isGrid = inject(IsGridInj, ref(false))
const { isSharedForm } = useSmartsheetStoreOrThrow()! const { isSharedForm } = useSmartsheetStoreOrThrow()!
const { isMobileMode } = useGlobal() const { isMobileMode } = useGlobal()
@ -197,7 +200,12 @@ const keydownSpace = (e: KeyboardEvent) => {
<div <div
ref="attachmentCellRef" ref="attachmentCellRef"
:style="{ :style="{
height: isForm || isExpandedForm ? undefined : `max(${(rowHeight || 1) * 1.8}rem, 41px)`, height:
isForm || isExpandedForm
? undefined
: `max(${!rowHeight || rowHeight === 1 ? rowHeightInPx['1'] - 10 : rowHeightInPx[`${rowHeight}`] - 18}px, ${
isGrid ? '22px' : '32px'
})`,
}" }"
class="nc-attachment-cell relative flex color-transition flex items-center w-full xs:(min-h-12 max-h-32)" class="nc-attachment-cell relative flex color-transition flex items-center w-full xs:(min-h-12 max-h-32)"
:class="{ 'justify-center': !active, 'justify-between': active, 'px-2': isExpandedForm }" :class="{ 'justify-center': !active, 'justify-between': active, 'px-2': isExpandedForm }"
@ -236,15 +244,15 @@ const keydownSpace = (e: KeyboardEvent) => {
<div <div
v-if="active || !visibleItems.length || (isForm && visibleItems.length)" v-if="active || !visibleItems.length || (isForm && visibleItems.length)"
class="flex items-center gap-1 xs:(w-full min-w-12 h-8 justify-center)" class="flex items-center gap-1 xs:(w-full min-w-12 h-7 justify-center)"
> >
<MaterialSymbolsAttachFile <MaterialSymbolsAttachFile
class="transform dark:(!text-white) group-hover:(!text-accent scale-120) text-gray-500 text-[0.75rem]" class="transform dark:(!text-white) group-hover:(!text-accent scale-120) text-gray-500 text-tiny"
/> />
<div <div
v-if="!visibleItems.length" v-if="!visibleItems.length"
data-rec="true" data-rec="true"
class="group-hover:text-primary text-gray-500 dark:text-gray-200 dark:group-hover:!text-white text-xs xs:(justify-center rounded-lg text-sm)" class="group-hover:text-primary text-gray-500 dark:text-gray-200 dark:group-hover:!text-white text-tiny xs:(justify-center rounded-lg text-sm)"
> >
{{ $t('activity.addFiles') }} {{ $t('activity.addFiles') }}
</div> </div>
@ -257,10 +265,14 @@ const keydownSpace = (e: KeyboardEvent) => {
<template v-if="visibleItems.length"> <template v-if="visibleItems.length">
<div <div
ref="sortableRef" ref="sortableRef"
:class="{ 'justify-center': !isExpandedForm && !isGallery && !isKanban }" :class="{
class="flex cursor-pointer w-full items-center flex-wrap gap-2 py-1.5 scrollbar-thin-dull overflow-hidden mt-0 items-start" 'justify-center': !isExpandedForm && !isGallery && !isKanban,
'py-1': rowHeight === 1 && !isForm && !isExpandedForm,
'py-1.5': rowHeight !== 1 || isForm || isExpandedForm,
}"
class="nc-attachment-wrapper flex cursor-pointer w-full items-center flex-wrap gap-2 scrollbar-thin-dull overflow-hidden mt-0 items-start"
:style="{ :style="{
maxHeight: isForm || isExpandedForm ? undefined : `max(${(rowHeight || 1) * 1.8}rem, 41px)`, maxHeight: isForm || isExpandedForm ? undefined : `max(100%, ${isGrid ? '22px' : '32px'})`,
}" }"
> >
<template v-for="(item, i) of visibleItems" :key="item.url || item.title"> <template v-for="(item, i) of visibleItems" :key="item.url || item.title">
@ -278,8 +290,10 @@ const keydownSpace = (e: KeyboardEvent) => {
:alt="item.title || `#${i}`" :alt="item.title || `#${i}`"
class="rounded" class="rounded"
:class="{ :class="{
'h-7.5 w-8.8': rowHeight === 1, 'h-5.5': !isGrid && (!rowHeight || rowHeight === 1),
'h-11.5 w-12.8': rowHeight === 2, 'h-4.5': isGrid && (!rowHeight || rowHeight === 1),
'w-8.8': rowHeight === 1,
'h-8 w-12.8': rowHeight === 2,
'h-16.8 w-20.8': rowHeight === 4, 'h-16.8 w-20.8': rowHeight === 4,
'h-20.8 !w-30': isForm || isExpandedForm || rowHeight === 6, 'h-20.8 !w-30': isForm || isExpandedForm || rowHeight === 6,
}" }"
@ -307,12 +321,12 @@ const keydownSpace = (e: KeyboardEvent) => {
> >
<component :is="iconMap.reload" v-if="isLoading" :class="{ 'animate-infinite animate-spin': isLoading }" /> <component :is="iconMap.reload" v-if="isLoading" :class="{ 'animate-infinite animate-spin': isLoading }" />
<NcTooltip v-else placement="bottom"> <NcTooltip v-else placement="bottom" class="flex">
<template #title> {{ $t('activity.viewAttachment') }}</template> <template #title> {{ $t('activity.viewAttachment') }}</template>
<component <component
:is="iconMap.expand" :is="iconMap.expand"
class="transform dark:(!text-white) group-hover:(!text-grey-800 scale-120) text-gray-500 text-[0.75rem]" class="flex-none transform dark:(!text-white) group-hover:(!text-grey-800 scale-120) text-gray-500 text-[0.75rem]"
@click.stop="onExpand" @click.stop="onExpand"
/> />
</NcTooltip> </NcTooltip>
@ -327,7 +341,7 @@ const keydownSpace = (e: KeyboardEvent) => {
.nc-cell { .nc-cell {
.nc-attachment-cell { .nc-attachment-cell {
.nc-attachment { .nc-attachment {
@apply min-h-[1.8rem] min-w-[1.8rem] !ring-1 !ring-gray-300 !rounded; @apply min-h-5.5 min-w-[1.8rem] !ring-1 !ring-gray-300 !rounded;
} }
.ghost, .ghost,

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

@ -194,7 +194,7 @@ onUnmounted(() => {
:class="[ :class="[
`nc-cell-${(column?.uidt || 'default').toLowerCase()}`, `nc-cell-${(column?.uidt || 'default').toLowerCase()}`,
{ {
'text-brand-500': isPrimary(column) && !props.virtual && !isForm && !isCalendar, 'nc-display-value-cell': isPrimary(column) && !props.virtual && !isForm && !isCalendar,
'nc-grid-numeric-cell-right': 'nc-grid-numeric-cell-right':
isGrid && isGrid &&
isNumericField && isNumericField &&
@ -278,6 +278,48 @@ onUnmounted(() => {
} }
.nc-cell { .nc-cell {
@apply text-sm text-gray-600;
font-weight: 500;
:deep(.nc-cell-field),
:deep(input),
:deep(textarea),
:deep(.nc-cell-field-link) {
@apply !text-sm;
font-weight: 500;
}
:deep(input::placeholder),
:deep(textarea::placeholder) {
@apply text-gray-400;
font-weight: 300;
}
&.nc-display-value-cell {
@apply !text-brand-500 !font-semibold;
:deep(.nc-cell-field),
:deep(input),
:deep(textarea),
:deep(.nc-cell-field-link) {
@apply !font-semibold;
}
}
&.nc-cell-longtext {
@apply leading-5;
}
:deep(.ant-picker-input) {
@apply text-sm leading-4;
font-weight: 500;
input {
@apply text-sm leading-4;
font-weight: 500;
}
}
:deep(.nc-cell-field) { :deep(.nc-cell-field) {
@apply px-0; @apply px-0;
} }

10
packages/nc-gui/components/smartsheet/VirtualCell.vue

@ -102,7 +102,7 @@ onUnmounted(() => {
class="nc-virtual-cell w-full flex items-center" class="nc-virtual-cell w-full flex items-center"
:class="{ :class="{
'text-right justify-end': isGrid && !isForm && isRollup(column) && !isExpandedForm, 'text-right justify-end': isGrid && !isForm && isRollup(column) && !isExpandedForm,
'text-brand-500': isPrimary(column) && !isForm, 'nc-display-value-cell': isPrimary(column) && !isForm,
}" }"
@keydown.enter.exact="onNavigate(NavigateDir.NEXT, $event)" @keydown.enter.exact="onNavigate(NavigateDir.NEXT, $event)"
@keydown.shift.enter.exact="onNavigate(NavigateDir.PREV, $event)" @keydown.shift.enter.exact="onNavigate(NavigateDir.PREV, $event)"
@ -124,3 +124,11 @@ onUnmounted(() => {
</template> </template>
</div> </div>
</template> </template>
<style lang="scss" scoped>
.nc-virtual-cell {
&.nc-display-value-cell {
@apply !text-brand-500;
}
}
</style>

2
packages/nc-gui/components/smartsheet/column/DefaultValue.vue

@ -56,7 +56,7 @@ watch(
:edit-enabled="true" :edit-enabled="true"
:model-value="cdfValue" :model-value="cdfValue"
:column="vModel" :column="vModel"
class="!border-none" class="!border-none h-auto my-auto"
@update:cdf="updateCdfValue" @update:cdf="updateCdfValue"
@update:edit-enabled="editEnabled = $event" @update:edit-enabled="editEnabled = $event"
@click="editEnabled = true" @click="editEnabled = true"

170
packages/nc-gui/components/smartsheet/grid/Table.vue

@ -242,6 +242,8 @@ const columnOrder = ref<Pick<ColumnReqType, 'column_order'> | null>(null)
const editEnabled = ref(false) const editEnabled = ref(false)
const isGridCellMouseDown = ref(false)
// #Context Menu // #Context Menu
const _contextMenu = ref(false) const _contextMenu = ref(false)
const contextMenu = computed({ const contextMenu = computed({
@ -1169,9 +1171,14 @@ useEventListener(document, 'mousedown', (e) => {
if (e.offsetX > (e.target as HTMLElement)?.clientWidth || e.offsetY > (e.target as HTMLElement)?.clientHeight) { if (e.offsetX > (e.target as HTMLElement)?.clientWidth || e.offsetY > (e.target as HTMLElement)?.clientHeight) {
scrolling.value = true scrolling.value = true
} }
if ((e.target as HTMLElement).closest('.nc-grid-cell:not(.caption)')) {
isGridCellMouseDown.value = true
}
}) })
useEventListener(document, 'mouseup', () => { useEventListener(document, 'mouseup', () => {
isGridCellMouseDown.value = false
// wait for click event to finish before setting scrolling to false // wait for click event to finish before setting scrolling to false
setTimeout(() => { setTimeout(() => {
scrolling.value = false scrolling.value = false
@ -1424,7 +1431,7 @@ onKeyStroke('ArrowDown', onDown)
v-for="(col, colIndex) of dummyColumnDataForLoading" v-for="(col, colIndex) of dummyColumnDataForLoading"
:key="colIndex" :key="colIndex"
class="!bg-gray-50 h-full border-b-1 border-r-1" class="!bg-gray-50 h-full border-b-1 border-r-1"
:class="{ 'min-w-50': colIndex !== 0, 'min-w-21.25': colIndex === 0 }" :class="{ 'min-w-45': colIndex !== 0, 'min-w-16': colIndex === 0 }"
> >
<a-skeleton <a-skeleton
:active="true" :active="true"
@ -1439,8 +1446,8 @@ onKeyStroke('ArrowDown', onDown)
</td> </td>
</tr> </tr>
<tr v-show="!isViewColumnsLoading" class="nc-grid-header"> <tr v-show="!isViewColumnsLoading" class="nc-grid-header">
<th class="w-[85px] min-w-[85px]" data-testid="grid-id-column" @dblclick="() => {}"> <th class="w-[64px] min-w-[64px]" data-testid="grid-id-column" @dblclick="() => {}">
<div class="w-full h-full flex pl-5 pr-1 items-center" data-testid="nc-check-all"> <div class="w-full h-full flex pl-2 pr-1 items-center" data-testid="nc-check-all">
<template v-if="!readOnly"> <template v-if="!readOnly">
<div class="nc-no-label text-gray-500" :class="{ hidden: vSelectedAllRecords }">#</div> <div class="nc-no-label text-gray-500" :class="{ hidden: vSelectedAllRecords }">#</div>
<div <div
@ -1467,9 +1474,9 @@ onKeyStroke('ArrowDown', onDown)
:data-col="col.id" :data-col="col.id"
:data-title="col.title" :data-title="col.title"
:style="{ :style="{
'min-width': gridViewCols[col.id]?.width || '200px', 'min-width': gridViewCols[col.id]?.width || '180px',
'max-width': gridViewCols[col.id]?.width || '200px', 'max-width': gridViewCols[col.id]?.width || '180px',
'width': gridViewCols[col.id]?.width || '200px', 'width': gridViewCols[col.id]?.width || '180px',
}" }"
class="nc-grid-column-header" class="nc-grid-column-header"
:class="{ :class="{
@ -1481,7 +1488,7 @@ onKeyStroke('ArrowDown', onDown)
@click="selectColumn(col.id!)" @click="selectColumn(col.id!)"
> >
<div <div
class="w-full h-full flex items-center" class="w-full h-full flex items-center text-gray-500 pl-2 pr-1"
:draggable="isMobileMode || index === 0 || readOnly || !hasEditPermission ? 'false' : 'true'" :draggable="isMobileMode || index === 0 || readOnly || !hasEditPermission ? 'false' : 'true'"
@dragstart.stop="onDragStart(col.id!, $event)" @dragstart.stop="onDragStart(col.id!, $event)"
@drag.stop="onDrag($event)" @drag.stop="onDrag($event)"
@ -1504,7 +1511,7 @@ onKeyStroke('ArrowDown', onDown)
}" }"
@click.stop="addColumnDropdown = true" @click.stop="addColumnDropdown = true"
> >
<div class="absolute top-0 left-0 h-10.25 border-b-1 border-r-1 border-gray-200 nc-grid-add-edit-column group"> <div class="absolute top-0 left-0 h-8 border-b-1 border-r-1 border-gray-200 nc-grid-add-edit-column group">
<a-dropdown <a-dropdown
v-model:visible="addColumnDropdown" v-model:visible="addColumnDropdown"
:trigger="['click']" :trigger="['click']"
@ -1623,7 +1630,7 @@ onKeyStroke('ArrowDown', onDown)
v-for="(col, colIndex) of dummyColumnDataForLoading" v-for="(col, colIndex) of dummyColumnDataForLoading"
:key="colIndex" :key="colIndex"
class="border-b-1 border-r-1" class="border-b-1 border-r-1"
:class="{ 'min-w-50': colIndex !== 0, 'min-w-21.25': colIndex === 0 }" :class="{ 'min-w-45': colIndex !== 0, 'min-w-16': colIndex === 0 }"
></td> ></td>
</tr> </tr>
</template> </template>
@ -1632,16 +1639,20 @@ onKeyStroke('ArrowDown', onDown)
<tr <tr
v-show="!showSkeleton" v-show="!showSkeleton"
class="nc-grid-row !xs:h-14" class="nc-grid-row !xs:h-14"
:style="{ height: rowHeight ? `${rowHeight * 1.8}rem` : `1.8rem` }" :class="{
'active-row': activeCell.row === rowIndex || selectedRange._start?.row === rowIndex,
'mouse-down': isGridCellMouseDown || isFillMode,
}"
:style="{ height: rowHeight ? `${rowHeightInPx[`${rowHeight}`]}px` : `${rowHeightInPx['1']}px` }"
:data-testid="`grid-row-${rowIndex}`" :data-testid="`grid-row-${rowIndex}`"
> >
<td <td
key="row-index" key="row-index"
class="caption nc-grid-cell pl-5 pr-1" class="caption nc-grid-cell w-[64px] min-w-[64px]"
:data-testid="`cell-Id-${rowIndex}`" :data-testid="`cell-Id-${rowIndex}`"
@contextmenu="contextMenuTarget = null" @contextmenu="contextMenuTarget = null"
> >
<div class="items-center flex gap-1 min-w-[60px]"> <div class="w-[60px] pl-2 pr-1 items-center flex gap-1">
<div <div
class="nc-row-no sm:min-w-4 text-xs text-gray-500" class="nc-row-no sm:min-w-4 text-xs text-gray-500"
:class="{ toggle: !readOnly, hidden: row.rowMeta.selected }" :class="{ toggle: !readOnly, hidden: row.rowMeta.selected }"
@ -1675,7 +1686,7 @@ onKeyStroke('ArrowDown', onDown)
<span <span
v-if="row.rowMeta?.commentCount && expandForm" v-if="row.rowMeta?.commentCount && expandForm"
v-e="['c:expanded-form:open']" v-e="['c:expanded-form:open']"
class="py-1 px-3 rounded-full text-xs cursor-pointer select-none transform hover:(scale-110)" class="py-1 px-1 rounded-full text-xs cursor-pointer select-none transform hover:(scale-110)"
:style="{ :style="{
backgroundColor: getEnumColorByIndex(row.rowMeta.commentCount || 0), backgroundColor: getEnumColorByIndex(row.rowMeta.commentCount || 0),
}" }"
@ -1725,9 +1736,9 @@ onKeyStroke('ArrowDown', onDown)
'!border-r-blue-400 !border-r-3': toBeDroppedColId === columnObj.id, '!border-r-blue-400 !border-r-3': toBeDroppedColId === columnObj.id,
}" }"
:style="{ :style="{
'min-width': gridViewCols[columnObj.id]?.width || '200px', 'min-width': gridViewCols[columnObj.id]?.width || '180px',
'max-width': gridViewCols[columnObj.id]?.width || '200px', 'max-width': gridViewCols[columnObj.id]?.width || '180px',
'width': gridViewCols[columnObj.id]?.width || '200px', 'width': gridViewCols[columnObj.id]?.width || '180px',
}" }"
:data-testid="`cell-${columnObj.title}-${rowIndex}`" :data-testid="`cell-${columnObj.title}-${rowIndex}`"
:data-key="`data-key-${rowIndex}-${columnObj.id}`" :data-key="`data-key-${rowIndex}-${columnObj.id}`"
@ -1785,7 +1796,7 @@ onKeyStroke('ArrowDown', onDown)
@click="addEmptyRow()" @click="addEmptyRow()"
> >
<div <div
class="h-10.5 border-b-1 border-gray-100 bg-white group-hover:bg-gray-50 absolute left-0 bottom-0 px-2 sticky z-40 w-full flex items-center text-gray-500" class="h-8 border-b-1 border-gray-100 bg-white group-hover:bg-gray-50 absolute left-0 bottom-0 px-2 sticky z-40 w-full flex items-center text-gray-500"
> >
<component <component
:is="iconMap.plus" :is="iconMap.plus"
@ -2091,14 +2102,23 @@ onKeyStroke('ArrowDown', onDown)
td, td,
th { th {
@apply border-gray-100 border-solid border-r bg-gray-50; @apply border-gray-100 border-solid border-r bg-gray-50 p-0;
min-height: 41px !important; min-height: 32px !important;
height: 41px !important; height: 32px !important;
position: relative; position: relative;
} }
th { th {
@apply border-b-1 border-gray-200; @apply border-b-1 border-gray-200;
:deep(.name) {
@apply text-small;
}
:deep(.nc-cell-icon),
:deep(.nc-virtual-cell-icon) {
@apply !w-3.5 !h-3.5 !text-gray-500 !text-small;
}
} }
.nc-grid-header th:last-child { .nc-grid-header th:last-child {
@ -2109,9 +2129,97 @@ onKeyStroke('ArrowDown', onDown)
@apply bg-white border-b; @apply bg-white border-b;
} }
td:not(:first-child) > div { td:not(:first-child) {
overflow: hidden; @apply px-3;
@apply flex px-1 h-auto;
&.align-top {
@apply py-2;
}
&.align-middle {
@apply py-0;
}
& > div {
overflow: hidden;
@apply flex h-auto;
}
:deep(.nc-cell),
:deep(.nc-virtual-cell) {
@apply !text-small;
.nc-cell-field,
input,
textarea {
@apply !text-small !p-0 m-0;
}
&:not(.nc-display-value-cell) {
@apply text-gray-600;
font-weight: 500;
.nc-cell-field,
input,
textarea {
@apply text-gray-600;
font-weight: 500;
}
}
.nc-cell-field,
a.nc-cell-field-link,
input,
textarea {
@apply !p-0 m-0;
}
&.nc-cell-longtext {
@apply leading-[18px];
textarea {
@apply pr-2;
}
}
.ant-picker-input {
@apply text-small leading-4;
font-weight: 500;
input {
@apply text-small leading-4;
font-weight: 500;
}
}
&.nc-cell-attachment {
.nc-attachment-cell {
.nc-attachment-wrapper {
@apply !py-0.5;
.nc-attachment {
@apply !min-h-4;
}
}
}
}
&.nc-cell-longtext .long-text-wrapper .nc-rich-text-grid {
@apply pl-0 -ml-1;
}
.ant-select:not(.ant-select-customize-input) {
.ant-select-selector {
@apply !border-none flex-nowrap pr-4.5;
}
.ant-select-arrow {
@apply right-[3px];
}
}
.ant-select-selection-search-input {
@apply !h-[23px];
}
}
} }
table { table {
@ -2188,14 +2296,14 @@ onKeyStroke('ArrowDown', onDown)
thead th:nth-child(2) { thead th:nth-child(2) {
position: sticky !important; position: sticky !important;
z-index: 5; z-index: 5;
left: 85px; left: 64px;
@apply border-r-1 border-r-gray-200; @apply border-r-1 border-r-gray-200;
} }
tbody tr:not(.nc-grid-add-new-cell) td:nth-child(2) { tbody tr:not(.nc-grid-add-new-cell) td:nth-child(2) {
position: sticky !important; position: sticky !important;
z-index: 4; z-index: 4;
left: 85px; left: 64px;
background: white; background: white;
@apply border-r-1 border-r-gray-100; @apply border-r-1 border-r-gray-100;
} }
@ -2235,7 +2343,8 @@ onKeyStroke('ArrowDown', onDown)
} }
} }
&:hover { &.active-row,
&:not(.mouse-down):hover {
.nc-row-no.toggle { .nc-row-no.toggle {
@apply hidden; @apply hidden;
} }
@ -2247,6 +2356,11 @@ onKeyStroke('ArrowDown', onDown)
.nc-row-expand-and-checkbox { .nc-row-expand-and-checkbox {
@apply !xs:hidden flex; @apply !xs:hidden flex;
} }
td.nc-grid-cell:not(.active),
td:nth-child(2):not(.active) {
@apply !bg-gray-50 border-b-gray-200 border-r-gray-200;
}
} }
} }
@ -2267,10 +2381,6 @@ onKeyStroke('ArrowDown', onDown)
} }
} }
tbody tr:hover {
@apply bg-gray-100 bg-opacity-50;
}
.nc-required-cell { .nc-required-cell {
box-shadow: inset 0 0 2px #f00; box-shadow: inset 0 0 2px #f00;
} }

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

@ -161,7 +161,7 @@ const openDropDown = (e: Event) => {
<template> <template>
<div <div
class="flex items-center w-full h-full text-xs text-gray-500 font-weight-medium" class="flex items-center w-full h-full text-small text-gray-500 font-weight-medium"
@dblclick="openHeaderMenu" @dblclick="openHeaderMenu"
@click.right="openDropDown" @click.right="openDropDown"
> >

9
packages/nc-gui/components/virtual-cell/Lookup.vue

@ -105,7 +105,13 @@ const { showEditNonEditableFieldWarning, showClearNonEditableFieldWarning, activ
<div <div
class="nc-cell-field h-full w-full nc-lookup-cell" class="nc-cell-field h-full w-full nc-lookup-cell"
tabindex="-1" tabindex="-1"
:style="{ height: isGroupByLabel ? undefined : rowHeight && rowHeight !== 1 ? `${rowHeight * 2}rem` : `2.85rem` }" :style="{
height: isGroupByLabel
? undefined
: rowHeight
? `${rowHeight === 1 ? rowHeightInPx['1'] - 4 : rowHeightInPx[`${rowHeight}`] - 18}px`
: `2.85rem`,
}"
@dblclick="activateShowEditNonEditableFieldWarning" @dblclick="activateShowEditNonEditableFieldWarning"
> >
<div <div
@ -170,6 +176,7 @@ const { showEditNonEditableFieldWarning, showClearNonEditableFieldWarning, activ
<div <div
v-for="(v, i) of arrValue" v-for="(v, i) of arrValue"
:key="i" :key="i"
class="flex-none"
:class="{ :class="{
'bg-gray-100 rounded-full': !isAttachment(lookupColumn), 'bg-gray-100 rounded-full': !isAttachment(lookupColumn),
'border-gray-200 rounded border-1': ![ 'border-gray-200 rounded border-1': ![

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

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { useQRCode } from '@vueuse/integrations/useQRCode' import { useQRCode } from '@vueuse/integrations/useQRCode'
import type QRCode from 'qrcode' import type QRCode from 'qrcode'
import { IsGalleryInj, RowHeightInj, computed, inject, ref } from '#imports' import { IsGalleryInj, RowHeightInj, computed, inject, ref, rowHeightInPx } from '#imports'
const maxNumberOfAllowedCharsForQrValue = 2000 const maxNumberOfAllowedCharsForQrValue = 2000
@ -87,7 +87,9 @@ const { showEditNonEditableFieldWarning, showClearNonEditableFieldWarning } = us
> >
<img <img
v-if="rowHeight" v-if="rowHeight"
:style="{ height: rowHeight ? `${rowHeight * 1.8}rem` : `1.8rem` }" :style="{
height: rowHeight ? `${rowHeight === 1 ? rowHeightInPx['1'] - 4 : rowHeightInPx[`${rowHeight}`] - 20}px` : `1.8rem`,
}"
:src="qrCode" :src="qrCode"
:alt="$t('title.qrCode')" :alt="$t('title.qrCode')"
class="min-w-[1.4em]" class="min-w-[1.4em]"

6
packages/nc-gui/components/virtual-cell/barcode/Barcode.vue

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import type { ComputedRef } from 'vue' import type { ComputedRef } from 'vue'
import JsBarcodeWrapper from './JsBarcodeWrapper.vue' import JsBarcodeWrapper from './JsBarcodeWrapper.vue'
import { RowHeightInj, computed, inject, ref } from '#imports' import { RowHeightInj, computed, inject, ref, rowHeightInPx } from '#imports'
const maxNumberOfAllowedCharsForBarcodeValue = 100 const maxNumberOfAllowedCharsForBarcodeValue = 100
@ -66,7 +66,9 @@ const rowHeight = inject(RowHeightInj, ref(undefined))
:barcode-value="barcodeValue" :barcode-value="barcodeValue"
tabindex="-1" tabindex="-1"
:barcode-format="barcodeMeta.barcodeFormat" :barcode-format="barcodeMeta.barcodeFormat"
:custom-style="{ height: rowHeight ? `${rowHeight * 1.8}rem` : `1.8rem` }" :custom-style="{
height: rowHeight ? `${rowHeight === 1 ? rowHeightInPx['1'] - 4 : rowHeightInPx[`${rowHeight}`] - 20}px` : `1.8rem`,
}"
@on-click-barcode="showBarcodeModal" @on-click-barcode="showBarcodeModal"
> >
<template #barcodeRenderError> <template #barcodeRenderError>

2
packages/nc-gui/composables/useViewColumns.ts

@ -309,7 +309,7 @@ const [useProvideViewColumns, useViewColumns] = useInjectionState(
{ immediate: true }, { immediate: true },
) )
const resizingColOldWith = ref('200px') const resizingColOldWith = ref('180px')
const updateGridViewColumn = async (id: string, props: Partial<GridColumnReqType>, undo = false) => { const updateGridViewColumn = async (id: string, props: Partial<GridColumnReqType>, undo = false) => {
if (!undo) { if (!undo) {

20
packages/nc-gui/utils/cell.ts

@ -93,3 +93,23 @@ export const isNumericFieldType = (column: ColumnType, abstractType: any) => {
isDuration(column) isDuration(column)
) )
} }
export const rowHeightInPx: Record<string, number> = {
1: 32,
2: 60,
4: 90,
6: 120,
}
export const rowHeightTruncateLines = (rowHeight?: number) => {
switch (rowHeight) {
case 2:
return 2
case 4:
return 3
case 6:
return 4
default:
return 1
}
}

4
packages/nc-gui/windi.config.ts

@ -71,6 +71,10 @@ export default defineConfig({
min: '1780px', min: '1780px',
}, },
}, },
fontSize: {
tiny: ['11px', '14px'],
small: ['13px', '16px'],
},
fontWeight: { fontWeight: {
thin: 150, thin: 150,
extraLight: 250, extraLight: 250,

4
packages/nocodb/src/models/GridViewColumn.ts

@ -100,7 +100,9 @@ export default class GridViewColumn implements GridColumnType {
insertObj.source_id = viewRef.source_id; insertObj.source_id = viewRef.source_id;
} }
const { id, fk_column_id } = await ncMeta.metaInsert2( insertObj.width = column?.width ?? '180px';
const { id } = await ncMeta.metaInsert2(
null, null,
null, null,
MetaTable.GRID_VIEW_COLUMNS, MetaTable.GRID_VIEW_COLUMNS,

2
tests/playwright/pages/Dashboard/Grid/Row.ts

@ -17,7 +17,7 @@ export class RowPageObject extends BasePage {
return this.get().nth(index); return this.get().nth(index);
} }
// style="height: 3rem;" // style="height: 32px;"
async getRecordHeight(index: number) { async getRecordHeight(index: number) {
const record = this.getRecord(index); const record = this.getRecord(index);
const style = await record.getAttribute('style'); const style = await record.getAttribute('style');

32
tests/playwright/pages/Dashboard/common/Cell/SelectOptionCell.ts

@ -37,7 +37,21 @@ export class SelectOptionCellPageObject extends BasePage {
if (!ignoreDblClick) await selectCell.click(); if (!ignoreDblClick) await selectCell.click();
} }
await selectCell.click(); if ((await selectCell.getAttribute('class')).includes('active')) {
await selectCell.locator('.ant-select').first().waitFor({ state: 'visible' });
await selectCell
.locator('.ant-select')
.first()
.click({
position: {
x: 2,
y: 1,
},
});
} else {
await selectCell.click();
}
if (multiSelect) { if (multiSelect) {
await this.rootPage.locator('.nc-dropdown-multi-select-cell').waitFor({ state: 'visible' }); await this.rootPage.locator('.nc-dropdown-multi-select-cell').waitFor({ state: 'visible' });
@ -136,7 +150,21 @@ export class SelectOptionCellPageObject extends BasePage {
await selectCell.click(); await selectCell.click();
} }
await this.get({ index, columnHeader }).click(); if ((await selectCell.getAttribute('class')).includes('active-cell')) {
await selectCell.locator('.ant-select').first().waitFor({ state: 'visible' });
await selectCell
.locator('.ant-select')
.first()
.click({
position: {
x: 2,
y: 1,
},
});
} else {
await this.get({ index, columnHeader }).click();
}
await this.rootPage.waitForTimeout(500); await this.rootPage.waitForTimeout(500);
let counter = 0; let counter = 0;

10
tests/playwright/tests/db/features/undo-redo.spec.ts

@ -308,26 +308,26 @@ test.describe('Undo Redo', () => {
const timeOut = 200; const timeOut = 200;
await verifyRowHeight({ height: '1.8rem' }); await verifyRowHeight({ height: '32px' });
// set row height & verify // set row height & verify
await toolbar.clickRowHeight(); await toolbar.clickRowHeight();
await toolbar.rowHeight.click({ title: 'Tall' }); await toolbar.rowHeight.click({ title: 'Tall' });
await new Promise(resolve => setTimeout(resolve, timeOut)); await new Promise(resolve => setTimeout(resolve, timeOut));
await verifyRowHeight({ height: '7.2rem' }); await verifyRowHeight({ height: '90px' });
await toolbar.clickRowHeight(); await toolbar.clickRowHeight();
await toolbar.rowHeight.click({ title: 'Medium' }); await toolbar.rowHeight.click({ title: 'Medium' });
await new Promise(resolve => setTimeout(resolve, timeOut)); await new Promise(resolve => setTimeout(resolve, timeOut));
await verifyRowHeight({ height: '3.6rem' }); await verifyRowHeight({ height: '60px' });
await undo({ page, dashboard }); await undo({ page, dashboard });
await new Promise(resolve => setTimeout(resolve, timeOut)); await new Promise(resolve => setTimeout(resolve, timeOut));
await verifyRowHeight({ height: '7.2rem' }); await verifyRowHeight({ height: '90px' });
await undo({ page, dashboard }); await undo({ page, dashboard });
await new Promise(resolve => setTimeout(resolve, timeOut)); await new Promise(resolve => setTimeout(resolve, timeOut));
await verifyRowHeight({ height: '1.8rem' }); await verifyRowHeight({ height: '32px' });
}); });
test('Column width', async ({ page }) => { test('Column width', async ({ page }) => {

8
tests/playwright/tests/db/general/toolbarOperations.spec.ts

@ -618,10 +618,10 @@ test.describe('Toolbar operations (GRID)', () => {
test('row height', async () => { test('row height', async () => {
// define an array of row heights // define an array of row heights
const rowHeight = [ const rowHeight = [
{ title: 'Short', height: '1.8rem' }, { title: 'Short', height: '32px' },
{ title: 'Medium', height: '3.6rem' }, { title: 'Medium', height: '60px' },
{ title: 'Tall', height: '7.2rem' }, { title: 'Tall', height: '90px' },
{ title: 'Extra', height: '10.8rem' }, { title: 'Extra', height: '120px' },
]; ];
// close 'Team & Auth' tab // close 'Team & Auth' tab

Loading…
Cancel
Save