diff --git a/packages/nc-gui/assets/nc-icons/link.svg b/packages/nc-gui/assets/nc-icons/link.svg
new file mode 100644
index 0000000000..1207f4b97f
--- /dev/null
+++ b/packages/nc-gui/assets/nc-icons/link.svg
@@ -0,0 +1,4 @@
+
diff --git a/packages/nc-gui/assets/style.scss b/packages/nc-gui/assets/style.scss
index 1986dd926c..5dee33bf42 100644
--- a/packages/nc-gui/assets/style.scss
+++ b/packages/nc-gui/assets/style.scss
@@ -33,7 +33,7 @@
}
.anticon-check-circle {
- @apply !relative top-[-1px] left-0;
+ @apply !relative top-[-1px] left-0;
}
html,
@@ -580,10 +580,6 @@ input[type='number'] {
@apply !block;
}
-.ant-card-body {
- @apply !p-2;
-}
-
.ant-pagination .ant-pagination-item-link-icon {
@apply !block !py-1.5;
}
diff --git a/packages/nc-gui/components.d.ts b/packages/nc-gui/components.d.ts
index 208fef4e85..6c96a14054 100644
--- a/packages/nc-gui/components.d.ts
+++ b/packages/nc-gui/components.d.ts
@@ -158,6 +158,7 @@ declare module '@vue/runtime-core' {
MdiWhatsapp: typeof import('~icons/mdi/whatsapp')['default']
MiCircleWarning: typeof import('~icons/mi/circle-warning')['default']
NcIconsInbox: typeof import('~icons/nc-icons/inbox')['default']
+ PhLink: typeof import('~icons/ph/link')['default']
PhMagnifyingGlassBold: typeof import('~icons/ph/magnifying-glass-bold')['default']
PhTriangleFill: typeof import('~icons/ph/triangle-fill')['default']
RiExternalLinkLine: typeof import('~icons/ri/external-link-line')['default']
diff --git a/packages/nc-gui/components/virtual-cell/components/Header.vue b/packages/nc-gui/components/virtual-cell/components/Header.vue
index da78143a2c..a3f79d4bd6 100644
--- a/packages/nc-gui/components/virtual-cell/components/Header.vue
+++ b/packages/nc-gui/components/virtual-cell/components/Header.vue
@@ -49,9 +49,9 @@ const relationMeta = computed(() => {
-
+
{{ showHeader ? 'Linked Records' : '' }}
-
+
@@ -108,7 +108,7 @@ const relationMeta = computed(() => {
-
+
diff --git a/packages/nc-gui/components/virtual-cell/components/ListChildItems.vue b/packages/nc-gui/components/virtual-cell/components/ListChildItems.vue
index fba37cf179..764b22c5f7 100644
--- a/packages/nc-gui/components/virtual-cell/components/ListChildItems.vue
+++ b/packages/nc-gui/components/virtual-cell/components/ListChildItems.vue
@@ -12,6 +12,7 @@ import {
computed,
inject,
isPrimary,
+ onKeyStroke,
ref,
useLTARStoreOrThrow,
useSmartsheetRowStoreOrThrow,
@@ -41,6 +42,7 @@ const {
unlink,
isChildrenListLoading,
isChildrenListLinked,
+ isChildrenLoading,
relatedTableMeta,
row,
link,
@@ -112,7 +114,13 @@ watch(
)
watch(expandedFormDlg, () => {
- loadChildrenList()
+ if (!expandedFormDlg.value) {
+ loadChildrenList()
+ }
+})
+
+onKeyStroke('Escape', () => {
+ vModel.value = false
})
@@ -131,6 +139,7 @@ watch(expandedFormDlg, () => {
:relation="relation"
:linked-records="childrenListCount"
:table-title="meta?.title"
+ :show-header="true"
:related-table-title="relatedTableMeta?.title"
:display-value="row.row[displayValueProp]"
/>
@@ -167,28 +176,33 @@ watch(expandedFormDlg, () => {
}"
class="overflow-scroll nc-scrollbar-md cursor-pointer pr-1"
>
-
{
- if (isPublic && !isForm) return
- isNew
- ? unlinkRow(refRow, Number.parseInt(id))
- : isChildrenListLinked[Number.parseInt(id)]
- ? unlinkRow(refRow, Number.parseInt(id))
- : linkRow(refRow, Number.parseInt(id))
- }
- "
- />
+
+
+
+
+ {
+ if (isPublic && !isForm) return
+ isNew
+ ? unlinkRow(refRow, Number.parseInt(id))
+ : isChildrenListLinked[Number.parseInt(id)]
+ ? unlinkRow(refRow, Number.parseInt(id))
+ : linkRow(refRow, Number.parseInt(id))
+ }
+ "
+ />
+
@@ -220,10 +234,10 @@ watch(expandedFormDlg, () => {
-
+
{{ childrenListCount || 0 }} records {{ childrenListCount !== 0 ? 'are' : '' }} linked
-
+
{{ state?.[colTitle]?.length || 0 }} records {{ state?.[colTitle]?.length !== 0 ? 'are' : '' }} linked
diff --git a/packages/nc-gui/components/virtual-cell/components/ListItem.vue b/packages/nc-gui/components/virtual-cell/components/ListItem.vue
index 5472dea5e1..2be57791d4 100644
--- a/packages/nc-gui/components/virtual-cell/components/ListItem.vue
+++ b/packages/nc-gui/components/virtual-cell/components/ListItem.vue
@@ -2,6 +2,7 @@
import { isVirtualCol } from 'nocodb-sdk'
import { IsFormInj, isImage, useAttachment } from '#imports'
import MaximizeIcon from '~icons/nc-icons/maximize'
+import LinkIcon from '~icons/nc-icons/link'
const { row, fields, relatedTableDisplayValueProp, isLoading, isLinked, attachment } = defineProps<{
row: any
@@ -46,42 +47,40 @@ const attachments: Attachment[] = computed(() => {
-
-
+
+
-
+
-
+
-
{{ row[relatedTableDisplayValueProp] }}
-
+
{{ row[relatedTableDisplayValueProp] }}
+
+
+ Linked
+
0 && !isPublic && !isForm" class="flex flex-row gap-4 w-10/12">
+
{
v-if="!isForm && !isPublic"
type="text"
size="lg"
- class="!px-2 nc-expand-item !group-hover:block !hidden !absolute right-1 bottom-1"
+ class="!px-2 nc-expand-item !group-hover:block !hidden !border-1 !shadow-sm !border-gray-200 !bg-white !absolute right-3 bottom-3"
@click.stop="$emit('expand', row)"
>
diff --git a/packages/nc-gui/components/virtual-cell/components/ListItems.vue b/packages/nc-gui/components/virtual-cell/components/ListItems.vue
index 501753dc85..4807e5c8e4 100644
--- a/packages/nc-gui/components/virtual-cell/components/ListItems.vue
+++ b/packages/nc-gui/components/virtual-cell/components/ListItems.vue
@@ -9,6 +9,7 @@ import {
SaveRowInj,
computed,
inject,
+ onKeyStroke,
ref,
useLTARStoreOrThrow,
useSmartsheetRowStoreOrThrow,
@@ -30,6 +31,7 @@ const {
isChildrenExcludedListLinked,
isChildrenExcludedListLoading,
displayValueProp,
+ isChildrenExcludedLoading,
childrenListCount,
loadChildrenExcludedList,
loadChildrenList,
@@ -138,7 +140,13 @@ const relation = computed(() => {
})
watch(expandedFormDlg, () => {
- loadChildrenExcludedList(rowState.value)
+ if (!expandedFormDlg.value) {
+ loadChildrenExcludedList(rowState.value)
+ }
+})
+
+onKeyStroke('Escape', () => {
+ vModel.value = false
})
@@ -203,29 +211,34 @@ watch(expandedFormDlg, () => {
@@ -242,7 +255,7 @@ watch(expandedFormDlg, () => {
-
+
{{ relation === 'bt' ? (row.row[relatedTableMeta?.title] ? '1' : 0) : childrenListCount ?? 'No' }} records
{{ childrenListCount !== 0 ? 'are' : '' }} linked
diff --git a/packages/nc-gui/composables/useLTARStore.ts b/packages/nc-gui/composables/useLTARStore.ts
index 360e1f3016..05101d740c 100644
--- a/packages/nc-gui/composables/useLTARStore.ts
+++ b/packages/nc-gui/composables/useLTARStore.ts
@@ -64,12 +64,16 @@ const [useProvideLTARStore, useLTARStore] = useInjectionState(
size: 10,
})
+ const isChildrenLoading = ref(false)
+
const isChildrenListLoading = ref
>([])
const isChildrenListLinked = ref>([])
const isChildrenExcludedListLoading = ref>([])
+ const isChildrenExcludedLoading = ref(false)
+
const isChildrenExcludedListLinked = ref>([])
const newRowState = reactive({
@@ -127,6 +131,7 @@ const [useProvideLTARStore, useLTARStore] = useInjectionState(
const loadChildrenExcludedList = async (activeState?: any) => {
if (activeState) newRowState.state = activeState
try {
+ isChildrenExcludedLoading.value = true
if (isPublic.value) {
const router = useRouter()
@@ -212,11 +217,16 @@ const [useProvideLTARStore, useLTARStore] = useInjectionState(
}
} catch (e: any) {
message.error(`${t('msg.error.failedToLoadList')}: ${await extractSdkResponseErrorMsg(e)}`)
+ } finally {
+ setTimeout(() => {
+ isChildrenExcludedLoading.value = false
+ }, 600)
}
}
const loadChildrenList = async () => {
try {
+ isChildrenLoading.value = true
if (colOptions.value.type === 'bt') return
if (!rowId.value || !column.value) return
if (isPublic.value) {
@@ -262,6 +272,10 @@ const [useProvideLTARStore, useLTARStore] = useInjectionState(
}
} catch (e: any) {
message.error(`${t('msg.error.failedToLoadChildrenList')}: ${await extractSdkResponseErrorMsg(e)}`)
+ } finally {
+ setTimeout(() => {
+ isChildrenLoading.value = false
+ }, 600)
}
}
@@ -357,8 +371,10 @@ const [useProvideLTARStore, useLTARStore] = useInjectionState(
} catch (e: any) {
message.error(`${t('msg.error.unlinkFailed')}: ${await extractSdkResponseErrorMsg(e)}`)
} finally {
- isChildrenExcludedListLoading.value[index] = false
- isChildrenListLoading.value[index] = false
+ setTimeout(() => {
+ isChildrenExcludedListLoading.value[index] = false
+ isChildrenListLoading.value[index] = false
+ }, 600)
}
reloadData?.(false)
@@ -422,8 +438,10 @@ const [useProvideLTARStore, useLTARStore] = useInjectionState(
} catch (e: any) {
message.error(`Linking failed: ${await extractSdkResponseErrorMsg(e)}`)
} finally {
- isChildrenExcludedListLoading.value[index] = false
- isChildrenListLoading.value[index] = false
+ setTimeout(() => {
+ isChildrenExcludedListLoading.value[index] = false
+ isChildrenListLoading.value[index] = false
+ }, 600)
}
reloadData?.(false)
@@ -466,6 +484,8 @@ const [useProvideLTARStore, useLTARStore] = useInjectionState(
isChildrenListLoading,
isChildrenExcludedListLoading,
row,
+ isChildrenLoading,
+ isChildrenExcludedLoading,
deleteRelatedRow,
getRelatedTableRowId,
}