diff --git a/src/content/local-docs/libs/expresskit/README-de.md b/src/content/local-docs/libs/expresskit/README-de.md index 496a30d72cef..d5d687bda063 100644 --- a/src/content/local-docs/libs/expresskit/README-de.md +++ b/src/content/local-docs/libs/expresskit/README-de.md @@ -1,6 +1,6 @@ # ExpressKit -ExpressKit ist ein leichtgewichtiger [express.js](https://expressjs.com/)-Wrapper, der sich in [NodeKit](https://github.com/gravity-ui/nodekit) integriert und einige nützliche Funktionen bietet, wie z. B. Request-Logging, Tracing-Unterstützung, asynchrone Controller & Middleware und eine detaillierte Routenbeschreibung. +ExpressKit ist ein leichtgewichtiger [express.js](https://expressjs.com/)-Wrapper, der sich in [NodeKit](https://github.com/gravity-ui/nodekit) integriert und einige nützliche Funktionen wie Request-Logging, Tracing-Unterstützung, asynchrone Controller & Middleware und detaillierte Routenbeschreibungen bietet. Installation: @@ -25,6 +25,18 @@ const app = new ExpressKit(nodekit, { app.run(); ``` +## Eigene Telemetrie + +Standardmäßig sendet die eigene Telemetrie die ursprüngliche Request-URL. Anwendungen mit großen oder +Query-Strings mit hoher Kardinalität können Query-Parameter entfernen, bevor Statistiken gesendet werden: + +```typescript +const config: Partial = { + appTelemetryChEnableSelfStats: true, + appTelemetryChSelfStatsStripQueryParams: true, +}; +``` + ## CSP `config.ts` @@ -56,7 +68,7 @@ export default config; ## CSRF-Schutz -ExpressKit bietet integrierten Schutz vor Cross-Site Request Forgery (CSRF), um Ihre Anwendungen vor bösartigen Cross-Origin-Anfragen zu sichern. Die CSRF-Middleware generiert und validiert automatisch Tokens für zustandsändernde HTTP-Anfragen. +ExpressKit bietet integrierten Schutz vor Cross-Site Request Forgery (CSRF), um Ihre Anwendungen vor bösartigen Cross-Origin-Anfragen zu schützen. Die CSRF-Middleware generiert und validiert automatisch Tokens für zustandsändernde HTTP-Anfragen. ### Grundlegende Konfiguration @@ -76,11 +88,11 @@ export default config; ### Konfigurationsoptionen | Option | Typ | Standard | Beschreibung | -| ------------------- | ------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------------ | -| `appCsrfSecret` | `string \| string[]` | - | **Erforderlich.** Geheimer Schlüssel/Schlüssel für die HMAC-Token-Generierung. Mehrere Schlüssel ermöglichen die Schlüsselrotation. | -| `appCsrfLifetime` | `number` | `2592000` (30 Tage) | Token-Lebensdauer in Sekunden. Setzen Sie auf `0` für kein Ablaufdatum. | -| `appCsrfHeaderName` | `string` | `'x-csrf-token'` | Name des HTTP-Headers für die Token-Validierung. | -| `appCsrfMethods` | `string[]` | `['POST', 'PUT', 'DELETE', 'PATCH']` | HTTP-Methoden, die eine CSRF-Validierung erfordern. | +| ------------------- | -------------------- | ------------------------------------ | ----------------------------------------------------------------------------------------------- | +| `appCsrfSecret` | `string \| string[]` | - | **Erforderlich.** Geheimer Schlüssel (oder Schlüssel) für die HMAC-Token-Generierung. Mehrere Schlüssel ermöglichen die Schlüsselrotation. | +| `appCsrfLifetime` | `number` | `2592000` (30 Tage) | Token-Lebensdauer in Sekunden. Setzen Sie auf `0` für kein Ablaufdatum. | +| `appCsrfHeaderName` | `string` | `'x-csrf-token'` | Name des HTTP-Headers für die Token-Validierung. | +| `appCsrfMethods` | `string[]` | `['POST', 'PUT', 'DELETE', 'PATCH']` | HTTP-Methoden, die eine CSRF-Validierung erfordern. | ### Verwendung @@ -109,7 +121,7 @@ const app = new ExpressKit(nodekit, { 'POST /api/submit': (req, res) => { // Diese Route validiert automatisch das CSRF-Token - res.json({message: 'Formular erfolgreich gesendet'}); + res.json({message: 'Formular erfolgreich übermittelt'}); }, }); ``` @@ -138,7 +150,7 @@ Standardmäßig setzt ExpressKit `no-cache`-Header auf alle Antworten. Sie könn ```typescript const config: Partial = { - expressEnableCaching: true, // Caching standardmäßig zulassen + expressEnableCaching: true, // Caching standardmäßig erlauben }; ``` @@ -147,11 +159,11 @@ const config: Partial = { ```typescript const app = new ExpressKit(nodekit, { 'GET /api/cached': { - enableCaching: true, // Caching für diese Route zulassen + enableCaching: true, // Caching für diese Route erlauben handler: (req, res) => res.json({data: 'cacheable'}), }, 'GET /api/fresh': { - enableCaching: false, // no-cache erzwingen + enableCaching: false, // No-Cache erzwingen handler: (req, res) => res.json({data: 'always fresh'}), }, }); @@ -161,4 +173,4 @@ const app = new ExpressKit(nodekit, { ## Validierung und Antwortserialisierung -- [Request Validation and Response Serialization](https://github.com/gravity-ui/expresskit/blob/main/docs/VALIDATOR.md) - Verwenden Sie Zod-Schemas für automatische Request-Validierung und Antwortserialisierung. \ No newline at end of file +- [Request Validation and Response Serialization](https://github.com/gravity-ui/expresskit/blob/main/docs/VALIDATOR.md) - nutze Zod-Schemas für automatische Request-Validierung und Response-Serialisierung. \ No newline at end of file diff --git a/src/content/local-docs/libs/expresskit/README-es.md b/src/content/local-docs/libs/expresskit/README-es.md index 635e0b012f8e..b4579cceb5e7 100644 --- a/src/content/local-docs/libs/expresskit/README-es.md +++ b/src/content/local-docs/libs/expresskit/README-es.md @@ -1,6 +1,6 @@ # ExpressKit -ExpressKit es un wrapper ligero para [express.js](https://expressjs.com/) que se integra con [NodeKit](https://github.com/gravity-ui/nodekit) y proporciona algunas características útiles como registro de solicitudes, soporte de tracing, controladores y middleware asíncronos, y descripciones detalladas de las rutas. +ExpressKit es un wrapper ligero para [express.js](https://expressjs.com/) que se integra con [NodeKit](https://github.com/gravity-ui/nodekit) y proporciona algunas características útiles como registro de solicitudes, soporte de tracing, controladores y middleware asíncronos, y descripciones detalladas de rutas. Instalación: @@ -25,6 +25,17 @@ const app = new ExpressKit(nodekit, { app.run(); ``` +## Telemetría propia + +Por defecto, la telemetría propia envía la URL de la solicitud original. Las aplicaciones con cadenas de consulta grandes o de alta cardinalidad pueden eliminar los parámetros de consulta antes de enviar las estadísticas: + +```typescript +const config: Partial = { + appTelemetryChEnableSelfStats: true, + appTelemetryChSelfStatsStripQueryParams: true, +}; +``` + ## CSP `config.ts` @@ -95,8 +106,8 @@ const nodekit = new NodeKit({ appCsrfSecret: 'tu-clave-secreta', appAuthPolicy: AuthPolicy.required, - // Asegúrate de que tu middleware establezca el ID de usuario en originalContext, de lo contrario, la generación del token CSRF fallará - appAuthHandler: tuManejadorDeAutenticacion, + // Asegúrate de que tu middleware establezca el ID de usuario en el originalContext, de lo contrario, la generación del token CSRF fallará + appAuthHandler: yourAuthHandler, }, }); @@ -160,4 +171,4 @@ El `enableCaching` a nivel de ruta anula la configuración global. El estado de ## Validación y serialización de respuestas -- [Validación de solicitudes y serialización de respuestas](https://github.com/gravity-ui/expresskit/blob/main/docs/VALIDATOR.md) - utiliza esquemas Zod para la validación automática de solicitudes y la serialización de respuestas. \ No newline at end of file +- [Validación de Solicitudes y Serialización de Respuestas](https://github.com/gravity-ui/expresskit/blob/main/docs/VALIDATOR.md) - utiliza esquemas Zod para la validación automática de solicitudes y la serialización de respuestas. \ No newline at end of file diff --git a/src/content/local-docs/libs/expresskit/README-fr.md b/src/content/local-docs/libs/expresskit/README-fr.md index f0e1fecd25a2..cfff9f9682fd 100644 --- a/src/content/local-docs/libs/expresskit/README-fr.md +++ b/src/content/local-docs/libs/expresskit/README-fr.md @@ -1,6 +1,6 @@ # ExpressKit -ExpressKit est un wrapper léger pour [express.js](https://expressjs.com/) qui s'intègre à [NodeKit](https://github.com/gravity-ui/nodekit) et offre des fonctionnalités utiles telles que la journalisation des requêtes, le support du traçage, les contrôleurs et middlewares asynchrones, ainsi qu'une description détaillée des routes. +ExpressKit est un wrapper léger pour [express.js](https://expressjs.com/) qui s'intègre à [NodeKit](https://github.com/gravity-ui/nodekit) et offre des fonctionnalités utiles telles que la journalisation des requêtes, la prise en charge du traçage, les contrôleurs et middlewares asynchrones, ainsi qu'une description détaillée des routes. Installation : @@ -25,6 +25,17 @@ const app = new ExpressKit(nodekit, { app.run(); ``` +## Télémétrie interne + +Par défaut, la télémétrie interne envoie l'URL de la requête d'origine. Les applications avec des chaînes de requête volumineuses ou à haute cardinalité peuvent supprimer les paramètres de requête avant d'envoyer les statistiques : + +```typescript +const config: Partial = { + appTelemetryChEnableSelfStats: true, + appTelemetryChSelfStatsStripQueryParams: true, +}; +``` + ## CSP `config.ts` @@ -56,7 +67,7 @@ export default config; ## Protection CSRF -ExpressKit fournit une protection intégrée contre le Cross-Site Request Forgery (CSRF) pour sécuriser vos applications contre les requêtes inter-sites malveillantes. Le middleware CSRF génère et valide automatiquement les jetons pour les requêtes HTTP modifiant l'état. +ExpressKit fournit une protection intégrée contre les falsifications de requêtes intersites (CSRF) pour sécuriser vos applications contre les requêtes inter-origines malveillantes. Le middleware CSRF génère et valide automatiquement les jetons pour les requêtes HTTP modifiant l'état. ### Configuration de base @@ -78,7 +89,7 @@ export default config; | Option | Type | Défaut | Description | | ------------------- | -------------------- | ------------------------------------ | ----------------------------------------------------------------------------------------------- | | `appCsrfSecret` | `string \| string[]` | - | **Requis.** Clé(s) secrète(s) pour la génération de jetons HMAC. Plusieurs secrets permettent la rotation des clés. | -| `appCsrfLifetime` | `number` | `2592000` (30 jours) | Durée de vie du jeton en secondes. Définissez à `0` pour aucune expiration. | +| `appCsrfLifetime` | `number` | `2592000` (30 jours) | Durée de vie du jeton en secondes. Définir à `0` pour aucune expiration. | | `appCsrfHeaderName` | `string` | `'x-csrf-token'` | Nom de l'en-tête HTTP pour la validation du jeton. | | `appCsrfMethods` | `string[]` | `['POST', 'PUT', 'DELETE', 'PATCH']` | Méthodes HTTP nécessitant une validation CSRF. | @@ -158,6 +169,6 @@ const app = new ExpressKit(nodekit, { Le paramètre `enableCaching` au niveau de la route remplace le réglage global. L'état de la mise en cache est disponible dans `req.routeInfo.enableCaching`. -## Validation et Sérialisation des réponses +## Validation et sérialisation des réponses - [Validation des requêtes et sérialisation des réponses](https://github.com/gravity-ui/expresskit/blob/main/docs/VALIDATOR.md) - utilisez les schémas Zod pour la validation automatique des requêtes et la sérialisation des réponses. \ No newline at end of file diff --git a/src/content/local-docs/libs/expresskit/README-ja.md b/src/content/local-docs/libs/expresskit/README-ja.md new file mode 100644 index 000000000000..a784847c66f7 --- /dev/null +++ b/src/content/local-docs/libs/expresskit/README-ja.md @@ -0,0 +1,174 @@ +# ExpressKit + +ExpressKit は、[express.js](https://expressjs.com/) をラップした軽量ライブラリで、[NodeKit](https://github.com/gravity-ui/nodekit) と統合されており、リクエストロギング、トレーシングサポート、非同期コントローラーとミドルウェア、詳細なルート説明などの便利な機能を提供します。 + +インストール: + +```bash +npm install --save @gravity-ui/nodekit @gravity-ui/expresskit +``` + +基本的な使い方: + +```typescript +import {ExpressKit} from '@gravity-ui/expresskit'; +import {NodeKit} from '@gravity-ui/nodekit'; + +const nodekit = new NodeKit(); + +const app = new ExpressKit(nodekit, { + 'GET /': (req, res) => { + res.send('Hello World!'); + }, +}); + +app.run(); +``` + +## セルフテレメトリ + +デフォルトでは、セルフテレメトリは元のリクエスト URL を送信します。クエリ文字列が大きい、またはカーディナリティが高いアプリケーションでは、統計情報を送信する前にクエリパラメータを削除できます。 + +```typescript +const config: Partial = { + appTelemetryChEnableSelfStats: true, + appTelemetryChSelfStatsStripQueryParams: true, +}; +``` + +## CSP + +`config.ts` + +```typescript +import type {AppConfig} from '@gravity-ui/nodekit'; +import {csp} from '@gravity-ui/expresskit'; + +const config: Partial = { + expressCspEnable: true, + expressCspPresets: ({getDefaultPresets}) => { + return getDefaultPresets({defaultNone: true}).concat([ + csp.inline(), + {csp.directives.REPORT_TO: 'my-report-group'}, + ]); + }, + expressCspReportTo: [ + { + group: 'my-report-group', + max_age: 30 * 60, + endpoints: [{ url: 'https://cspreport.com/send'}], + include_subdomains: true, + } + ] +} + +export default config; +``` + +## CSRF 保護 + +ExpressKit は、アプリケーションを悪意のあるクロスオリジンリクエストから保護するために、クロスサイトリクエストフォージェリ (CSRF) 保護を組み込んでいます。CSRF ミドルウェアは、状態を変更する HTTP リクエストのトークンを自動的に生成および検証します。 + +### 基本設定 + +CSRF 保護を有効にするには、設定でシークレットキーを設定します。 + +```typescript +import type {AppConfig} from '@gravity-ui/nodekit'; + +const config: Partial = { + // ... + appCsrfSecret: 'your-secret-key-here', +}; + +export default config; +``` + +### 設定オプション + +| オプション | タイプ | デフォルト | 説明 | +| ------------------- | -------------------- | ------------------------------------ | ----------------------------------------------------------------------------------------------- | +| `appCsrfSecret` | `string \| string[]` | - | **必須。** HMAC トークン生成用のシークレットキー。複数のシークレットでキーローテーションが可能です。 | +| `appCsrfLifetime` | `number` | `2592000` (30 日) | トークンの有効期間 (秒)。`0` に設定すると有効期限なしになります。 | +| `appCsrfHeaderName` | `string` | `'x-csrf-token'` | トークン検証用の HTTP ヘッダー名。 | +| `appCsrfMethods` | `string[]` | `['POST', 'PUT', 'DELETE', 'PATCH']` | CSRF 検証が必要な HTTP メソッド。 | + +### 使用方法 + +設定後、CSRF 保護は指定された HTTP メソッドを持つすべてのルートに自動的に適用されます。 + +```typescript +import {ExpressKit, AuthPolicy} from '@gravity-ui/expresskit'; +import {NodeKit} from '@gravity-ui/nodekit'; + +const nodekit = new NodeKit({ + config: { + appCsrfSecret: 'your-secret-key', + appAuthPolicy: AuthPolicy.required, + + // ミドルウェアが originalContext にユーザー ID を設定していることを確認してください。そうしないと、CSRF トークン生成が失敗します。 + appAuthHandler: yourAuthHandler, + }, +}); + +const app = new ExpressKit(nodekit, { + 'GET /api/form': (req, res) => { + // トークンはリクエストコンテキストで利用可能です + res.json({csrfToken: req.originalContext.get('csrfToken')}); + }, + + 'POST /api/submit': (req, res) => { + // このルートは CSRF トークンを自動的に検証します + res.json({message: 'Form submitted successfully'}); + }, +}); +``` + +### ルートごとの設定 + +特定のルートで CSRF 保護を無効にすることができます。 + +```typescript +const app = new ExpressKit(nodekit, { + 'POST /api/webhook': { + authPolicy: AuthPolicy.required, + disableCsrf: true, // このルートの CSRF を無効にする + handler: (req, res) => { + res.json({message: 'Webhook processed'}); + }, + }, +}); +``` + +## キャッシュ制御 + +デフォルトでは、ExpressKit はすべてのレスポンスに `no-cache` ヘッダーを設定します。この動作はグローバルまたはルートごとに制御できます。 + +### グローバル設定 + +```typescript +const config: Partial = { + expressEnableCaching: true, // デフォルトでキャッシュを許可する +}; +``` + +### ルートごとの設定 + +```typescript +const app = new ExpressKit(nodekit, { + 'GET /api/cached': { + enableCaching: true, // このルートのキャッシュを許可する + handler: (req, res) => res.json({data: 'cacheable'}), + }, + 'GET /api/fresh': { + enableCaching: false, // no-cache を強制する + handler: (req, res) => res.json({data: 'always fresh'}), + }, +}); +``` + +ルートレベルの `enableCaching` はグローバル設定を上書きします。キャッシュの状態は `req.routeInfo.enableCaching` で利用可能です。 + +## 検証とレスポンスシリアライゼーション + +- [リクエストバリデーションとレスポンスシリアライゼーション](https://github.com/gravity-ui/expresskit/blob/main/docs/VALIDATOR.md) - Zodスキーマを使用して、リクエストのバリデーションとレスポンスのシリアライゼーションを自動化します。 \ No newline at end of file diff --git a/src/content/local-docs/libs/expresskit/README-ko.md b/src/content/local-docs/libs/expresskit/README-ko.md index 5a2087a99717..37f92cf30e8f 100644 --- a/src/content/local-docs/libs/expresskit/README-ko.md +++ b/src/content/local-docs/libs/expresskit/README-ko.md @@ -25,6 +25,17 @@ const app = new ExpressKit(nodekit, { app.run(); ``` +## 자체 원격 측정 + +기본적으로 자체 원격 측정은 원본 요청 URL을 전송합니다. 쿼리 문자열이 크거나 고유성이 높은 애플리케이션의 경우 통계를 전송하기 전에 쿼리 매개변수를 제거할 수 있습니다. + +```typescript +const config: Partial = { + appTelemetryChEnableSelfStats: true, + appTelemetryChSelfStatsStripQueryParams: true, +}; +``` + ## CSP `config.ts` @@ -58,9 +69,9 @@ export default config; ExpressKit는 애플리케이션을 악의적인 교차 출처 요청으로부터 보호하기 위해 내장된 교차 사이트 요청 위조(CSRF) 보호 기능을 제공합니다. CSRF 미들웨어는 상태 변경 HTTP 요청에 대한 토큰을 자동으로 생성하고 검증합니다. -### 기본 설정 +### 기본 구성 -CSRF 보호를 활성화하려면 설정에서 비밀 키를 구성하십시오. +CSRF 보호를 활성화하려면 구성에서 비밀 키를 설정하십시오. ```typescript import type {AppConfig} from '@gravity-ui/nodekit'; @@ -73,14 +84,14 @@ const config: Partial = { export default config; ``` -### 설정 옵션 +### 구성 옵션 -| 옵션 | 타입 | 기본값 | 설명 | +| 옵션 | 유형 | 기본값 | 설명 | | ------------------- | -------------------- | ------------------------------------ | ----------------------------------------------------------------------------------------------- | -| `appCsrfSecret` | `string \| string[]` | - | **필수.** HMAC 토큰 생성을 위한 비밀 키. 여러 개의 비밀 키를 사용하여 키 로테이션이 가능합니다. | -| `appCsrfLifetime` | `number` | `2592000` (30일) | 토큰 유효 시간(초). 만료 없음으로 설정하려면 `0`으로 설정하십시오. | -| `appCsrfHeaderName` | `string` | `'x-csrf-token'` | 토큰 검증을 위한 HTTP 헤더 이름. | -| `appCsrfMethods` | `string[]` | `['POST', 'PUT', 'DELETE', 'PATCH']` | CSRF 검증이 필요한 HTTP 메서드. | +| `appCsrfSecret` | `string \| string[]` | - | **필수.** HMAC 토큰 생성을 위한 비밀 키입니다. 여러 개의 비밀 키를 사용하여 키 로테이션이 가능합니다. | +| `appCsrfLifetime` | `number` | `2592000` (30일) | 토큰 수명(초). 만료 없음으로 설정하려면 `0`으로 설정하십시오. | +| `appCsrfHeaderName` | `string` | `'x-csrf-token'` | 토큰 검증을 위한 HTTP 헤더 이름입니다. | +| `appCsrfMethods` | `string[]` | `['POST', 'PUT', 'DELETE', 'PATCH']` | CSRF 검증이 필요한 HTTP 메서드입니다. | ### 사용법 @@ -113,7 +124,7 @@ const app = new ExpressKit(nodekit, { }); ``` -### 라우트별 설정 +### 라우트별 구성 특정 라우트에 대해 CSRF 보호를 비활성화할 수 있습니다. @@ -133,7 +144,7 @@ const app = new ExpressKit(nodekit, { 기본적으로 ExpressKit는 모든 응답에 `no-cache` 헤더를 설정합니다. 이 동작은 전역적으로 또는 라우트별로 제어할 수 있습니다. -### 전역 설정 +### 전역 구성 ```typescript const config: Partial = { @@ -141,7 +152,7 @@ const config: Partial = { }; ``` -### 라우트별 설정 +### 라우트별 구성 ```typescript const app = new ExpressKit(nodekit, { @@ -160,4 +171,4 @@ const app = new ExpressKit(nodekit, { ## 유효성 검사 및 응답 직렬화 -- [요청 유효성 검사 및 응답 직렬화](https://github.com/gravity-ui/expresskit/blob/main/docs/VALIDATOR.md) - Zod 스키마를 사용하여 자동 요청 유효성 검사 및 응답 직렬화를 수행합니다. \ No newline at end of file +- [요청 유효성 검사 및 응답 직렬화](https://github.com/gravity-ui/expresskit/blob/main/docs/VALIDATOR.md) - Zod 스키마를 사용하여 요청 유효성 검사 및 응답 직렬화를 자동으로 처리합니다. \ No newline at end of file diff --git a/src/content/local-docs/libs/expresskit/README-pt.md b/src/content/local-docs/libs/expresskit/README-pt.md new file mode 100644 index 000000000000..fa6f4f3dc930 --- /dev/null +++ b/src/content/local-docs/libs/expresskit/README-pt.md @@ -0,0 +1,174 @@ +# ExpressKit + +ExpressKit é um wrapper leve para [express.js](https://expressjs.com/) que se integra com [NodeKit](https://github.com/gravity-ui/nodekit) e oferece recursos úteis como logging de requisições, suporte a tracing, controllers e middlewares assíncronos, e descrição detalhada de rotas. + +Instalação: + +```bash +npm install --save @gravity-ui/nodekit @gravity-ui/expresskit +``` + +Uso básico: + +```typescript +import {ExpressKit} from '@gravity-ui/expresskit'; +import {NodeKit} from '@gravity-ui/nodekit'; + +const nodekit = new NodeKit(); + +const app = new ExpressKit(nodekit, { + 'GET /': (req, res) => { + res.send('Hello World!'); + }, +}); + +app.run(); +``` + +## Telemetria própria + +Por padrão, a telemetria própria envia a URL original da requisição. Aplicações com strings de consulta grandes ou de alta cardinalidade podem remover parâmetros de consulta antes de enviar estatísticas: + +```typescript +const config: Partial = { + appTelemetryChEnableSelfStats: true, + appTelemetryChSelfStatsStripQueryParams: true, +}; +``` + +## CSP + +`config.ts` + +```typescript +import type {AppConfig} from '@gravity-ui/nodekit'; +import {csp} from '@gravity-ui/expresskit'; + +const config: Partial = { + expressCspEnable: true, + expressCspPresets: ({getDefaultPresets}) => { + return getDefaultPresets({defaultNone: true}).concat([ + csp.inline(), + {csp.directives.REPORT_TO: 'my-report-group'}, + ]); + }, + expressCspReportTo: [ + { + group: 'my-report-group', + max_age: 30 * 60, + endpoints: [{ url: 'https://cspreport.com/send'}], + include_subdomains: true, + } + ] +} + +export default config; +``` + +## Proteção CSRF + +O ExpressKit oferece proteção integrada contra Cross-Site Request Forgery (CSRF) para proteger suas aplicações contra requisições maliciosas de origem cruzada. O middleware CSRF gera e valida automaticamente tokens para requisições HTTP que alteram o estado. + +### Configuração Básica + +Para habilitar a proteção CSRF, configure a chave secreta em seu arquivo de configuração: + +```typescript +import type {AppConfig} from '@gravity-ui/nodekit'; + +const config: Partial = { + // ... + appCsrfSecret: 'sua-chave-secreta-aqui', +}; + +export default config; +``` + +### Opções de Configuração + +| Opção | Tipo | Padrão | Descrição | +| ------------------ | -------------------- | -------------------------------- | ----------------------------------------------------------------------------------------------- | +| `appCsrfSecret` | `string \| string[]` | - | **Obrigatório.** Chave(s) secreta(s) para geração de token HMAC. Múltiplas chaves permitem rotação. | +| `appCsrfLifetime` | `number` | `2592000` (30 dias) | Tempo de vida do token em segundos. Defina como `0` para expiração infinita. | +| `appCsrfHeaderName`| `string` | `'x-csrf-token'` | Nome do cabeçalho HTTP para validação do token. | +| `appCsrfMethods` | `string[]` | `['POST', 'PUT', 'DELETE', 'PATCH']` | Métodos HTTP que exigem validação CSRF. | + +### Uso + +Uma vez configurada, a proteção CSRF é aplicada automaticamente a todas as rotas com os métodos HTTP especificados: + +```typescript +import {ExpressKit, AuthPolicy} from '@gravity-ui/expresskit'; +import {NodeKit} from '@gravity-ui/nodekit'; + +const nodekit = new NodeKit({ + config: { + appCsrfSecret: 'sua-chave-secreta', + appAuthPolicy: AuthPolicy.required, + + // Certifique-se de que seu middleware define o ID do usuário no originalContext, caso contrário, a geração do token CSRF falhará + appAuthHandler: seuAuthHandler, + }, +}); + +const app = new ExpressKit(nodekit, { + 'GET /api/form': (req, res) => { + // O token está disponível no contexto da requisição + res.json({csrfToken: req.originalContext.get('csrfToken')}); + }, + + 'POST /api/submit': (req, res) => { + // Esta rota valida automaticamente o token CSRF + res.json({message: 'Formulário enviado com sucesso'}); + }, +}); +``` + +### Configuração por Rota + +Você pode desabilitar a proteção CSRF para rotas específicas: + +```typescript +const app = new ExpressKit(nodekit, { + 'POST /api/webhook': { + authPolicy: AuthPolicy.required, + disableCsrf: true, // Desabilita CSRF para esta rota + handler: (req, res) => { + res.json({message: 'Webhook processado'}); + }, + }, +}); +``` + +## Controle de Cache + +Por padrão, o ExpressKit define cabeçalhos `no-cache` em todas as respostas. Você pode controlar esse comportamento globalmente ou por rota. + +### Configuração Global + +```typescript +const config: Partial = { + expressEnableCaching: true, // Permite cache por padrão +}; +``` + +### Configuração por Rota + +```typescript +const app = new ExpressKit(nodekit, { + 'GET /api/cached': { + enableCaching: true, // Permite cache para esta rota + handler: (req, res) => res.json({data: 'cacheable'}), + }, + 'GET /api/fresh': { + enableCaching: false, // Força no-cache + handler: (req, res) => res.json({data: 'always fresh'}), + }, +}); +``` + +O `enableCaching` em nível de rota substitui a configuração global. O estado do cache está disponível em `req.routeInfo.enableCaching`. + +## Validação e Serialização de Resposta + +- [Validação de Requisição e Serialização de Resposta](https://github.com/gravity-ui/expresskit/blob/main/docs/VALIDATOR.md) - use esquemas Zod para validação automática de requisição e serialização de resposta. \ No newline at end of file diff --git a/src/content/local-docs/libs/expresskit/README-zh.md b/src/content/local-docs/libs/expresskit/README-zh.md index f1a4b59de993..42dcb09ab36e 100644 --- a/src/content/local-docs/libs/expresskit/README-zh.md +++ b/src/content/local-docs/libs/expresskit/README-zh.md @@ -1,6 +1,6 @@ # ExpressKit -ExpressKit 是一个轻量级的 [express.js](https://expressjs.com/) 包装器,它集成了 [NodeKit](https://github.com/gravity-ui/nodekit),并提供了一些有用的功能,例如请求日志记录、追踪支持、异步控制器和中间件以及详细的路由描述。 +ExpressKit 是一个轻量级的 [express.js](https://expressjs.com/) 包装器,它集成了 [NodeKit](https://github.com/gravity-ui/nodekit),并提供了一些有用的功能,例如请求日志记录、跟踪支持、异步控制器和中间件以及详细的路由描述。 安装: @@ -25,6 +25,17 @@ const app = new ExpressKit(nodekit, { app.run(); ``` +## 自我遥测 + +默认情况下,自我遥测会发送原始请求 URL。查询字符串很大或基数很高的应用程序可以在发送统计信息之前剥离查询参数: + +```typescript +const config: Partial = { + appTelemetryChEnableSelfStats: true, + appTelemetryChSelfStatsStripQueryParams: true, +}; +``` + ## CSP `config.ts` @@ -56,7 +67,7 @@ export default config; ## CSRF 防护 -ExpressKit 提供内置的跨站请求伪造 (CSRF) 防护功能,以保护您的应用程序免受恶意跨域请求的侵害。CSRF 中间件会自动生成和验证用于状态更改的 HTTP 请求的令牌。 +ExpressKit 提供内置的跨站请求伪造 (CSRF) 防护功能,以保护您的应用程序免受恶意跨域请求的侵害。CSRF 中间件会自动为状态更改的 HTTP 请求生成和验证令牌。 ### 基本配置 @@ -75,16 +86,16 @@ export default config; ### 配置选项 -| 选项 | 类型 | 默认值 | 描述 | -| ------------------- | -------------------- | ------------------------------------ | ----------------------------------------------------------------------------------------------- | -| `appCsrfSecret` | `string \| string[]` | - | **必需。** 用于 HMAC 令牌生成的密钥。多个密钥允许进行密钥轮换。 | -| `appCsrfLifetime` | `number` | `2592000` (30 天) | 令牌的有效期(秒)。设置为 `0` 表示无过期时间。 | -| `appCsrfHeaderName` | `string` | `'x-csrf-token'` | 用于令牌验证的 HTTP 头部名称。 | +| 选项 | 类型 | 默认值 | 描述 | +| ------------------- | -------------------- | -------------------------------- | ----------------------------------------------------------------------------------------------- | +| `appCsrfSecret` | `string \| string[]` | - | **必需。** 用于 HMAC 令牌生成的密钥。多个密钥允许密钥轮换。 | +| `appCsrfLifetime` | `number` | `2592000` (30 天) | 令牌有效期(秒)。设置为 `0` 表示无过期时间。 | +| `appCsrfHeaderName` | `string` | `'x-csrf-token'` | 用于令牌验证的 HTTP 标头名称。 | | `appCsrfMethods` | `string[]` | `['POST', 'PUT', 'DELETE', 'PATCH']` | 需要 CSRF 验证的 HTTP 方法。 | ### 用法 -配置完成后,CSRF 防护将自动应用于所有具有指定 HTTP 方法的路由: +配置完成后,CSRF 防护将自动应用于具有指定 HTTP 方法的所有路由: ```typescript import {ExpressKit, AuthPolicy} from '@gravity-ui/expresskit'; @@ -131,7 +142,7 @@ const app = new ExpressKit(nodekit, { ## 缓存控制 -默认情况下,ExpressKit 会为所有响应设置 `no-cache` 头部。您可以全局或按路由控制此行为。 +默认情况下,ExpressKit 会为所有响应设置 `no-cache` 标头。您可以全局或按路由控制此行为。 ### 全局配置 @@ -150,14 +161,14 @@ const app = new ExpressKit(nodekit, { handler: (req, res) => res.json({data: 'cacheable'}), }, 'GET /api/fresh': { - enableCaching: false, // 强制不缓存 + enableCaching: false, // 强制 no-cache handler: (req, res) => res.json({data: 'always fresh'}), }, }); ``` -路由级别的 `enableCaching` 会覆盖全局设置。缓存状态可在 `req.routeInfo.enableCaching` 中获取。 +路由级别的 `enableCaching` 会覆盖全局设置。缓存状态可在 `req.routeInfo.enableCaching` 中找到。 ## 验证和响应序列化 -- [请求验证和响应序列化](https://github.com/gravity-ui/expresskit/blob/main/docs/VALIDATOR.md) - 使用 Zod schema 进行自动请求验证和响应序列化。 \ No newline at end of file +- [请求验证与响应序列化](https://github.com/gravity-ui/expresskit/blob/main/docs/VALIDATOR.md) - 使用 Zod schema 实现自动化的请求验证和响应序列化。 \ No newline at end of file diff --git a/src/content/local-docs/libs/expresskit/README.md b/src/content/local-docs/libs/expresskit/README.md index 59cefb562669..bceeb96a518c 100644 --- a/src/content/local-docs/libs/expresskit/README.md +++ b/src/content/local-docs/libs/expresskit/README.md @@ -25,6 +25,18 @@ const app = new ExpressKit(nodekit, { app.run(); ``` +## Self telemetry + +By default, self telemetry sends the original request URL. Applications with large or +high-cardinality query strings can strip query parameters before sending stats: + +```typescript +const config: Partial = { + appTelemetryChEnableSelfStats: true, + appTelemetryChSelfStatsStripQueryParams: true, +}; +``` + ## CSP `config.ts` diff --git a/src/content/local-docs/libs/timeline/README-de.md b/src/content/local-docs/libs/timeline/README-de.md index f4a179ab539d..9921d44c2e94 100644 --- a/src/content/local-docs/libs/timeline/README-de.md +++ b/src/content/local-docs/libs/timeline/README-de.md @@ -1,8 +1,8 @@ # @gravity-ui/timeline [![npm package](https://img.shields.io/npm/v/@gravity-ui/timeline)](https://www.npmjs.com/package/@gravity-ui/timeline) [![Release](https://img.shields.io/github/actions/workflow/status/gravity-ui/timeline/release.yml?branch=main&label=Release)](https://github.com/gravity-ui/timeline/actions/workflows/release.yml?query=branch:main) [![storybook](https://img.shields.io/badge/Storybook-deployed-ff4685)](https://preview.gravity-ui.com/timeline/) -> [Русская версия](./README-ru.md) +> [English version](./README.md) -Eine auf React basierende Bibliothek zum Erstellen interaktiver Timeline-Visualisierungen mit Canvas-Rendering. +Eine auf React basierende Bibliothek zum Erstellen interaktiver Zeitlinienvisualisierungen mit Canvas-Rendering. ## Dokumentation @@ -10,22 +10,23 @@ Details finden Sie in der [Dokumentation](./docs/docs.md). ## Vorschau -Grundlegende Timeline mit Ereignissen und Achsen: +Grundlegende Zeitlinie mit Ereignissen und Achsen: -![Grundlegende Timeline mit Ereignissen](./docs/img/lines.png) +![Grundlegende Zeitlinie mit Ereignissen](./docs/img/lines.png) Benutzerdefiniertes Rendering mit erweiterbaren verschachtelten Ereignissen ([NestedEvents](https://preview.gravity-ui.com/timeline/?path=/story/integrations-gravity-ui--nested-events-story) Beispiel): -![Verschachtelte Ereignisse Timeline](./docs/img/events.png) +![Verschachtelte Ereignisse Zeitlinie](./docs/img/events.png) ## Funktionen - Canvas-basiertes Rendering für hohe Leistung -- Interaktive Timeline mit Zoom- und Schwenkfunktionen +- Interaktive Zeitlinie mit Zoom- und Schwenkfunktionen +- Flexible Rad- und Touchpad-Interaktionen, einschließlich vertikalem Scroll-Pass-Through - Unterstützung für Ereignisse, Markierungen, Abschnitte, Achsen und Gitter - Hintergrundabschnitte zur visuellen Organisation und Hervorhebung von Zeiträumen -- Intelligente Gruppierung von Markierungen mit automatischem Zoom auf die Gruppe - Klicken Sie auf gruppierte Markierungen, um in ihre einzelnen Komponenten zu zoomen -- Virtualisiertes Rendering für verbesserte Leistung bei großen Datensätzen (nur aktiv, wenn der Timeline-Inhalt den Viewport überschreitet) +- Intelligente Gruppierung von Markierungen mit automatischem Zoom auf die Gruppe – Klicken Sie auf gruppierte Markierungen, um in ihre einzelnen Komponenten hineinzuzoomen +- Virtualisiertes Rendering für verbesserte Leistung bei großen Datensätzen (nur aktiv, wenn der Zeitlinieninhalt den Viewport überschreitet) - Anpassbares Erscheinungsbild und Verhalten - TypeScript-Unterstützung mit vollständigen Typdefinitionen - React-Integration mit benutzerdefinierten Hooks @@ -38,7 +39,7 @@ npm install @gravity-ui/timeline ## Verwendung -Die Timeline-Komponente kann in React-Anwendungen mit der folgenden grundlegenden Einrichtung verwendet werden: +Die Zeitlinienkomponente kann in React-Anwendungen mit der folgenden grundlegenden Einrichtung verwendet werden: ```tsx import { TimelineCanvas, useTimeline } from '@gravity-ui/timeline/react'; @@ -60,8 +61,8 @@ const MyTimelineComponent = () => { // timeline - Timeline-Instanz // api - CanvasApi-Instanz (identisch mit timeline.api) - // start - Funktion zur Initialisierung der Timeline mit Canvas - // stop - Funktion zur Zerstörung der Timeline + // start - Funktion zur Initialisierung der Zeitlinie mit Canvas + // stop - Funktion zur Zerstörung der Zeitlinie return (
@@ -77,29 +78,74 @@ Jede Achse hat die folgende Struktur: ```typescript type TimelineAxis = { - id: string; // Eindeutige Achsenkennung + id: string; // Eindeutiger Achsenidentifikator tracksCount: number; // Anzahl der Spuren in der Achse top: number; // Vertikale Position (px) height: number; // Höhe pro Spur (px) }; ``` +### Horizontale Achsenlinien + +Konfigurieren Sie die Platzierung horizontaler Linien über `viewConfiguration.axes.linePosition`: + +- `"center"` (Standard) zeichnet eine Linie durch die Mitte jeder Spur. +- `"between"` zeichnet eine Linie nach jeder Spur, an ihrer unteren Grenze. Dies ist nützlich für tabellenähnliche Zeilen mit zentrierten Ereignisbalken. + +```typescript +viewConfiguration: { + axes: { + linePosition: 'between' + } +} +``` + +### Flexible Kamera-Interaktionen + +`ZoomMode` bietet vertraute Interaktions-Presets, während `camera.interactions` es Ihnen ermöglicht, eine einzelne Geste zu überschreiben. Dies ist nützlich, wenn sich eine Zeitlinie innerhalb einer vertikal vertikal scrollbaren Seite befindet: Behalten Sie das horizontale Schwenken und das Touchpad-Zoomen bei, aber lassen Sie das normale Scrollen mit dem Mausrad den übergeordneten Container erreichen. + +```tsx +import {ZoomMode} from '@gravity-ui/timeline'; + +const {timeline} = useTimeline({ + settings: { /* ... */ }, + viewConfiguration: { + camera: { + zoom: ZoomMode.DEFAULT, + interactions: { + verticalWheel: 'pass-through', + horizontalWheel: 'pan', + pinch: 'zoom', + }, + zoomSensitivity: { + in: 0.5, + out: 0.5, + }, + minRange: 5_000, + maxRange: 1000 * 60 * 60 * 24 * 365, + }, + }, +}); +``` + +Jede Interaktion akzeptiert `'zoom'`, `'pan'` oder `'pass-through'`. `pinch` repräsentiert die Ctrl+Rad-Geste des Browsers auf dem Touchpad. `zoomSensitivity.in` und `zoomSensitivity.out` multiplizieren unabhängig voneinander die Geschwindigkeit des Ein- und Auszooms: `1` ist der Standard, niedrigere Werte sind sanfter und `0` deaktiviert das Zoomen in dieser Richtung. Kleine Deltas von Touchpads werden automatisch geglättet. `minRange` und `maxRange` sind Dauern in Millisekunden; das Minimum beträgt standardmäßig 5 Sekunden und das Maximum ist unbeschränkt, sofern nicht konfiguriert, setzen Sie also `maxRange`, um zu begrenzen, wie weit Benutzer herauszoomen können. Sehen Sie sich das interaktive [Camera interactions Storybook-Beispiel](https://preview.gravity-ui.com/timeline/?path=/story/components-timelinecanvas--interaction-and-focus) an. + ### Abschnittsstruktur Jeder Abschnitt erfordert die folgende Struktur: ```typescript type TimelineSection = { - id: string; // Eindeutige Abschnittskennung + id: string; // Eindeutiger Abschnittsidentifikator from: number; // Start-Zeitstempel - to?: number; // Optionaler End-Zeitstempel (standardmäßig das Ende der Timeline) + to?: number; // Optionaler End-Zeitstempel (standardmäßig auf das Ende der Zeitlinie gesetzt) color: string; // Hintergrundfarbe des Abschnitts hoverColor?: string; // Optionale Farbe, wenn der Abschnitt überfahren wird - renderer?: AbstractSectionRenderer; // Optionaler benutzerdefinierter Renderer (aus dem Paket exportiert) + renderer?: AbstractSectionRenderer; // Optionaler benutzerdefinierter Renderer (exportiert aus dem Paket) }; ``` -Abschnitte bieten Hintergrundfarben für Zeiträume und helfen bei der visuellen Organisation von Timeline-Inhalten: +Abschnitte bieten Hintergrundfarben für Zeiträume und helfen bei der visuellen Organisation von Zeitlinieninhalten: ```tsx const MyTimelineComponent = () => { @@ -115,21 +161,21 @@ const MyTimelineComponent = () => { id: 'morning', from: Date.now(), to: Date.now() + 1800000, // 30 Minuten - color: 'rgba(255, 235, 59, 0.3)', // Halbtransparentes Gelb + color: 'rgba(255, 235, 59, 0.3)', // Halbtransparenter gelber Farbton hoverColor: 'rgba(255, 235, 59, 0.4)' }, { id: 'afternoon', from: Date.now() + 1800000, - // Kein 'to' angegeben - erstreckt sich bis zum Ende der Timeline - color: 'rgba(76, 175, 80, 0.2)', // Halbtransparentes Grün + // Kein 'to' angegeben - erstreckt sich bis zum Ende der Zeitleiste + color: 'rgba(76, 175, 80, 0.2)', // Halbtransparenter grüner Farbton hoverColor: 'rgba(76, 175, 80, 0.3)' } ] }, viewConfiguration: { sections: { - hitboxPadding: 2 // Abstand für die Erkennung von Hover-Effekten + hitboxPadding: 2 // Polsterung für die Hover-Erkennung } } }); @@ -138,28 +184,28 @@ const MyTimelineComponent = () => { }; ``` -### Markierungsstruktur +### Marker-Struktur -Jede Markierung erfordert die folgende Struktur: +Jeder Marker erfordert die folgende Struktur: ```typescript type TimelineMarker = { - time: number; // Zeitstempel für die Position der Markierung - color: string; // Farbe der Markierungslinie - activeColor: string; // Farbe, wenn die Markierung ausgewählt ist (erforderlich) - hoverColor: string; // Farbe, wenn die Markierung überfahren wird (erforderlich) - lineWidth?: number; // Optionale Breite der Markierungslinie + time: number; // Zeitstempel für die Position des Markers + color: string; // Farbe der Markerlinie + activeColor: string; // Farbe, wenn der Marker ausgewählt ist (erforderlich) + hoverColor: string; // Farbe, wenn der Marker überfahren wird (erforderlich) + lineWidth?: number; // Optionale Breite der Markerlinie label?: string; // Optionaler Beschriftungstext - labelColor?: string; // Optionale Beschriftungsfarbe + labelColor?: string; // Optionale Farbe der Beschriftung renderer?: AbstractMarkerRenderer; // Optionaler benutzerdefinierter Renderer - nonSelectable?: boolean;// Ob die Markierung ausgewählt werden kann - group?: boolean; // Ob die Markierung eine Gruppe darstellt + nonSelectable?: boolean;// Ob der Marker ausgewählt werden kann + group?: boolean; // Ob der Marker eine Gruppe darstellt }; ``` -### Markierungsgruppierung und Zoom +### Gruppierung und Zoom von Markern -Die Timeline gruppiert Markierungen, die nahe beieinander liegen, automatisch und bietet Zoom-Funktionalität: +Die Zeitleiste gruppiert automatisch eng beieinander liegende Marker und bietet Zoom-Funktionalität: ```tsx const MyTimelineComponent = () => { @@ -179,8 +225,8 @@ const MyTimelineComponent = () => { viewConfiguration: { markers: { collapseMinDistance: 8, // Marker innerhalb von 8 Pixeln gruppieren - groupZoomEnabled: true, // Zoom bei Gruppenklick aktivieren - groupZoomPadding: 0.3, // 30% Abstand um die Gruppe + groupZoomEnabled: true, // Zoom bei Klick auf Gruppe aktivieren + groupZoomPadding: 0.3, // 30% Polsterung um die Gruppe groupZoomMaxFactor: 0.3, // Maximaler Zoomfaktor } } @@ -197,15 +243,15 @@ const MyTimelineComponent = () => { ## Funktionsweise -Die Timeline-Komponente ist mit React erstellt und bietet eine flexible Möglichkeit, interaktive Timeline-Visualisierungen zu erstellen. So funktioniert sie: +Die Zeitleistenkomponente ist mit React erstellt und bietet eine flexible Möglichkeit, interaktive Zeitleistenvisualisierungen zu erstellen. So funktioniert sie: ### Komponentenarchitektur -Die Timeline ist als React-Komponente implementiert, die über zwei Hauptobjekte konfiguriert werden kann: +Die Zeitleiste ist als React-Komponente implementiert, die über zwei Hauptobjekte konfiguriert werden kann: -1. **TimelineSettings**: Steuert das Kernverhalten und die Darstellung der Timeline - - `start`: Startzeit der Timeline - - `end`: Endzeit der Timeline +1. **TimelineSettings**: Steuert das Kernverhalten und die Darstellung der Zeitleiste + - `start`: Startzeit der Zeitleiste + - `end`: Endzeit der Zeitleiste - `axes`: Array von Achsenkonfigurationen (siehe Struktur unten) - `events`: Array von Ereigniskonfigurationen - `markers`: Array von Marker-Konfigurationen @@ -217,13 +263,13 @@ Die Timeline ist als React-Komponente implementiert, die über zwei Hauptobjekte ### Ereignisbehandlung -Die Timeline-Komponente unterstützt mehrere interaktive Ereignisse: +Die Zeitleistenkomponente unterstützt mehrere interaktive Ereignisse: -- `on-click`: Wird beim Klicken auf die Timeline ausgelöst +- `on-click`: Wird beim Klicken auf die Zeitleiste ausgelöst - `on-context-click`: Wird bei Rechtsklick/Kontextmenü ausgelöst - `on-select-change`: Wird ausgelöst, wenn sich die Auswahl ändert -- `on-hover`: Wird beim Überfahren von Timeline-Elementen mit der Maus ausgelöst -- `on-leave`: Wird ausgelöst, wenn die Maus Timeline-Elemente verlässt +- `on-hover`: Wird beim Überfahren von Zeilenelementen ausgelöst +- `on-leave`: Wird ausgelöst, wenn die Maus Zeilenelemente verlässt Beispiel für die Ereignisbehandlung: @@ -234,7 +280,7 @@ const MyTimelineComponent = () => { const { timeline } = useTimeline({ /* ... */ }); useTimelineEvent(timeline, 'on-click', (data) => { - console.log('Timeline geklickt:', data); + console.log('Zeitleiste geklickt:', data); }); useTimelineEvent(timeline, 'on-select-change', (data) => { @@ -247,46 +293,140 @@ const MyTimelineComponent = () => { ### React-Integration -Die Komponente verwendet benutzerdefinierte Hooks zur Timeline-Verwaltung: +Die Komponente verwendet benutzerdefinierte Hooks für die Zeitleistenverwaltung: -- `useTimeline`: Verwaltet die Timeline-Instanz und ihren Lebenszyklus - - Erstellt und initialisiert die Timeline - - Kümmert sich um die Bereinigung beim Ausblenden der Komponente - - Bietet Zugriff auf die Timeline-Instanz +- `useTimeline`: Verwaltet die Zeitleisteninstanz und ihren Lebenszyklus + - Erstellt und initialisiert die Zeitleiste + - Kümmert sich um die Bereinigung beim Ausbau der Komponente + - Bietet Zugriff auf die Zeitleisteninstanz -- `useTimelineEvent`: Verwaltet Ereignisabonnements und die Bereinigung +- `useTimelineEvent`: Kümmert sich um Ereignisabonnements und Bereinigung - Verwaltet den Lebenszyklus von Ereignis-Listenern - - Bereinigt Listener automatisch beim Ausblenden + - Bereinigt Listener automatisch beim Ausbau + +Die Komponente kümmert sich automatisch um die Bereinigung und Zerstörung der Zeitleisteninstanz, wenn sie ausgebaut wird. + +### Ereignis-Popup + +Installieren Sie `@gravity-ui/uikit` und seine Stile, um Ereignisdetails anzuzeigen, ohne +Ereignisse für Hover zu abonnieren oder Koordinaten selbst zu berechnen: -Die Komponente kümmert sich automatisch um die Bereinigung und Zerstörung der Timeline-Instanz, wenn sie ausgeblendet wird. +```tsx +import '@gravity-ui/uikit/styles/fonts.css'; +import '@gravity-ui/uikit/styles/styles.css'; +import {EventPopup} from '@gravity-ui/timeline/react/uikit'; + +<> + + } + /> + +``` + +`EventPopup` öffnet sich nach 150 ms und schließt sich 200 ms, nachdem der Zeiger das +Ereignis verlassen hat. Legen Sie `openDelay`, `closeDelay`, `placement`, `offset`, `className` oder +`aria-label` bei Bedarf fest. Das Popup bleibt geöffnet, solange sein Inhalt den Zeiger hat oder den Fokus, schließt sich bei Escape oder Klick außerhalb und verwendet das letzte Ereignis in der Datenreihenfolge, wenn sich Ereignisse überlappen. `hoverColor` und `isHovered` steuern die Darstellung von Ereignissen; `EventPopup` steuert seine Detail-UI. -### Ereignisstruktur +### Ereignis-Struktur -Ereignisse in der Timeline folgen dieser Struktur: +Ereignisse in der Zeitleiste folgen dieser Struktur: ```typescript type TimelineEvent = { - id: string; // Eindeutiger Bezeichner + id: string; // Eindeutige Kennung from: number; // Start-Zeitstempel - to?: number; // End-Zeitstempel (optional für Punkt-Ereignisse) - axisId: string; // ID der Achse, zu der dieses Ereignis gehört + to?: number; // End-Zeitstempel (optional für Punkt-Events) + axisId: string; // ID der Achse, zu der dieses Event gehört trackIndex: number; // Index im Track der Achse renderer?: AbstractEventRenderer; // Optionaler benutzerdefinierter Renderer - color?: string; // Optionale Ereignisfarbe - selectedColor?: string; // Optionale Farbe für den ausgewählten Zustand + color?: string; // Optionale Event-Farbe + hoverColor?: string; // Optionale Farbe im Hover-Zustand + selectedColor?: string; // Optionale Farbe im ausgewählten Zustand + cursor?: string; // Optionaler CSS-Cursor beim Hovern über das Event }; ``` +Setzen Sie `cursor: 'pointer'` für Events, die bei einem Klick eine Aktion ausführen. Der Cursor +wird nur angewendet, wenn sich der Zeiger über diesem Event befindet. Wenn sich Events überlappen, +bestimmt das letzte Event in der Datenreihenfolge den Cursor. + +### Gravity UI Farben + +Canvas kann CSS-Custom-Properties nicht von sich aus auflösen. Timeline löst vollständige +`var(--token)`-Werte für sein Canvas-Element auf, sodass Gravity UI-Semantik-Tokens für +eingebaute Events, Marker, Abschnitte, Achsen, Gitter und Lineale funktionieren. + +```tsx +import '@gravity-ui/uikit/styles/fonts.css'; +import '@gravity-ui/uikit/styles/styles.css'; +import {ThemeProvider} from '@gravity-ui/uikit'; +import {useTimeline} from '@gravity-ui/timeline/react'; +import {GravityTimelineCanvas} from '@gravity-ui/timeline/react/uikit'; + + + + +``` + +Übergeben Sie Tokens direkt in jedem Farbfeld, z. B. +`color: 'var(--g-color-base-positive-medium)'`. `GravityTimelineCanvas` +zeichnet automatisch neu, wenn sich das effektive Gravity UI-Theme ändert. Für ein +fehlendes Token verwenden Sie einen CSS-Fallback wie `var(--app-event-color, transparent)` +oder rufen Sie `timeline.api.resolveColor(color, fallback)` aus einem benutzerdefinierten Renderer auf. + +Für Events wird `color` normal verwendet, `hoverColor` beim Hovern mit der Maus und +`selectedColor` nach der Auswahl: + +```ts +const events = [ + { + id: 'deploy', + from: start, + to: end, + axisId: 'main', + trackIndex: 0, + color: 'var(--g-color-base-positive-medium)', + hoverColor: 'var(--g-color-base-positive-medium-hover)', + selectedColor: 'var(--g-color-base-positive-heavy)', + }, +]; +``` + +Benutzerdefinierte Event-Renderer erhalten `resolveColor` als letztes optionales Argument; +benutzerdefinierte Marker- und Abschnitts-Renderer erhalten es in ihren Render-Daten. + +### Canvas-Schriftarten + +Setzen Sie `viewConfiguration.font` einmal, um die Standardschriftart für Lineal, +Events und Marker zu konfigurieren. Eine komponenten-spezifische `ruler.font`, `events.font` oder +`markers.font` hat Vorrang. Der Standardwert bleibt `10px sans-serif`. + +Canvas kann CSS-Variablen oder `inherit` nicht direkt in `ctx.font` verwenden, daher löst Timeline +vollständige Token im CSS-Kontext des Canvas auf: + +```ts +viewConfiguration: { + font: 'var(--g-text-caption-2-font)', +} +``` + +Verwenden Sie `font: 'inherit'`, um die berechnete Schriftart des Canvas-Elements zu verwenden. Benutzerdefinierte +Renderer erhalten `resolveFont` zusammen mit `resolveColor` oder können +`timeline.api.resolveFont(font)` aufrufen. Nachdem eine Web-Schriftart dynamisch geladen wurde, rufen Sie +`timeline.api.rerender()` auf, um den Canvas-Text damit neu zu zeichnen. + ### Direkte TypeScript-Nutzung -Die `Timeline`-Klasse kann direkt in TypeScript ohne React verwendet werden. Dies ist nützlich für die Integration mit anderen Frameworks oder Vanilla-JavaScript-Anwendungen: +Die Timeline-Klasse kann direkt in TypeScript ohne React verwendet werden. Dies ist nützlich für die Integration mit anderen Frameworks oder Vanilla-JavaScript-Anwendungen: ```typescript import { Timeline } from '@gravity-ui/timeline'; const timestamp = Date.now(); -// Eine Timeline-Instanz erstellen +// Timeline-Instanz erstellen const timeline = new Timeline({ settings: { start: timestamp, @@ -304,7 +444,7 @@ const timeline = new Timeline({ id: 'event1', from: timestamp + 1800000, // 30 Minuten ab jetzt to: timestamp + 2400000, // 40 Minuten ab jetzt - label: 'Beispiel-Ereignis', + label: 'Beispiel-Event', axisId: 'main' } ], @@ -342,7 +482,7 @@ if (canvas instanceof HTMLCanvasElement) { timeline.init(canvas); } -// Ereignis-Listener hinzufügen +// Event-Listener hinzufügen timeline.on('on-click', (detail) => { console.log('Timeline geklickt:', detail); }); @@ -355,24 +495,22 @@ timeline.on('on-select-change', (detail) => { timeline.destroy(); ``` -Die `Timeline`-Klasse bietet eine umfangreiche API zur Verwaltung der Timeline: +Die Timeline-Klasse bietet eine umfangreiche API zur Verwaltung der Timeline: -- **Ereignisverwaltung**: +- **Event-Verwaltung**: ```typescript - // Ereignis-Listener hinzufügen + // Event-Listener hinzufügen timeline.on('eventClick', (detail) => { - console.log('Ereignis geklickt:', detail); + console.log('Event geklickt:', detail); }); -``` -```markdown // Event-Listener entfernen const handler = (detail) => console.log(detail); timeline.on('eventClick', handler); timeline.off('eventClick', handler); // Benutzerdefinierte Events auslösen - timeline.emit('customEvent', { data: 'custom data' }); + timeline.emit('customEvent', { data: 'benutzerdefinierte Daten' }); ``` - **Timeline-Steuerung**: @@ -398,8 +536,10 @@ Die `Timeline`-Klasse bietet eine umfangreiche API zur Verwaltung der Timeline: height: 80 } ]); +``` - // Marker aktualisieren +```javascript + // Update markers timeline.api.setMarkers([ { id: 'newMarker', @@ -411,7 +551,7 @@ Die `Timeline`-Klasse bietet eine umfangreiche API zur Verwaltung der Timeline: } ]); - // Abschnitte aktualisieren + // Update sections timeline.api.setSections([ { id: 'newSection', @@ -422,18 +562,19 @@ Die `Timeline`-Klasse bietet eine umfangreiche API zur Verwaltung der Timeline: } ]); - // Ansichtskonfiguration aktualisieren (wird mit der aktuellen Konfiguration zusammengeführt) + // Update view configuration (merges with current config) timeline.api.setViewConfiguration({ hideRuler: true }); ``` ## Live-Beispiele -Interaktive Beispiele finden Sie in unserem [Storybook](https://preview.gravity-ui.com/timeline/): +Erkunden Sie interaktive Beispiele in unserem [Storybook](https://preview.gravity-ui.com/timeline/): -- [Basis-Timeline](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--basic) - Einfache Timeline mit Events und Achsen -- [Endlose Timeline](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--endless-timelines) - Endlose Timeline mit Events und Achsen +- [Basis-Timeline](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--basic) - Einfache Timeline mit Ereignissen und Achsen +- [Endlose Timeline](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--endless-timelines) - Endlose Timeline mit Ereignissen und Achsen - [Marker](https://preview.gravity-ui.com/timeline/?path=/story/timeline-markers--basic) - Timeline mit vertikalen Markern und Beschriftungen -- [Benutzerdefinierte Events](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--custom-renderer) - Timeline mit benutzerdefinierter Event-Darstellung +- [Kamera-Interaktionen](https://preview.gravity-ui.com/timeline/?path=/story/components-timelinecanvas--interaction-and-focus) - Konfigurieren Sie das Verhalten für Mausrad, horizontales Scrollen und Pinch-Gesten auf dem Trackpad +- [Benutzerdefinierte Ereignisse](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--custom-renderer) - Timeline mit benutzerdefinierter Ereignisdarstellung - [Integrationen](https://preview.gravity-ui.com/timeline/?path=/story/integrations-gravity-ui--timeline-ruler) - RangeDateSelection, DragHandler, NestedEvents, Popup, List diff --git a/src/content/local-docs/libs/timeline/README-es.md b/src/content/local-docs/libs/timeline/README-es.md index 05b1e412575b..77423ce7538b 100644 --- a/src/content/local-docs/libs/timeline/README-es.md +++ b/src/content/local-docs/libs/timeline/README-es.md @@ -8,7 +8,7 @@ Una biblioteca basada en React para crear visualizaciones interactivas de línea Para más detalles, consulta [Documentación](./docs/docs.md). -## Vista Previa +## Vista previa Línea de tiempo básica con eventos y ejes: @@ -16,16 +16,17 @@ Línea de tiempo básica con eventos y ejes: Renderizado personalizado con eventos anidados expandibles (ejemplo de [NestedEvents](https://preview.gravity-ui.com/timeline/?path=/story/integrations-gravity-ui--nested-events-story)): -![Línea de tiempo de eventos anidados](./docs/img/events.png) +![Línea de tiempo con eventos anidados](./docs/img/events.png) ## Características -- Renderizado basado en canvas para alto rendimiento +- Renderizado basado en Canvas para alto rendimiento - Línea de tiempo interactiva con capacidades de zoom y desplazamiento (pan) +- Interacciones flexibles con rueda y trackpad, incluyendo el paso del scroll vertical - Soporte para eventos, marcadores, secciones, ejes y cuadrícula - Secciones de fondo para organización visual y resaltado de períodos de tiempo -- Agrupación inteligente de marcadores con zoom automático al grupo - Haz clic en los marcadores agrupados para hacer zoom en sus componentes individuales -- Renderizado virtualizado para mejorar el rendimiento con grandes conjuntos de datos (solo activo cuando el contenido de la línea de tiempo excede el viewport) +- Agrupación inteligente de marcadores con zoom automático al grupo - Haz clic en marcadores agrupados para hacer zoom en sus componentes individuales +- Renderizado virtualizado para mejorar el rendimiento con grandes conjuntos de datos (solo activo cuando el contenido de la línea de tiempo excede la ventana gráfica) - Apariencia y comportamiento personalizables - Soporte de TypeScript con definiciones de tipos completas - Integración con React mediante hooks personalizados @@ -84,6 +85,51 @@ type TimelineAxis = { }; ``` +### Líneas Horizontales de Eje + +Configura la colocación de las líneas horizontales a través de `viewConfiguration.axes.linePosition`: + +- `"center"` (predeterminado) dibuja una línea a través del centro de cada pista. +- `"between"` dibuja una línea después de cada pista, en su límite inferior. Esto es útil para filas de estilo tabla con barras de eventos centradas. + +```typescript +viewConfiguration: { + axes: { + linePosition: 'between' + } +} +``` + +### Interacciones Flexibles de Cámara + +`ZoomMode` proporciona preajustes de interacción familiares, mientras que `camera.interactions` te permite anular un gesto individual. Esto es útil cuando una línea de tiempo se encuentra dentro de una página con desplazamiento vertical: mantén el desplazamiento horizontal y el zoom del trackpad, pero deja que el scroll normal de la rueda llegue al contenedor padre. + +```tsx +import {ZoomMode} from '@gravity-ui/timeline'; + +const {timeline} = useTimeline({ + settings: { /* ... */ }, + viewConfiguration: { + camera: { + zoom: ZoomMode.DEFAULT, + interactions: { + verticalWheel: 'pass-through', + horizontalWheel: 'pan', + pinch: 'zoom', + }, + zoomSensitivity: { + in: 0.5, + out: 0.5, + }, + minRange: 5_000, + maxRange: 1000 * 60 * 60 * 24 * 365, + }, + }, +}); +``` + +Cada interacción acepta `'zoom'`, `'pan'` o `'pass-through'`. `pinch` representa el gesto de Ctrl+rueda del trackpad del navegador. `zoomSensitivity.in` y `zoomSensitivity.out` multiplican independientemente la velocidad de zoom hacia adentro y hacia afuera: `1` es el valor predeterminado, valores más bajos son más suaves y `0` deshabilita el zoom en esa dirección. Las pequeñas deltas del trackpad se suavizan automáticamente. `minRange` y `maxRange` son duraciones en milisegundos; el mínimo por defecto es 5 segundos y el máximo no está restringido a menos que se configure, así que establece `maxRange` para limitar hasta dónde pueden hacer zoom los usuarios. Consulta el ejemplo interactivo de [Interacciones de Cámara en Storybook](https://preview.gravity-ui.com/timeline/?path=/story/components-timelinecanvas--interaction-and-focus). + ### Estructura de Secciones Cada sección requiere la siguiente estructura: @@ -94,7 +140,7 @@ type TimelineSection = { from: number; // Marca de tiempo de inicio to?: number; // Marca de tiempo de fin opcional (por defecto, el final de la línea de tiempo) color: string; // Color de fondo de la sección - hoverColor?: string; // Color opcional al pasar el ratón por encima de la sección + hoverColor?: string; // Color opcional cuando la sección está en foco (hover) renderer?: AbstractSectionRenderer; // Renderizador personalizado opcional (exportado del paquete) }; ``` @@ -121,7 +167,7 @@ const MyTimelineComponent = () => { { id: 'afternoon', from: Date.now() + 1800000, - // No se especifica 'to' - se extiende hasta el final de la línea de tiempo + // 'to' no especificado - se extiende hasta el final de la línea de tiempo color: 'rgba(76, 175, 80, 0.2)', // Verde semitransparente hoverColor: 'rgba(76, 175, 80, 0.3)' } @@ -147,7 +193,7 @@ type TimelineMarker = { time: number; // Marca de tiempo para la posición del marcador color: string; // Color de la línea del marcador activeColor: string; // Color cuando el marcador está seleccionado (requerido) - hoverColor: string; // Color al pasar el ratón por encima del marcador (requerido) + hoverColor: string; // Color cuando el marcador está en hover (requerido) lineWidth?: number; // Ancho opcional de la línea del marcador label?: string; // Texto de etiqueta opcional labelColor?: string; // Color de etiqueta opcional @@ -178,9 +224,9 @@ const MyTimelineComponent = () => { }, viewConfiguration: { markers: { - collapseMinDistance: 8, // Agrupar marcadores a una distancia mínima de 8 píxeles + collapseMinDistance: 8, // Agrupar marcadores a menos de 8 píxeles groupZoomEnabled: true, // Habilitar zoom al hacer clic en un grupo - groupZoomPadding: 0.3, // Relleno del 30% alrededor del grupo + groupZoomPadding: 0.3, // 30% de relleno alrededor del grupo groupZoomMaxFactor: 0.3, // Factor de zoom máximo } } @@ -205,7 +251,7 @@ La línea de tiempo se implementa como un componente de React que se puede confi 1. **TimelineSettings**: Controla el comportamiento y la apariencia principal de la línea de tiempo. - `start`: Hora de inicio de la línea de tiempo. - - `end`: Hora de finalización de la línea de tiempo. + - `end`: Hora de fin de la línea de tiempo. - `axes`: Matriz de configuraciones de ejes (ver estructura a continuación). - `events`: Matriz de configuraciones de eventos. - `markers`: Matriz de configuraciones de marcadores. @@ -222,8 +268,8 @@ El componente de línea de tiempo admite varios eventos interactivos: - `on-click`: Se activa al hacer clic en la línea de tiempo. - `on-context-click`: Se activa al hacer clic derecho/menú contextual. - `on-select-change`: Se dispara cuando cambia la selección. -- `on-hover`: Se activa al pasar el ratón por encima de los elementos de la línea de tiempo. -- `on-leave`: Se activa cuando el ratón sale de los elementos de la línea de tiempo. +- `on-hover`: Se activa al pasar el cursor sobre elementos de la línea de tiempo. +- `on-leave`: Se dispara cuando el ratón sale de los elementos de la línea de tiempo. Ejemplo de manejo de eventos: @@ -260,6 +306,33 @@ El componente utiliza hooks personalizados para la gestión de la línea de tiem El componente maneja automáticamente la limpieza y destrucción de la instancia de la línea de tiempo cuando se desmonta. +### Popup de Eventos + +Instala `@gravity-ui/uikit` y sus estilos para mostrar detalles de eventos sin +necesidad de suscribirte a eventos de hover o calcular coordenadas tú mismo: + +```tsx +import '@gravity-ui/uikit/styles/fonts.css'; +import '@gravity-ui/uikit/styles/styles.css'; +import {EventPopup} from '@gravity-ui/timeline/react/uikit'; + +<> + + } + /> + +``` + +`EventPopup` se abre después de 150 ms y se cierra 200 ms después de que el puntero +sale del evento. Establece `openDelay`, `closeDelay`, `placement`, `offset`, +`className` o `aria-label` cuando sea necesario. El popup permanece abierto +mientras su contenido tenga puntero o foco, se cierra al presionar Escape o al +hacer clic fuera, y utiliza el último evento en orden de datos cuando los eventos +se superponen. `hoverColor` e `isHovered` controlan el dibujo del evento; +`EventPopup` controla su interfaz de detalles. + ### Estructura de Eventos Los eventos en la línea de tiempo siguen esta estructura: @@ -268,25 +341,95 @@ Los eventos en la línea de tiempo siguen esta estructura: type TimelineEvent = { id: string; // Identificador único from: number; // Marca de tiempo de inicio - to?: number; // Marca de tiempo de finalización (opcional para eventos puntuales) + to?: number; // Marca de tiempo de fin (opcional para eventos puntuales) axisId: string; // ID del eje al que pertenece este evento trackIndex: number; // Índice en la pista del eje renderer?: AbstractEventRenderer; // Renderizador personalizado opcional - color?: string; // Color del evento opcional - selectedColor?: string; // Color del estado seleccionado opcional + color?: string; // Color opcional del evento + hoverColor?: string; // Color opcional para el estado al pasar el ratón por encima + selectedColor?: string; // Color opcional para el estado seleccionado + cursor?: string; // Cursor CSS opcional al pasar el ratón por encima del evento }; ``` +Establece `cursor: 'pointer'` en los eventos que realizan una acción al hacer clic. El cursor +se aplica solo mientras el puntero está sobre ese evento; cuando los eventos se superponen, el +último evento en el orden de los datos determina el cursor. + +### Colores de Gravity UI + +Canvas no puede resolver propiedades CSS personalizadas por sí solo. Timeline resuelve un +token de valor completo `var(--token)` contra su elemento canvas, por lo que los tokens semánticos de Gravity UI +funcionan para eventos, marcadores, secciones, ejes, cuadrícula y regla integrados. + +```tsx +import '@gravity-ui/uikit/styles/fonts.css'; +import '@gravity-ui/uikit/styles/styles.css'; +import {ThemeProvider} from '@gravity-ui/uikit'; +import {useTimeline} from '@gravity-ui/timeline/react'; +import {GravityTimelineCanvas} from '@gravity-ui/timeline/react/uikit'; + + + + +``` + +Pasa tokens directamente en cualquier campo de color, por ejemplo +`color: 'var(--g-color-base-positive-medium)'`. `GravityTimelineCanvas` +se redibuja automáticamente cuando el tema efectivo de Gravity UI cambia. Para un token +faltante, usa un fallback CSS como `var(--app-event-color, transparent)` +o llama a `timeline.api.resolveColor(color, fallback)` desde un renderizador personalizado. + +Para eventos, `color` se usa normalmente, `hoverColor` al pasar el ratón por encima, y +`selectedColor` después de la selección: + +```ts +const events = [ + { + id: 'deploy', + from: start, + to: end, + axisId: 'main', + trackIndex: 0, + color: 'var(--g-color-base-positive-medium)', + hoverColor: 'var(--g-color-base-positive-medium-hover)', + selectedColor: 'var(--g-color-base-positive-heavy)', + }, +]; +``` + +Los renderizadores de eventos personalizados reciben `resolveColor` como su argumento opcional final; +los renderizadores de marcadores y secciones personalizados lo reciben en sus datos de renderizado. + +### Fuentes de Canvas + +Establece `viewConfiguration.font` una vez para configurar la fuente predeterminada para la regla, +eventos y marcadores. Un `ruler.font`, `events.font` o `markers.font` específico del componente +tiene precedencia. El valor predeterminado sigue siendo `10px sans-serif`. + +Canvas no puede usar variables CSS o `inherit` directamente en `ctx.font`, por lo que Timeline +resuelve tokens de valor completo en el contexto CSS del canvas: + +```ts +viewConfiguration: { + font: 'var(--g-text-caption-2-font)', +} +``` + +Usa `font: 'inherit'` para usar la fuente calculada del elemento canvas. Los renderizadores personalizados +reciben `resolveFont` junto con `resolveColor`, o pueden llamar a `timeline.api.resolveFont(font)`. +Después de que una fuente web se cargue dinámicamente, llama a `timeline.api.rerender()` para redibujar el texto del canvas con ella. + ### Uso Directo de TypeScript -La clase `Timeline` se puede usar directamente en TypeScript sin React. Esto es útil para integrarse con otros frameworks o aplicaciones JavaScript puras: +La clase Timeline se puede usar directamente en TypeScript sin React. Esto es útil para integrarse con otros frameworks o aplicaciones JavaScript vanilla: ```typescript import { Timeline } from '@gravity-ui/timeline'; const timestamp = Date.now(); -// Crear una instancia de línea de tiempo +// Crear una instancia de timeline const timeline = new Timeline({ settings: { start: timestamp, @@ -304,7 +447,7 @@ const timeline = new Timeline({ id: 'event1', from: timestamp + 1800000, // 30 minutos a partir de ahora to: timestamp + 2400000, // 40 minutos a partir de ahora - label: 'Evento de Muestra', + label: 'Evento de Ejemplo', axisId: 'main' } ], @@ -342,277 +485,102 @@ if (canvas instanceof HTMLCanvasElement) { timeline.init(canvas); } -// Agregar oyentes de eventos +// Añadir listeners de eventos timeline.on('on-click', (detail) => { - console.log('Línea de tiempo clickeada:', detail); + console.log('Timeline clickeado:', detail); }); timeline.on('on-select-change', (detail) => { console.log('Selección cambiada:', detail); }); -// Limpiar cuando se termine +// Limpiar al terminar timeline.destroy(); ``` -La clase `Timeline` proporciona una API rica para gestionar la línea de tiempo: +La clase Timeline proporciona una API rica para gestionar la línea de tiempo: - **Gestión de Eventos**: ```typescript - // Agregar oyente de eventos + // Añadir listener de eventos timeline.on('eventClick', (detail) => { console.log('Evento clickeado:', detail); }); -``` - -```markdown -# @gravity-ui/timeline - -Una biblioteca de componentes de línea de tiempo interactiva y personalizable para React. - -## Instalación -```bash -npm install @gravity-ui/timeline -# o -yarn add @gravity-ui/timeline -``` + // Eliminar listener de eventos + const handler = (detail) => console.log(detail); + timeline.on('eventClick', handler); + timeline.off('eventClick', handler); -## Uso - -### Componente básico - -```jsx -import React from 'react'; -import { Timeline } from '@gravity-ui/timeline'; + // Emitir eventos personalizados + timeline.emit('customEvent', { data: 'datos personalizados' }); + ``` -const App = () => { - const events = [ - { - id: 'event1', - from: new Date(2023, 10, 15, 10, 0, 0), - to: new Date(2023, 10, 15, 12, 0, 0), - label: 'Meeting', - axisId: 'main', - trackIndex: 0, - }, +- **Control de la Línea de Tiempo**: + ```typescript + // Actualizar datos de la línea de tiempo + timeline.api.setEvents([ { - id: 'event2', - from: new Date(2023, 10, 15, 14, 0, 0), - to: new Date(2023, 10, 15, 15, 30, 0), - label: 'Presentation', + id: 'newEvent', + from: Date.now(), + to: Date.now() + 3600000, + label: 'Nuevo Evento', axisId: 'main', - trackIndex: 1, - }, - ]; + trackIndex: 0 + } + ]); - const axes = [ + // Actualizar ejes + timeline.api.setAxes([ { - id: 'main', + id: 'newAxis', tracksCount: 2, top: 0, - height: 80, - }, - ]; - - return ; -}; - -export default App; -``` - -### Personalización - -El componente `Timeline` acepta varias propiedades para personalizar su apariencia y comportamiento: - -- `events`: Una matriz de objetos de eventos. Cada evento debe tener las siguientes propiedades: - - `id`: Identificador único del evento. - - `from`: Fecha y hora de inicio del evento. - - `to`: Fecha y hora de finalización del evento. - - `label`: Etiqueta que se muestra para el evento. - - `axisId`: El ID del eje al que pertenece el evento. - - `trackIndex`: El índice de la pista dentro del eje. - - `color` (opcional): Color de fondo del evento. - - `textColor` (opcional): Color del texto del evento. - - `hoverColor` (opcional): Color de fondo del evento al pasar el ratón por encima. - - `onClick` (opcional): Función de devolución de llamada que se llama cuando se hace clic en el evento. - - `onHover` (opcional): Función de devolución de llamada que se llama cuando el ratón pasa por encima del evento. - - `render` (opcional): Una función de renderizado personalizada para el evento. - -- `axes`: Una matriz de objetos de ejes. Cada eje debe tener las siguientes propiedades: - - `id`: Identificador único del eje. - - `tracksCount`: El número de pistas en el eje. - - `top`: La posición superior del eje. - - `height`: La altura del eje. - - `label` (opcional): Etiqueta para el eje. - - `color` (opcional): Color de fondo del eje. - -- `markers`: Una matriz de objetos de marcadores. Cada marcador debe tener las siguientes propiedades: - - `id`: Identificador único del marcador. - - `time`: La hora a la que se mostrará el marcador. - - `label` (opcional): Etiqueta para el marcador. - - `color` (opcional): Color de la línea del marcador. - - `activeColor` (opcional): Color de la línea del marcador cuando está activo. - - `hoverColor` (opcional): Color de la línea del marcador al pasar el ratón por encima. - -- `sections`: Una matriz de objetos de secciones. Cada sección debe tener las siguientes propiedades: - - `id`: Identificador único de la sección. - - `from`: Fecha y hora de inicio de la sección. - - `to`: Fecha y hora de finalización de la sección. - - `color` (opcional): Color de fondo de la sección. - - `hoverColor` (opcional): Color de fondo de la sección al pasar el ratón por encima. - -- `currentTime` (opcional): La hora actual que se mostrará como una línea vertical. - -- `onEventClick` (opcional): Función de devolución de llamada que se llama cuando se hace clic en un evento. - -- `onMarkerClick` (opcional): Función de devolución de llamada que se llama cuando se hace clic en un marcador. - -- `onSectionClick` (opcional): Función de devolución de llamada que se llama cuando se hace clic en una sección. - -- `onRangeChange` (opcional): Función de devolución de llamada que se llama cuando cambia el rango visible de la línea de tiempo. - -- `onTimeChange` (opcional): Función de devolución de llamada que se llama cuando cambia la hora actual. - -### API - -El componente `Timeline` expone una API para controlar la línea de tiempo de forma programática. Puede acceder a la API a través de la prop `apiRef`. - -```jsx -import React, { useRef } from 'react'; -import { Timeline, TimelineApi } from '@gravity-ui/timeline'; - -const App = () => { - const timelineRef = useRef(null); - - const handleClick = () => { - if (timelineRef.current) { - // Add a new event - timelineRef.current.addEvent({ - id: 'newEvent', - from: new Date(), - to: new Date(Date.now() + 3600000), - label: 'New Event', - axisId: 'main', - trackIndex: 0, - }); + height: 80 } - }; - - return ( -
- - -
- ); -}; - -export default App; + ]); ``` -La API proporciona los siguientes métodos: - -- `addEvent(event)`: Agrega un nuevo evento a la línea de tiempo. -- `removeEvent(eventId)`: Elimina un evento de la línea de tiempo. -- `updateEvent(event)`: Actualiza un evento existente en la línea de tiempo. -- `addAxis(axis)`: Agrega un nuevo eje a la línea de tiempo. -- `removeAxis(axisId)`: Elimina un eje de la línea de tiempo. -- `updateAxis(axis)`: Actualiza un eje existente en la línea de tiempo. -- `addMarker(marker)`: Agrega un nuevo marcador a la línea de tiempo. -- `removeMarker(markerId)`: Elimina un marcador de la línea de tiempo. -- `updateMarker(marker)`: Actualiza un marcador existente en la línea de tiempo. -- `addSection(section)`: Agrega una nueva sección a la línea de tiempo. -- `removeSection(sectionId)`: Elimina una sección de la línea de tiempo. -- `updateSection(section)`: Actualiza una sección existente en la línea de tiempo. -- `scrollToTime(time)`: Desplaza la línea de tiempo a una hora específica. -- `zoomIn()`: Acerca la línea de tiempo. -- `zoomOut()`: Aleja la línea de tiempo. -- `setEvents(events)`: Establece todos los eventos en la línea de tiempo. -- `setAxes(axes)`: Establece todos los ejes en la línea de tiempo. -- `setMarkers(markers)`: Establece todos los marcadores en la línea de tiempo. -- `setSections(sections)`: Establece todas las secciones en la línea de tiempo. -- `setViewConfiguration(config)`: Actualiza la configuración de la vista de la línea de tiempo. - -### Manejo de eventos - -Puede escuchar eventos personalizados de la línea de tiempo utilizando el método `on`. - -```typescript -// Escuchar eventos -const handler = (detail) => console.log(detail); -timeline.on('eventClick', handler); - -// Eliminar el listener de eventos -timeline.off('eventClick', handler); - -// Emitir eventos personalizados -timeline.emit('customEvent', { data: 'datos personalizados' }); -``` - -### Control de la línea de tiempo - -Puede controlar la línea de tiempo mediante la API. - -```typescript -// Actualizar datos de eventos -timeline.api.setEvents([ - { - id: 'newEvent', - from: Date.now(), - to: Date.now() + 3600000, - label: 'Nuevo Evento', - axisId: 'main', - trackIndex: 0 - } -]); - -// Actualizar ejes -timeline.api.setAxes([ - { - id: 'newAxis', - tracksCount: 2, - top: 0, - height: 80 - } -]); - -// Actualizar marcadores -timeline.api.setMarkers([ - { - id: 'newMarker', - time: Date.now(), - label: 'Nuevo Marcador', - color: '#00ff00', - activeColor: '#4caf50', - hoverColor: '#2e7d32' - } -]); +```javascript + // Actualizar marcadores + timeline.api.setMarkers([ + { + id: 'newMarker', + time: Date.now(), + label: 'Nuevo Marcador', + color: '#00ff00', + activeColor: '#4caf50', + hoverColor: '#2e7d32' + } + ]); -// Actualizar secciones -timeline.api.setSections([ - { - id: 'newSection', - from: Date.now(), - to: Date.now() + 1800000, - color: 'rgba(255, 193, 7, 0.2)', // Fondo ámbar claro - hoverColor: 'rgba(255, 193, 7, 0.3)' - } -]); + // Actualizar secciones + timeline.api.setSections([ + { + id: 'newSection', + from: Date.now(), + to: Date.now() + 1800000, + color: 'rgba(255, 193, 7, 0.2)', // Fondo ámbar claro + hoverColor: 'rgba(255, 193, 7, 0.3)' + } + ]); -// Actualizar configuración de vista (se fusiona con la configuración actual) -timeline.api.setViewConfiguration({ hideRuler: true }); -``` + // Actualizar configuración de vista (se fusiona con la configuración actual) + timeline.api.setViewConfiguration({ hideRuler: true }); + ``` -## Ejemplos en vivo +## Ejemplos en Vivo -Explore ejemplos interactivos en nuestro [Storybook](https://preview.gravity-ui.com/timeline/): +Explora ejemplos interactivos en nuestro [Storybook](https://preview.gravity-ui.com/timeline/): -- [Línea de tiempo básica](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--basic) - Línea de tiempo simple con eventos y ejes -- [Línea de tiempo infinita](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--endless-timelines) - Línea de tiempo infinita con eventos y ejes +- [Línea de Tiempo Básica](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--basic) - Línea de tiempo simple con eventos y ejes +- [Línea de Tiempo Infinita](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--endless-timelines) - Línea de tiempo infinita con eventos y ejes - [Marcadores](https://preview.gravity-ui.com/timeline/?path=/story/timeline-markers--basic) - Línea de tiempo con marcadores verticales y etiquetas -- [Eventos personalizados](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--custom-renderer) - Línea de tiempo con renderizado de eventos personalizado +- [Interacciones de Cámara](https://preview.gravity-ui.com/timeline/?path=/story/components-timelinecanvas--interaction-and-focus) - Configura el comportamiento de la rueda, el desplazamiento horizontal y el pellizco del trackpad +- [Eventos Personalizados](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--custom-renderer) - Línea de tiempo con renderizado de eventos personalizado - [Integraciones](https://preview.gravity-ui.com/timeline/?path=/story/integrations-gravity-ui--timeline-ruler) - RangeDateSelection, DragHandler, NestedEvents, Popup, List + ## Desarrollo ### Storybook @@ -625,9 +593,9 @@ Para ejecutar Storybook: npm run storybook ``` -Esto iniciará el servidor de desarrollo de Storybook en el puerto 6006. Puede acceder a él en http://localhost:6006. +Esto iniciará el servidor de desarrollo de Storybook en el puerto 6006. Puedes acceder a él en http://localhost:6006. -Para compilar una versión estática de Storybook para su implementación: +Para generar una versión estática de Storybook para su despliegue: ```bash npm run build-storybook @@ -635,5 +603,4 @@ npm run build-storybook ## Licencia -MIT -``` \ No newline at end of file +MIT \ No newline at end of file diff --git a/src/content/local-docs/libs/timeline/README-fr.md b/src/content/local-docs/libs/timeline/README-fr.md index 12ff1b3162c2..f80c2c7b9f85 100644 --- a/src/content/local-docs/libs/timeline/README-fr.md +++ b/src/content/local-docs/libs/timeline/README-fr.md @@ -1,8 +1,8 @@ # @gravity-ui/timeline [![npm package](https://img.shields.io/npm/v/@gravity-ui/timeline)](https://www.npmjs.com/package/@gravity-ui/timeline) [![Release](https://img.shields.io/github/actions/workflow/status/gravity-ui/timeline/release.yml?branch=main&label=Release)](https://github.com/gravity-ui/timeline/actions/workflows/release.yml?query=branch:main) [![storybook](https://img.shields.io/badge/Storybook-deployed-ff4685)](https://preview.gravity-ui.com/timeline/) -> [Version française](./README.md) +> [Version française](./README-fr.md) -Une bibliothèque basée sur React pour créer des visualisations interactives de chronologies avec rendu sur toile (canvas). +Une bibliothèque basée sur React pour créer des visualisations interactives de chronologies avec rendu sur canvas. ## Documentation @@ -20,12 +20,13 @@ Rendu personnalisé avec des événements imbriqués extensibles (exemple [Neste ## Fonctionnalités -- Rendu basé sur la toile (canvas) pour des performances élevées +- Rendu basé sur le canvas pour des performances élevées - Chronologie interactive avec capacités de zoom et de panoramique +- Interactions flexibles avec la molette et le pavé tactile, y compris le passage du défilement vertical - Prise en charge des événements, des marqueurs, des sections, des axes et de la grille - Sections d'arrière-plan pour l'organisation visuelle et la mise en évidence des périodes - Regroupement intelligent des marqueurs avec zoom automatique sur le groupe - Cliquez sur les marqueurs groupés pour zoomer sur leurs composants individuels -- Rendu virtualisé pour améliorer les performances avec de grands ensembles de données (actif uniquement lorsque le contenu de la chronologie dépasse la zone d'affichage) +- Rendu virtualisé pour des performances améliorées avec de grands ensembles de données (actif uniquement lorsque le contenu de la chronologie dépasse la fenêtre d'affichage) - Apparence et comportement personnalisables - Prise en charge de TypeScript avec des définitions de types complètes - Intégration React avec des hooks personnalisés @@ -38,7 +39,7 @@ npm install @gravity-ui/timeline ## Utilisation -Le composant de chronologie peut être utilisé dans des applications React avec la configuration de base suivante : +Le composant de chronologie peut être utilisé dans les applications React avec la configuration de base suivante : ```tsx import { TimelineCanvas, useTimeline } from '@gravity-ui/timeline/react'; @@ -60,7 +61,7 @@ const MyTimelineComponent = () => { // timeline - Instance de la chronologie // api - Instance de CanvasApi (identique à timeline.api) - // start - fonction pour initialiser la chronologie avec la toile + // start - fonction pour initialiser la chronologie avec le canvas // stop - fonction pour détruire la chronologie return ( @@ -84,6 +85,51 @@ type TimelineAxis = { }; ``` +### Lignes d'axe horizontales + +Configurez le placement des lignes horizontales via `viewConfiguration.axes.linePosition` : + +- `"center"` (par défaut) dessine une ligne au centre de chaque piste. +- `"between"` dessine une ligne après chaque piste, à sa limite inférieure. Ceci est utile pour les lignes de style tableau avec des barres d'événements centrées. + +```typescript +viewConfiguration: { + axes: { + linePosition: 'between' + } +} +``` + +### Interactions flexibles de la caméra + +`ZoomMode` fournit des préréglages d'interaction familiers, tandis que `camera.interactions` vous permet de remplacer un geste individuel. Ceci est utile lorsqu'une chronologie se trouve à l'intérieur d'une page défilante verticalement : conservez le panoramique horizontal et le zoom du pavé tactile, mais laissez le défilement normal de la molette atteindre le conteneur parent. + +```tsx +import {ZoomMode} from '@gravity-ui/timeline'; + +const {timeline} = useTimeline({ + settings: { /* ... */ }, + viewConfiguration: { + camera: { + zoom: ZoomMode.DEFAULT, + interactions: { + verticalWheel: 'pass-through', + horizontalWheel: 'pan', + pinch: 'zoom', + }, + zoomSensitivity: { + in: 0.5, + out: 0.5, + }, + minRange: 5_000, + maxRange: 1000 * 60 * 60 * 24 * 365, + }, + }, +}); +``` + +Chaque interaction accepte `'zoom'`, `'pan'` ou `'pass-through'`. `pinch` représente le geste Ctrl+molette du pavé tactile du navigateur. `zoomSensitivity.in` et `zoomSensitivity.out` multiplient indépendamment la vitesse de zoom avant et arrière : `1` est la valeur par défaut, des valeurs inférieures sont plus douces et `0` désactive le zoom dans cette direction. Les petits décalages du pavé tactile sont lissés automatiquement. `minRange` et `maxRange` sont des durées en millisecondes ; le minimum est de 5 secondes par défaut et le maximum n'est pas limité sauf configuration, définissez donc `maxRange` pour limiter la distance de dézoom des utilisateurs. Consultez l'exemple interactif [Camera interactions Storybook](https://preview.gravity-ui.com/timeline/?path=/story/components-timelinecanvas--interaction-and-focus). + ### Structure des sections Chaque section nécessite la structure suivante : @@ -94,7 +140,7 @@ type TimelineSection = { from: number; // Horodatage de début to?: number; // Horodatage de fin optionnel (par défaut, fin de la chronologie) color: string; // Couleur d'arrière-plan de la section - hoverColor?: string; // Couleur optionnelle lors du survol de la section + hoverColor?: string; // Couleur optionnelle lorsque la section est survolée renderer?: AbstractSectionRenderer; // Renderer personnalisé optionnel (exporté du package) }; ``` @@ -121,7 +167,7 @@ const MyTimelineComponent = () => { { id: 'afternoon', from: Date.now() + 1800000, - // Pas de 'to' spécifié - s'étend jusqu'à la fin de la chronologie + // Pas de 'to' spécifié - s'étend jusqu'à la fin de la timeline color: 'rgba(76, 175, 80, 0.2)', // Vert semi-transparent hoverColor: 'rgba(76, 175, 80, 0.3)' } @@ -129,7 +175,7 @@ const MyTimelineComponent = () => { }, viewConfiguration: { sections: { - hitboxPadding: 2 // Marge pour la détection de survol + hitboxPadding: 2 // Marge pour la détection du survol } } }); @@ -138,7 +184,7 @@ const MyTimelineComponent = () => { }; ``` -### Structure des marqueurs +### Structure d'un marqueur Chaque marqueur nécessite la structure suivante : @@ -147,19 +193,19 @@ type TimelineMarker = { time: number; // Horodatage pour la position du marqueur color: string; // Couleur de la ligne du marqueur activeColor: string; // Couleur lorsque le marqueur est sélectionné (obligatoire) - hoverColor: string; // Couleur lors du survol du marqueur (obligatoire) + hoverColor: string; // Couleur lorsque le marqueur est survolé (obligatoire) lineWidth?: number; // Largeur optionnelle de la ligne du marqueur - label?: string; // Texte d'étiquette optionnel - labelColor?: string; // Couleur d'étiquette optionnelle - renderer?: AbstractMarkerRenderer; // Renderer personnalisé optionnel + label?: string; // Texte optionnel de l'étiquette + labelColor?: string; // Couleur optionnelle de l'étiquette + renderer?: AbstractMarkerRenderer; // Rendu personnalisé optionnel nonSelectable?: boolean;// Indique si le marqueur peut être sélectionné group?: boolean; // Indique si le marqueur représente un groupe }; ``` -### Regroupement et zoom des marqueurs +### Groupement et zoom des marqueurs -La chronologie regroupe automatiquement les marqueurs proches et offre une fonctionnalité de zoom : +La timeline regroupe automatiquement les marqueurs proches les uns des autres et offre une fonctionnalité de zoom : ```tsx const MyTimelineComponent = () => { @@ -178,7 +224,7 @@ const MyTimelineComponent = () => { }, viewConfiguration: { markers: { - collapseMinDistance: 8, // Regrouper les marqueurs dans un rayon de 8 pixels + collapseMinDistance: 8, // Regrouper les marqueurs à moins de 8 pixels groupZoomEnabled: true, // Activer le zoom sur le clic du groupe groupZoomPadding: 0.3, // Marge de 30% autour du groupe groupZoomMaxFactor: 0.3, // Facteur de zoom maximum @@ -197,33 +243,33 @@ const MyTimelineComponent = () => { ## Comment ça marche -Le composant de chronologie est construit avec React et offre un moyen flexible de créer des visualisations de chronologie interactives. Voici comment il fonctionne : +Le composant de timeline est construit avec React et offre un moyen flexible de créer des visualisations de timeline interactives. Voici comment il fonctionne : ### Architecture du composant -La chronologie est implémentée comme un composant React qui peut être configuré via deux objets principaux : +La timeline est implémentée comme un composant React qui peut être configuré via deux objets principaux : -1. **TimelineSettings** : Contrôle le comportement et l'apparence de base de la chronologie - - `start` : Heure de début de la chronologie - - `end` : Heure de fin de la chronologie - - `axes` : Tableau de configurations d'axes (voir la structure ci-dessous) - - `events` : Tableau de configurations d'événements - - `markers` : Tableau de configurations de marqueurs - - `sections` : Tableau de configurations de sections +1. **TimelineSettings** : Contrôle le comportement et l'apparence de base de la timeline. + - `start` : Heure de début de la timeline. + - `end` : Heure de fin de la timeline. + - `axes` : Tableau de configurations d'axes (voir la structure ci-dessous). + - `events` : Tableau de configurations d'événements. + - `markers` : Tableau de configurations de marqueurs. + - `sections` : Tableau de configurations de sections. -2. **ViewConfiguration** : Gère la représentation visuelle et les paramètres d'interaction - - Contrôle l'apparence, les niveaux de zoom et le comportement d'interaction - - Peut être personnalisé ou utiliser les valeurs par défaut +2. **ViewConfiguration** : Gère la représentation visuelle et les paramètres d'interaction. + - Contrôle l'apparence, les niveaux de zoom et le comportement d'interaction. + - Peut être personnalisé ou utiliser des valeurs par défaut. ### Gestion des événements -Le composant de chronologie prend en charge plusieurs événements interactifs : +Le composant de timeline prend en charge plusieurs événements interactifs : -- `on-click` : Déclenché lors d'un clic sur la chronologie -- `on-context-click` : Déclenché lors d'un clic droit/menu contextuel -- `on-select-change` : Déclenché lorsque la sélection change -- `on-hover` : Déclenché lors du survol des éléments de la chronologie -- `on-leave` : Déclenché lorsque la souris quitte les éléments de la chronologie +- `on-click` : Déclenché lors d'un clic sur la timeline. +- `on-context-click` : Déclenché lors d'un clic droit/menu contextuel. +- `on-select-change` : Déclenché lorsque la sélection change. +- `on-hover` : Déclenché lors du survol d'éléments de la timeline. +- `on-leave` : Déclenché lorsque la souris quitte des éléments de la timeline. Exemple de gestion d'événements : @@ -234,7 +280,7 @@ const MyTimelineComponent = () => { const { timeline } = useTimeline({ /* ... */ }); useTimelineEvent(timeline, 'on-click', (data) => { - console.log('Chronologie cliquée :', data); + console.log('Timeline cliquée :', data); }); useTimelineEvent(timeline, 'on-select-change', (data) => { @@ -247,36 +293,131 @@ const MyTimelineComponent = () => { ### Intégration React -Le composant utilise des hooks personnalisés pour la gestion de la chronologie : +Le composant utilise des hooks personnalisés pour la gestion de la timeline : + +- `useTimeline` : Gère l'instance de la timeline et son cycle de vie. + - Crée et initialise la timeline. + - Gère le nettoyage lors du démontage du composant. + - Fournit l'accès à l'instance de la timeline. + +- `useTimelineEvent` : Gère les abonnements aux événements et le nettoyage. + - Gère le cycle de vie des écouteurs d'événements. + - Nettoie automatiquement les écouteurs lors du démontage. + +Le composant gère automatiquement le nettoyage et la destruction de l'instance de la timeline lorsqu'elle est démontée. -- `useTimeline` : Gère l'instance de chronologie et son cycle de vie - - Crée et initialise la chronologie - - Gère le nettoyage lors du démontage du composant - - Fournit l'accès à l'instance de chronologie +### Popup d'événement -- `useTimelineEvent` : Gère les abonnements aux événements et le nettoyage - - Gère le cycle de vie des écouteurs d'événements - - Nettoie automatiquement les écouteurs lors du démontage +Installez `@gravity-ui/uikit` et ses styles pour afficher les détails des événements sans +vous abonner aux événements de survol ni calculer les coordonnées vous-même : -Le composant gère automatiquement le nettoyage et la destruction de l'instance de chronologie lorsqu'elle est démontée. +```tsx +import '@gravity-ui/uikit/styles/fonts.css'; +import '@gravity-ui/uikit/styles/styles.css'; +import {EventPopup} from '@gravity-ui/timeline/react/uikit'; + +<> + + } + /> + +``` + +`EventPopup` s'ouvre après 150 ms et se ferme 200 ms après que le pointeur quitte +l'événement. Définissez `openDelay`, `closeDelay`, `placement`, `offset`, `className`, ou +`aria-label` si nécessaire. Le popup reste ouvert tant que son contenu a le pointeur +ou le focus, se ferme sur Échap ou un clic extérieur, et utilise le dernier événement dans l'ordre des données lorsque les événements se chevauchent. `hoverColor` et `isHovered` contrôlent le rendu des événements ; `EventPopup` contrôle son interface utilisateur de détails. -### Structure des événements +### Structure d'un événement -Les événements dans la chronologie suivent cette structure : +Les événements dans la timeline suivent cette structure : ```typescript type TimelineEvent = { id: string; // Identifiant unique - from: number; // Horodatage de début - to?: number; // Horodatage de fin (optionnel pour les événements ponctuels) - axisId: string; // ID de l'axe auquel cet événement appartient + from: number; // Timestamp de début + to?: number; // Timestamp de fin (optionnel pour les événements ponctuels) + axisId: string; // ID de l'axe auquel appartient cet événement trackIndex: number; // Index dans la piste de l'axe renderer?: AbstractEventRenderer; // Renderer personnalisé optionnel color?: string; // Couleur optionnelle de l'événement - selectedColor?: string; // Couleur optionnelle de l'état sélectionné + hoverColor?: string; // Couleur optionnelle pour l'état au survol + selectedColor?: string; // Couleur optionnelle pour l'état sélectionné + cursor?: string; // Curseur CSS optionnel lors du survol de l'événement }; ``` +Définissez `cursor: 'pointer'` sur les événements qui effectuent une action au clic. Le curseur +est appliqué uniquement lorsque le pointeur se trouve sur cet événement ; lorsque les événements se chevauchent, le +dernier événement dans l'ordre des données détermine le curseur. + +### Couleurs Gravity UI + +Canvas ne peut pas résoudre les propriétés personnalisées CSS par lui-même. Timeline résout un +valeur complète `var(--token)` par rapport à son élément canvas, de sorte que les jetons sémantiques Gravity UI +fonctionnent pour les événements intégrés, les marqueurs, les sections, les axes, la grille et la règle. + +```tsx +import '@gravity-ui/uikit/styles/fonts.css'; +import '@gravity-ui/uikit/styles/styles.css'; +import {ThemeProvider} from '@gravity-ui/uikit'; +import {useTimeline} from '@gravity-ui/timeline/react'; +import {GravityTimelineCanvas} from '@gravity-ui/timeline/react/uikit'; + + + + +``` + +Passez les jetons directement dans n'importe quel champ de couleur, par exemple +`color: 'var(--g-color-base-positive-medium)'`. `GravityTimelineCanvas` +redessine automatiquement lorsque le thème Gravity UI effectif change. Pour un +jeton manquant, utilisez un fallback CSS tel que `var(--app-event-color, transparent)` +ou appelez `timeline.api.resolveColor(color, fallback)` à partir d'un renderer personnalisé. + +Pour les événements, `color` est utilisé normalement, `hoverColor` au survol du pointeur, et +`selectedColor` après la sélection : + +```ts +const events = [ + { + id: 'deploy', + from: start, + to: end, + axisId: 'main', + trackIndex: 0, + color: 'var(--g-color-base-positive-medium)', + hoverColor: 'var(--g-color-base-positive-medium-hover)', + selectedColor: 'var(--g-color-base-positive-heavy)', + }, +]; +``` + +Les renderers d'événements personnalisés reçoivent `resolveColor` comme dernier argument optionnel ; +les renderers de marqueurs et de sections personnalisés le reçoivent dans leurs données de rendu. + +### Polices Canvas + +Définissez `viewConfiguration.font` une fois pour configurer la police par défaut pour la règle, +les événements et les marqueurs. Un `ruler.font`, `events.font`, ou +`markers.font` spécifique au composant a la priorité. La valeur par défaut reste `10px sans-serif`. + +Canvas ne peut pas utiliser directement les variables CSS ou `inherit` dans `ctx.font`, donc +Timeline résout les jetons de valeur complète dans le contexte CSS du canvas : + +```ts +viewConfiguration: { + font: 'var(--g-text-caption-2-font)', +} +``` + +Utilisez `font: 'inherit'` pour utiliser la police calculée de l'élément canvas. Les renderers personnalisés +reçoivent `resolveFont` en plus de `resolveColor`, ou peuvent appeler +`timeline.api.resolveFont(font)`. Après le chargement dynamique d'une police web, appelez +`timeline.api.rerender()` pour redessiner le texte du canvas avec celle-ci. + ### Utilisation directe de TypeScript La classe Timeline peut être utilisée directement en TypeScript sans React. Ceci est utile pour l'intégration avec d'autres frameworks ou des applications JavaScript vanilla : @@ -286,7 +427,7 @@ import { Timeline } from '@gravity-ui/timeline'; const timestamp = Date.now(); -// Créer une instance de chronologie +// Créer une instance de timeline const timeline = new Timeline({ settings: { start: timestamp, @@ -344,7 +485,7 @@ if (canvas instanceof HTMLCanvasElement) { // Ajouter des écouteurs d'événements timeline.on('on-click', (detail) => { - console.log('Chronologie cliquée :', detail); + console.log('Timeline cliquée :', detail); }); timeline.on('on-select-change', (detail) => { @@ -355,29 +496,27 @@ timeline.on('on-select-change', (detail) => { timeline.destroy(); ``` -La classe Timeline fournit une API riche pour gérer la chronologie : +La classe Timeline fournit une API riche pour gérer la timeline : - **Gestion des événements** : ```typescript - // Ajouter un écouteur d'événements + // Ajouter un écouteur d'événement timeline.on('eventClick', (detail) => { console.log('Événement cliqué :', detail); }); -``` -```markdown - // Supprimer un écouteur d'événements + // Supprimer un écouteur d'événement const handler = (detail) => console.log(detail); timeline.on('eventClick', handler); timeline.off('eventClick', handler); // Émettre des événements personnalisés - timeline.emit('customEvent', { data: 'custom data' }); + timeline.emit('customEvent', { data: 'données personnalisées' }); ``` -- **Contrôle de la chronologie**: +- **Contrôle de la timeline** : ```typescript - // Mettre à jour les données de la chronologie + // Mettre à jour les données de la timeline timeline.api.setEvents([ { id: 'newEvent', @@ -398,20 +537,22 @@ La classe Timeline fournit une API riche pour gérer la chronologie : height: 80 } ]); +``` - // Mettre à jour les marqueurs +```javascript + // Mise à jour des marqueurs timeline.api.setMarkers([ { id: 'newMarker', time: Date.now(), - label: 'Nouveau marqueur', + label: 'Nouveau Marqueur', color: '#00ff00', activeColor: '#4caf50', hoverColor: '#2e7d32' } ]); - // Mettre à jour les sections + // Mise à jour des sections timeline.api.setSections([ { id: 'newSection', @@ -422,7 +563,7 @@ La classe Timeline fournit une API riche pour gérer la chronologie : } ]); - // Mettre à jour la configuration de la vue (fusionne avec la configuration actuelle) + // Mise à jour de la configuration de la vue (fusionne avec la configuration actuelle) timeline.api.setViewConfiguration({ hideRuler: true }); ``` @@ -430,11 +571,12 @@ La classe Timeline fournit une API riche pour gérer la chronologie : Explorez des exemples interactifs dans notre [Storybook](https://preview.gravity-ui.com/timeline/) : -- [Chronologie de base](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--basic) - Chronologie simple avec événements et axes -- [Chronologie infinie](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--endless-timelines) - Chronologie infinie avec événements et axes -- [Marqueurs](https://preview.gravity-ui.com/timeline/?path=/story/timeline-markers--basic) - Chronologie avec marqueurs verticaux et étiquettes -- [Événements personnalisés](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--custom-renderer) - Chronologie avec rendu d'événements personnalisé -- [Intégrations](https://preview.gravity-ui.com/timeline/?path=/story/integrations-gravity-ui--timeline-ruler) - Sélection de dates de plage, gestionnaire de glisser-déposer, événements imbriqués, pop-up, liste +- [Timeline de base](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--basic) - Timeline simple avec événements et axes +- [Timeline infinie](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--endless-timelines) - Timeline infinie avec événements et axes +- [Marqueurs](https://preview.gravity-ui.com/timeline/?path=/story/timeline-markers--basic) - Timeline avec marqueurs verticaux et étiquettes +- [Interactions de caméra](https://preview.gravity-ui.com/timeline/?path=/story/components-timelinecanvas--interaction-and-focus) - Configurez le comportement de la molette, du défilement horizontal et du pincement du trackpad +- [Événements personnalisés](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--custom-renderer) - Timeline avec rendu d'événements personnalisé +- [Intégrations](https://preview.gravity-ui.com/timeline/?path=/story/integrations-gravity-ui--timeline-ruler) - RangeDateSelection, DragHandler, NestedEvents, Popup, List ## Développement @@ -443,7 +585,7 @@ Explorez des exemples interactifs dans notre [Storybook](https://preview.gravity Ce projet inclut Storybook pour le développement et la documentation des composants. -Pour exécuter Storybook : +Pour lancer Storybook : ```bash npm run storybook diff --git a/src/content/local-docs/libs/timeline/README-ja.md b/src/content/local-docs/libs/timeline/README-ja.md index 2cf85c9d69a3..79e3a5ded161 100644 --- a/src/content/local-docs/libs/timeline/README-ja.md +++ b/src/content/local-docs/libs/timeline/README-ja.md @@ -10,21 +10,22 @@ Canvasレンダリングによるインタラクティブなタイムライン ## プレビュー -イベントと軸を備えた基本的なタイムライン: +イベントと軸を持つ基本的なタイムライン: ![Basic timeline with events](./docs/img/lines.png) -展開可能なネストされたイベントを使用したカスタムレンダリング([NestedEvents](https://preview.gravity-ui.com/timeline/?path=/story/integrations-gravity-ui--nested-events-story)の例): +展開可能なネストされたイベントを持つカスタムレンダリング([NestedEvents](https://preview.gravity-ui.com/timeline/?path=/story/integrations-gravity-ui--nested-events-story)の例): ![Nested events timeline](./docs/img/events.png) ## 特徴 -- 高パフォーマンスのためのCanvasベースレンダリング -- ズームおよびパン機能を備えたインタラクティブなタイムライン +- 高パフォーマンスのためのCanvasベースのレンダリング +- ズームとパン機能を備えたインタラクティブなタイムライン +- 柔軟なホイールとトラックパッドの操作、垂直スクロールのパススルーを含む - イベント、マーカー、セクション、軸、グリッドのサポート - 視覚的な整理と時間範囲のハイライトのための背景セクション -- スマートマーカーグルーピングとグループへの自動ズーム - グループ化されたマーカーをクリックして個々のコンポーネントにズームします +- スマートマーカーグルーピングと自動ズーム機能 - グループ化されたマーカーをクリックすると、個々のコンポーネントにズームインします - 大規模データセットでのパフォーマンス向上のための仮想化レンダリング(タイムラインコンテンツがビューポートを超える場合にのみアクティブ) - カスタマイズ可能な外観と動作 - 完全な型定義によるTypeScriptサポート @@ -59,8 +60,8 @@ const MyTimelineComponent = () => { }); // timeline - Timelineインスタンス - // api - CanvasApiインスタンス(timeline.apiと同じ) - // start - canvasでタイムラインを初期化する関数 + // api - CanvasApiインスタンス (timeline.apiと同じ) + // start - タイムラインをキャンバスで初期化する関数 // stop - タイムラインを破棄する関数 return ( @@ -78,28 +79,73 @@ const MyTimelineComponent = () => { ```typescript type TimelineAxis = { id: string; // 一意の軸識別子 - tracksCount: number; // 軸のトラック数 - top: number; // 垂直位置(px) - height: number; // トラックごとの高さ(px) + tracksCount: number; // 軸内のトラック数 + top: number; // 垂直位置 (px) + height: number; // トラックごとの高さ (px) }; ``` +### 水平軸線 + +`viewConfiguration.axes.linePosition` を通じて水平線の配置を設定します。 + +- `"center"` (デフォルト) は、各トラックの中央に線を描画します。 +- `"between"` は、各トラックの後、その下端に線を描画します。これは、中央揃えのイベントバーを持つテーブルスタイルの行に便利です。 + +```typescript +viewConfiguration: { + axes: { + linePosition: 'between' + } +} +``` + +### 柔軟なカメラ操作 + +`ZoomMode` は使い慣れた操作プリセットを提供し、`camera.interactions` は個々のジェスチャーをオーバーライドできます。これは、タイムラインが垂直スクロール可能なページ内に存在する場合に便利です。水平パンとトラックパッドズームは維持しつつ、通常のホイールスクロールは親コンテナに到達させます。 + +```tsx +import {ZoomMode} from '@gravity-ui/timeline'; + +const {timeline} = useTimeline({ + settings: { /* ... */ }, + viewConfiguration: { + camera: { + zoom: ZoomMode.DEFAULT, + interactions: { + verticalWheel: 'pass-through', + horizontalWheel: 'pan', + pinch: 'zoom', + }, + zoomSensitivity: { + in: 0.5, + out: 0.5, + }, + minRange: 5_000, + maxRange: 1000 * 60 * 60 * 24 * 365, + }, + }, +}); +``` + +各インタラクションは `'zoom'`, `'pan'`, または `'pass-through'` を受け入れます。`pinch` はブラウザのCtrl+ホイールトラックパッドジェスチャーを表します。`zoomSensitivity.in` と `zoomSensitivity.out` は、ズームインとズームアウトの速度をそれぞれ乗算します。`1` はデフォルトで、値が小さいほど穏やかになり、`0` はその方向のズームを無効にします。小さなトラックパッドデルタは自動的にスムーズになります。`minRange` と `maxRange` はミリ秒単位の期間です。最小値はデフォルトで5秒、最大値は設定されない限り無制限です。したがって、ユーザーがズームアウトできる範囲を制限するには `maxRange` を設定してください。インタラクティブな[Camera interactions Storybook例](https://preview.gravity-ui.com/timeline/?path=/story/components-timelinecanvas--interaction-and-focus)を参照してください。 + ### セクションの構造 -各セクションには以下の構造が必要です。 +各セクションは以下の構造が必要です。 ```typescript type TimelineSection = { id: string; // 一意のセクション識別子 from: number; // 開始タイムスタンプ - to?: number; // オプションの終了タイムスタンプ(デフォルトはタイムラインの終了) + to?: number; // オプションの終了タイムスタンプ (デフォルトはタイムラインの終了) color: string; // セクションの背景色 - hoverColor?: string; // オプションでセクションにホバーしたときのカラー - renderer?: AbstractSectionRenderer; // オプションのカスタムレンダラー(パッケージからエクスポート) + hoverColor?: string; // セクションがホバーされたときのオプションの色 + renderer?: AbstractSectionRenderer; // オプションのカスタムレンダラー (パッケージからエクスポート) }; ``` -セクションは時間範囲の背景色を提供し、タイムラインコンテンツを視覚的に整理するのに役立ちます。 +セクションは、時間範囲の背景色を提供し、タイムラインコンテンツを視覚的に整理するのに役立ちます。 ```tsx const MyTimelineComponent = () => { @@ -114,22 +160,22 @@ const MyTimelineComponent = () => { { id: 'morning', from: Date.now(), - to: Date.now() + 1800000, // 30分 - color: 'rgba(255, 235, 59, 0.3)', // 半透明の黄色 + to: Date.now() + 1800000, // 30 minutes + color: 'rgba(255, 235, 59, 0.3)', // Semi-transparent yellow hoverColor: 'rgba(255, 235, 59, 0.4)' }, { id: 'afternoon', from: Date.now() + 1800000, - // 'to' が指定されていない - タイムラインの終了まで拡張されます - color: 'rgba(76, 175, 80, 0.2)', // 半透明の緑色 + // No 'to' specified - extends to timeline end + color: 'rgba(76, 175, 80, 0.2)', // Semi-transparent green hoverColor: 'rgba(76, 175, 80, 0.3)' } ] }, viewConfiguration: { sections: { - hitboxPadding: 2 // ホバー検出パディング + hitboxPadding: 2 // Hover detection padding } } }); @@ -144,20 +190,20 @@ const MyTimelineComponent = () => { ```typescript type TimelineMarker = { - time: number; // マーカー位置のタイムスタンプ + time: number; // マーカーの位置を示すタイムスタンプ color: string; // マーカー線の色 - activeColor: string; // マーカーが選択されたときのカラー(必須) - hoverColor: string; // マーカーにホバーしたときのカラー(必須) - lineWidth?: number; // オプションのマーカー線の幅 - label?: string; // オプションのラベルテキスト - labelColor?: string; // オプションのラベルカラー - renderer?: AbstractMarkerRenderer; // オプションのカスタムレンダラー - nonSelectable?: boolean;// マーカーを選択できるかどうか + activeColor: string; // マーカーが選択されたときの色(必須) + hoverColor: string; // マーカーにホバーしたときの色(必須) + lineWidth?: number; // マーカー線の幅(オプション) + label?: string; // ラベルテキスト(オプション) + labelColor?: string; // ラベルの色(オプション) + renderer?: AbstractMarkerRenderer; // カスタムレンダラー(オプション) + nonSelectable?: boolean;// マーカーを選択可能かどうか group?: boolean; // マーカーがグループを表すかどうか }; ``` -### マーカーのグルーピングとズーム +### マーカーのグループ化とズーム タイムラインは、近くにあるマーカーを自動的にグループ化し、ズーム機能を提供します。 @@ -171,9 +217,9 @@ const MyTimelineComponent = () => { events: [], markers: [ // これらのマーカーはグループ化されます - { time: Date.now(), color: '#ff0000', activeColor: '#ff5252', hoverColor: '#ff1744', label: 'イベント 1' }, - { time: Date.now() + 1000, color: '#ff0000', activeColor: '#ff5252', hoverColor: '#ff1744', label: 'イベント 2' }, - { time: Date.now() + 2000, color: '#ff0000', activeColor: '#ff5252', hoverColor: '#ff1744', label: 'イベント 3' }, + { time: Date.now(), color: '#ff0000', activeColor: '#ff5252', hoverColor: '#ff1744', label: 'Event 1' }, + { time: Date.now() + 1000, color: '#ff0000', activeColor: '#ff5252', hoverColor: '#ff1744', label: 'Event 2' }, + { time: Date.now() + 2000, color: '#ff0000', activeColor: '#ff5252', hoverColor: '#ff1744', label: 'Event 3' }, ] }, viewConfiguration: { @@ -186,9 +232,9 @@ const MyTimelineComponent = () => { } }); - // グループズームイベントをリッスン + // グループズームイベントのリスニング useTimelineEvent(timeline, 'on-group-marker-click', (data) => { - console.log('グループがズームされました:', data); + console.log('Group zoomed:', data); }); return ; @@ -197,13 +243,13 @@ const MyTimelineComponent = () => { ## 仕組み -このタイムラインコンポーネントはReactで構築されており、インタラクティブなタイムラインビジュアライゼーションを作成するための柔軟な方法を提供します。仕組みは以下の通りです。 +タイムラインコンポーネントはReactを使用して構築されており、インタラクティブなタイムラインビジュアライゼーションを作成するための柔軟な方法を提供します。仕組みは以下の通りです。 ### コンポーネントアーキテクチャ タイムラインはReactコンポーネントとして実装されており、主に2つのオブジェクトを通じて設定できます。 -1. **TimelineSettings**: タイムラインのコアな動作と外観を制御します。 +1. **TimelineSettings**: コアとなるタイムラインの動作と外観を制御します。 - `start`: タイムラインの開始時刻 - `end`: タイムラインの終了時刻 - `axes`: 軸設定の配列(構造は以下を参照) @@ -213,7 +259,7 @@ const MyTimelineComponent = () => { 2. **ViewConfiguration**: ビジュアル表現とインタラクション設定を管理します。 - 外観、ズームレベル、インタラクションの動作を制御します。 - - カスタマイズ可能であり、デフォルト値を使用することもできます。 + - カスタマイズすることも、デフォルト値を使用することもできます。 ### イベント処理 @@ -221,9 +267,9 @@ const MyTimelineComponent = () => { - `on-click`: タイムラインをクリックしたときにトリガーされます。 - `on-context-click`: 右クリック/コンテキストメニューでトリガーされます。 -- `on-select-change`: 選択範囲が変更されたときに発生します。 -- `on-hover`: タイムライン要素にマウスカーソルが乗ったときにトリガーされます。 -- `on-leave`: マウスカーソルがタイムライン要素から離れたときに発生します。 +- `on-select-change`: 選択範囲が変更されたときにトリガーされます。 +- `on-hover`: タイムライン要素にホバーしたときにトリガーされます。 +- `on-leave`: マウスがタイムライン要素から離れたときにトリガーされます。 イベント処理の例: @@ -234,11 +280,11 @@ const MyTimelineComponent = () => { const { timeline } = useTimeline({ /* ... */ }); useTimelineEvent(timeline, 'on-click', (data) => { - console.log('タイムラインがクリックされました:', data); + console.log('Timeline clicked:', data); }); useTimelineEvent(timeline, 'on-select-change', (data) => { - console.log('選択範囲が変更されました:', data); + console.log('Selection changed:', data); }); return ; @@ -254,43 +300,118 @@ const MyTimelineComponent = () => { - コンポーネントのアンマウント時にクリーンアップを処理します。 - タイムラインインスタンスへのアクセスを提供します。 -- `useTimelineEvent`: イベントのサブスクリプションとクリーンアップを処理します。 +- `useTimelineEvent`: イベントサブスクリプションとクリーンアップを処理します。 - イベントリスナーのライフサイクルを管理します。 - アンマウント時にリスナーを自動的にクリーンアップします。 コンポーネントは、アンマウント時にタイムラインインスタンスのクリーンアップと破棄を自動的に処理します。 -### イベント構造 +### イベントポップアップ + +イベントの詳細を表示するために、`@gravity-ui/uikit`とそのスタイルをインストールしてください。これにより、ホバーイベントのサブスクライブや座標の計算を自分で行う必要がなくなります。 + +```tsx +import '@gravity-ui/uikit/styles/fonts.css'; +import '@gravity-ui/uikit/styles/styles.css'; +import {EventPopup} from '@gravity-ui/timeline/react/uikit'; + +<> + + } + /> + +``` + +`EventPopup`は150ミリ秒後に開き、ポインターがイベントから離れてから200ミリ秒後に閉じます。必要に応じて `openDelay`、`closeDelay`、`placement`、`offset`、`className`、または `aria-label` を設定してください。ポップアップは、そのコンテンツがポインターまたはフォーカスを持っている間は開いたままになり、Escapeキーまたは外部クリックで閉じます。イベントが重なっている場合は、データ順で最後のイベントが使用されます。`hoverColor` と `isHovered` はイベントの描画を制御し、`EventPopup` は詳細UIを制御します。 + +### イベントの構造 -タイムライン内のイベントは、この構造に従います。 +タイムライン内のイベントは以下の構造に従います。 ```typescript type TimelineEvent = { - id: string; // 一意の識別子 + id: string; // ユニークな識別子 from: number; // 開始タイムスタンプ - to?: number; // 終了タイムスタンプ(ポイントイベントの場合はオプション) + to?: number; // 終了タイムスタンプ (ポイントイベントの場合はオプション) axisId: string; // このイベントが属する軸のID trackIndex: number; // 軸トラック内のインデックス renderer?: AbstractEventRenderer; // オプションのカスタムレンダラー color?: string; // オプションのイベントカラー - selectedColor?: string; // オプションの選択状態カラー + hoverColor?: string; // オプションのホバー時のカラー + selectedColor?: string; // オプションの選択時のカラー + cursor?: string; // オプションのイベントホバー時のCSSカーソル }; ``` -### TypeScriptの直接利用 +クリック時にアクションを実行するイベントには `cursor: 'pointer'` を設定してください。カーソルはポインターがそのイベント上にある間のみ適用されます。イベントが重なっている場合、データ順で最後のイベントがカーソルを決定します。 + +### Gravity UI カラー + +Canvas は CSS カスタムプロパティを自身で解決できません。Timeline は `var(--token)` のような完全な値をその canvas 要素に対して解決するため、Gravity UI のセマンティックトークンは組み込みイベント、マーカー、セクション、軸、グリッド、ルーラーで機能します。 + +```tsx +import '@gravity-ui/uikit/styles/fonts.css'; +import '@gravity-ui/uikit/styles/styles.css'; +import {ThemeProvider} from '@gravity-ui/uikit'; +import {useTimeline} from '@gravity-ui/timeline/react'; +import {GravityTimelineCanvas} from '@gravity-ui/timeline/react/uikit'; + + + + +``` + +`color: 'var(--g-color-base-positive-medium)'` のように、任意のカラーフィールドに直接トークンを渡してください。`GravityTimelineCanvas` は、Gravity UI のテーマが変更されると自動的に再描画されます。トークンが見つからない場合は、`var(--app-event-color, transparent)` のような CSS フォールバックを使用するか、カスタムレンダラーから `timeline.api.resolveColor(color, fallback)` を呼び出してください。 + +イベントの場合、`color` は通常通り使用され、`hoverColor` はポインターホバー時、`selectedColor` は選択後に使用されます。 + +```ts +const events = [ + { + id: 'deploy', + from: start, + to: end, + axisId: 'main', + trackIndex: 0, + color: 'var(--g-color-base-positive-medium)', + hoverColor: 'var(--g-color-base-positive-medium-hover)', + selectedColor: 'var(--g-color-base-positive-heavy)', + }, +]; +``` + +カスタムイベントレンダラーは、最後のオプション引数として `resolveColor` を受け取ります。カスタムマーカーおよびセクションレンダラーは、レンダリングデータ内でこれを受け取ります。 + +### Canvas フォント + +ルーラー、イベント、マーカーのデフォルトフォントを設定するには、`viewConfiguration.font` を一度設定してください。コンポーネント固有の `ruler.font`、`events.font`、または `markers.font` が優先されます。デフォルトは `10px sans-serif` のままです。 -Timelineクラスは、Reactなしで直接TypeScriptで使用できます。これは、他のフレームワークやバニラJavaScriptアプリケーションとの統合に役立ちます。 +Canvas は `ctx.font` で CSS 変数や `inherit` を直接使用できないため、Timeline は canvas の CSS コンテキストで完全な値のトークンを解決します。 + +```ts +viewConfiguration: { + font: 'var(--g-text-caption-2-font)', +} +``` + +canvas 要素の計算されたフォントを使用するには、`font: 'inherit'` を使用してください。カスタムレンダラーは `resolveColor` と共に `resolveFont` を受け取るか、`timeline.api.resolveFont(font)` を呼び出すことができます。Web フォントが動的にロードされた後、`timeline.api.rerender()` を呼び出して canvas テキストを再描画してください。 + +### TypeScript の直接利用 + +Timeline クラスは、React なしで TypeScript で直接使用できます。これは、他のフレームワークやバニラ JavaScript アプリケーションとの統合に便利です。 ```typescript import { Timeline } from '@gravity-ui/timeline'; const timestamp = Date.now(); -// タイムラインインスタンスを作成 +// タイムラインインスタンスの作成 const timeline = new Timeline({ settings: { start: timestamp, - end: timestamp + 3600000, // 今から1時間後 + end: timestamp + 3600000, // 現在から1時間後 axes: [ { id: 'main', @@ -302,17 +423,17 @@ const timeline = new Timeline({ events: [ { id: 'event1', - from: timestamp + 1800000, // 今から30分後 - to: timestamp + 2400000, // 今から40分後 - label: 'サンプルイベント', + from: timestamp + 1800000, // 現在から30分後 + to: timestamp + 2400000, // 現在から40分後 + label: 'Sample Event', axisId: 'main' } ], markers: [ { id: 'marker1', - time: timestamp + 1200000, // 今から20分後 - label: '重要なポイント', + time: timestamp + 1200000, // 現在から20分後 + label: 'Important Point', color: '#ff0000', activeColor: '#ff5252', hoverColor: '#ff1744' @@ -336,36 +457,34 @@ const timeline = new Timeline({ } }); -// canvas要素で初期化 +// canvas 要素で初期化 const canvas = document.querySelector('canvas'); if (canvas instanceof HTMLCanvasElement) { timeline.init(canvas); } -// イベントリスナーを追加 +// イベントリスナーの追加 timeline.on('on-click', (detail) => { - console.log('タイムラインがクリックされました:', detail); + console.log('Timeline clicked:', detail); }); timeline.on('on-select-change', (detail) => { - console.log('選択範囲が変更されました:', detail); + console.log('Selection changed:', detail); }); -// 完了したらクリーンアップ +// 完了時にクリーンアップ timeline.destroy(); ``` -Timelineクラスは、タイムラインを管理するための豊富なAPIを提供します。 +Timeline クラスは、タイムラインを管理するための豊富な API を提供します。 - **イベント管理**: ```typescript - // イベントリスナーを追加 + // イベントリスナーの追加 timeline.on('eventClick', (detail) => { - console.log('イベントがクリックされました:', detail); + console.log('Event clicked:', detail); }); - ``` -```markdown // イベントリスナーの削除 const handler = (detail) => console.log(detail); timeline.on('eventClick', handler); @@ -375,7 +494,7 @@ Timelineクラスは、タイムラインを管理するための豊富なAPIを timeline.emit('customEvent', { data: 'custom data' }); ``` -- **タイムラインの制御**: +- **タイムライン制御**: ```typescript // タイムラインデータの更新 timeline.api.setEvents([ @@ -398,13 +517,15 @@ Timelineクラスは、タイムラインを管理するための豊富なAPIを height: 80 } ]); +``` +```javascript // マーカーの更新 timeline.api.setMarkers([ { id: 'newMarker', time: Date.now(), - label: 'New Marker', + label: '新しいマーカー', color: '#00ff00', activeColor: '#4caf50', hoverColor: '#2e7d32' @@ -428,30 +549,31 @@ Timelineクラスは、タイムラインを管理するための豊富なAPIを ## ライブデモ -[Storybook](https://preview.gravity-ui.com/timeline/) でインタラクティブなデモをご覧ください: +インタラクティブなデモは[Storybook](https://preview.gravity-ui.com/timeline/)でご覧いただけます。 - [基本的なタイムライン](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--basic) - イベントと軸を持つシンプルなタイムライン - [無限タイムライン](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--endless-timelines) - イベントと軸を持つ無限タイムライン -- [マーカー](https://preview.gravity-ui.com/timeline/?path=/story/timeline-markers--basic) - 垂直マーカーとラベルを持つタイムライン -- [カスタムイベント](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--custom-renderer) - カスタムイベントレンダリングを持つタイムライン -- [インテグレーション](https://preview.gravity-ui.com/timeline/?path=/story/integrations-gravity-ui--timeline-ruler) - RangeDateSelection, DragHandler, NestedEvents, Popup, List +- [マーカー](https://preview.gravity-ui.com/timeline/?path=/story/timeline-markers--basic) - 垂直マーカーとラベル付きタイムライン +- [カメラ操作](https://preview.gravity-ui.com/timeline/?path=/story/components-timelinecanvas--interaction-and-focus) - ホイール、水平スクロール、トラックパッドのピンチ操作の挙動を設定 +- [カスタムイベント](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--custom-renderer) - カスタムイベントレンダリング付きタイムライン +- [連携機能](https://preview.gravity-ui.com/timeline/?path=/story/integrations-gravity-ui--timeline-ruler) - RangeDateSelection, DragHandler, NestedEvents, Popup, List ## 開発 ### Storybook -このプロジェクトには、コンポーネント開発とドキュメントのための Storybook が含まれています。 +このプロジェクトには、コンポーネント開発とドキュメントのためのStorybookが含まれています。 -Storybook を実行するには: +Storybookを実行するには: ```bash npm run storybook ``` -これにより、ポート 6006 で Storybook 開発サーバーが起動します。http://localhost:6006 からアクセスできます。 +これにより、ポート6006でStorybook開発サーバーが起動します。http://localhost:6006 からアクセスできます。 -デプロイ用に Storybook の静的バージョンをビルドするには: +デプロイ用にStorybookの静的バージョンをビルドするには: ```bash npm run build-storybook diff --git a/src/content/local-docs/libs/timeline/README-ko.md b/src/content/local-docs/libs/timeline/README-ko.md index 2289ad0ab7b1..b7f5f425d450 100644 --- a/src/content/local-docs/libs/timeline/README-ko.md +++ b/src/content/local-docs/libs/timeline/README-ko.md @@ -10,9 +10,9 @@ Canvas 렌더링을 사용하여 대화형 타임라인 시각화를 구축하 ## 미리보기 -이벤트와 축이 있는 기본 타임라인: +이벤트 및 축이 있는 기본 타임라인: -![이벤트가 있는 기본 타임라인](./docs/img/lines.png) +![기본 타임라인과 이벤트](./docs/img/lines.png) 확장 가능한 중첩 이벤트가 있는 사용자 정의 렌더링 ([NestedEvents](https://preview.gravity-ui.com/timeline/?path=/story/integrations-gravity-ui--nested-events-story) 예시): @@ -22,8 +22,9 @@ Canvas 렌더링을 사용하여 대화형 타임라인 시각화를 구축하 - 높은 성능을 위한 Canvas 기반 렌더링 - 확대/축소 및 이동 기능이 있는 대화형 타임라인 +- 수직 스크롤 통과를 포함한 유연한 휠 및 트랙패드 상호 작용 - 이벤트, 마커, 섹션, 축 및 그리드 지원 -- 시각적 구성 및 시간대 강조 표시를 위한 배경 섹션 +- 시각적 구성 및 시간 기간 강조 표시를 위한 배경 섹션 - 스마트 마커 그룹화 및 자동 확대/축소 - 그룹화된 마커를 클릭하여 개별 구성 요소로 확대/축소 - 대규모 데이터셋에 대한 성능 향상을 위한 가상화 렌더링 (타임라인 콘텐츠가 뷰포트를 초과할 때만 활성화) - 사용자 정의 가능한 모양 및 동작 @@ -54,14 +55,14 @@ const MyTimelineComponent = () => { sections: [] }, viewConfiguration: { - // 선택 사항인 보기 구성 + // 선택적 보기 구성 } }); // timeline - Timeline 인스턴스 // api - CanvasApi 인스턴스 (timeline.api와 동일) - // start - 캔버스로 타임라인 초기화 함수 - // stop - 타임라인 삭제 함수 + // start - 타임라인을 캔버스로 초기화하는 함수 + // stop - 타임라인을 파괴하는 함수 return (
@@ -84,6 +85,51 @@ type TimelineAxis = { }; ``` +### 수평 축 선 + +`viewConfiguration.axes.linePosition`을 통해 수평 선 배치를 구성합니다. + +- `"center"` (기본값)는 각 트랙의 중앙을 가로지르는 선을 그립니다. +- `"between"`는 각 트랙 뒤, 즉 하단 경계에 선을 그립니다. 이는 중앙 이벤트 막대가 있는 테이블 스타일 행에 유용합니다. + +```typescript +viewConfiguration: { + axes: { + linePosition: 'between' + } +} +``` + +### 유연한 카메라 상호 작용 + +`ZoomMode`는 익숙한 상호 작용 사전 설정을 제공하며, `camera.interactions`를 사용하면 개별 제스처를 재정의할 수 있습니다. 이는 타임라인이 수직으로 스크롤 가능한 페이지 내에 있을 때 유용합니다. 수평 이동 및 트랙패드 확대/축소를 유지하되, 일반 휠 스크롤이 부모 컨테이너에 도달하도록 합니다. + +```tsx +import {ZoomMode} from '@gravity-ui/timeline'; + +const {timeline} = useTimeline({ + settings: { /* ... */ }, + viewConfiguration: { + camera: { + zoom: ZoomMode.DEFAULT, + interactions: { + verticalWheel: 'pass-through', + horizontalWheel: 'pan', + pinch: 'zoom', + }, + zoomSensitivity: { + in: 0.5, + out: 0.5, + }, + minRange: 5_000, + maxRange: 1000 * 60 * 60 * 24 * 365, + }, + }, +}); +``` + +각 상호 작용은 `'zoom'`, `'pan'`, 또는 `'pass-through'`를 허용합니다. `pinch`는 브라우저의 Ctrl+휠 트랙패드 제스처를 나타냅니다. `zoomSensitivity.in` 및 `zoomSensitivity.out`은 확대 및 축소 속도에 독립적으로 곱셈합니다. `1`은 기본값이며, 더 낮은 값은 더 부드럽고 `0`은 해당 방향의 확대/축소를 비활성화합니다. 작은 트랙패드 델타는 자동으로 부드럽게 처리됩니다. `minRange` 및 `maxRange`는 밀리초 단위의 기간입니다. 최소값은 기본적으로 5초이며, 최대값은 구성되지 않은 경우 제한이 없습니다. 따라서 사용자가 얼마나 멀리 축소할 수 있는지 제한하려면 `maxRange`를 설정하세요. 대화형 [카메라 상호 작용 Storybook 예시](https://preview.gravity-ui.com/timeline/?path=/story/components-timelinecanvas--interaction-and-focus)를 참조하세요. + ### 섹션 구조 각 섹션에는 다음 구조가 필요합니다. @@ -92,14 +138,14 @@ type TimelineAxis = { type TimelineSection = { id: string; // 고유한 섹션 식별자 from: number; // 시작 타임스탬프 - to?: number; // 선택 사항인 종료 타임스탬프 (기본값은 타임라인 종료) + to?: number; // 선택적 종료 타임스탬프 (기본값은 타임라인 끝) color: string; // 섹션의 배경색 hoverColor?: string; // 섹션에 마우스를 올렸을 때의 선택적 색상 - renderer?: AbstractSectionRenderer; // 선택 사항인 사용자 정의 렌더러 (패키지에서 내보냄) + renderer?: AbstractSectionRenderer; // 선택적 사용자 정의 렌더러 (패키지에서 내보냄) }; ``` -섹션은 시간대에 배경색을 제공하고 타임라인 콘텐츠를 시각적으로 구성하는 데 도움이 됩니다. +섹션은 시간 기간에 대한 배경색을 제공하고 타임라인 콘텐츠를 시각적으로 구성하는 데 도움이 됩니다. ```tsx const MyTimelineComponent = () => { @@ -114,14 +160,14 @@ const MyTimelineComponent = () => { { id: 'morning', from: Date.now(), - to: Date.now() + 1800000, // 30분 + to: Date.now() + 1800000, // 30 minutes color: 'rgba(255, 235, 59, 0.3)', // 반투명 노란색 hoverColor: 'rgba(255, 235, 59, 0.4)' }, { id: 'afternoon', from: Date.now() + 1800000, - // 'to'가 지정되지 않음 - 타임라인 끝까지 확장 + // 'to'가 지정되지 않음 - 타임라인 끝까지 확장됩니다. color: 'rgba(76, 175, 80, 0.2)', // 반투명 녹색 hoverColor: 'rgba(76, 175, 80, 0.3)' } @@ -129,7 +175,7 @@ const MyTimelineComponent = () => { }, viewConfiguration: { sections: { - hitboxPadding: 2 // 마우스 감지 패딩 + hitboxPadding: 2 // 호버 감지 패딩 } } }); @@ -140,18 +186,18 @@ const MyTimelineComponent = () => { ### 마커 구조 -각 마커에는 다음 구조가 필요합니다. +각 마커는 다음 구조를 요구합니다: ```typescript type TimelineMarker = { time: number; // 마커 위치의 타임스탬프 color: string; // 마커 선의 색상 activeColor: string; // 마커가 선택되었을 때의 색상 (필수) - hoverColor: string; // 마커에 마우스를 올렸을 때의 색상 (필수) + hoverColor: string; // 마커에 호버되었을 때의 색상 (필수) lineWidth?: number; // 마커 선의 선택적 너비 label?: string; // 선택적 레이블 텍스트 labelColor?: string; // 선택적 레이블 색상 - renderer?: AbstractMarkerRenderer; // 선택 사항인 사용자 정의 렌더러 + renderer?: AbstractMarkerRenderer; // 선택적 사용자 정의 렌더러 nonSelectable?: boolean;// 마커를 선택할 수 있는지 여부 group?: boolean; // 마커가 그룹을 나타내는지 여부 }; @@ -159,7 +205,7 @@ type TimelineMarker = { ### 마커 그룹화 및 확대/축소 -타임라인은 서로 가까이 있는 마커를 자동으로 그룹화하고 확대/축소 기능을 제공합니다. +타임라인은 서로 가까이 있는 마커를 자동으로 그룹화하고 확대/축소 기능을 제공합니다: ```tsx const MyTimelineComponent = () => { @@ -178,10 +224,10 @@ const MyTimelineComponent = () => { }, viewConfiguration: { markers: { - collapseMinDistance: 8, // 8픽셀 이내의 마커 그룹화 + collapseMinDistance: 8, // 8픽셀 내의 마커 그룹화 groupZoomEnabled: true, // 그룹 클릭 시 확대/축소 활성화 groupZoomPadding: 0.3, // 그룹 주변 30% 패딩 - groupZoomMaxFactor: 0.3, // 최대 확대/축소 비율 + groupZoomMaxFactor: 0.3, // 최대 확대/축소 계수 } } }); @@ -197,32 +243,32 @@ const MyTimelineComponent = () => { ## 작동 방식 -타임라인 컴포넌트는 React를 사용하여 구축되었으며, 인터랙티브한 타임라인 시각화를 유연하게 생성할 수 있는 방법을 제공합니다. 작동 방식은 다음과 같습니다. +타임라인 컴포넌트는 React를 사용하여 구축되었으며, 상호작용 가능한 타임라인 시각화를 유연하게 생성할 수 있는 방법을 제공합니다. 작동 방식은 다음과 같습니다: ### 컴포넌트 아키텍처 -타임라인은 두 가지 주요 객체를 통해 구성할 수 있는 React 컴포넌트로 구현됩니다. +타임라인은 두 가지 주요 객체를 통해 구성할 수 있는 React 컴포넌트로 구현됩니다: 1. **TimelineSettings**: 핵심 타임라인 동작 및 모양을 제어합니다. - `start`: 타임라인 시작 시간 - `end`: 타임라인 종료 시간 - - `axes`: 축 구성 배열 (아래 구조 참조) + - `axes`: 축 구성 배열 (구조는 아래 참조) - `events`: 이벤트 구성 배열 - `markers`: 마커 구성 배열 - `sections`: 섹션 구성 배열 -2. **ViewConfiguration**: 시각적 표현 및 상호 작용 설정을 관리합니다. - - 모양, 확대/축소 수준 및 상호 작용 동작을 제어합니다. +2. **ViewConfiguration**: 시각적 표현 및 상호작용 설정을 관리합니다. + - 모양, 확대/축소 수준 및 상호작용 동작을 제어합니다. - 사용자 정의하거나 기본값을 사용할 수 있습니다. ### 이벤트 처리 -타임라인 컴포넌트는 여러 가지 인터랙티브 이벤트를 지원합니다. +타임라인 컴포넌트는 여러 상호작용 이벤트를 지원합니다: - `on-click`: 타임라인 클릭 시 트리거됩니다. - `on-context-click`: 마우스 오른쪽 클릭/컨텍스트 메뉴 시 트리거됩니다. -- `on-select-change`: 선택 항목이 변경될 때 발생합니다. -- `on-hover`: 타임라인 요소 위로 마우스를 올렸을 때 트리거됩니다. +- `on-select-change`: 선택이 변경될 때 발생합니다. +- `on-hover`: 타임라인 요소에 호버될 때 트리거됩니다. - `on-leave`: 마우스가 타임라인 요소를 벗어날 때 발생합니다. 이벤트 처리 예시: @@ -238,7 +284,7 @@ const MyTimelineComponent = () => { }); useTimelineEvent(timeline, 'on-select-change', (data) => { - console.log('선택 항목 변경됨:', data); + console.log('선택 변경됨:', data); }); return ; @@ -247,11 +293,11 @@ const MyTimelineComponent = () => { ### React 통합 -컴포넌트는 타임라인 관리를 위해 사용자 정의 훅을 사용합니다. +컴포넌트는 타임라인 관리를 위해 사용자 정의 훅을 사용합니다: - `useTimeline`: 타임라인 인스턴스 및 해당 수명 주기를 관리합니다. - 타임라인을 생성하고 초기화합니다. - - 컴포넌트 언마운트 시 정리 작업을 처리합니다. + - 컴포넌트 언마운트 시 정리합니다. - 타임라인 인스턴스에 대한 액세스를 제공합니다. - `useTimelineEvent`: 이벤트 구독 및 정리를 처리합니다. @@ -260,26 +306,101 @@ const MyTimelineComponent = () => { 컴포넌트는 언마운트 시 타임라인 인스턴스의 정리 및 파괴를 자동으로 처리합니다. +### 이벤트 팝업 + +이벤트 세부 정보를 구독하거나 좌표를 직접 계산하지 않고 표시하려면 `@gravity-ui/uikit` 및 해당 스타일을 설치하세요: + +```tsx +import '@gravity-ui/uikit/styles/fonts.css'; +import '@gravity-ui/uikit/styles/styles.css'; +import {EventPopup} from '@gravity-ui/timeline/react/uikit'; + +<> + + } + /> + +``` + +`EventPopup`은 150ms 후에 열리고 포인터가 이벤트를 벗어난 후 200ms 후에 닫힙니다. 필요한 경우 `openDelay`, `closeDelay`, `placement`, `offset`, `className` 또는 `aria-label`을 설정하세요. 팝업은 콘텐츠에 포인터 또는 포커스가 있는 동안 열린 상태를 유지하고, Escape 키 또는 외부 클릭 시 닫히며, 이벤트가 겹칠 경우 데이터 순서에 따라 마지막 이벤트를 사용합니다. `hoverColor` 및 `isHovered`는 이벤트 그리기를 제어하며, `EventPopup`은 세부 정보 UI를 제어합니다. + ### 이벤트 구조 -타임라인의 이벤트는 이 구조를 따릅니다. +타임라인의 이벤트는 다음 구조를 따릅니다: ```typescript type TimelineEvent = { id: string; // 고유 식별자 from: number; // 시작 타임스탬프 to?: number; // 종료 타임스탬프 (포인트 이벤트의 경우 선택 사항) - axisId: string; // 이 이벤트가 속한 축의 ID - trackIndex: number; // 축 트랙에서의 인덱스 - renderer?: AbstractEventRenderer; // 선택 사항인 사용자 정의 렌더러 - color?: string; // 선택 사항인 이벤트 색상 - selectedColor?: string; // 선택 사항인 선택 상태 색상 + axisId: string; // 이벤트가 속한 축의 ID + trackIndex: number; // 축 트랙 내 인덱스 + renderer?: AbstractEventRenderer; // 선택적 사용자 정의 렌더러 + color?: string; // 선택적 이벤트 색상 + hoverColor?: string; // 선택적 호버 상태 색상 + selectedColor?: string; // 선택적 선택 상태 색상 + cursor?: string; // 이벤트 호버 시 선택적 CSS 커서 }; ``` -### 직접 TypeScript 사용 +`cursor: 'pointer'`를 클릭 시 동작하는 이벤트에 설정하세요. 커서는 해당 이벤트 위에 포인터가 있을 때만 적용됩니다. 이벤트가 겹치는 경우, 데이터 순서상 마지막 이벤트가 커서를 결정합니다. + +### Gravity UI 색상 + +Canvas는 CSS 사용자 정의 속성을 자체적으로 해석할 수 없습니다. Timeline은 `var(--token)` 전체 값을 캔버스 요소에 대해 해석하므로, Gravity UI의 시맨틱 토큰은 내장 이벤트, 마커, 섹션, 축, 그리드 및 눈금자에 대해 작동합니다. + +```tsx +import '@gravity-ui/uikit/styles/fonts.css'; +import '@gravity-ui/uikit/styles/styles.css'; +import {ThemeProvider} from '@gravity-ui/uikit'; +import {useTimeline} from '@gravity-ui/timeline/react'; +import {GravityTimelineCanvas} from '@gravity-ui/timeline/react/uikit'; + + + + +``` -Timeline 클래스는 React 없이 TypeScript에서 직접 사용할 수 있습니다. 이는 다른 프레임워크 또는 일반 JavaScript 애플리케이션과 통합하는 데 유용합니다. +예를 들어, `color: 'var(--g-color-base-positive-medium)'`와 같이 모든 색상 필드에 토큰을 직접 전달하세요. `GravityTimelineCanvas`는 Gravity UI 테마가 변경되면 자동으로 다시 그려집니다. 토큰이 누락된 경우, `var(--app-event-color, transparent)`와 같은 CSS 폴백을 사용하거나 사용자 정의 렌더러에서 `timeline.api.resolveColor(color, fallback)`를 호출하세요. + +이벤트의 경우, `color`는 일반적인 용도로, `hoverColor`는 포인터 호버 시, `selectedColor`는 선택 후 사용됩니다. + +```ts +const events = [ + { + id: 'deploy', + from: start, + to: end, + axisId: 'main', + trackIndex: 0, + color: 'var(--g-color-base-positive-medium)', + hoverColor: 'var(--g-color-base-positive-medium-hover)', + selectedColor: 'var(--g-color-base-positive-heavy)', + }, +]; +``` + +사용자 정의 이벤트 렌더러는 `resolveColor`를 마지막 선택적 인수로 받습니다. 사용자 정의 마커 및 섹션 렌더러는 렌더 데이터에서 이를 받습니다. + +### Canvas 글꼴 + +눈금자, 이벤트 및 마커의 기본 글꼴을 구성하려면 `viewConfiguration.font`를 한 번 설정하세요. 컴포넌트별 `ruler.font`, `events.font` 또는 `markers.font`가 우선 적용됩니다. 기본값은 `10px sans-serif`입니다. + +Canvas는 `ctx.font`에서 CSS 변수나 `inherit`을 직접 사용할 수 없으므로, Timeline은 캔버스 CSS 컨텍스트에서 전체 값 토큰을 해석합니다. + +```ts +viewConfiguration: { + font: 'var(--g-text-caption-2-font)', +} +``` + +`font: 'inherit'`를 사용하여 캔버스 요소의 계산된 글꼴을 사용하세요. 사용자 정의 렌더러는 `resolveColor`와 함께 `resolveFont`를 받거나 `timeline.api.resolveFont(font)`를 호출할 수 있습니다. 웹 글꼴이 동적으로 로드된 후에는 `timeline.api.rerender()`를 호출하여 캔버스 텍스트를 다시 그리세요. + +### TypeScript 직접 사용 + +Timeline 클래스는 React 없이 TypeScript에서 직접 사용할 수 있습니다. 이는 다른 프레임워크 또는 일반 JavaScript 애플리케이션과의 통합에 유용합니다. ```typescript import { Timeline } from '@gravity-ui/timeline'; @@ -312,7 +433,7 @@ const timeline = new Timeline({ { id: 'marker1', time: timestamp + 1200000, // 지금으로부터 20분 후 - label: '중요한 지점', + label: '중요 지점', color: '#ff0000', activeColor: '#ff5252', hoverColor: '#ff1744' @@ -348,14 +469,14 @@ timeline.on('on-click', (detail) => { }); timeline.on('on-select-change', (detail) => { - console.log('선택 항목 변경됨:', detail); + console.log('선택 변경됨:', detail); }); // 완료 시 정리 timeline.destroy(); ``` -Timeline 클래스는 타임라인을 관리하기 위한 풍부한 API를 제공합니다. +Timeline 클래스는 타임라인 관리를 위한 풍부한 API를 제공합니다. - **이벤트 관리**: ```typescript @@ -363,266 +484,102 @@ Timeline 클래스는 타임라인을 관리하기 위한 풍부한 API를 제 timeline.on('eventClick', (detail) => { console.log('이벤트 클릭됨:', detail); }); -``` - -```markdown -# @gravity-ui/timeline - -A flexible and powerful timeline component for React. - -## Installation - -```bash -npm install @gravity-ui/timeline -# or -yarn add @gravity-ui/timeline -``` - -## Usage - -```jsx -import { Timeline } from '@gravity-ui/timeline'; - -function App() { - return ( - - ); -} -``` - -## API - -### Props - -| Prop Name | Type | Default | Description | -|---|---|---|---| -| `events` | `Array` | `[]` | An array of timeline events. | -| `axes` | `Array` | `[]` | An array of timeline axes. | -| `markers` | `Array` | `[]` | An array of timeline markers. | -| `sections` | `Array
` | `[]` | An array of timeline sections. | -| `viewConfiguration` | `ViewConfiguration` | `{}` | Configuration for the timeline view. | -| `onEventClick` | `(event: Event) => void` | `undefined` | Callback function when an event is clicked. | -| `onEventHover` | `(event: Event) => void` | `undefined` | Callback function when an event is hovered. | -| `onEventOut` | `(event: Event) => void` | `undefined` | Callback function when an event hover ends. | -| `onMarkerClick` | `(marker: Marker) => void` | `undefined` | Callback function when a marker is clicked. | -| `onMarkerHover` | `(marker: Marker) => void` | `undefined` | Callback function when a marker is hovered. | -| `onMarkerOut` | `(marker: Marker) => void` | `undefined` | Callback function when a marker hover ends. | -| `onSectionClick` | `(section: Section) => void` | `undefined` | Callback function when a section is clicked. | -| `onSectionHover` | `(section: Section) => void` | `undefined` | Callback function when a section is hovered. | -| `onSectionOut` | `(section: Section) => void` | `undefined` | Callback function when a section hover ends. | -| `onRangeChange` | `(range: { from: Date, to: Date }) => void` | `undefined` | Callback function when the visible time range changes. | -| `onZoom` | `(zoom: number) => void` | `undefined` | Callback function when the timeline is zoomed. | -| `onScroll` | `(scroll: { x: number, y: number }) => void` | `undefined` | Callback function when the timeline is scrolled. | -| `onReady` | `(api: TimelineApi) => void` | `undefined` | Callback function when the timeline is ready and the API is available. | - -### Types - -```typescript -interface Event { - id: string; - from: Date; - to: Date; - label: string; - axisId: string; - trackIndex: number; - color?: string; - hoverColor?: string; - activeColor?: string; - // ... other properties -} - -interface Axis { - id: string; - tracksCount: number; - top: number; - height: number; - // ... other properties -} - -interface Marker { - id: string; - time: Date; - label: string; - color?: string; - hoverColor?: string; - activeColor?: string; - // ... other properties -} -interface Section { - id: string; - from: Date; - to: Date; - color?: string; - hoverColor?: string; - // ... other properties -} - -interface ViewConfiguration { - hideRuler?: boolean; - // ... other view configurations -} - -interface TimelineApi { - setEvents: (events: Event[]) => void; - setAxes: (axes: Axis[]) => void; - setMarkers: (markers: Marker[]) => void; - setSections: (sections: Section[]) => void; - setViewConfiguration: (config: ViewConfiguration) => void; - // ... other API methods -} -``` + // 이벤트 리스너 제거 + const handler = (detail) => console.log(detail); + timeline.on('eventClick', handler); + timeline.off('eventClick', handler); -## Methods + // 사용자 정의 이벤트 발생 + timeline.emit('customEvent', { data: '사용자 정의 데이터' }); + ``` -The `Timeline` component exposes an API through the `onReady` prop. - -```typescript -// Get the timeline API -const timelineApi = useRef(null); - -const handleReady = (api: TimelineApi) => { - timelineApi.current = api; -}; - -// ... in your component - - -// Example usage of the API -if (timelineApi.current) { - // Add a new event - timelineApi.current.setEvents([ +- **타임라인 제어**: + ```typescript + // 타임라인 데이터 업데이트 + timeline.api.setEvents([ { id: 'newEvent', - from: new Date(), - to: new Date(Date.now() + 3600000), - label: 'New Event', + from: Date.now(), + to: Date.now() + 3600000, + label: '새 이벤트', axisId: 'main', - trackIndex: 0, - }, + trackIndex: 0 + } ]); -} -``` - -### Event Handling - -You can listen to various events emitted by the timeline component. - -```typescript -// Remove event listener -const handler = (detail) => console.log(detail); -timeline.on('eventClick', handler); -timeline.off('eventClick', handler); - -// Emit custom events -timeline.emit('customEvent', { data: 'custom data' }); -``` -- **Timeline Control**: -```typescript -// Update timeline data -timeline.api.setEvents([ - { - id: 'newEvent', - from: Date.now(), - to: Date.now() + 3600000, - label: 'New Event', - axisId: 'main', - trackIndex: 0 - } -]); - -// Update axes -timeline.api.setAxes([ - { - id: 'newAxis', - tracksCount: 2, - top: 0, - height: 80 - } -]); + // 축 업데이트 + timeline.api.setAxes([ + { + id: 'newAxis', + tracksCount: 2, + top: 0, + height: 80 + } + ]); + ``` -// Update markers -timeline.api.setMarkers([ - { - id: 'newMarker', - time: Date.now(), - label: 'New Marker', - color: '#00ff00', - activeColor: '#4caf50', - hoverColor: '#2e7d32' - } -]); +```javascript + // 마커 업데이트 + timeline.api.setMarkers([ + { + id: 'newMarker', + time: Date.now(), + label: '새 마커', + color: '#00ff00', + activeColor: '#4caf50', + hoverColor: '#2e7d32' + } + ]); -// Update sections -timeline.api.setSections([ - { - id: 'newSection', - from: Date.now(), - to: Date.now() + 1800000, - color: 'rgba(255, 193, 7, 0.2)', // Light amber background - hoverColor: 'rgba(255, 193, 7, 0.3)' - } -]); + // 섹션 업데이트 + timeline.api.setSections([ + { + id: 'newSection', + from: Date.now(), + to: Date.now() + 1800000, + color: 'rgba(255, 193, 7, 0.2)', // 연한 호박색 배경 + hoverColor: 'rgba(255, 193, 7, 0.3)' + } + ]); -// Update view configuration (merges with current config) -timeline.api.setViewConfiguration({ hideRuler: true }); -``` + // 보기 설정 업데이트 (현재 설정과 병합) + timeline.api.setViewConfiguration({ hideRuler: true }); + ``` -## Live Examples +## 라이브 예제 -Explore interactive examples in our [Storybook](https://preview.gravity-ui.com/timeline/): +[Storybook](https://preview.gravity-ui.com/timeline/)에서 대화형 예제를 살펴보세요: -- [Basic Timeline](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--basic) - Simple timeline with events and axes -- [Endless Timeline](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--endless-timelines) - Endless timeline with events and axes -- [Markers](https://preview.gravity-ui.com/timeline/?path=/story/timeline-markers--basic) - Timeline with vertical markers and labels -- [Custom Events](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--custom-renderer) - Timeline with custom event rendering -- [Integrations](https://preview.gravity-ui.com/timeline/?path=/story/integrations-gravity-ui--timeline-ruler) - RangeDateSelection, DragHandler, NestedEvents, Popup, List +- [기본 타임라인](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--basic) - 이벤트와 축이 있는 간단한 타임라인 +- [무한 타임라인](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--endless-timelines) - 이벤트와 축이 있는 무한 타임라인 +- [마커](https://preview.gravity-ui.com/timeline/?path=/story/timeline-markers--basic) - 세로 마커와 레이블이 있는 타임라인 +- [카메라 상호작용](https://preview.gravity-ui.com/timeline/?path=/story/components-timelinecanvas--interaction-and-focus) - 휠, 가로 스크롤, 트랙패드 핀치 동작 설정 +- [사용자 지정 이벤트](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--custom-renderer) - 사용자 지정 이벤트 렌더링이 있는 타임라인 +- [통합](https://preview.gravity-ui.com/timeline/?path=/story/integrations-gravity-ui--timeline-ruler) - RangeDateSelection, DragHandler, NestedEvents, Popup, List -## Development +## 개발 ### Storybook -This project includes Storybook for component development and documentation. +이 프로젝트에는 컴포넌트 개발 및 문서화를 위한 Storybook이 포함되어 있습니다. -To run Storybook: +Storybook을 실행하려면 다음을 입력하세요: ```bash npm run storybook ``` -This will start the Storybook development server on port 6006. You can access it at http://localhost:6006. +그러면 포트 6006에서 Storybook 개발 서버가 시작됩니다. http://localhost:6006에서 접속할 수 있습니다. -To build a static version of Storybook for deployment: +배포를 위해 Storybook의 정적 버전을 빌드하려면 다음을 입력하세요: ```bash npm run build-storybook ``` -## License +## 라이선스 MIT ``` \ No newline at end of file diff --git a/src/content/local-docs/libs/timeline/README-pt.md b/src/content/local-docs/libs/timeline/README-pt.md index a53daa23c1ee..0dfdc91e328d 100644 --- a/src/content/local-docs/libs/timeline/README-pt.md +++ b/src/content/local-docs/libs/timeline/README-pt.md @@ -2,7 +2,7 @@ > [Versão em Português](./README-pt.md) -Uma biblioteca baseada em React para construir visualizações de linha do tempo interativas com renderização em canvas. +Uma biblioteca baseada em React para construir visualizações interativas de linha do tempo com renderização em canvas. ## Documentação @@ -14,18 +14,19 @@ Linha do tempo básica com eventos e eixos: ![Linha do tempo básica com eventos](./docs/img/lines.png) -Renderização personalizada com eventos aninhados expansíveis (exemplo [NestedEvents](https://preview.gravity-ui.com/timeline/?path=/story/integrations-gravity-ui--nested-events-story)): +Renderização personalizada com eventos aninhados expansíveis ([NestedEvents](https://preview.gravity-ui.com/timeline/?path=/story/integrations-gravity-ui--nested-events-story) exemplo): ![Linha do tempo com eventos aninhados](./docs/img/events.png) ## Funcionalidades -- Renderização baseada em canvas para alto desempenho +- Renderização baseada em canvas para alta performance - Linha do tempo interativa com capacidades de zoom e pan +- Interações flexíveis com roda e touchpad, incluindo passagem de scroll vertical - Suporte para eventos, marcadores, seções, eixos e grade - Seções de fundo para organização visual e destaque de períodos de tempo - Agrupamento inteligente de marcadores com zoom automático para o grupo - Clique em marcadores agrupados para dar zoom em seus componentes individuais -- Renderização virtualizada para melhor desempenho com grandes conjuntos de dados (ativa apenas quando o conteúdo da linha do tempo excede a viewport) +- Renderização virtualizada para melhor performance com grandes conjuntos de dados (ativa apenas quando o conteúdo da linha do tempo excede a viewport) - Aparência e comportamento personalizáveis - Suporte a TypeScript com definições de tipo completas - Integração com React com hooks personalizados @@ -73,7 +74,7 @@ const MyTimelineComponent = () => { ### Estrutura do Eixo -Cada eixo tem a seguinte estrutura: +Cada eixo possui a seguinte estrutura: ```typescript type TimelineAxis = { @@ -84,6 +85,51 @@ type TimelineAxis = { }; ``` +### Linhas Horizontais do Eixo + +Configure a posição das linhas horizontais através de `viewConfiguration.axes.linePosition`: + +- `"center"` (padrão) desenha uma linha no centro de cada trilha. +- `"between"` desenha uma linha após cada trilha, em sua borda inferior. Isso é útil para linhas no estilo de tabela com barras de eventos centralizadas. + +```typescript +viewConfiguration: { + axes: { + linePosition: 'between' + } +} +``` + +### Interações Flexíveis da Câmera + +`ZoomMode` fornece predefinições de interação familiares, enquanto `camera.interactions` permite sobrescrever um gesto individual. Isso é útil quando uma linha do tempo está dentro de uma página com scroll vertical: mantenha o pan horizontal e o zoom do touchpad, mas permita que o scroll normal da roda alcance o contêiner pai. + +```tsx +import {ZoomMode} from '@gravity-ui/timeline'; + +const {timeline} = useTimeline({ + settings: { /* ... */ }, + viewConfiguration: { + camera: { + zoom: ZoomMode.DEFAULT, + interactions: { + verticalWheel: 'pass-through', + horizontalWheel: 'pan', + pinch: 'zoom', + }, + zoomSensitivity: { + in: 0.5, + out: 0.5, + }, + minRange: 5_000, + maxRange: 1000 * 60 * 60 * 24 * 365, + }, + }, +}); +``` + +Cada interação aceita `'zoom'`, `'pan'` ou `'pass-through'`. `pinch` representa um gesto de Ctrl+roda do touchpad do navegador. `zoomSensitivity.in` e `zoomSensitivity.out` multiplicam independentemente a velocidade de zoom-in e zoom-out: `1` é o padrão, valores menores são mais suaves e `0` desativa o zoom nessa direção. Pequenos deltas do touchpad são suavizados automaticamente. `minRange` e `maxRange` são durações em milissegundos; o mínimo é de 5 segundos por padrão e o máximo é irrestrito, a menos que configurado, então defina `maxRange` para limitar o quão longe os usuários podem dar zoom para fora. Veja o exemplo interativo [Camera interactions Storybook](https://preview.gravity-ui.com/timeline/?path=/story/components-timelinecanvas--interaction-and-focus). + ### Estrutura da Seção Cada seção requer a seguinte estrutura: @@ -99,7 +145,7 @@ type TimelineSection = { }; ``` -As seções fornecem cores de fundo para períodos de tempo e ajudam a organizar o conteúdo da linha do tempo visualmente: +As seções fornecem coloração de fundo para períodos de tempo e ajudam a organizar o conteúdo da linha do tempo visualmente: ```tsx const MyTimelineComponent = () => { @@ -138,7 +184,7 @@ const MyTimelineComponent = () => { }; ``` -### Estrutura do Marcador +### Estrutura de Marcadores Cada marcador requer a seguinte estrutura: @@ -149,9 +195,9 @@ type TimelineMarker = { activeColor: string; // Cor quando o marcador está selecionado (obrigatório) hoverColor: string; // Cor quando o marcador está em hover (obrigatório) lineWidth?: number; // Largura opcional da linha do marcador - label?: string; // Texto de rótulo opcional - labelColor?: string; // Cor do rótulo opcional - renderer?: AbstractMarkerRenderer; // Renderizador personalizado opcional + label?: string; // Texto opcional do rótulo + labelColor?: string; // Cor opcional do rótulo + renderer?: AbstractMarkerRenderer; // Renderizador customizado opcional nonSelectable?: boolean;// Se o marcador pode ser selecionado group?: boolean; // Se o marcador representa um grupo }; @@ -159,7 +205,7 @@ type TimelineMarker = { ### Agrupamento e Zoom de Marcadores -A linha do tempo agrupa automaticamente marcadores que estão próximos e fornece funcionalidade de zoom: +A linha do tempo agrupa automaticamente marcadores que estão próximos e oferece funcionalidade de zoom: ```tsx const MyTimelineComponent = () => { @@ -178,8 +224,8 @@ const MyTimelineComponent = () => { }, viewConfiguration: { markers: { - collapseMinDistance: 8, // Agrupa marcadores a uma distância de 8 pixels - groupZoomEnabled: true, // Habilita zoom ao clicar em um grupo + collapseMinDistance: 8, // Agrupa marcadores em até 8 pixels + groupZoomEnabled: true, // Habilita zoom ao clicar no grupo groupZoomPadding: 0.3, // 30% de preenchimento ao redor do grupo groupZoomMaxFactor: 0.3, // Fator máximo de zoom } @@ -197,7 +243,7 @@ const MyTimelineComponent = () => { ## Como Funciona -O componente de linha do tempo é construído usando React e oferece uma maneira flexível de criar visualizações interativas de linha do tempo. Veja como funciona: +O componente de linha do tempo é construído usando React e oferece uma maneira flexível de criar visualizações de linha do tempo interativas. Veja como funciona: ### Arquitetura do Componente @@ -206,13 +252,13 @@ A linha do tempo é implementada como um componente React que pode ser configura 1. **TimelineSettings**: Controla o comportamento e a aparência principal da linha do tempo - `start`: Hora de início da linha do tempo - `end`: Hora de término da linha do tempo - - `axes`: Matriz de configurações de eixo (veja a estrutura abaixo) - - `events`: Matriz de configurações de evento - - `markers`: Matriz de configurações de marcador - - `sections`: Matriz de configurações de seção + - `axes`: Array de configurações de eixos (veja a estrutura abaixo) + - `events`: Array de configurações de eventos + - `markers`: Array de configurações de marcadores + - `sections`: Array de configurações de seções 2. **ViewConfiguration**: Gerencia a representação visual e as configurações de interação - - Controla a aparência, os níveis de zoom e o comportamento de interação + - Controla a aparência, níveis de zoom e comportamento de interação - Pode ser personalizado ou usar valores padrão ### Tratamento de Eventos @@ -223,7 +269,7 @@ O componente de linha do tempo suporta vários eventos interativos: - `on-context-click`: Disparado ao clicar com o botão direito/menu de contexto - `on-select-change`: Disparado quando a seleção muda - `on-hover`: Disparado ao passar o mouse sobre elementos da linha do tempo -- `on-leave`: Disparado quando o mouse sai dos elementos da linha do tempo +- `on-leave`: Disparado quando o mouse sai de elementos da linha do tempo Exemplo de tratamento de eventos: @@ -247,46 +293,143 @@ const MyTimelineComponent = () => { ### Integração com React -O componente usa hooks personalizados para gerenciar a linha do tempo: +O componente usa hooks customizados para gerenciamento da linha do tempo: - `useTimeline`: Gerencia a instância da linha do tempo e seu ciclo de vida - Cria e inicializa a linha do tempo - Lida com a limpeza ao desmontar o componente - Fornece acesso à instância da linha do tempo -- `useTimelineEvent`: Lida com a assinatura de eventos e a limpeza +- `useTimelineEvent`: Lida com a assinatura de eventos e limpeza - Gerencia o ciclo de vida do ouvinte de eventos - Limpa automaticamente os ouvintes ao desmontar O componente lida automaticamente com a limpeza e destruição da instância da linha do tempo quando desmontado. -### Estrutura de Eventos +### Popup de Evento + +Instale `@gravity-ui/uikit` e seus estilos para exibir detalhes do evento sem +precisar assinar eventos de hover ou calcular coordenadas: + +```tsx +import '@gravity-ui/uikit/styles/fonts.css'; +import '@gravity-ui/uikit/styles/styles.css'; +import {EventPopup} from '@gravity-ui/timeline/react/uikit'; + +<> + + } + /> + +``` + +O `EventPopup` abre após 150 ms e fecha 200 ms após o ponteiro sair do +evento. Defina `openDelay`, `closeDelay`, `placement`, `offset`, `className` ou +`aria-label` quando necessário. O popup permanece aberto enquanto seu conteúdo tiver ponteiro +ou foco, fecha ao pressionar Escape ou clicar fora, e usa o último evento na ordem dos dados quando os eventos se sobrepõem. `hoverColor` e `isHovered` controlam o desenho do evento; +o `EventPopup` controla sua UI de detalhes. + +### Estrutura de Evento -Os eventos na linha do tempo seguem esta estrutura: +Eventos na linha do tempo seguem esta estrutura: ```typescript type TimelineEvent = { id: string; // Identificador único from: number; // Timestamp de início - to?: number; // Timestamp de término (opcional para eventos pontuais) + to?: number; // Timestamp de fim (opcional para eventos pontuais) axisId: string; // ID do eixo ao qual este evento pertence trackIndex: number; // Índice na trilha do eixo - renderer?: AbstractEventRenderer; // Renderizador personalizado opcional + renderer?: AbstractEventRenderer; // Renderizador customizado opcional color?: string; // Cor opcional do evento - selectedColor?: string; // Cor opcional do estado selecionado + hoverColor?: string; // Cor opcional para o estado de hover + selectedColor?: string; // Cor opcional para o estado selecionado + cursor?: string; // Cursor CSS opcional ao passar o mouse sobre o evento }; ``` -### Uso Direto com TypeScript +Defina `cursor: 'pointer'` em eventos que realizam uma ação ao serem clicados. O cursor +é aplicado apenas enquanto o ponteiro estiver sobre o evento; quando eventos se sobrepõem, +o último evento na ordem dos dados determina o cursor. + +### Cores do Gravity UI + +O Canvas não consegue resolver propriedades CSS customizadas por si só. O Timeline resolve +um valor completo `var(--token)` em relação ao seu elemento canvas, portanto, os tokens +semânticos do Gravity UI funcionam para eventos, marcadores, seções, eixos, grid e régua +integrados. + +```tsx +import '@gravity-ui/uikit/styles/fonts.css'; +import '@gravity-ui/uikit/styles/styles.css'; +import {ThemeProvider} from '@gravity-ui/uikit'; +import {useTimeline} from '@gravity-ui/timeline/react'; +import {GravityTimelineCanvas} from '@gravity-ui/timeline/react/uikit'; + + + + +``` + +Passe tokens diretamente em qualquer campo de cor, por exemplo +`color: 'var(--g-color-base-positive-medium)'`. O `GravityTimelineCanvas` +redesenha automaticamente quando o tema efetivo do Gravity UI muda. Para um token ausente, +use um fallback CSS como `var(--app-event-color, transparent)` ou chame +`timeline.api.resolveColor(color, fallback)` de um renderizador customizado. + +Para eventos, `color` é usado normalmente, `hoverColor` ao passar o mouse, e +`selectedColor` após a seleção: + +```ts +const events = [ + { + id: 'deploy', + from: start, + to: end, + axisId: 'main', + trackIndex: 0, + color: 'var(--g-color-base-positive-medium)', + hoverColor: 'var(--g-color-base-positive-medium-hover)', + selectedColor: 'var(--g-color-base-positive-heavy)', + }, +]; +``` + +Renderizadores de eventos customizados recebem `resolveColor` como seu último argumento opcional; +renderizadores de marcadores e seções customizados o recebem em seus dados de renderização. + +### Fontes do Canvas + +Defina `viewConfiguration.font` uma vez para configurar a fonte padrão para a régua, +eventos e marcadores. Um `ruler.font`, `events.font` ou `markers.font` específico do componente +tem precedência. O padrão permanece `10px sans-serif`. -A classe `Timeline` pode ser usada diretamente em TypeScript sem React. Isso é útil para integrar com outros frameworks ou aplicações JavaScript vanilla: +O Canvas não pode usar variáveis CSS ou `inherit` diretamente em `ctx.font`, então +o Timeline resolve tokens de valor completo no contexto CSS do canvas: + +```ts +viewConfiguration: { + font: 'var(--g-text-caption-2-font)', +} +``` + +Use `font: 'inherit'` para usar a fonte computada do elemento canvas. Renderizadores customizados +recebem `resolveFont` junto com `resolveColor`, ou podem chamar +`timeline.api.resolveFont(font)`. Após uma fonte web carregar dinamicamente, chame +`timeline.api.rerender()` para redesenhar o texto do canvas com ela. + +### Uso Direto de TypeScript + +A classe Timeline pode ser usada diretamente em TypeScript sem React. Isso é útil para integrar com outros frameworks ou aplicações JavaScript vanilla: ```typescript import { Timeline } from '@gravity-ui/timeline'; const timestamp = Date.now(); -// Cria uma instância de linha do tempo +// Cria uma instância do timeline const timeline = new Timeline({ settings: { start: timestamp, @@ -344,7 +487,7 @@ if (canvas instanceof HTMLCanvasElement) { // Adiciona ouvintes de eventos timeline.on('on-click', (detail) => { - console.log('Linha do tempo clicada:', detail); + console.log('Timeline clicado:', detail); }); timeline.on('on-select-change', (detail) => { @@ -355,7 +498,7 @@ timeline.on('on-select-change', (detail) => { timeline.destroy(); ``` -A classe `Timeline` fornece uma API rica para gerenciar a linha do tempo: +A classe Timeline fornece uma API rica para gerenciar o timeline: - **Gerenciamento de Eventos**: ```typescript @@ -363,24 +506,22 @@ A classe `Timeline` fornece uma API rica para gerenciar a linha do tempo: timeline.on('eventClick', (detail) => { console.log('Evento clicado:', detail); }); -``` -```markdown - // Remove event listener + // Remove um ouvinte de evento const handler = (detail) => console.log(detail); timeline.on('eventClick', handler); timeline.off('eventClick', handler); - // Emit custom events - timeline.emit('customEvent', { data: 'custom data' }); + // Emite eventos customizados + timeline.emit('customEvent', { data: 'dados customizados' }); ``` -- **Controle da Linha do Tempo**: +- **Controle do Timeline**: ```typescript - // Atualiza os dados dos eventos + // Atualiza os dados do timeline timeline.api.setEvents([ { - id: 'newEvent', + id: 'novoEvento', from: Date.now(), to: Date.now() + 3600000, label: 'Novo Evento', @@ -392,14 +533,16 @@ A classe `Timeline` fornece uma API rica para gerenciar a linha do tempo: // Atualiza os eixos timeline.api.setAxes([ { - id: 'newAxis', + id: 'novoEixo', tracksCount: 2, top: 0, height: 80 } ]); +``` - // Atualiza os marcadores +```javascript + // Atualiza marcadores timeline.api.setMarkers([ { id: 'newMarker', @@ -411,7 +554,7 @@ A classe `Timeline` fornece uma API rica para gerenciar a linha do tempo: } ]); - // Atualiza as seções + // Atualiza seções timeline.api.setSections([ { id: 'newSection', @@ -422,7 +565,7 @@ A classe `Timeline` fornece uma API rica para gerenciar a linha do tempo: } ]); - // Atualiza a configuração de visualização (mescla com a configuração atual) + // Atualiza configuração de visualização (mescla com a configuração atual) timeline.api.setViewConfiguration({ hideRuler: true }); ``` @@ -433,6 +576,7 @@ Explore exemplos interativos em nosso [Storybook](https://preview.gravity-ui.com - [Linha do Tempo Básica](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--basic) - Linha do tempo simples com eventos e eixos - [Linha do Tempo Infinita](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--endless-timelines) - Linha do tempo infinita com eventos e eixos - [Marcadores](https://preview.gravity-ui.com/timeline/?path=/story/timeline-markers--basic) - Linha do tempo com marcadores verticais e rótulos +- [Interações da Câmera](https://preview.gravity-ui.com/timeline/?path=/story/components-timelinecanvas--interaction-and-focus) - Configure o comportamento de rolagem com a roda, rolagem horizontal e zoom com trackpad - [Eventos Personalizados](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--custom-renderer) - Linha do tempo com renderização de eventos personalizada - [Integrações](https://preview.gravity-ui.com/timeline/?path=/story/integrations-gravity-ui--timeline-ruler) - RangeDateSelection, DragHandler, NestedEvents, Popup, List @@ -451,7 +595,7 @@ npm run storybook Isso iniciará o servidor de desenvolvimento do Storybook na porta 6006. Você pode acessá-lo em http://localhost:6006. -Para construir uma versão estática do Storybook para implantação: +Para compilar uma versão estática do Storybook para implantação: ```bash npm run build-storybook diff --git a/src/content/local-docs/libs/timeline/README-zh.md b/src/content/local-docs/libs/timeline/README-zh.md index 33644a24717e..dd72cbac1a00 100644 --- a/src/content/local-docs/libs/timeline/README-zh.md +++ b/src/content/local-docs/libs/timeline/README-zh.md @@ -1,6 +1,6 @@ # @gravity-ui/timeline [![npm package](https://img.shields.io/npm/v/@gravity-ui/timeline)](https://www.npmjs.com/package/@gravity-ui/timeline) [![Release](https://img.shields.io/github/actions/workflow/status/gravity-ui/timeline/release.yml?branch=main&label=Release)](https://github.com/gravity-ui/timeline/actions/workflows/release.yml?query=branch:main) [![storybook](https://img.shields.io/badge/Storybook-deployed-ff4685)](https://preview.gravity-ui.com/timeline/) -> [中文版本](./README.md) +> [English version](./README.md) 一个基于 React 的库,用于构建具有 Canvas 渲染的交互式时间轴可视化。 @@ -12,23 +12,24 @@ 带有事件和轴的基本时间轴: -![带有事件的基本时间轴](./docs/img/lines.png) +![Basic timeline with events](./docs/img/lines.png) -自定义渲染,支持可展开的嵌套事件([NestedEvents](https://preview.gravity-ui.com/timeline/?path=/story/integrations-gravity-ui--nested-events-story) 示例): +带有可展开嵌套事件的自定义渲染([NestedEvents](https://preview.gravity-ui.com/timeline/?path=/story/integrations-gravity-ui--nested-events-story) 示例): -![嵌套事件时间轴](./docs/img/events.png) +![Nested events timeline](./docs/img/events.png) ## 特性 -- 基于 Canvas 的渲染,性能卓越 -- 支持缩放和平移的交互式时间轴 +- 基于 Canvas 的渲染,性能高 +- 交互式时间轴,支持缩放和平移 +- 灵活的滚轮和触控板交互,包括垂直滚动穿透 - 支持事件、标记、区域、轴和网格 -- 背景区域,用于视觉组织和时间段高亮显示 -- 智能标记分组,并自动缩放到组 - 点击分组标记可缩放到其独立组件 -- 虚拟化渲染,提高处理大型数据集时的性能(仅当时间轴内容超出视口时激活) +- 背景区域用于视觉组织和时间段高亮 +- 智能标记分组,自动缩放到组 - 点击分组标记可缩放到其个体组件 +- 虚拟化渲染,提高大型数据集的性能(仅在时间轴内容超出视口时激活) - 可自定义的外观和行为 - 支持 TypeScript,提供完整的类型定义 -- React 集成,包含自定义 Hooks +- React 集成,提供自定义 Hook ## 安装 @@ -60,8 +61,8 @@ const MyTimelineComponent = () => { // timeline - Timeline 实例 // api - CanvasApi 实例 (与 timeline.api 相同) - // start - 用于使用 canvas 初始化时间轴的函数 - // stop - 用于销毁时间轴的函数 + // start - 初始化时间轴并传入 canvas 的函数 + // stop - 销毁时间轴的函数 return (
@@ -80,10 +81,55 @@ type TimelineAxis = { id: string; // 唯一的轴标识符 tracksCount: number; // 轴中的轨道数量 top: number; // 垂直位置 (px) - height: number; // 每个轨道的高度 (px) + height: number; // 每条轨道的像素高度 }; ``` +### 水平轴线 + +通过 `viewConfiguration.axes.linePosition` 配置水平线的位置: + +- `"center"` (默认) 在每条轨道的中心绘制一条线。 +- `"between"` 在每条轨道之后绘制一条线,位于其底部边界。这对于带有居中事件条的表格样式行非常有用。 + +```typescript +viewConfiguration: { + axes: { + linePosition: 'between' + } +} +``` + +### 灵活的相机交互 + +`ZoomMode` 提供了熟悉的交互预设,而 `camera.interactions` 则允许您覆盖单个手势。当时间轴位于可垂直滚动的页面中时,这非常有用:保留水平平移和触控板缩放,但允许正常的滚轮滚动到达父容器。 + +```tsx +import {ZoomMode} from '@gravity-ui/timeline'; + +const {timeline} = useTimeline({ + settings: { /* ... */ }, + viewConfiguration: { + camera: { + zoom: ZoomMode.DEFAULT, + interactions: { + verticalWheel: 'pass-through', + horizontalWheel: 'pan', + pinch: 'zoom', + }, + zoomSensitivity: { + in: 0.5, + out: 0.5, + }, + minRange: 5_000, + maxRange: 1000 * 60 * 60 * 24 * 365, + }, + }, +}); +``` + +每个交互都可以接受 `'zoom'`、`'pan'` 或 `'pass-through'`。`pinch` 代表浏览器按住 Ctrl 键的滚轮触控板手势。`zoomSensitivity.in` 和 `zoomSensitivity.out` 分别独立地乘以放大和缩小的速度:`1` 是默认值,较低的值更平缓,`0` 则在该方向禁用缩放。小的触控板滚动差值会被自动平滑处理。`minRange` 和 `maxRange` 是以毫秒为单位的时长;最小值默认为 5 秒,最大值在未配置时不受限制,因此设置 `maxRange` 可以限制用户可以缩小的程度。请参阅交互式的 [Camera interactions Storybook 示例](https://preview.gravity-ui.com/timeline/?path=/story/components-timelinecanvas--interaction-and-focus)。 + ### 区域结构 每个区域需要以下结构: @@ -121,7 +167,7 @@ const MyTimelineComponent = () => { { id: 'afternoon', from: Date.now() + 1800000, - // 未指定 'to' - 延伸至时间轴结束 + // 未指定 'to' - 延伸至时间轴末尾 color: 'rgba(76, 175, 80, 0.2)', // 半透明绿色 hoverColor: 'rgba(76, 175, 80, 0.3)' } @@ -129,7 +175,7 @@ const MyTimelineComponent = () => { }, viewConfiguration: { sections: { - hitboxPadding: 2 // 悬停检测填充区域 + hitboxPadding: 2 // 鼠标悬停检测的内边距 } } }); @@ -140,26 +186,26 @@ const MyTimelineComponent = () => { ### 标记结构 -每个标记需要以下结构: +每个标记都需要遵循以下结构: ```typescript type TimelineMarker = { time: number; // 标记位置的时间戳 color: string; // 标记线的颜色 - activeColor: string; // 标记被选中时的颜色 (必需) - hoverColor: string; // 标记悬停时的颜色 (必需) - lineWidth?: number; // 可选的标记线宽度 + activeColor: string; // 标记被选中时的颜色(必需) + hoverColor: string; // 标记鼠标悬停时的颜色(必需) + lineWidth?: number; // 标记线的可选宽度 label?: string; // 可选的标签文本 labelColor?: string; // 可选的标签颜色 renderer?: AbstractMarkerRenderer; // 可选的自定义渲染器 nonSelectable?: boolean;// 标记是否可被选中 - group?: boolean; // 标记是否代表一个组 + group?: boolean; // 标记是否代表一个分组 }; ``` -### 标记分组和缩放 +### 标记分组与缩放 -时间轴会自动将彼此靠近的标记进行分组,并提供缩放功能: +时间轴会自动将距离较近的标记进行分组,并提供缩放功能: ```tsx const MyTimelineComponent = () => { @@ -179,8 +225,8 @@ const MyTimelineComponent = () => { viewConfiguration: { markers: { collapseMinDistance: 8, // 将相距 8 像素内的标记分组 - groupZoomEnabled: true, // 点击分组时启用缩放 - groupZoomPadding: 0.3, // 分组周围的填充为 30% + groupZoomEnabled: true, // 启用点击分组进行缩放 + groupZoomPadding: 0.3, // 分组周围的 30% 填充 groupZoomMaxFactor: 0.3, // 最大缩放因子 } } @@ -201,12 +247,12 @@ const MyTimelineComponent = () => { ### 组件架构 -时间轴实现为一个 React 组件,可以通过两个主要对象进行配置: +时间轴被实现为一个 React 组件,可以通过两个主要对象进行配置: 1. **TimelineSettings**: 控制时间轴的核心行为和外观 - `start`: 时间轴的开始时间 - `end`: 时间轴的结束时间 - - `axes`: 轴配置数组(参见下方结构) + - `axes`: 轴配置数组(见下方结构) - `events`: 事件配置数组 - `markers`: 标记配置数组 - `sections`: 区段配置数组 @@ -221,7 +267,7 @@ const MyTimelineComponent = () => { - `on-click`: 点击时间轴时触发 - `on-context-click`: 右键点击/上下文菜单时触发 -- `on-select-change`: 选择更改时触发 +- `on-select-change`: 选择发生变化时触发 - `on-hover`: 鼠标悬停在时间轴元素上时触发 - `on-leave`: 鼠标离开时间轴元素时触发 @@ -247,22 +293,42 @@ const MyTimelineComponent = () => { ### React 集成 -该组件使用自定义钩子来管理时间轴: +该组件使用自定义 Hook 来管理时间轴: - `useTimeline`: 管理时间轴实例及其生命周期 - 创建并初始化时间轴 - - 在组件卸载时处理清理 + - 在组件卸载时处理清理工作 - 提供对时间轴实例的访问 - `useTimelineEvent`: 处理事件订阅和清理 - 管理事件监听器的生命周期 - - 在卸载时自动清理监听器 + - 在组件卸载时自动清理监听器 + +组件会自动处理时间轴实例的清理和销毁。 + +### 事件弹出框 + +安装 `@gravity-ui/uikit` 及其样式,即可显示事件详情,无需手动订阅悬停事件或计算坐标: -当组件卸载时,该组件会自动处理时间轴实例的清理和销毁。 +```tsx +import '@gravity-ui/uikit/styles/fonts.css'; +import '@gravity-ui/uikit/styles/styles.css'; +import {EventPopup} from '@gravity-ui/timeline/react/uikit'; + +<> + + } + /> + +``` + +`EventPopup` 在 150 毫秒后打开,并在鼠标指针离开事件 200 毫秒后关闭。如有需要,可设置 `openDelay`、`closeDelay`、`placement`、`offset`、`className` 或 `aria-label`。当其内容具有指针或焦点时,弹出框将保持打开状态,按 Escape 键或点击外部区域即可关闭。当事件重叠时,它会使用数据顺序中的最后一个事件。`hoverColor` 和 `isHovered` 控制事件的绘制;`EventPopup` 则负责其详情 UI。 ### 事件结构 -时间轴中的事件遵循此结构: +时间轴中的事件遵循以下结构: ```typescript type TimelineEvent = { @@ -270,27 +336,82 @@ type TimelineEvent = { from: number; // 开始时间戳 to?: number; // 结束时间戳(点事件可选) axisId: string; // 事件所属的轴的 ID - trackIndex: number; // 轴轨道中的索引 + trackIndex: number; // 事件在轴轨道中的索引 renderer?: AbstractEventRenderer; // 可选的自定义渲染器 color?: string; // 可选的事件颜色 - selectedColor?: string; // 可选的选中状态颜色 + hoverColor?: string; // 可选的鼠标悬停时的颜色 + selectedColor?: string; // 可选的选中状态的颜色 + cursor?: string; // 可选的鼠标悬停在事件上时的 CSS 光标 }; ``` -### 直接 TypeScript 用法 +为执行点击操作的事件设置 `cursor: 'pointer'`。光标仅在指针悬停在该事件上时应用;当事件重叠时,数据顺序中的最后一个事件决定了光标。 + +### Gravity UI 颜色 + +Canvas 本身无法解析 CSS 自定义属性。Timeline 会在其 canvas 元素上解析完整的 `var(--token)` 值,因此 Gravity UI 的语义化 token 可以用于内置事件、标记、区域、轴、网格和标尺。 + +```tsx +import '@gravity-ui/uikit/styles/fonts.css'; +import '@gravity-ui/uikit/styles/styles.css'; +import {ThemeProvider} from '@gravity-ui/uikit'; +import {useTimeline} from '@gravity-ui/timeline/react'; +import {GravityTimelineCanvas} from '@gravity-ui/timeline/react/uikit'; + + + + +``` + +可以直接在任何颜色字段中传递 token,例如 `color: 'var(--g-color-base-positive-medium)'`。`GravityTimelineCanvas` 会在 Gravity UI 主题生效时自动重绘。对于缺失的 token,可以使用 CSS 回退值,例如 `var(--app-event-color, transparent)`,或者从自定义渲染器中调用 `timeline.api.resolveColor(color, fallback)`。 + +对于事件,`color` 用于正常状态,`hoverColor` 用于鼠标悬停时,`selectedColor` 用于选中后: + +```ts +const events = [ + { + id: 'deploy', + from: start, + to: end, + axisId: 'main', + trackIndex: 0, + color: 'var(--g-color-base-positive-medium)', + hoverColor: 'var(--g-color-base-positive-medium-hover)', + selectedColor: 'var(--g-color-base-positive-heavy)', + }, +]; +``` + +自定义事件渲染器会接收 `resolveColor` 作为其最后一个可选参数;自定义标记和区域渲染器会在其渲染数据中接收它。 + +### Canvas 字体 + +一次性设置 `viewConfiguration.font` 来配置标尺、事件和标记的默认字体。组件特定的 `ruler.font`、`events.font` 或 `markers.font` 会优先。默认值为 `10px sans-serif`。 -Timeline 类可以直接在 TypeScript 中使用,无需 React。这对于与框架或其他 JavaScript 应用程序集成非常有用: +Canvas 不能直接在 `ctx.font` 中使用 CSS 变量或 `inherit`,因此 Timeline 会在 canvas 的 CSS 上下文中解析完整的 token 值: + +```ts +viewConfiguration: { + font: 'var(--g-text-caption-2-font)', +} +``` + +使用 `font: 'inherit'` 来使用 canvas 元素的计算字体。自定义渲染器会接收 `resolveFont` 和 `resolveColor`,或者可以调用 `timeline.api.resolveFont(font)`。动态加载的 Web 字体加载完成后,请调用 `timeline.api.rerender()` 来用新字体重绘 canvas 文本。 + +### 直接使用 TypeScript + +Timeline 类可以直接在 TypeScript 中使用,无需 React。这对于与其它框架或原生 JavaScript 应用程序集成非常有用: ```typescript import { Timeline } from '@gravity-ui/timeline'; const timestamp = Date.now(); -// 创建一个时间轴实例 +// 创建一个 timeline 实例 const timeline = new Timeline({ settings: { start: timestamp, - end: timestamp + 3600000, // 从现在开始 1 小时 + end: timestamp + 3600000, // 1 小时后 axes: [ { id: 'main', @@ -302,17 +423,17 @@ const timeline = new Timeline({ events: [ { id: 'event1', - from: timestamp + 1800000, // 从现在开始 30 分钟 - to: timestamp + 2400000, // 从现在开始 40 分钟 - label: '示例事件', + from: timestamp + 1800000, // 30 分钟后 + to: timestamp + 2400000, // 40 分钟后 + label: 'Sample Event', axisId: 'main' } ], markers: [ { id: 'marker1', - time: timestamp + 1200000, // 从现在开始 20 分钟 - label: '重要节点', + time: timestamp + 1200000, // 20 分钟后 + label: 'Important Point', color: '#ff0000', activeColor: '#ff5252', hoverColor: '#ff1744' @@ -323,7 +444,7 @@ const timeline = new Timeline({ id: 'section1', from: timestamp, to: timestamp + 1800000, // 前 30 分钟 - color: 'rgba(33, 150, 243, 0.2)', // 浅蓝色背景 + color: 'rgba(33, 150, 243, 0.2)', // 淡蓝色背景 hoverColor: 'rgba(33, 150, 243, 0.3)' } ] @@ -336,7 +457,7 @@ const timeline = new Timeline({ } }); -// 使用 canvas 元素初始化 +// 使用 canvas 元素进行初始化 const canvas = document.querySelector('canvas'); if (canvas instanceof HTMLCanvasElement) { timeline.init(canvas); @@ -344,14 +465,14 @@ if (canvas instanceof HTMLCanvasElement) { // 添加事件监听器 timeline.on('on-click', (detail) => { - console.log('时间轴被点击:', detail); + console.log('Timeline clicked:', detail); }); timeline.on('on-select-change', (detail) => { - console.log('选择已更改:', detail); + console.log('Selection changed:', detail); }); -// 完成后清理 +// 完成后进行清理 timeline.destroy(); ``` @@ -361,11 +482,9 @@ Timeline 类提供了一个丰富的 API 来管理时间轴: ```typescript // 添加事件监听器 timeline.on('eventClick', (detail) => { - console.log('事件被点击:', detail); + console.log('Event clicked:', detail); }); -``` -```markdown // 移除事件监听器 const handler = (detail) => console.log(detail); timeline.on('eventClick', handler); @@ -383,7 +502,7 @@ Timeline 类提供了一个丰富的 API 来管理时间轴: id: 'newEvent', from: Date.now(), to: Date.now() + 3600000, - label: '新事件', + label: 'New Event', axisId: 'main', trackIndex: 0 } @@ -398,13 +517,15 @@ Timeline 类提供了一个丰富的 API 来管理时间轴: height: 80 } ]); +``` +```javascript // 更新标记 timeline.api.setMarkers([ { id: 'newMarker', time: Date.now(), - label: '新标记', + label: 'New Marker', color: '#00ff00', activeColor: '#4caf50', hoverColor: '#2e7d32' @@ -433,7 +554,8 @@ Timeline 类提供了一个丰富的 API 来管理时间轴: - [基础时间轴](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--basic) - 带有事件和轴的简单时间轴 - [无限时间轴](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--endless-timelines) - 带有事件和轴的无限时间轴 - [标记](https://preview.gravity-ui.com/timeline/?path=/story/timeline-markers--basic) - 带垂直标记和标签的时间轴 -- [自定义事件](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--custom-renderer) - 带自定义事件渲染的时间轴 +- [相机交互](https://preview.gravity-ui.com/timeline/?path=/story/components-timelinecanvas--interaction-and-focus) - 配置滚轮、水平滚动和触控板捏合行为 +- [自定义事件](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--custom-renderer) - 带有自定义事件渲染的时间轴 - [集成](https://preview.gravity-ui.com/timeline/?path=/story/integrations-gravity-ui--timeline-ruler) - RangeDateSelection、DragHandler、NestedEvents、Popup、List diff --git a/src/content/local-docs/libs/timeline/README.md b/src/content/local-docs/libs/timeline/README.md index 93ed95545bb2..5378b146152d 100644 --- a/src/content/local-docs/libs/timeline/README.md +++ b/src/content/local-docs/libs/timeline/README.md @@ -22,6 +22,7 @@ Custom rendering with expandable nested events ([NestedEvents](https://preview.g - Canvas-based rendering for high performance - Interactive timeline with zoom and pan capabilities +- Flexible wheel and trackpad interactions, including vertical scroll pass-through - Support for events, markers, sections, axes, and grid - Background sections for visual organization and time period highlighting - Smart marker grouping with automatic zoom to group - Click on grouped markers to zoom into their individual components @@ -84,6 +85,51 @@ type TimelineAxis = { }; ``` +### Horizontal Axis Lines + +Configure horizontal line placement through `viewConfiguration.axes.linePosition`: + +- `"center"` (default) draws a line through the center of every track. +- `"between"` draws a line after every track, at its bottom boundary. This is useful for table-style rows with centered event bars. + +```typescript +viewConfiguration: { + axes: { + linePosition: 'between' + } +} +``` + +### Flexible Camera Interactions + +`ZoomMode` provides familiar interaction presets, while `camera.interactions` lets you override an individual gesture. This is useful when a timeline lives inside a vertically scrollable page: keep horizontal pan and trackpad zoom, but let normal wheel scrolling reach the parent container. + +```tsx +import {ZoomMode} from '@gravity-ui/timeline'; + +const {timeline} = useTimeline({ + settings: { /* ... */ }, + viewConfiguration: { + camera: { + zoom: ZoomMode.DEFAULT, + interactions: { + verticalWheel: 'pass-through', + horizontalWheel: 'pan', + pinch: 'zoom', + }, + zoomSensitivity: { + in: 0.5, + out: 0.5, + }, + minRange: 5_000, + maxRange: 1000 * 60 * 60 * 24 * 365, + }, + }, +}); +``` + +Each interaction accepts `'zoom'`, `'pan'`, or `'pass-through'`. `pinch` represents a browser's Ctrl+wheel trackpad gesture. `zoomSensitivity.in` and `zoomSensitivity.out` independently multiply zoom-in and zoom-out speed: `1` is the default, lower values are gentler, and `0` disables zoom in that direction. Small trackpad deltas are smoothed automatically. `minRange` and `maxRange` are durations in milliseconds; the minimum defaults to 5 seconds and the maximum is unrestricted unless configured, so set `maxRange` to limit how far users can zoom out. See the interactive [Camera interactions Storybook example](https://preview.gravity-ui.com/timeline/?path=/story/components-timelinecanvas--interaction-and-focus). + ### Section Structure Each section requires the following structure: @@ -260,6 +306,32 @@ The component uses custom hooks for timeline management: The component automatically handles cleanup and destruction of the timeline instance when unmounted. +### Event popup + +Install `@gravity-ui/uikit` and its styles to display event details without +subscribing to hover events or calculating coordinates yourself: + +```tsx +import '@gravity-ui/uikit/styles/fonts.css'; +import '@gravity-ui/uikit/styles/styles.css'; +import {EventPopup} from '@gravity-ui/timeline/react/uikit'; + +<> + + } + /> + +``` + +`EventPopup` opens after 150 ms and closes 200 ms after the pointer leaves the +event. Set `openDelay`, `closeDelay`, `placement`, `offset`, `className`, or +`aria-label` when needed. The popup remains open while its content has pointer +or focus, closes on Escape or outside click, and uses the last event in data +order when events overlap. `hoverColor` and `isHovered` control event drawing; +`EventPopup` controls its details UI. + ### Event Structure Events in the timeline follow this structure: @@ -273,10 +345,81 @@ type TimelineEvent = { trackIndex: number; // Index in the axis track renderer?: AbstractEventRenderer; // Optional custom renderer color?: string; // Optional event color + hoverColor?: string; // Optional hovered state color selectedColor?: string; // Optional selected state color + cursor?: string; // Optional CSS cursor while hovering the event }; ``` +Set `cursor: 'pointer'` on events that perform an action on click. The cursor +is applied only while the pointer is over that event; when events overlap, the +last event in data order determines the cursor. + +### Gravity UI colors + +Canvas cannot resolve CSS custom properties by itself. Timeline resolves a +whole-value `var(--token)` against its canvas element, so Gravity UI semantic +tokens work for built-in events, markers, sections, axes, grid, and ruler. + +```tsx +import '@gravity-ui/uikit/styles/fonts.css'; +import '@gravity-ui/uikit/styles/styles.css'; +import {ThemeProvider} from '@gravity-ui/uikit'; +import {useTimeline} from '@gravity-ui/timeline/react'; +import {GravityTimelineCanvas} from '@gravity-ui/timeline/react/uikit'; + + + + +``` + +Pass tokens directly in any color field, for example +`color: 'var(--g-color-base-positive-medium)'`. `GravityTimelineCanvas` +automatically redraws when the effective Gravity UI theme changes. For a +missing token, use a CSS fallback such as `var(--app-event-color, transparent)` +or call `timeline.api.resolveColor(color, fallback)` from a custom renderer. + +For events, `color` is used normally, `hoverColor` on pointer hover, and +`selectedColor` after selection: + +```ts +const events = [ + { + id: 'deploy', + from: start, + to: end, + axisId: 'main', + trackIndex: 0, + color: 'var(--g-color-base-positive-medium)', + hoverColor: 'var(--g-color-base-positive-medium-hover)', + selectedColor: 'var(--g-color-base-positive-heavy)', + }, +]; +``` + +Custom event renderers receive `resolveColor` as their final optional argument; +custom marker and section renderers receive it in their render data. + +### Canvas fonts + +Set `viewConfiguration.font` once to configure the default font for ruler, +events, and markers. A component-specific `ruler.font`, `events.font`, or +`markers.font` takes precedence. The default remains `10px sans-serif`. + +Canvas cannot use CSS variables or `inherit` directly in `ctx.font`, so +Timeline resolves whole-value tokens in the canvas CSS context: + +```ts +viewConfiguration: { + font: 'var(--g-text-caption-2-font)', +} +``` + +Use `font: 'inherit'` to use the computed font of the canvas element. Custom +renderers receive `resolveFont` alongside `resolveColor`, or can call +`timeline.api.resolveFont(font)`. After a web font loads dynamically, call +`timeline.api.rerender()` to redraw canvas text with it. + ### Direct TypeScript Usage The Timeline class can be used directly in TypeScript without React. This is useful for integrating with other frameworks or vanilla JavaScript applications: @@ -431,6 +574,7 @@ Explore interactive examples in our [Storybook](https://preview.gravity-ui.com/t - [Basic Timeline](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--basic) - Simple timeline with events and axes - [Endless Timeline](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--endless-timelines) - Endless timeline with events and axes - [Markers](https://preview.gravity-ui.com/timeline/?path=/story/timeline-markers--basic) - Timeline with vertical markers and labels +- [Camera interactions](https://preview.gravity-ui.com/timeline/?path=/story/components-timelinecanvas--interaction-and-focus) - Configure wheel, horizontal scroll, and trackpad pinch behavior - [Custom Events](https://preview.gravity-ui.com/timeline/?path=/story/timeline-events--custom-renderer) - Timeline with custom event rendering - [Integrations](https://preview.gravity-ui.com/timeline/?path=/story/integrations-gravity-ui--timeline-ruler) - RangeDateSelection, DragHandler, NestedEvents, Popup, List