Add a Table display mode to relation field widgets (#20929)

## Context

Adds a new Table layout to the FIELD widget for to-many relation fields.
On a record page, a relation can now be displayed as a full record table
(the same component used for record indexes and dashboard table widgets)
scoped to the records related to the current record.



https://github.com/user-attachments/assets/320b24dc-f019-4d0e-bc71-3e64d032d75a



https://github.com/user-attachments/assets/2f6d4f8e-de26-4fc1-ae12-c9b9c19654dc



https://github.com/user-attachments/assets/3fb6d512-f83c-4818-823e-46ad2644fbc2
This commit is contained in:
Weiko
2026-05-27 14:13:58 +02:00
committed by GitHub
parent fc3004b2f6
commit 2f358a1775
47 changed files with 695 additions and 158 deletions
@@ -20,6 +20,7 @@ import { resolveEntityRelationUniversalIdentifiers } from 'src/engine/metadata-m
import { splitEntitiesByRemovalStrategy } from 'src/engine/metadata-modules/flat-entity/utils/split-entities-by-removal-strategy.util';
import { type FlatFieldMetadata } from 'src/engine/metadata-modules/flat-field-metadata/types/flat-field-metadata.type';
import { isFlatPageLayoutWidgetConfigurationOfType } from 'src/engine/metadata-modules/flat-page-layout-widget/utils/is-flat-page-layout-widget-configuration-of-type.util';
import { FieldDisplayMode } from 'src/engine/metadata-modules/page-layout-widget/enums/field-display-mode.enum';
import { DEFAULT_VIEW_FIELD_SIZE } from 'src/engine/metadata-modules/flat-view-field/constants/default-view-field-size.constant';
import { type FlatViewField } from 'src/engine/metadata-modules/flat-view-field/types/flat-view-field.type';
import { fromViewFieldOverridesToUniversalOverrides } from 'src/engine/metadata-modules/flat-view-field/utils/from-view-field-overrides-to-universal-overrides.util';
@@ -121,20 +122,35 @@ export class ViewWidgetUpsertService {
flatEntityMaps: flatPageLayoutWidgetMaps,
});
if (
!isDefined(widget) ||
!isFlatPageLayoutWidgetConfigurationOfType(
widget,
WidgetConfigurationType.RECORD_TABLE,
)
) {
if (!isDefined(widget)) {
throw new ViewException(
t`Record table widget not found`,
ViewExceptionCode.VIEW_WIDGET_NOT_FOUND,
);
}
const viewId = widget.configuration.viewId;
const isRecordTableWidget = isFlatPageLayoutWidgetConfigurationOfType(
widget,
WidgetConfigurationType.RECORD_TABLE,
);
const isFieldTableWidget =
isFlatPageLayoutWidgetConfigurationOfType(
widget,
WidgetConfigurationType.FIELD,
) && widget.configuration.fieldDisplayMode === FieldDisplayMode.TABLE;
if (!isRecordTableWidget && !isFieldTableWidget) {
throw new ViewException(
t`Record table widget not found`,
ViewExceptionCode.VIEW_WIDGET_NOT_FOUND,
);
}
const viewId =
'viewId' in widget.configuration
? widget.configuration.viewId
: undefined;
if (!isDefined(viewId)) {
throw new ViewException(