Some checks failed
App Prod-Parity E2E Dispatch / dispatch (push) Failing after 10s
CD Storybook UI / build (push) Failing after 3m25s
CD Storybook UI / dispatch (push) Has been skipped
CD deploy main / website-changed-files (push) Successful in 18m8s
CI Agent Skills / changed-files-check (push) Successful in 18m9s
CI Create App E2E minimal / changed-files-check (push) Successful in 24s
CI Create App / changed-files-check (push) Successful in 26s
CI Docs / changed-files-check (push) Successful in 26s
CI Emails / changed-files-check (push) Successful in 28s
CI Example App Hello World / changed-files-check (push) Successful in 23s
CI Front / changed-files-check (push) Has been skipped
CI Example App Postcard / changed-files-check (push) Successful in 27s
CI UI / changed-files-check (push) Has been skipped
CI Utils Test / changed-files-check (push) Successful in 45s
CI Twenty Apps / discover (push) Successful in 1m3s
CD deploy main / deploy-main (push) Failing after 3s
CI Agent Skills / agent-skills-validate (push) Has been skipped
CI Create App / create-app-test (lint) (push) Has been skipped
CI Emails / emails-test (push) Has been skipped
CI Front / front-task (lingui:compile) (push) Has been skipped
CI Front / front-task (lint) (push) Has been skipped
CI Example App Postcard / example-app-postcard (push) Has been skipped
CI UI / ui-task (lint) (push) Has been skipped
CI UI / ui-task (pack-size) (push) Has been skipped
CI UI / ui-task (size) (push) Has been skipped
CI UI / ui-task (test) (push) Has been skipped
CI UI / ui-task (typecheck) (push) Has been skipped
CI Utils Test / utils-test (push) Has been skipped
CI Twenty Apps / ${{ matrix.app.name }} (push) Has been skipped
CI Emails / ci-emails-status-check (push) Successful in 2s
CI Example App Hello World / ci-example-app-hello-world-status-check (push) Successful in 2s
CI Example App Postcard / ci-example-app-postcard-status-check (push) Successful in 3s
CI UI / ui-sb-test (push) Has been skipped
CI Utils Test / ci-utils-test-status-check (push) Successful in 2s
CI Twenty Apps / ci-twenty-apps-status-check (push) Successful in 2s
CI UI / ci-ui-status-check (push) Successful in 2s
CI Desktop / desktop (push) Failing after 1m8s
Push docs to Crowdin / Push documentation to Crowdin (push) Failing after 55s
Push translations to Crowdin / Extract and upload translations (push) Failing after 5s
Push website translations to Crowdin / Extract and upload website translations (push) Failing after 5s
CI Create App / create-app-test (test) (push) Has been skipped
CI Create App / create-app-test (typecheck) (push) Has been skipped
CI Docs / docs-lint (push) Has been skipped
CI Example App Hello World / example-app-hello-world (push) Has been skipped
CI Front / front-task (lingui:extract) (push) Has been skipped
CI Front / front-task (test) (push) Has been skipped
CI Front / front-task (typecheck) (push) Has been skipped
CI UI / ui-sb-build (push) Failing after 1m32s
CI Twenty Apps / ${{ matrix.app.name }} (${{ matrix.server-source }}) (push) Has been skipped
CI Agent Skills / agent-skills-publish (push) Has been skipped
CI Create App / ci-create-app-status-check (push) Successful in 2s
CI Agent Skills / agent-skills-remote-source (push) Has been skipped
CI Create App E2E minimal / create-app-e2e-minimal (push) Has been cancelled
CI E2E Main / e2e-test (push) Has been cancelled
CI Front / front-sb-build (push) Has been cancelled
CI Front / front-build (push) Has been cancelled
CI Create App E2E minimal / ci-create-app-e2e-minimal-status-check (push) Has been cancelled
CI E2E Main / ci-e2e-main-status-check (push) Has been cancelled
CI E2E Main / notify-main-ci-failure (push) Has been cancelled
CI E2E Main / qa-scout-comment (push) Has been cancelled
CI Front / front-sb-test (push) Has been cancelled
CI Front / front-sb-screenshots (push) Has been cancelled
CI Front / ci-front-status-check (push) Has been cancelled
DPA Sub-Processor Sync / sync-subprocessors (push) Failing after 3s
AI Catalog Sync / sync-catalog (push) Failing after 2s
159 lines
11 KiB
Plaintext
159 lines
11 KiB
Plaintext
---
|
||
title: ページレイアウト
|
||
description: definePageLayout と definePageLayoutTab を使用して、レコードの詳細ページのカスタマイズ(タブ、ウィジェット、front components のレンダー位置)を行います。
|
||
icon: table-columns
|
||
---
|
||
|
||
**ページレイアウト**は、レコードの詳細ページがどのように構成されるかを制御します。どのタブを表示し、そのタブにどのウィジェットを含めるかを決定します。 自分が所有するオブジェクトに対してレイアウトを宣言するには `definePageLayout()` を使用し、既に存在するレイアウト(自分のもの、または標準の Twenty のレイアウト)に単一のタブを追加するには `definePageLayoutTab()` を使用します。
|
||
|
||
| ユースケース | エンティティ |
|
||
| ----------------------------------------- | --------------------- |
|
||
| 自分が所有するオブジェクト上のレコードページ全体のレイアウトを定義する | `definePageLayout` |
|
||
| 既存のレイアウト(自分のオブジェクト、または標準オブジェクト)にタブを1つ追加する | `definePageLayoutTab` |
|
||
|
||
## definePageLayout
|
||
|
||
自分が詳細ページ全体を所有している場合に使用します。通常は、自分で定義したカスタムオブジェクトに対して使用します。
|
||
|
||
```ts src/page-layouts/example-record-page-layout.ts
|
||
import { definePageLayout, PageLayoutTabLayoutMode } from 'twenty-sdk/define';
|
||
import { EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER } from '../objects/example-object';
|
||
import { HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from '../front-components/hello-world';
|
||
|
||
export default definePageLayout({
|
||
universalIdentifier: '203aeb94-6701-46d6-9af1-be2bbcc9e134',
|
||
name: 'Example Record Page',
|
||
type: 'RECORD_PAGE',
|
||
objectUniversalIdentifier: EXAMPLE_OBJECT_UNIVERSAL_IDENTIFIER,
|
||
tabs: [
|
||
{
|
||
universalIdentifier: '6ed26b60-a51d-4ad7-86dd-1c04c7f3cac5',
|
||
title: 'Hello World',
|
||
position: 50,
|
||
icon: 'IconWorld',
|
||
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
|
||
widgets: [
|
||
{
|
||
universalIdentifier: 'aa4234e0-2e5f-4c02-a96a-573449e2351d',
|
||
title: 'Hello World',
|
||
type: 'FRONT_COMPONENT',
|
||
configuration: {
|
||
configurationType: 'FRONT_COMPONENT',
|
||
frontComponentUniversalIdentifier:
|
||
HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER,
|
||
},
|
||
},
|
||
],
|
||
},
|
||
],
|
||
});
|
||
```
|
||
|
||
### 主なポイント
|
||
|
||
* `type` は `'RECORD_INDEX'`、`'RECORD_PAGE'`、`'DASHBOARD'`、`'STANDALONE_PAGE'` のいずれかです。 特定のオブジェクトの詳細ビューをカスタマイズするには `'RECORD_PAGE'` を使用します。
|
||
* `objectUniversalIdentifier` は、このレイアウトを適用するオブジェクトを指定します。
|
||
* 各 `tab` は、`title`、`position`、`layoutMode` を持つページのセクションを定義します。レコードページおよびスタンドアロンページには `VERTICAL_LIST`、ダッシュボードには `GRID`、タブのビューポートを埋める単一のウィジェットには `CANVAS` を使用します。 `VERTICAL_LIST` タブでは、ウィジェットが縦方向に積み重ねられます。 タイムライン、ファイル、メモ、タスク、ワークフローなど、独自のスクロールを管理する組み込みウィジェットは 1 つのビューポートを埋めます。フィールド、フロントコンポーネント、グラフ、その他のコンテンツに合わせたウィジェットは、そのコンテンツの高さまたは設定された高さでレンダリングされます。 `GRID` タブでは、ウィジェットは常に 12 列グリッド上のカードとして配置されます。 `CANVAS` ウィジェットには明示的な位置がありません。キャンバスタブに複数のウィジェットが含まれる場合、それらはビューポート全体を埋めるのではなく、コンテンツの高さでレンダリングされます。
|
||
* `layoutMode` を明示的に設定します。 `layoutMode` を省略すると、`STANDALONE_PAGE` では `VERTICAL_LIST`、それ以外では `GRID` が適用されますが、これはレコードページで望ましい動作であることはほとんどありません。
|
||
* タブ内の各 `widget` は、[front component](/l/ja/developers/extend/apps/layout/front-components)、リレーションリスト、その他のビルトインウィジェットタイプをレンダリングできます。
|
||
* `FRONT_COMPONENT` ウィジェットでは、同じアプリのコマンドメニュー項目ユニバーサル識別子を順序付き配列として `headerCommandMenuItemUniversalIdentifiers` に設定できます。 これらのアクションはウィジェットカードヘッダーにアイコンボタンとして表示され、コマンドレベルの可用性チェックと権限チェックが維持されます。 識別子は一意である必要があり、アプリのインストール時に解決される必要があります。
|
||
* タブの `position` は表示順を制御します。 組み込みタブの後にカスタムタブを配置するには、より大きな値(例:50)を使用します。
|
||
|
||
### フィールド ウィジェット
|
||
|
||
`FIELD` ウィジェットは、レコードの 1 つのフィールドをレンダリングします。 リレーションフィールドの場合、関連レコードの一覧を埋め込むこともできます。
|
||
|
||
```ts
|
||
{
|
||
universalIdentifier: 'c1c2c3c4-c5c6-4000-8000-000000000003',
|
||
title: 'People → Opportunities',
|
||
type: 'FIELD',
|
||
configuration: {
|
||
configurationType: 'FIELD',
|
||
fieldMetadataId: PEOPLE_FIELD_UNIVERSAL_IDENTIFIER,
|
||
fieldDisplayMode: 'TABLE',
|
||
nestedRelationFieldMetadataId: OPPORTUNITIES_FIELD_UNIVERSAL_IDENTIFIER,
|
||
},
|
||
}
|
||
```
|
||
|
||
* `fieldMetadataId` は、レイアウトのオブジェクト上のフィールドのユニバーサル識別子を受け取ります。
|
||
* `fieldDisplayMode` は `'FIELD'`、`'CARD'`、`'EDITOR'`、`'VIEW'`、`'TABLE'` のいずれかです。 `TABLE` は、1 対多のリレーションフィールドのレコードを一覧表示するビューを埋め込みます。
|
||
* `nestedRelationFieldMetadataId` は任意で、リレーション先オブジェクト上の 1 対多リレーションフィールドのユニバーサル識別子を受け取り、2 ホップ先のリレーションにあるレコードを一覧表示するために使用されます(例: 会社ページでその会社の担当者が持つ商談を一覧表示する、または人物ページでその人物の所属する会社の商談を一覧表示する、など)。 最初のホップは 1 対多または多対 1 のリレーションフィールドのどちらでもかまいませんが、2 つ目は必ず 1 対多でなければなりません(ジャンクションリレーションはサポートされません)。また、`fieldDisplayMode: 'TABLE'` が必須であり、ネストされたウィジェットは常に埋め込みビューとしてレンダリングされるため、これを他の表示モードと組み合わせると検証エラーになります。
|
||
|
||
## definePageLayoutTab
|
||
|
||
既存のレイアウトにタブを**追加**したい場合にのみ使用します。たとえば、標準の Company ページにアナリティクスタブを追加する場合や、自分のオブジェクトのレイアウトに AI サマリータブを追加する場合などです。
|
||
|
||
```ts src/page-layouts/example-extra-tab.ts
|
||
import {
|
||
definePageLayoutTab,
|
||
PageLayoutTabLayoutMode,
|
||
STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS,
|
||
} from 'twenty-sdk/define';
|
||
import { HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER } from '../front-components/hello-world';
|
||
|
||
export default definePageLayoutTab({
|
||
universalIdentifier: 'b1b2b3b4-b5b6-4000-8000-000000000001',
|
||
pageLayoutUniversalIdentifier:
|
||
STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.companyRecordPage
|
||
.universalIdentifier,
|
||
title: 'Hello World',
|
||
position: 1000,
|
||
icon: 'IconWorld',
|
||
layoutMode: PageLayoutTabLayoutMode.VERTICAL_LIST,
|
||
widgets: [
|
||
{
|
||
universalIdentifier: 'b1b2b3b4-b5b6-4000-8000-000000000002',
|
||
title: 'Hello World',
|
||
type: 'FRONT_COMPONENT',
|
||
configuration: {
|
||
configurationType: 'FRONT_COMPONENT',
|
||
frontComponentUniversalIdentifier:
|
||
HELLO_WORLD_FRONT_COMPONENT_UNIVERSAL_IDENTIFIER,
|
||
},
|
||
},
|
||
],
|
||
});
|
||
```
|
||
|
||
### 主なポイント
|
||
|
||
* `pageLayoutUniversalIdentifier` は**必須**であり、インストール時点ですでに存在するページレイアウトを指している必要があります。標準の Twenty のレイアウト、または自分のアプリで定義したレイアウトのいずれかです。 別のインストール済みアプリが所有するレイアウトへのアプリ間参照は、現時点ではサポートされていません。 親レイアウトが存在しない場合、インストールは明確な検証エラーとともに失敗します。
|
||
|
||
* 標準の Twenty レイアウトの場合は、`twenty-sdk/define` から識別子を import します:
|
||
|
||
```ts
|
||
import { STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS } from 'twenty-sdk/define';
|
||
|
||
// STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.companyRecordPage.universalIdentifier
|
||
// STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.personRecordPage.universalIdentifier
|
||
// STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.taskRecordPage.universalIdentifier
|
||
// STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.opportunityRecordPage.universalIdentifier
|
||
// STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.noteRecordPage.universalIdentifier
|
||
// …
|
||
```
|
||
|
||
各レイアウトエントリは、その `tabs` とその `widgets` も公開しているため、任意の階層を参照できます:
|
||
|
||
```ts
|
||
STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.taskRecordPage.tabs.home
|
||
.universalIdentifier;
|
||
STANDARD_PAGE_LAYOUT_UNIVERSAL_IDENTIFIERS.taskRecordPage.tabs.home.widgets
|
||
.fields.universalIdentifier;
|
||
```
|
||
|
||
短いエイリアス `STANDARD_PAGE_LAYOUT` も利用できます:
|
||
|
||
```ts
|
||
import { STANDARD_PAGE_LAYOUT } from 'twenty-sdk/define';
|
||
|
||
STANDARD_PAGE_LAYOUT.companyRecordPage.universalIdentifier;
|
||
```
|
||
|
||
* `widgets` はこのタブのみにスコープされます。`definePageLayout` 内でインライン定義されたウィジェットとまったく同様に、[front components](/l/ja/developers/extend/apps/layout/front-components)、views などを参照します。
|
||
|
||
* `position` は、対象のレイアウト上にある既存のタブとの順序を制御します。 組み込みタブとの相対位置が希望どおりになるような値を選択してください。
|
||
|
||
* 既存のレイアウトに追加だけを行いたい場合は、`definePageLayout` の代わりにこちらを使用してください。 レイアウト全体を自分が所有している場合は、`definePageLayout` を使用してください。
|