import { CustomObjectFieldSection } from "./custom-object-field-section.js"; import type { CustomObject, CustomObjectFieldInput } from "./custom-object.js"; import { type ExistingManifestContextOptions } from "./existing-manifest-context.js"; import type { Field } from "./field.js"; import type { ManifestComponentDeletion } from "./manifest-builder.js"; export interface PageLayoutDeletionIdentifier { layoutId: string; objectRqlName: string; } export type ObjectRqlName = string; export type BlueprintKey = string; export type LayoutApiName = string; export type TabKey = string; export type SectionKey = string; export type HeaderFieldKey = string; export type FieldRqlName = string; export type RelatedObjectRqlName = string; export type CanvasCompositionId = string; export type HeaderButtonKey = string; export type JsonPrimitive = string | number | boolean | null; export type JsonValue = JsonPrimitive | JsonObject | JsonValue[]; export interface JsonObject { [key: string]: JsonValue; } /** Edit to a built-in (system) header field. */ export interface SystemHeaderFieldEdit { /** Replace the system field's RQL field reference. Optional. */ newRqlField?: FieldRqlName; /** Remove this system field from the header. Optional. */ deleted?: boolean; } /** * Custom header field added via `newFields`. * * `key` is required and must be non-empty — it is used as the stable identity * for this field in visibility conditions. */ export interface NewHeaderField { /** RQL field name to display. Required. */ rqlName: FieldRqlName; /** Stable key for this field. Required. */ key: HeaderFieldKey; /** Whether the user can remove this field. Optional. */ canBeDeleted?: boolean; /** Whether the user can swap this field for another. Optional. */ canBeChanged?: boolean; /** Whether the user can reorder this field. Optional. */ canBeMoved?: boolean; } /** * Header configuration for a page layout (`header_edits`). * * Only the keys listed here are accepted — anything else is silently dropped * on the wire. Pass `{}` for an unchanged header. * * > **Caution:** key names are camelCase on the wire. Snake_case keys are * > silently dropped and produce an empty header on deploy. */ export interface HeaderEdits { /** Replace the record title field (pass the field's api_name). Optional. */ newTitleField?: FieldRqlName; /** Remove the title field from the header. Optional. */ titleFieldDeleted?: boolean; /** Replace the record description field. Optional. */ newDescriptionField?: FieldRqlName; /** Remove the description field from the header. Optional. */ descriptionFieldDeleted?: boolean; /** Edits to built-in system header fields, keyed by system field key. Optional. */ systemFieldEdits?: Record; /** Additional custom fields to add to the header. Optional. */ newFields?: NewHeaderField[]; /** Ordered list of header field RQL names. Custom header keys are accepted and converted. Optional. */ headerFieldsOrder?: FieldRqlName[]; /** Action button keys to show in the header. Optional. */ buttons?: HeaderButtonKey[]; } export type SectionFieldsLayout = 'one_column' | 'two_column' | 'three_column' | 'responsive'; export type TabLayout = 'vertical' | 'horizontal'; export type SectionMovementOption = 'static' | 'tab' | 'free'; export type FieldMovementOption = 'tab' | 'free'; export type CustomObjectCustomSectionComponentKey = 'attachments' | 'comments'; /** Default value wrapper used by `SectionField.defaultValue`. */ export interface SectionFieldDefaultValue { value: JsonValue; } /** Options for one field entry inside a `fields_section`. */ export interface SectionFieldOptions { /** Hide this field from view. Optional. */ hidden?: boolean; /** Allow inline editing of this field. Optional. */ editable?: boolean; /** Render this field read-only. Optional. */ readOnly?: boolean; /** Default value metadata for the field. Optional. */ defaultValue?: SectionFieldDefaultValue; /** Rich-text field height. Optional. */ height?: number; /** Whether the layout manager can remove this field. Optional. */ canBeDeleted?: boolean; /** Controls how the field can be moved in the UI. Optional. */ movementOption?: FieldMovementOption; } /** A typed field entry inside a `fields_section`. */ export interface SectionField extends SectionFieldOptions { /** Field to display. Required. */ field: CustomObjectFieldInput; } /** @deprecated Use `SectionField` for object entries or `SectionFieldInput` for field arrays. */ export type SectionFieldRef = SectionField; /** One field entry inside a `fields_section`. */ export type SectionFieldInput = CustomObjectFieldInput | SectionField; /** Common edit controls for sections. */ export interface PageLayoutSectionOptions { /** Whether the layout manager can delete this section. Optional. */ canBeDeleted?: boolean; /** Whether the layout manager can rename this section. Optional. */ canBeRenamed?: boolean; /** Controls how the section can be moved. Optional. */ movementOption?: SectionMovementOption; /** Hide the section title. Optional. */ hideName?: boolean; } /** A standard field-grid section inside a tab. */ export interface FieldsSection extends PageLayoutSectionOptions { type: 'fields_section'; /** Field section metadata for the fields displayed in this layout section. Required. */ section: CustomObjectFieldSection; /** Stable layout section key. Optional. Defaults to `section.getSectionId()`. */ key?: SectionKey; /** Display name of the layout section. Optional. Defaults to the field section name. */ name?: string; /** Fields to display in this section. Required. */ fields: SectionFieldInput[]; /** Column layout for the fields. Optional. @default `'responsive'` (backend default). */ layout?: SectionFieldsLayout; } export interface RelatedObjectSection extends PageLayoutSectionOptions { type: 'related_object_section'; /** Stable layout section key. Required. */ key: SectionKey; /** Display name of the layout section. Required. */ name: string; /** Related object whose records point back to this custom object. Required. */ relatedObjectRqlName: RelatedObjectRqlName; /** Field on the related object that points back to this custom object. Required. */ relatedFieldRqlName: FieldRqlName; /** Field RQL names to display for related records. Required. */ fields: FieldRqlName[]; } export interface RelatedFieldSection extends PageLayoutSectionOptions { type: 'related_field_section'; /** Stable layout section key. Required. */ key: SectionKey; /** Display name of the layout section. Required. */ name: string; /** Related object reached from this custom object. Required. */ relatedObjectRqlName: RelatedObjectRqlName; /** Field on this custom object that points to the related object. Required. */ relatedFieldRqlName: FieldRqlName; /** Field RQL names to display from the related object. Required. */ fields: FieldRqlName[]; /** Column layout for the fields. Optional. @default `'responsive'` (backend default). */ layout?: SectionFieldsLayout; } export interface CustomSection extends PageLayoutSectionOptions { type: 'custom_section'; /** Stable layout section key. Required. */ key: SectionKey; /** Display name of the layout section. Required. */ name: string; /** Custom object section component key. Required. */ componentKey: CustomObjectCustomSectionComponentKey; } export interface ResizableLayoutPosition { identifier: string; width: number; height: number; minWidth?: number; minHeight?: number; maxWidth?: number; maxHeight?: number; } export interface ReportsSectionItem { key: string; reportId: string; position: ResizableLayoutPosition; contextualFieldAttrName?: FieldRqlName; visualizationType?: string; } export interface ReportsSection extends PageLayoutSectionOptions { type: 'reports_section'; /** Stable layout section key. Required. */ key: SectionKey; /** Display name of the layout section. Required. */ name: string; /** Reports to display in this section. Required. */ reports: ReportsSectionItem[]; } export interface CanvasSection extends PageLayoutSectionOptions { type: 'canvas_section'; /** Stable layout section key. Required. */ key: SectionKey; /** Display name of the layout section. Required. */ name: string; /** Canvas composition to render. Required. */ canvasCompositionId: CanvasCompositionId; /** Composition input name to field or state path mapping. Optional. */ inputMapping?: Record; } /** A section inside a page layout tab. */ export type PageLayoutSection = FieldsSection | RelatedObjectSection | RelatedFieldSection | CustomSection | ReportsSection | CanvasSection; /** A tab that contains one or more field-grid sections. */ export interface TabWithSections { type: 'tab_with_sections'; /** Stable key for this tab. Required. */ key: TabKey; /** Display name of the tab. Required. */ name: string; /** Sections inside this tab. Required. */ sections: PageLayoutSection[]; } /** A tab that hosts a custom UI component. */ export interface CustomTab { type: 'custom_tab'; /** Stable key for this tab. Required. */ key: TabKey; /** Display name of the tab. Required. */ name: string; /** Key of the custom component to render. Required. */ componentKey: string; } /** A tab entry in a page layout — either a sections-based tab or a custom component tab. */ export type PageLayoutTab = TabWithSections | CustomTab; /** Column layout for a `fields_section`. */ /** Edits to a built-in section inside a system tab. */ export interface SystemSectionEdit { /** Rename this section. Optional. */ newName?: string; /** Remove this section. Optional. */ deleted?: boolean; /** Change the column layout. Optional. */ newLayout?: SectionFieldsLayout; /** Add fields to this section. Optional. */ newFields?: SectionFieldInput[]; /** Hide the section name header. Optional. */ newHideName?: boolean; } /** Edits to a built-in (system) tab. */ export interface SystemTabEdit { /** Rename this tab. Optional. */ newName?: string; /** Remove this tab. Optional. */ deleted?: boolean; /** Add new sections to this tab. Optional. */ newSections?: PageLayoutSection[]; /** Edits to built-in sections inside this tab, keyed by section key. Optional. */ systemSectionEdits?: Record; /** Reorder sections by key. Optional. */ sectionsOrder?: SectionKey[]; } /** * Tab structure configuration for a page layout (`tab_edits`). * * All fields are optional. An omitted `tabEdits` defaults to `{}`. * * > **Caution:** key names are camelCase on the wire. Snake_case keys are * > silently dropped and produce an empty layout on deploy. */ export interface TabEdits { /** New tabs to add to this layout. Each must include a `type` discriminator. Optional. */ newTabs?: PageLayoutTab[]; /** Edits to existing system tabs, keyed by tab key. Optional. */ systemTabEdits?: Record; /** Ordered list of tab `key` values (new + system). Optional. */ tabsOrder?: TabKey[]; /** Tab strip orientation. Optional. */ newTabLayout?: TabLayout; } /** * A single visibility condition applied to a section or tab. * * Both fields are required by the backend — pass an empty string / empty object * if you have no formula or condition tree. */ export interface VisibilityCondition { /** RQL formula that controls visibility. Required. */ rqlFormula: string; /** Layered conditions tree (AST). Required. */ layeredConditionsTree: JsonObject; } /** * Visibility conditions for sections and tabs on a page layout. * * Pass `{}` for no conditions. */ export interface VisibilityConditions { /** Per-section visibility conditions, keyed by section key. Optional. */ sections?: Record; /** Per-tab visibility conditions, keyed by tab key. Optional. */ tabs?: Record; } /** Where to insert a new layout item. Defaults to `'end'`. */ export type PageLayoutMutationPosition = 'start' | 'end' | number; interface AddPageLayoutFieldOptionsBase extends SectionFieldOptions { /** Insert position inside the section field list. Optional. @default `'end'` */ position?: PageLayoutMutationPosition; /** Allow the same field to appear twice in the same section. Optional. @default false */ allowDuplicate?: boolean; } /** Options for adding a field to a typed custom-object field section. */ export interface AddCustomPageLayoutFieldOptions extends AddPageLayoutFieldOptionsBase { /** * Tab key containing the target section. Optional when `section` is unique * across the layout. */ tabKey?: string; /** Field-section metadata used to find typed custom layout sections and assign field metadata. */ section?: CustomObjectFieldSection; /** Layout section key. Optional escape hatch for ambiguous custom sections. */ sectionKey?: SectionKey; /** Deprecated. Use `section`. */ fieldSection?: never; /** Custom layout sections are selected by `section`; `sectionKey` can disambiguate. */ systemSection?: false; } /** Options for adding a field to a built-in system section. */ export interface AddSystemPageLayoutFieldOptions extends AddPageLayoutFieldOptionsBase { /** * Add to a built-in system section via `systemTabEdits`. * * When true, `tabKey` and `sectionKey` are required and the edit is created * if it does not already exist. */ systemSection: true; /** System tab key containing the target section. Required. */ tabKey: string; /** Built-in system section key. Required. */ sectionKey: string; /** Custom field-section objects are only accepted for custom layout sections. */ section?: never; /** Deprecated. Use `section` for custom sections, or `sectionKey` for system sections. */ fieldSection?: never; } /** Options for adding a field to an existing layout section. */ export type AddPageLayoutFieldOptions = AddCustomPageLayoutFieldOptions | AddSystemPageLayoutFieldOptions; /** Options for adding multiple fields to an existing layout section. */ export type AddPageLayoutFieldsOptions = AddPageLayoutFieldOptions; /** Options for removing fields from a layout. */ interface RemovePageLayoutFieldOptionsBase { /** * Remove all matching occurrences. When false, removing a field that appears * more than once throws. Optional. @default true */ removeAll?: boolean; } /** Options for removing fields from typed custom-object field sections. */ export interface RemoveCustomPageLayoutFieldOptions extends RemovePageLayoutFieldOptionsBase { /** Limit removal to one tab. Optional. */ tabKey?: string; /** Limit removal to one field section. Optional. */ section?: CustomObjectFieldSection; /** Limit removal to one layout section key. Optional. */ sectionKey?: SectionKey; /** Custom layout sections are selected by `section`; `sectionKey` can disambiguate. */ systemSection?: false; } /** Options for removing fields from built-in system section edits. */ export interface RemoveSystemPageLayoutFieldOptions extends RemovePageLayoutFieldOptionsBase { /** Remove from a built-in system section edit. Required. */ systemSection: true; /** System tab key containing the target section. Required. */ tabKey: string; /** Built-in system section key. Required. */ sectionKey: string; /** Custom field-section objects are only accepted for custom layout sections. */ section?: never; } /** Options for removing fields from a layout. */ export type RemovePageLayoutFieldOptions = RemoveCustomPageLayoutFieldOptions | RemoveSystemPageLayoutFieldOptions; /** Options for adding a section to an existing tab. */ export interface AddPageLayoutSectionOptions { /** Tab key to add the section to. Required. */ tabKey: string; /** Insert position inside the tab. Optional. @default `'end'` */ position?: PageLayoutMutationPosition; /** Allow another section with the same key in the same tab. Optional. @default false */ allowDuplicate?: boolean; /** * Add to a built-in system tab via `systemTabEdits.newSections`. * * When false, the tab must already be present in `newTabs`. */ systemTab?: boolean; } /** Options for removing sections from a layout. */ export interface RemovePageLayoutSectionOptions { /** Limit removal to one tab. Optional. */ tabKey?: string; /** * Remove all matching occurrences. When false, removing a section that appears * more than once throws. Optional. @default true */ removeAll?: boolean; /** Custom layout sections are selected by layout section key or `CustomObjectFieldSection`. */ systemSection?: false; } /** Options for removing a built-in system section from a layout. */ export interface RemoveSystemPageLayoutSectionOptions { /** System tab key containing the target section. Required. */ tabKey: string; /** Delete a built-in system section via `systemSectionEdits`. Required. */ systemSection: true; } /** Options for adding a tab to a layout. */ export interface AddPageLayoutTabOptions { /** Insert position inside `newTabs` and `tabsOrder`. Optional. @default `'end'` */ position?: PageLayoutMutationPosition; /** Allow another tab with the same key. Optional. @default false */ allowDuplicate?: boolean; } /** Options for removing a tab from a layout. */ export interface RemovePageLayoutTabOptions { /** * Remove all matching occurrences. When false, removing a tab that appears * more than once throws. Optional. @default true */ removeAll?: boolean; /** Delete a built-in system tab via `systemTabEdits`. */ systemTab?: boolean; } /** Options for adding a custom header field. */ export interface AddHeaderFieldOptions extends Omit { /** * Stable header key. Defaults to the field api_name when adding a `Field`. * Required when adding a raw `NewHeaderField`. */ key?: string; /** Insert position inside `newFields` and `headerFieldsOrder`. Optional. @default `'end'` */ position?: PageLayoutMutationPosition; /** Allow another custom header field with the same key. Optional. @default false */ allowDuplicate?: boolean; } /** Options for removing custom header fields. */ export interface RemoveHeaderFieldOptions { /** * Remove all matching occurrences. When false, removing a header field that * appears more than once throws. Optional. @default true */ removeAll?: boolean; } /** * Initialization properties for {@link CustomObjectPageLayout}. */ export interface CustomObjectPageLayoutProps { /** * Stable identifier for this layout (e.g. `'default'`). * * Required. Use `'default'` for the layout that drives the **Add** form and * the default record detail view. Each custom object supports one `'default'` * layout. */ apiName: string; /** * Display name of this layout shown in the layout selector. * * Required. */ name: string; /** * Tab structure — which tabs and sections to show on the detail page. * * Optional. @default `{}` */ tabEdits?: TabEdits; /** * Header configuration — title field, description field, and action buttons. * * Optional. @default `{}` */ headerEdits?: HeaderEdits; /** * Visibility conditions for individual tabs and sections. * * Optional. @default `{}` */ visibilityConditions?: VisibilityConditions; /** * Stable identifier for this layout record. Pass a human-readable slug * to keep it stable across regenerations. * * Optional. Auto-generated when omitted. @default generateId('layout') */ layoutId?: string; /** * Blueprint key used during install. Defaults to the parent object's api_name. * * Optional. */ blueprintKey?: string; } export interface CustomObjectPageLayoutLoadFromExistingOptions extends ExistingManifestContextOptions { customObjectApiName?: string; } type ResolveFieldSectionId = (fieldRqlName: string) => string | undefined; /** * Defines a page layout and registers it with the manifest. * * A page layout controls the tab structure, section groupings, header fields, * and visibility conditions on a custom object's record detail page. You would * normally define one layout per custom object using `apiName: 'default'`. * * For simple single-tab layouts, use the {@link CustomObjectPageLayout.basic} * factory instead of constructing the full props manually. * * > **Caution:** all keys inside `tabEdits` and `headerEdits` must be camelCase. * > Snake_case keys are silently dropped on the wire and produce an empty layout. * * @example * ```ts * new CustomObjectPageLayout(memberObj, { * apiName: 'default', * name: 'Member', * layoutId: 'layout_gym_member', * tabEdits: { * newTabs: [{ * key: 'member', name: 'Member', type: 'tab_with_sections', * sections: [{ * name: 'Profile', * type: 'fields_section', * section: profileSection, * fields: [{ field: 'name', editable: true }, memberEmail], * }], * }], * systemTabEdits: {}, * tabsOrder: ['member'], * }, * visibilityConditions: {}, * }); * ``` * * @see {@link CustomObjectPageLayout.basic} — factory for simple single-tab layouts. */ export declare class CustomObjectPageLayout { static readonly componentType: "CUSTOM_OBJECT_PAGE_LAYOUT"; static toDeletionIdentifier(identifier: PageLayoutDeletionIdentifier): ManifestComponentDeletion; private readonly _layoutId; private readonly _apiName; private readonly _customObjectApiName; private readonly _name; private readonly _tabEdits; private readonly _headerEdits; private readonly _visibilityConditions; private readonly _blueprintKey; private readonly _fieldSectionLayoutSections; private readonly _customObject; /** * @param customObject - The custom object this layout belongs to. * @param props - Initialization properties. * @throws {Error} If `apiName` or `name` is empty. */ constructor(customObject: CustomObject, props: CustomObjectPageLayoutProps); /** * Loads this page layout from the active existing-manifest JSON context. * * `id` resolves against `layout_id` first, then `api_name`. When loading by * api_name such as `'default'`, pass `customObjectApiName` if multiple objects * have a layout with the same api_name. */ static loadFromExisting(layoutIdOrApiName: string, options?: CustomObjectPageLayoutLoadFromExistingOptions): CustomObjectPageLayout; /** @internal Hydrates a page layout from existing manifest wire JSON. */ static _fromExistingComponent(customObject: CustomObject, component: Record, resolveFieldSectionId?: ResolveFieldSectionId): CustomObjectPageLayout; /** * Returns the stable identifier for this layout record (e.g. `'layout_gym_member'`). */ getLayoutId(): string; /** * Returns the api_name of this layout (e.g. `'default'`). */ getApiName(): string; /** * Returns the api_name of the custom object this layout belongs to. */ getCustomObjectApiName(): string; /** * Adds one field to an existing layout section. * * For loaded layouts, this mutates the preserved `tab_edits` JSON in place * and normalizes only the inserted field entry. */ addField(field: CustomObjectFieldInput, options: AddPageLayoutFieldOptions): this; /** * Adds multiple fields to an existing layout section. * * Fields are inserted in the order provided. For custom layout sections, * pass `section` to choose the typed field section. Use `sectionKey` only * when a field section maps to multiple layout sections. */ addFields(fields: CustomObjectFieldInput[], options: AddPageLayoutFieldsOptions): this; /** * Removes a field from the layout's field sections. * * This only removes the field from the page layout. It does not delete the * `CUSTOM_OBJECT_FIELD` component from the manifest. */ removeField(field: Field | string, options?: RemovePageLayoutFieldOptions): this; /** Removes multiple fields from the layout's field sections. */ removeFields(fields: Array, options?: RemovePageLayoutFieldOptions): this; /** * Adds a section to an existing tab. * * Pass `systemTab: true` to add a new section to a built-in tab via * `systemTabEdits.newSections`. */ addSection(section: PageLayoutSection, options: AddPageLayoutSectionOptions): this; /** * Removes a section from `newTabs` / `systemTabEdits.newSections`. * * Pass `systemSection: true` with `tabKey` to delete a built-in section via * `systemSectionEdits[sectionKey].deleted`. */ removeSection(section: CustomObjectFieldSection, options?: RemovePageLayoutSectionOptions): this; removeSection(sectionKey: string, options?: RemovePageLayoutSectionOptions): this; removeSection(sectionKey: string, options: RemoveSystemPageLayoutSectionOptions): this; /** Adds a new tab to `tab_edits.newTabs`. */ addTab(tab: PageLayoutTab, options?: AddPageLayoutTabOptions): this; /** * Removes a tab from `newTabs`. * * Pass `systemTab: true` to delete a built-in tab via `systemTabEdits[tabKey].deleted`. */ removeTab(tabKey: string, options?: RemovePageLayoutTabOptions): this; /** Adds a custom header field to `header_edits.newFields`. */ addHeaderField(field: Field | NewHeaderField, options?: AddHeaderFieldOptions): this; /** Removes custom header fields from `header_edits.newFields`. */ removeHeaderField(fieldOrKey: Field | string, options?: RemoveHeaderFieldOptions): this; /** Sets the record title field in `header_edits`. */ setTitleField(field: Field): this; setTitleField(fieldApiName: FieldRqlName): this; /** Removes the record title field from the header. */ removeTitleField(): this; /** Sets the record description field in `header_edits`. */ setDescriptionField(field: Field): this; setDescriptionField(fieldApiName: FieldRqlName): this; /** Removes the record description field from the header. */ removeDescriptionField(): this; private ensureTabsOrder; private ensureSystemSectionsOrder; private ensureHeaderFieldsOrder; private headerOrderFieldIsUsed; private removeHeaderOrderFieldIfUnused; private serializeFieldForMutation; private normalizeSectionFieldForMutation; private normalizeSectionForMutation; private normalizeTabForMutation; private assignFieldSectionForMutation; private assertFieldBelongsToLayout; private _resolveField; private fieldApiName; private assertFieldSectionBelongsToLayout; private rememberFieldSectionLayoutSection; private layoutSectionKeyForFieldSection; private requireOneFieldContainer; private findFieldContainers; private ensureSystemSectionFieldContainer; private ensureSystemTabEdit; private ensureSystemSectionEdit; private findNewTabWithSections; private assertSectionKeyAvailable; private normalizeHeaderFieldForMutation; private headerFieldApiName; private removeSectionVisibility; private removeTabVisibility; /** * Serializes this layout to the wire format consumed by the manifest install endpoint. * * @returns A plain object with `type: 'CUSTOM_OBJECT_PAGE_LAYOUT'`. */ toDict(): Record; /** * Creates a simple single-tab "Details" layout with a single "General" section. * * Use this factory when you want a straightforward layout without customizing * tabs or sections. Always uses `apiName: 'default'`. * * @param customObject - The custom object to create the layout for. * @param props - Optional. Pass `section` and `fields` to populate the General section. * @returns A new `CustomObjectPageLayout` registered with the manifest. * * @example * ```ts * CustomObjectPageLayout.basic(memberObj, { * section: profileSection, * fields: [memberFirstName, memberEmail, memberStatusField], * }); * ``` */ static basic(customObject: CustomObject, props?: { section?: CustomObjectFieldSection; fields?: CustomObjectFieldInput[]; }): CustomObjectPageLayout; } export {}; //# sourceMappingURL=custom-object-page-layout.d.ts.map