Page constructor
@gravity-ui/page-constructor ·

Page constructor
Page-constructor は、JSON データに基づいてウェブページまたはその一部をレンダリングするためのライブラリです(後で YAML フォーマットのサポートを追加する予定です)。
ページを作成する際には、コンポーネントベースのアプローチが採用されています。ページは、任意の順序で配置できる一連の既製のブロックを使用して構築されます。各ブロックには特定のタイプと入力データパラメータのセットがあります。
入力データのフォーマットと利用可能なブロックのリストについては、ドキュメント を参照してください。
Install
npm install @gravity-ui/page-constructor
Quick start
まず、React プロジェクトと何らかのサーバーが必要です。たとえば、Vite と Express サーバーを使用して React プロジェクトを作成するか、Next.js アプリケーションを作成できます。これにより、クライアントとサーバーの両方のサイドが一度に利用可能になります。
必要な依存関係をインストールします。
npm install @gravity-ui/page-constructor @diplodoc/transform @gravity-ui/uikit
Page Constructor をページに挿入します。正しく機能するには、PageConstructorProvider でラップする必要があります。
import {PageConstructor, PageConstructorProvider} from '@gravity-ui/page-constructor';
import '@gravity-ui/page-constructor/styles/styles.scss';
const App = () => {
const content = {
blocks: [
{
type: 'header-block',
title: 'Hello world',
background: {color: '#f0f0f0'},
description:
'**Congratulations!** Have you built a [page-constructor](https://github.com/gravity-ui/page-constructor) into your website',
},
],
};
return (
<PageConstructorProvider>
<PageConstructor content={content} />
</PageConstructorProvider>
);
};
export default App;
これは最もシンプルな接続例でした。YFM マークアップを機能させるには、サーバーでコンテンツを処理し、クライアントで受信する必要があります。
サーバーが別のアプリケーションである場合は、page-constructor をインストールする必要があります。
npm install @gravity-ui/page-constructor
すべての基本ブロックで YFM を処理するには、contentTransformer を呼び出し、コンテンツとオプションを渡します。
const express = require('express');
const app = express();
const {contentTransformer} = require('@gravity-ui/page-constructor/server');
const content = {
blocks: [
{
type: 'header-block',
title: 'Hello world',
background: {color: '#f0f0f0'},
description:
'**Congratulations!** Have you built a [page-constructor](https://github.com/gravity-ui/page-constructor) into your website',
},
],
};
app.get('/content', (req, res) => {
res.send({content: contentTransformer({content, options: {lang: 'en'}})});
});
app.listen(3000);
クライアントでは、コンテンツを受信するためにエンドポイント呼び出しを追加します。
import {PageConstructor, PageConstructorProvider} from '@gravity-ui/page-constructor';
import '@gravity-ui/page-constructor/styles/styles.scss';
import {useEffect, useState} from 'react';
const App = () => {
const [content, setContent] = useState();
useEffect(() => {
(async () => {
const response = await fetch('http://localhost:3000/content').then((r) => r.json());
setContent(response.content);
})();
}, []);
return (
<PageConstructorProvider>
<PageConstructor content={content} />
</PageConstructorProvider>
);
};
export default App;
Ready-made template
新しいプロジェクトを開始するには、準備済みの Next.js のテンプレート を使用できます。
Static site builder
Page Constructor Builder - @gravity-ui/page-constructor を使用して YAML 設定から静的ページをビルドするためのコマンドラインユーティリティ
Documentation
Parameters
interface PageConstructorProps {
content: PageContent; // JSON フォーマットのブロックデータ。
shouldRenderBlock?: ShouldRenderBlock; // 各ブロックのレンダリング時に呼び出され、表示条件を設定できる関数。
custom?: Custom; // カスタムブロック(「カスタマイズ」を参照)。
renderMenu?: () => React.ReactNode; // ページメニューとナビゲーションをレンダリングする関数(デフォルトのメニューバージョンのレンダリングを追加する予定です)。
navigation?: NavigationData; // JSON フォーマットのナビゲーションコンポーネントを使用するためのナビゲーションデータ
isBranded?: boolean; // true の場合、https://gravity-ui.com/ へのリンクを含むフッターが追加されます。BrandFooter コンポーネントでさらにカスタマイズできます。
}
interface PageConstructorProviderProps {
isMobile?: boolean; // モバイルモードでコードが実行されていることを示すフラグ。
locale?: LocaleContextProps; // 言語とドメインに関する情報(リンクの生成とフォーマットに使用されます)。
location?: Location; // ブラウザまたはルーターの履歴 API、ページ URL。
analytics?: AnalyticsContextProps; // 分析イベントを処理する関数
ssrConfig?: SSR; // サーバーサイドでコードが実行されていることを示すフラグ。
theme?: 'light' | 'dark'; // ページをレンダリングするためのテーマ。
mapsContext?: MapsContextType; // マップのパラメータ: apikey、type、scriptSrc、nonce
}
export interface PageContent extends Animatable {
blocks: Block[];
menu?: Menu;
background?: MediaProps;
}
interface Custom {
blocks?: CustomItems;
subBlocks?: CustomItems;
headers?: CustomItems;
loadable?: LoadableConfig;
}
type ShouldRenderBlock = (block: Block, blockKey: string) => Boolean;
interface Location {
history?: History;
search?: string;
hash?: string;
pathname?: string;
hostname?: string;
}
interface Locale {
lang?: Lang;
tld?: string;
}
interface SSR {
isServer?: boolean;
}
interface NavigationData {
logo: NavigationLogo;
header: HeaderData;
}
interface NavigationLogo {
icon: ImageProps;
text?: string;
url?: string;
}
interface HeaderData {
leftItems: NavigationItem[];
rightItems?: NavigationItem[];
}
interface NavigationLogo {
icon: ImageProps;
text?: string;
url?: string;
}
サーバーユーティリティ
このパッケージは、コンテンツを変換するためのサーバーユーティリティを提供します。
const {fullTransform} = require('@gravity-ui/page-constructor/server');
const {html} = fullTransform(content, {
lang,
extractTitle: true,
allowHTML: true,
path: __dirname,
plugins,
});
内部では、Yandex Flavored Markdown を HTML に変換するパッケージ diplodoc/transfrom が使用されています。これも peer dependencies に含まれています。
カスタムコンポーネントなど、必要な場所で便利なユーティリティを使用することもできます。
const {
typografToText,
typografToHTML,
yfmTransformer,
} = require('@gravity-ui/page-constructor/server');
const post = {
title: typografToText(title, lang),
content: typografToHTML(content, lang),
description: yfmTransformer(lang, description, {plugins}),
};
その他のユーティリティについては、このセクションを参照してください。
サーバーユーティリティとトランスフォーマーの詳細ドキュメント
サーバーユーティリティの使用に関する包括的なガイド、詳細な説明、高度なユースケースについては、サーバーユーティリティの使用に関する追加チャプターをご覧ください。
カスタムブロック
ページコンストラクタでは、ユーザーが自身のアプリケーションで定義したブロックを使用できます。ブロックは通常の React コンポーネントです。
カスタムブロックをコンストラクタに渡すには:
-
アプリケーションでブロックを作成します。
-
コード内で、ブロックタイプ(文字列)をキー、インポートしたブロックコンポーネントを値とするオブジェクトを作成します。
-
作成したオブジェクトを、
PageConstructorコンポーネントのcustom.blocks、custom.headers、またはcustom.subBlocksパラメータに渡します(custom.headersは、一般的なコンテンツの上に個別にレンダリングされるブロックヘッダーを指定します)。 -
これで、入力データ(
contentパラメータ)で、作成したブロックのタイプとデータを指定して使用できるようになります。
カスタムブロックを作成する際にミックスインやコンストラクタのスタイル変数を使用するには、ファイルにインポートを追加します。
@import '~@gravity-ui/page-constructor/styles/styles.scss';
デフォルトフォントを使用するには、ファイルにインポートを追加します。
@import '~@gravity-ui/page-constructor/styles/fonts.scss';
ロード可能なブロック
ブロックがデータを基に自身をレンダリングする必要がある場合があります。この場合、ロード可能なブロックが使用されます。
カスタム loadable ブロックを追加するには、PageConstructor に custom.loadable プロパティを渡します。このプロパティは、コンポーネントのデータソース名(文字列)をキー、オブジェクトを値とします。
export interface LoadableConfigItem {
fetch: FetchLoadableData; // データロードメソッド
component: React.ComponentType; // ロードされたデータを渡すブロック
}
type FetchLoadableData<TData = any> = (blockKey: string) => Promise<TData>;
グリッド
ページコンストラクタは bootstrap グリッドとその React コンポーネントベースの実装を使用しており、これらは独自のプロジェクトでも(コンストラクタとは別に)使用できます。
使用例:
import {Grid, Row, Col} from '@gravity-ui/page-constructor';
const Page = ({children}: PropsWithChildren<PageProps>) => (
<Grid>
<Row>
<Col sizes={{lg: 4, sm: 6, all: 12}}>{children}</Col>
</Row>
</Grid>
);
ナビゲーション
ページナビゲーションもコンストラクタとは別に使用できます。
import {Navigation} from '@gravity-ui/page-constructor';
const Page= ({data, logo}: React.PropsWithChildren<PageProps>) => <Navigation data={data} logo={logo} />;
ブロック
各ブロックは、アトミックなトップレベルコンポーネントです。これらは src/units/constructor/blocks ディレクトリに格納されています。
サブブロック
サブブロックは、ブロックの children プロパティで使用できるコンポーネントです。設定では、サブブロックからの子コンポーネントのリストが指定されます。レンダリングされると、これらのサブブロックは children としてブロックに渡されます。
page-constructor に新しいブロックを追加する方法
-
src/blocksまたはsrc/sub-blocksディレクトリに、ブロックまたはサブブロックのコードを含むフォルダを作成します。 -
ブロックまたはサブブロックの名前を
BlockTypeまたはSubBlockType列挙型に追加し、src/models/constructor-items/blocks.tsまたはsrc/models/constructor-items/sub-blocks.tsファイルで、既存のものと同様の方法でプロパティを記述します。 -
src/blocks/index.tsファイルにブロックのエクスポートを、src/sub-blocks/index.tsファイルにサブブロックのエクスポートを追加します。 -
src/constructor-items.tsのマッピングに新しいコンポーネントまたはブロックを追加します。 -
新しいブロックのバリデーターを追加します。
- ブロックまたはサブブロックのディレクトリに
schema.tsファイルを作成します。このファイルで、json-schema形式でコンポーネントのパラメータバリデーターを記述します。 schema/validators/blocks.tsまたはschema/validators/sub-blocks.tsファイルでエクスポートします。schema/index.tsファイルのenumまたはselectCasesに追加します。
- ブロックまたはサブブロックのディレクトリに
-
ブロックディレクトリに、入力パラメータの説明を含む
README.mdファイルを追加します。 -
ブロックディレクトリに
__stories__フォルダ内にストーリーブックのデモを追加します。ストーリーのすべてのデモコンテンツは、ストーリーディレクトリのdata.jsonに配置する必要があります。汎用Storyは、ブロックプロップのタイプを受け入れる必要があります。そうしないと、ストーリーブックで不正確なブロックプロップが表示されます。 -
ブロックデータテンプレートを
src/editor/data/templates/フォルダに追加します。ファイル名はブロックタイプと一致する必要があります。 -
(オプション)ブロックプレビューアイコンを
src/editor/data/previews/フォルダに追加します。ファイル名はブロックタイプと一致する必要があります。
テーマ
PageConstructor ではテーマを使用できます。アプリで選択されたテーマに応じて、個々のブロックプロパティに異なる値を設定できます。
ブロックプロパティにテーマを追加するには:
-
models/blocks.tsファイルで、該当するブロックプロパティの型をThemeSupporting<T>ジェネリックを使用して定義します。ここでTはプロパティの型です。 -
ブロックの
reactコンポーネントが含まれるファイルで、getThemedValueとuseThemeフックを使用して、テーマ付きのプロパティ値を取得します(MediaBlock.tsxブロックの例を参照)。 -
プロパティバリデーターにテーマサポートを追加します。ブロックの
schema.tsファイルで、そのプロパティをwithThemeでラップします。
i18n
page-constructor は uikit-based ライブラリであり、uikit の i18n インスタンスを使用しています。国際化を設定するには、uikit の configure を使用するだけです。
import {configure} from '@gravity-ui/uikit';
configure({
lang: 'ru',
});
マップ
マップを使用するには、マップタイプ、scriptSrc、および apiKey を PageConstructorProvider の mapContext フィールドに配置します。
プロジェクトルートの .env.development ファイルで開発モードの環境変数を定義できます。
STORYBOOK_GMAP_API_KEY - Google マップの apiKey
アナリティクス
初期化
アナリティクスを開始するには、コンストラクタにハンドラーを渡します。ハンドラーはプロジェクト側で作成する必要があります。ハンドラーは default および custom イベントオブジェクトを受け取ります。1つのハンドラーがすべてのイベント処理に使用されるため、ハンドラーを作成する際に異なるイベントをどのように処理するか注意してください。複雑なロジックを構築するのに役立つ定義済みのフィールドがあります。
自動設定されたイベントを発生させるには、コンストラクタに autoEvents: true を渡します。
function sendEvents(events: MyEventType []) {
...
}
<PageConstructorProvider
...
analytics={{sendEvents, autoEvents: true}}
...
/>
イベントオブジェクトには、必須フィールドとして name のみが存在します。また、複雑なロジックの管理を助けるための定義済みのフィールドも存在します。例えば、複数のアナリティクスシステムがプロジェクトで使用されている場合、counter.include は特定のカウンターにイベントを送信するのに役立ちます。
type AnalyticsEvent<T = {}> = T & {
name: string;
type?: string;
counters?: AnalyticsCounters;
context?: string;
};
プロジェクトに必要なイベントタイプを設定することが可能です。
type MyEventType = AnalyticsEvent<{
[key: string]?: string; // 'string' 型のみサポートされています
}>;
カウンターセレクター
どの分析システムにイベントを送信するかをイベントごとに設定することが可能です。
type AnalyticsCounters = {
include?: string[]; // 適用される分析カウンターIDの配列
exclude?: string[]; // 適用されない分析カウンターIDの配列
};
context パラメータ
イベントが発生したプロジェクト内の場所を定義するために context 値を渡します。
プロジェクトのニーズに合わせたロジックを作成するか、以下のセレクターを使用してください。
// analyticsHandler.ts
if (isCounterAllowed(counterName, counters)) {
analyticsCounter.reachGoal(counterName, name, parameters);
}
予約済みイベントタイプ
自動設定されたイベントをマークするために、いくつかの定義済みのイベントタイプが使用されます。例えば、これらのタイプを使用してデフォルトのイベントをフィルタリングできます。
enum PredefinedEventTypes {
Default = 'default-event', // すべてのボタンクリックで発生するデフォルトイベント
Play = 'play', // React Player イベント
Stop = 'stop', // React Player イベント
}
開発
npm ci
npm run dev
Vite に関する注意点
import react from '@vitejs/plugin-react-swc';
import dynamicImport from 'vite-plugin-dynamic-import';
export default defineConfig({
plugins: [
react(),
dynamicImport({
filter: (id) => id.includes('/node_modules/@gravity-ui/page-constructor'),
}),
],
});
Vite の場合、vite-plugin-dynamic-import プラグインをインストールし、動的インポートが機能するように設定を構成する必要があります。
リリースフロー
通常、コミットには2つのタイプを使用します。
fix:fixタイプは、コードベースのバグを修正するコミットです(これはセマンティックバージョニングの PATCH に対応します)。feat:featタイプは、コードベースに新機能をもたらすコミットです(これはセマンティックバージョニングの MINOR に対応します)。BREAKING CHANGE: フッターにBREAKING CHANGE:を持つコミット、またはタイプ/スコープの後に!を追加するコミットは、破壊的な API 変更を導入します(セマンティックバージョニングの MAJOR に対応します)。BREAKING CHANGEは、どのタイプのコミットにも含めることができます。- リリースパッケージのバージョンを手動で設定するには、コミットメッセージに
Release-As: <version>を追加する必要があります。例:
git commit -m 'chore: bump release
Release-As: 1.2.3'
すべての情報はこちらで確認できます。
プルリクエストがコードオーナーから承認され、すべてのチェックをパスしたら、以下の手順を実行してください。
- 他のコントリビューターからの変更を含むロボットによるリリースプルリクエスト(例:
chore(main): release 0.0.0)が存在するかどうかを確認してください。存在する場合は、なぜマージされていないのかを確認してください。コントリビューターが共有バージョンをリリースすることに同意する場合は、次のステップに進んでください。同意しない場合は、彼らにバージョンをリリースするように依頼し、次のステップに進んでください。 - PR を squash and merge してください(新しいバージョンを Github-Actions でリリースすることが重要です)。
- ロボットがパッケージの新しいバージョンと CHANGELOG.md の変更に関する情報を含む PR を作成するまで待ちます。このプロセスはActions タブで確認できます。
- CHANGELOG.md で変更を確認し、ロボットの PR を承認します。
- PR を squash and merge します。リリースプロセスはActions タブで確認できます。
アルファバージョンのリリース
ブランチからパッケージのアルファバージョンをリリースしたい場合は、手動で行うことができます。
- Actions タブに移動します。
- 左側のページサイドで「Release alpha version」ワークフローを選択します。
- 右側にある「Run workflow」ボタンが表示されます。ここでブランチを選択できます。
- 手動バージョンのフィールドも表示されます。初めてブランチでアルファをリリースする場合、ここでは何も設定しないでください。最初のリリース後、ブランチがすぐに期限切れになる可能性があるため、
package.jsonを変更しないように、バージョンを手動で設定する必要があります。手動バージョンにalphaプレフィックスを付けないとエラーが発生します。 - 「Run workflow」をプッシュし、アクションが完了するまで待ちます。必要な場合に限り、何度でもバージョンをリリースできます。それ以外の場合は、npm pack を使用してください。
ベータ版メジャーバージョンのリリース
安定版の前にベータバージョンが必要になる可能性が高い新しいメジャーバージョンをリリースしたい場合は、次の手順を実行してください。
betaブランチを作成または更新します。- そこに変更を追加します。
- 新しいベータバージョンの準備ができたら、空のコミットで手動でリリースします(または、最後のコミットに以下のコミットメッセージとフッターを追加できます)。
git commit -m 'fix: last commit
Release-As: 3.0.0-beta.0' --allow-empty
- Release please ロボットが CHANGELOG.md を更新し、パッケージのバージョンを上げた PR を
betaブランチに作成します。 - 必要に応じて何度でも繰り返すことができます。最新のメジャーバージョンをベータタグなしでリリースする準備ができたら、
betaブランチからmainブランチに PR を作成する必要があります。パッケージバージョンにベータタグが付いているのは正常なことであることに注意してください。ロボットはそれを認識し、適切に変更します。3.0.0-beta.0は3.0.0になります。
前のメジャーバージョンのリリースフロー
メインブランチにコミットした後、前のメジャーバージョンに新しいバージョンをリリースしたい場合は、次の手順を実行してください。
- 必要なブランチを更新します。前のメジャーリリースブランチ名は次のとおりです。
version-1.x.x/fixes- メジャーバージョン 1.x.x の場合version-2.x.x- メジャーバージョン 2.x.x の場合
- 前のメジャーリリースブランチから新しいブランチをチェックアウトします。
mainブランチからコミットを cherry-pick します。- PR を作成し、承認を得て、前のメジャーリリースブランチにマージします。
- PR を squash and merge します(新しいバージョンを Github-Actions でリリースすることが重要です)。
- ロボットがパッケージの新しいバージョンと CHANGELOG.md の変更に関する情報を含む PR を作成するまで待ちます。このプロセスはActions タブで確認できます。
- CHANGELOG.md で変更を確認し、ロボットの PR を承認します。
- PR を squash and merge します。リリースプロセスはActions タブで確認できます。
Page constructor editor
エディターは、リアルタイムプレビューによるページコンテンツ管理のためのユーザーインターフェースを提供します。
使用方法:
import {Editor} from '@gravity-ui/page-constructor/editor';
interface MyAppEditorProps {
initialContent: PageContent;
transformContent: ContentTransformer;
onChange: (content: PageContent) => void;
}
export const MyAppEditor = ({initialContent, onChange, transformContent}: MyAppEditorProps) => (
<Editor content={initialContent} onChange={onChange} transformContent={transformContent} />
);
Memory Bank
このプロジェクトには、プロジェクトのアーキテクチャ、コンポーネント、および使用パターンに関する詳細情報を提供するMarkdownドキュメントファイルのコレクションである包括的なメモリバンクが含まれています。メモリバンクは、特にAIエージェントと連携する場合に役立ちます。構造化された情報が含まれています。
- プロジェクト概要: コア要件、目標、およびコンテキスト
- コンポーネントドキュメント: すべてのコンポーネントの詳細な使用ガイド
- システムアーキテクチャ: 技術的なパターンと設計上の決定
- 開発進捗: 現在のステータスと実装の詳細
メモリバンクの使用方法
メモリバンクは memory-bank/ ディレクトリにあり、他のドキュメントと同様に読み取ることができる通常のMarkdownファイルで構成されています。
projectbrief.md- コア要件を含む基礎ドキュメントproductContext.md- プロジェクトの目的とユーザーエクスペリエンスの目標systemPatterns.md- アーキテクチャと技術的な決定techContext.md- 技術、セットアップ、および制約activeContext.md- 現在の作業の焦点と最近の変更progress.md- 実装ステータスと既知の問題usage/- コンポーネント固有の使用ドキュメントstorybookComponents.md- Storybook統合の詳細
AIエージェント向け
このプロジェクトでAIエージェントと連携する場合、メモリバンクは包括的な知識ベースとして機能し、エージェントが以下を理解するのに役立ちます。
- プロジェクトの構造とパターン
- コンポーネントのAPIと使用例
- 開発ワークフローとベストプラクティス
- 現在の実装ステータスと次のステップ
AIエージェントはこれらのファイルを読み取ることで、プロジェクトのコンテキストを迅速に把握し、コードの変更や実装についてより的確な意思決定を行うことができます。
テスト
包括的なドキュメントは、提供されたリンクで入手できます。