diff --git a/package-lock.json b/package-lock.json index ecf8880..50720b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -45,13 +45,13 @@ "@rdlabo/capacitor-screenshot-event": "8.0.0", "@rdlabo/eslint-plugin-rules": "22.1.0", "@rdlabo/ionic-angular-collect-icons": "3.0.0", - "@rdlabo/ionic-angular-kit": "22.0.0", - "@rdlabo/ionic-angular-photo-editor": "22.0.0", - "@rdlabo/ionic-angular-scroll-header": "22.0.0", + "@rdlabo/ionic-angular-kit": "22.0.3", + "@rdlabo/ionic-angular-photo-editor": "22.0.3", + "@rdlabo/ionic-angular-scroll-header": "22.0.3", "@rdlabo/ionic-theme-ios26": "9.2.0", "@rdlabo/ionic-theme-ios27": "0.1.0-1", "@rdlabo/ionic-theme-md3": "9.1.0", - "@rdlabo/ngx-cdk-scroll-strategies": "22.0.0", + "@rdlabo/ngx-cdk-scroll-strategies": "22.0.3", "@rdlabo/workers-hono-kit": "0.12.2", "@rdlabo/workers-mysql": "0.12.2", "@rdlabo/workers-timezone": "0.12.2", @@ -5173,9 +5173,9 @@ } }, "node_modules/@rdlabo/ionic-angular-kit": { - "version": "22.0.0", - "resolved": "https://registry.npmjs.org/@rdlabo/ionic-angular-kit/-/ionic-angular-kit-22.0.0.tgz", - "integrity": "sha512-u/+dlI2DH0yNTBQD6QnOZl7ES5zeVB5VHaUqYMzYae+odGq29e3qMUIoLHRmgztKZIfp+dX9bz8eHaklCO389A==", + "version": "22.0.3", + "resolved": "https://registry.npmjs.org/@rdlabo/ionic-angular-kit/-/ionic-angular-kit-22.0.3.tgz", + "integrity": "sha512-n24V29oPrcq54VvL+muHS3Q4hfnrTGrMbfo5TUEZ3yQyqtj9D7zWMWzt98OCi3qusLxrwcFcILF3aOkFXXPxDw==", "dev": true, "dependencies": { "tslib": "^2.3.0" @@ -5186,7 +5186,6 @@ "@angular/forms": ">=21.0.0 <23.0.0", "@angular/router": ">=21.0.0 <23.0.0", "@angular/service-worker": ">=21.0.0 <23.0.0", - "@capacitor-community/apple-sign-in": "*", "@capacitor-community/facebook-login": ">=8.0.0 <9.0.0", "@capacitor-community/in-app-review": ">=7.0.0 <9.0.0", "@capacitor/app": ">=7.0.0 <9.0.0", @@ -5196,6 +5195,8 @@ "@capacitor/network": ">=7.0.0 <9.0.0", "@capacitor/preferences": ">=7.0.0 <9.0.0", "@capacitor/status-bar": ">=7.0.0 <9.0.0", + "@capawesome/capacitor-apple-sign-in": ">=0.1.4 <1", + "@capawesome/capacitor-google-sign-in": "^0.1.4", "@capawesome/capacitor-live-update": ">=7.0.0 <9.0.0", "@ionic/angular": "^9.0.0", "@ionic/storage-angular": "^4.0.0", @@ -5209,9 +5210,6 @@ "@angular/service-worker": { "optional": true }, - "@capacitor-community/apple-sign-in": { - "optional": true - }, "@capacitor-community/facebook-login": { "optional": true }, @@ -5224,6 +5222,12 @@ "@capacitor/status-bar": { "optional": true }, + "@capawesome/capacitor-apple-sign-in": { + "optional": true + }, + "@capawesome/capacitor-google-sign-in": { + "optional": true + }, "@capawesome/capacitor-live-update": { "optional": true }, @@ -5242,9 +5246,9 @@ } }, "node_modules/@rdlabo/ionic-angular-photo-editor": { - "version": "22.0.0", - "resolved": "https://registry.npmjs.org/@rdlabo/ionic-angular-photo-editor/-/ionic-angular-photo-editor-22.0.0.tgz", - "integrity": "sha512-UnSAnd6B0QvSoxn1XSW/myzVvt5lbynN4b+QtlWWEOk5MkyNeweYlwNxfJYYcZxsSbj2jtHVk2/5qVeuFs4vWw==", + "version": "22.0.3", + "resolved": "https://registry.npmjs.org/@rdlabo/ionic-angular-photo-editor/-/ionic-angular-photo-editor-22.0.3.tgz", + "integrity": "sha512-vJXYXV0CJB1bTJR8KHHqGED4Bc1HEFfowI60G8KOZN42AQsj6xJPL0cFT7cgGIc7sPh/KcUo3uYUbaEpXdqe+A==", "dev": true, "dependencies": { "tslib": "^2.3.0" @@ -5275,9 +5279,9 @@ } }, "node_modules/@rdlabo/ionic-angular-scroll-header": { - "version": "22.0.0", - "resolved": "https://registry.npmjs.org/@rdlabo/ionic-angular-scroll-header/-/ionic-angular-scroll-header-22.0.0.tgz", - "integrity": "sha512-JJDLZ6f9tyh1/lo5YefvHNaAGAp3K4qresm7MJ8HlbJgq+mZSb5B2Zq8v5aUQlE/rqIT5MGHg3Ji+MBWi2EBJw==", + "version": "22.0.3", + "resolved": "https://registry.npmjs.org/@rdlabo/ionic-angular-scroll-header/-/ionic-angular-scroll-header-22.0.3.tgz", + "integrity": "sha512-36fmMYL7Cow75YZnWFHk8sZXWLIwwDuil+1WWAucK3kQBT0Q6TBj3mBBiy0Dno+/8prQFQaUv7sLa9h9M35GTg==", "dev": true, "dependencies": { "tslib": "^2.3.0" @@ -5320,9 +5324,9 @@ } }, "node_modules/@rdlabo/ngx-cdk-scroll-strategies": { - "version": "22.0.0", - "resolved": "https://registry.npmjs.org/@rdlabo/ngx-cdk-scroll-strategies/-/ngx-cdk-scroll-strategies-22.0.0.tgz", - "integrity": "sha512-lWeIpJK8/2pMSO4o2KPzv9fVo3DXMDM7cEN7jvORqPalUpW5fI0PLuxmIvkrE/5AJyP3xwp3faq88Bele0Heew==", + "version": "22.0.3", + "resolved": "https://registry.npmjs.org/@rdlabo/ngx-cdk-scroll-strategies/-/ngx-cdk-scroll-strategies-22.0.3.tgz", + "integrity": "sha512-K040VFA5EGQaReEOVmWiwYdUbsJt9+pL1ncpBd+7hlgVBBVjEX+udKOpM9ZhsO63iCMIn+FWi6Vy3xv+uXes0w==", "dev": true, "dependencies": { "tslib": "^2.3.0" diff --git a/package.json b/package.json index e0de336..8f103ae 100644 --- a/package.json +++ b/package.json @@ -79,13 +79,13 @@ "@rdlabo/capacitor-screenshot-event": "8.0.0", "@rdlabo/eslint-plugin-rules": "22.1.0", "@rdlabo/ionic-angular-collect-icons": "3.0.0", - "@rdlabo/ionic-angular-kit": "22.0.0", - "@rdlabo/ionic-angular-photo-editor": "22.0.0", - "@rdlabo/ionic-angular-scroll-header": "22.0.0", + "@rdlabo/ionic-angular-kit": "22.0.3", + "@rdlabo/ionic-angular-photo-editor": "22.0.3", + "@rdlabo/ionic-angular-scroll-header": "22.0.3", "@rdlabo/ionic-theme-ios26": "9.2.0", "@rdlabo/ionic-theme-ios27": "0.1.0-1", "@rdlabo/ionic-theme-md3": "9.1.0", - "@rdlabo/ngx-cdk-scroll-strategies": "22.0.0", + "@rdlabo/ngx-cdk-scroll-strategies": "22.0.3", "@rdlabo/workers-hono-kit": "0.12.2", "@rdlabo/workers-mysql": "0.12.2", "@rdlabo/workers-timezone": "0.12.2", diff --git a/projects/docs/src/app/docs/plugin-index.spec.ts b/projects/docs/src/app/docs/plugin-index.spec.ts index ae9d10e..3745b0c 100644 --- a/projects/docs/src/app/docs/plugin-index.spec.ts +++ b/projects/docs/src/app/docs/plugin-index.spec.ts @@ -172,7 +172,7 @@ describe('PluginIndexComponent', () => { 'primaryHyperdrive', ); const ionic = await loadProject('ionic-angular-kit', 'ja'); - expect(ionic?.version).toBe('22.0.0'); + expect(ionic?.version).toBe('22.0.3'); expect(ionic?.pages.find((page) => page.slug === 'offline-realtime')?.html).toContain( 'createOfflineAuthBridge', ); diff --git a/projects/docs/src/app/generated/project-catalog.generated.ts b/projects/docs/src/app/generated/project-catalog.generated.ts index ce8df68..edd8f1d 100644 --- a/projects/docs/src/app/generated/project-catalog.generated.ts +++ b/projects/docs/src/app/generated/project-catalog.generated.ts @@ -133,7 +133,7 @@ export const PROJECTS_EN = [ "repositoryUrl": "https://github.com/rdlabo-dev/ionic-angular-library", "category": "frontend-tools", "icon": "app", - "version": "22.0.0", + "version": "22.0.3", "description": "Shared application infrastructure for Ionic Angular projects.", "headline": "Build consistent, resilient Ionic Angular applications", "overview": "Save typed preferences, open modals with typed results, and connect Ionic controls to Angular Signal Forms. Add authentication and native features as your app grows.", @@ -230,7 +230,7 @@ export const PROJECTS_EN = [ "demoUrl": "https://rdlabo-ionic-angular-library.netlify.app/main/photo-editor", "category": "frontend-tools", "icon": "app", - "version": "22.0.0", + "version": "22.0.3", "description": "Photo editing and viewing flows for Ionic Angular and Capacitor applications.", "headline": "Edit and review photos in Ionic modals", "overview": "Load photos from camera or album, crop and edit images, and present a configurable photo viewer from one Ionic Angular package.", @@ -316,7 +316,7 @@ export const PROJECTS_EN = [ "demoUrl": "https://rdlabo-ionic-angular-library.netlify.app/main/scroll-header", "category": "frontend-tools", "icon": "app", - "version": "22.0.0", + "version": "22.0.3", "description": "Scroll-aware header directives for Ionic and Angular CDK viewports.", "headline": "Build headers that respond to content scrolling", "overview": "Hide and reveal Ionic headers for IonContent and CDK virtual scrolling while preserving safe-area and native-header layouts.", @@ -395,7 +395,7 @@ export const PROJECTS_EN = [ "demoUrl": "https://rdlabo-ionic-angular-library.netlify.app/main/scroll-strategies", "category": "frontend-tools", "icon": "app", - "version": "22.0.0", + "version": "22.0.3", "seoTitle": "Angular CDK Virtual Scroll: Variable Item Heights | rdlabo", "description": "Use Angular CDK virtual scroll with variable or dynamic item heights. Supply exact per-item sizes for stable lists, chat UIs, and reverse scrolling.", "headline": "Angular CDK virtual scroll with variable item heights", @@ -2294,7 +2294,7 @@ export const PROJECTS_JA = [ "repositoryUrl": "https://github.com/rdlabo-dev/ionic-angular-library", "category": "frontend-tools", "icon": "app", - "version": "22.0.0", + "version": "22.0.3", "description": "Ionic Angularプロジェクト向けの共有アプリケーション基盤。", "headline": "一貫性と耐障害性を備えたIonic Angularアプリを構築する", "overview": "型安全な設定の保存、戻り値に型が付くモーダル、IonicとAngular Signal Formsの連携から始められます。認証やNative機能は必要に応じて追加できます。", @@ -2391,7 +2391,7 @@ export const PROJECTS_JA = [ "demoUrl": "https://rdlabo-ionic-angular-library.netlify.app/main/photo-editor", "category": "frontend-tools", "icon": "app", - "version": "22.0.0", + "version": "22.0.3", "description": "Ionic Angular・Capacitorアプリ向けの写真編集・閲覧フロー。", "headline": "Ionic Modalで写真を編集・確認する", "overview": "カメラやアルバムから写真を読み込み、切り抜き・編集し、設定可能な写真Viewerを1つのIonic Angularパッケージから利用できます。", @@ -2477,7 +2477,7 @@ export const PROJECTS_JA = [ "demoUrl": "https://rdlabo-ionic-angular-library.netlify.app/main/scroll-header", "category": "frontend-tools", "icon": "app", - "version": "22.0.0", + "version": "22.0.3", "description": "Ionic・Angular CDK viewport向けのScroll連動Header Directive。", "headline": "Scrollに追従するHeaderを実装する", "overview": "Safe AreaとNative Headerレイアウトを維持しながら、IonContentとCDK Virtual Scrollに応じてIonic Headerを表示・非表示にします。", @@ -2556,7 +2556,7 @@ export const PROJECTS_JA = [ "demoUrl": "https://rdlabo-ionic-angular-library.netlify.app/main/scroll-strategies", "category": "frontend-tools", "icon": "app", - "version": "22.0.0", + "version": "22.0.3", "seoTitle": "Angular CDK Virtual Scrollの可変アイテム高さ対応 | rdlabo", "description": "Angular CDK Virtual Scrollで可変・動的なItem Heightに対応。Itemごとの正確なSizeにより、List、Chat UI、Reverse Scrollを安定させます。", "headline": "Angular CDK Virtual Scrollを可変Item Heightに対応", diff --git a/projects/docs/src/app/generated/projects/ionic-angular-kit.en.generated.ts b/projects/docs/src/app/generated/projects/ionic-angular-kit.en.generated.ts index e9afef5..41a5329 100644 --- a/projects/docs/src/app/generated/projects/ionic-angular-kit.en.generated.ts +++ b/projects/docs/src/app/generated/projects/ionic-angular-kit.en.generated.ts @@ -8,7 +8,7 @@ export const PROJECT = { "repositoryUrl": "https://github.com/rdlabo-dev/ionic-angular-library", "category": "frontend-tools", "icon": "app", - "version": "22.0.0", + "version": "22.0.3", "description": "Shared application infrastructure for Ionic Angular projects.", "headline": "Build consistent, resilient Ionic Angular applications", "overview": "Save typed preferences, open modals with typed results, and connect Ionic controls to Angular Signal Forms. Add authentication and native features as your app grows.", @@ -290,7 +290,7 @@ export const PROJECT = { "file": "api.md", "section": "Reference", "path": "/projects/ionic-angular-kit/docs/api", - "html": "
Public entry-point map for @rdlabo/ionic-angular-kit v22.0.0. The focused guide pages describe lifecycle and integration requirements; this page defines which package path owns each API family.
module @rdlabo/ionic-angular-kit| API family | \nPrincipal exports | \nGuide | \n
|---|---|---|
| Storage | \nKitStorageService, kitClearStoragePreservingKeys | \nStorage and Overlays | \n
| Overlay | \nprovideKitOverlay, KitOverlayController, KitLoadingController | \nStorage and Overlays | \n
| Authentication | \nprovideKitAuth, guard functions, KitAuthAccessService | \nAuthentication and HTTP | \n
| HTTP | \nprovideKitHttp, kitAuthInterceptor | \nAuthentication and HTTP | \n
| Realtime | \nKitRealtimeConnection, KitRealtimeLivenessWatchdog | \nOffline and Realtime | \n
module @rdlabo/ionic-angular-kit/offlineOffline repository, synchronized replica, outbox, request-policy, schema, identity, and recovery APIs. Use provideOffline or its focused provider variants as the composition root.
module @rdlabo/ionic-angular-kit/formsKitIonicFormField, provideKitIonicSignalForms, kitDefaultSignalFormErrorMessage, KIT_SIGNAL_FORM_ERROR_MESSAGE_RESOLVER, and KitSignalFormErrorMessageResolver for adapting Angular 22 Signal Forms to Ionic controls. See Forms.
module @rdlabo/ionic-angular-kit/auth-firebaseFirebase authentication providers and typed sign-in, sign-up, linking, reauthentication, verification, password, and account-update functions.
module @rdlabo/ionic-angular-kit/auth-firebase/socialApple and Facebook social authentication helpers and their response and option types.
module @rdlabo/ionic-angular-kit/app-updateprovideKitAppUpdate and KitAppUpdateService for coordinated Angular service-worker updates.
module @rdlabo/ionic-angular-kit/live-updateprovideLiveUpdateReadiness for coordinating Live Update startup readiness.
module @rdlabo/ionic-angular-kit/printerPDF layout, DOM-to-PNG, preview, download, rotation, paper-size, and Brother Print settings helpers.
module @rdlabo/ionic-angular-kit/reviewkitRequestReview and KitRequestReviewOptions for native review prompts.
module @rdlabo/ionic-angular-kit/themeprovideKitTheme, KitThemeController, KitThemeConfig, and KitThemeMode for persisted theme selection.
Public entry-point map for @rdlabo/ionic-angular-kit v22.0.3. The focused guide pages describe lifecycle and integration requirements; this page defines which package path owns each API family.
module @rdlabo/ionic-angular-kit| API family | \nPrincipal exports | \nGuide | \n
|---|---|---|
| Storage | \nKitStorageService, kitClearStoragePreservingKeys | \nStorage and Overlays | \n
| Overlay | \nprovideKitOverlay, KitOverlayController, KitLoadingController | \nStorage and Overlays | \n
| Authentication | \nprovideKitAuth, guard functions, KitAuthAccessService | \nAuthentication and HTTP | \n
| HTTP | \nprovideKitHttp, kitAuthInterceptor | \nAuthentication and HTTP | \n
| Realtime | \nKitRealtimeConnection, KitRealtimeLivenessWatchdog | \nOffline and Realtime | \n
module @rdlabo/ionic-angular-kit/offlineOffline repository, synchronized replica, outbox, request-policy, schema, identity, and recovery APIs. Use provideOffline or its focused provider variants as the composition root.
module @rdlabo/ionic-angular-kit/formsKitIonicFormField, provideKitIonicSignalForms, kitDefaultSignalFormErrorMessage, KIT_SIGNAL_FORM_ERROR_MESSAGE_RESOLVER, and KitSignalFormErrorMessageResolver for adapting Angular 22 Signal Forms to Ionic controls. See Forms.
module @rdlabo/ionic-angular-kit/auth-firebaseFirebase authentication providers and typed sign-in, sign-up, linking, reauthentication, verification, password, and account-update functions.
module @rdlabo/ionic-angular-kit/auth-firebase/socialApple and Facebook social authentication helpers and their response and option types.
module @rdlabo/ionic-angular-kit/app-updateprovideKitAppUpdate and KitAppUpdateService for coordinated Angular service-worker updates.
module @rdlabo/ionic-angular-kit/live-updateprovideLiveUpdateReadiness for coordinating Live Update startup readiness.
module @rdlabo/ionic-angular-kit/printerPDF layout, DOM-to-PNG, preview, download, rotation, paper-size, and Brother Print settings helpers.
module @rdlabo/ionic-angular-kit/reviewkitRequestReview and KitRequestReviewOptions for native review prompts.
module @rdlabo/ionic-angular-kit/themeprovideKitTheme, KitThemeController, KitThemeConfig, and KitThemeMode for persisted theme selection.
@rdlabo/ionic-angular-kit v22.0.0 のpublic entry point一覧です。Lifecycleと統合要件は各Guideで説明し、このページではAPI familyを所有するpackage pathを定義します。
module @rdlabo/ionic-angular-kit| API family | \nPrincipal exports | \nGuide | \n
|---|---|---|
| Storage | \nKitStorageService, kitClearStoragePreservingKeys | \nStorage・Overlay | \n
| Overlay | \nprovideKitOverlay, KitOverlayController, KitLoadingController | \nStorage・Overlay | \n
| Authentication | \nprovideKitAuth、Guard function、KitAuthAccessService | \n認証・HTTP | \n
| HTTP | \nprovideKitHttp, kitAuthInterceptor | \n認証・HTTP | \n
| Realtime | \nKitRealtimeConnection, KitRealtimeLivenessWatchdog | \nOffline・Realtime | \n
module @rdlabo/ionic-angular-kit/offlineOffline repository、同期Replica、Outbox、Request Policy、Schema、Identity、Recovery APIです。Composition Rootには provideOffline または用途別Providerを使います。
module @rdlabo/ionic-angular-kit/formsAngular 22 Signal FormsをIonic controlへadaptする KitIonicFormField、provideKitIonicSignalForms、kitDefaultSignalFormErrorMessage、KIT_SIGNAL_FORM_ERROR_MESSAGE_RESOLVER、KitSignalFormErrorMessageResolver です。フォームも参照してください。
module @rdlabo/ionic-angular-kit/auth-firebaseFirebase Authentication Providerと、Sign-in、Sign-up、Link、Reauthentication、Verification、Password、Account更新の型付き関数です。
module @rdlabo/ionic-angular-kit/auth-firebase/socialApple・Facebook Social Authentication helperとresponse・option typeです。
module @rdlabo/ionic-angular-kit/app-updateAngular Service Workerのupdateを連携する provideKitAppUpdate と KitAppUpdateService です。
module @rdlabo/ionic-angular-kit/live-updateLive Updateの起動準備を連携する provideLiveUpdateReadiness です。
module @rdlabo/ionic-angular-kit/printerPDF Layout、DOM-to-PNG、Preview、Download、Rotation、Paper Size、Brother Print設定のhelperです。
module @rdlabo/ionic-angular-kit/reviewNative Review Prompt用の kitRequestReview と KitRequestReviewOptions です。
module @rdlabo/ionic-angular-kit/themeTheme選択を保存する provideKitTheme、KitThemeController、KitThemeConfig、KitThemeMode です。
@rdlabo/ionic-angular-kit v22.0.3 のpublic entry point一覧です。Lifecycleと統合要件は各Guideで説明し、このページではAPI familyを所有するpackage pathを定義します。
module @rdlabo/ionic-angular-kit| API family | \nPrincipal exports | \nGuide | \n
|---|---|---|
| Storage | \nKitStorageService, kitClearStoragePreservingKeys | \nStorage・Overlay | \n
| Overlay | \nprovideKitOverlay, KitOverlayController, KitLoadingController | \nStorage・Overlay | \n
| Authentication | \nprovideKitAuth、Guard function、KitAuthAccessService | \n認証・HTTP | \n
| HTTP | \nprovideKitHttp, kitAuthInterceptor | \n認証・HTTP | \n
| Realtime | \nKitRealtimeConnection, KitRealtimeLivenessWatchdog | \nOffline・Realtime | \n
module @rdlabo/ionic-angular-kit/offlineOffline repository、同期Replica、Outbox、Request Policy、Schema、Identity、Recovery APIです。Composition Rootには provideOffline または用途別Providerを使います。
module @rdlabo/ionic-angular-kit/formsAngular 22 Signal FormsをIonic controlへadaptする KitIonicFormField、provideKitIonicSignalForms、kitDefaultSignalFormErrorMessage、KIT_SIGNAL_FORM_ERROR_MESSAGE_RESOLVER、KitSignalFormErrorMessageResolver です。フォームも参照してください。
module @rdlabo/ionic-angular-kit/auth-firebaseFirebase Authentication Providerと、Sign-in、Sign-up、Link、Reauthentication、Verification、Password、Account更新の型付き関数です。
module @rdlabo/ionic-angular-kit/auth-firebase/socialApple・Facebook Social Authentication helperとresponse・option typeです。
module @rdlabo/ionic-angular-kit/app-updateAngular Service Workerのupdateを連携する provideKitAppUpdate と KitAppUpdateService です。
module @rdlabo/ionic-angular-kit/live-updateLive Updateの起動準備を連携する provideLiveUpdateReadiness です。
module @rdlabo/ionic-angular-kit/printerPDF Layout、DOM-to-PNG、Preview、Download、Rotation、Paper Size、Brother Print設定のhelperです。
module @rdlabo/ionic-angular-kit/reviewNative Review Prompt用の kitRequestReview と KitRequestReviewOptions です。
module @rdlabo/ionic-angular-kit/themeTheme選択を保存する provideKitTheme、KitThemeController、KitThemeConfig、KitThemeMode です。
Photo editor and viewer modal pages for Ionic Angular applications, with browser file selection and optional Capacitor camera/album support.
\nnpm install @rdlabo/ionic-angular-photo-editor tui-image-editor\nRegister the TUI resize adapter (required for loadPhoto resizing), then pick a file and show the returned data URL:
import { Component, inject, signal } from '@angular/core';\nimport type { ApplicationConfig } from '@angular/core';\nimport { IonButton, IonImg } from '@ionic/angular';\nimport { providePhotoEditor, PhotoLoadError } from '@rdlabo/ionic-angular-photo-editor';\nimport { createTuiImageEditor } from '@rdlabo/ionic-angular-photo-editor/editor/tui';\nimport { PhotoFileService } from '@rdlabo/ionic-angular-photo-editor/file';\n\nexport const appConfig: ApplicationConfig = {\n providers: [\n providePhotoEditor({\n maxSize: 1000,\n createImageEditor: createTuiImageEditor,\n }),\n ],\n};\n\n@Component({\n selector: 'app-photo-pick',\n imports: [IonButton, IonImg],\n template: `\n <ion-button type=\"button\" (click)=\"pick()\">Select photo</ion-button>\n @if (previewUrl()) {\n <ion-img [src]=\"previewUrl()\" alt=\"Selected photo\"></ion-img>\n }\n `,\n})\nexport class PhotoPickPage {\n private readonly photoFileService = inject(PhotoFileService);\n readonly previewUrl = signal('');\n\n async pick(): Promise<void> {\n try {\n const files = await this.photoFileService.loadPhoto({ limit: 1 });\n this.previewUrl.set(files[0] ?? '');\n } catch (error) {\n if (error instanceof PhotoLoadError && error.code === 'cancelled') {\n return;\n }\n throw error;\n }\n }\n}\nClick Select photo, choose an image — the preview appears. Native camera/album setup comes later under PhotoFileService.
\nInstall the dependencies for the features you add:
\n# viewer\nnpm install swiper\n\n# native camera and album selection\nnpm install @capacitor/camera\nFor native camera access, configure Camera permissions and the adapter in PhotoFileService. Native iOS apps require iOS/iPadOS 16.4 or later.
\n| Import path | \nExports | \n
|---|---|
@rdlabo/ionic-angular-photo-editor | \nTypes, providePhotoEditor, PHOTO_EDITOR_CONFIG, PhotoLoadError | \n
@rdlabo/ionic-angular-photo-editor/editor | \nPhotoEditorPage | \n
@rdlabo/ionic-angular-photo-editor/editor/tui | \nopt-in createTuiImageEditor adapter | \n
@rdlabo/ionic-angular-photo-editor/viewer | \nPhotoViewerPage | \n
@rdlabo/ionic-angular-photo-editor/file | \nPhotoFileService | \n
@rdlabo/ionic-angular-photo-editor/file/capacitor | \nopt-in loadCapacitorPhotoCamera adapter | \n
Import components and services only from their entry point. Import shared types and configuration from the root package.
\n| Goal | \nGuide | \n
|---|---|
| Load a photo from camera or album | \nPhotoFileService | \n
| Crop and edit in a modal | \nPhoto Editor | \n
| Browse images in a modal | \nPhoto Viewer | \n
| Override editor colors | \nTheme | \n
| Upgrade from an earlier release | \nMigration guide | \n
Photo editor and viewer modal pages for Ionic Angular applications, with browser file selection and optional Capacitor camera/album support.
\nnpm install @rdlabo/ionic-angular-photo-editor tui-image-editor\nRegister the TUI resize adapter (required for loadPhoto resizing), then pick a file and show the returned data URL:
import { Component, inject, signal } from '@angular/core';\nimport type { ApplicationConfig } from '@angular/core';\nimport { IonButton, IonImg } from '@ionic/angular';\nimport { providePhotoEditor, PhotoLoadError } from '@rdlabo/ionic-angular-photo-editor';\nimport { createTuiImageEditor } from '@rdlabo/ionic-angular-photo-editor/editor/tui';\nimport { PhotoFileService } from '@rdlabo/ionic-angular-photo-editor/file';\n\nexport const appConfig: ApplicationConfig = {\n providers: [\n providePhotoEditor({\n maxSize: 1000,\n createImageEditor: createTuiImageEditor,\n }),\n ],\n};\n\n@Component({\n selector: 'app-photo-pick',\n imports: [IonButton, IonImg],\n template: `\n <ion-button type=\"button\" (click)=\"pick()\">Select photo</ion-button>\n @if (previewUrl()) {\n <ion-img [src]=\"previewUrl()\" alt=\"Selected photo\"></ion-img>\n }\n `,\n})\nexport class PhotoPickPage {\n private readonly photoFileService = inject(PhotoFileService);\n readonly previewUrl = signal('');\n\n async pick(): Promise<void> {\n try {\n const files = await this.photoFileService.loadPhoto({ limit: 1 });\n this.previewUrl.set(files[0] ?? '');\n } catch (error) {\n if (error instanceof PhotoLoadError && error.code === 'cancelled') {\n return;\n }\n throw error;\n }\n }\n}\nClick Select photo, choose an image — the preview appears. Native camera/album setup comes later under PhotoFileService.
\nInstall the dependencies for the features you add:
\n# viewer\nnpm install swiper\n\n# native camera and album selection\nnpm install @capacitor/camera\nFor native camera access, configure Camera permissions and the adapter in PhotoFileService. Native iOS apps require iOS/iPadOS 16.4 or later.
\n| Import path | \nExports | \n
|---|---|
@rdlabo/ionic-angular-photo-editor | \nTypes, providePhotoEditor, PHOTO_EDITOR_CONFIG, PhotoLoadError | \n
@rdlabo/ionic-angular-photo-editor/editor | \nPhotoEditorPage | \n
@rdlabo/ionic-angular-photo-editor/editor/tui | \nopt-in createTuiImageEditor adapter | \n
@rdlabo/ionic-angular-photo-editor/viewer | \nPhotoViewerPage | \n
@rdlabo/ionic-angular-photo-editor/file | \nPhotoFileService | \n
@rdlabo/ionic-angular-photo-editor/file/capacitor | \nopt-in loadCapacitorPhotoCamera adapter | \n
Import components and services only from their entry point. Import shared types and configuration from the root package.
\n| Goal | \nGuide | \n
|---|---|
| Load a photo from camera or album | \nPhotoFileService | \n
| Crop and edit in a modal | \nPhoto Editor | \n
| Browse images in a modal | \nPhoto Viewer | \n
| Override editor colors | \nTheme | \n
| Upgrade from an earlier release | \nMigration guide | \n
Override the editor colors after Installation.
\nDefault colors are defined in the library stylesheet. Override them with CSS variables:
\n:root {\n --ion-photo-editor-background: #2a2a2a;\n --ion-photo-editor-background-tint: #414141;\n\n --ion-photo-editor-color: #f0f0f0;\n --ion-photo-editor-color-tint: #dbdbdb;\n\n --ion-photo-editor-primary: #4d8dff;\n --ion-photo-editor-danger: #f24c58;\n --ion-photo-editor-success: #2dd55b;\n\n --ion-photo-editor-header-button-color-on-light: #222428;\n --ion-photo-editor-header-button-color-on-dark: #f4f5f8;\n}\nSource reference: core.scss.
PhotoEditorPage and PhotoViewerPage require toolbarColorScheme: 'light' | 'dark' in modal componentProps. Select dark for a dark/black ion-toolbar and light for a light/white toolbar. The consumer must choose because the library cannot reliably infer the final toolbar appearance from CSS, translucency, or runtime theme overrides.
For @rdlabo/ionic-theme-ios26 v3, import the optional integration stylesheet after the iOS 26 theme and dark-mode styles:
@import '@rdlabo/ionic-theme-ios26/dist/css/ionic-theme-ios26.css';\n@import '@ionic/angular/css/palettes/dark.class.css';\n@import '@rdlabo/ionic-theme-ios26/dist/css/ionic-theme-ios26-dark-class.css';\n@import '@rdlabo/ionic-angular-photo-editor/css/ios26-header-button-color-scheme.css';\nUse the matching Always or System dark-mode import instead when appropriate. The photo-editor integration stylesheet must remain last so its local header scheme can override the ambient application scheme. Applications that do not use the iOS 26 theme should not import this optional stylesheet; they receive only the regular Ionic button foreground-color switch.
\n", + "html": "Override the editor colors after Installation.
\nDefault colors are defined in the library stylesheet. Override them with CSS variables:
\n:root {\n --ion-photo-editor-background: #2a2a2a;\n --ion-photo-editor-background-tint: #414141;\n\n --ion-photo-editor-color: #f0f0f0;\n --ion-photo-editor-color-tint: #dbdbdb;\n\n --ion-photo-editor-primary: #4d8dff;\n --ion-photo-editor-danger: #f24c58;\n --ion-photo-editor-success: #2dd55b;\n\n --ion-photo-editor-header-button-color-on-light: #222428;\n --ion-photo-editor-header-button-color-on-dark: #f4f5f8;\n}\nSource reference: core.scss.
PhotoEditorPage and PhotoViewerPage require toolbarColorScheme: 'light' | 'dark' in modal componentProps. Select dark for a dark/black ion-toolbar and light for a light/white toolbar. The consumer must choose because the library cannot reliably infer the final toolbar appearance from CSS, translucency, or runtime theme overrides.
For @rdlabo/ionic-theme-ios26 v3, import the optional integration stylesheet after the iOS 26 theme and dark-mode styles:
@import '@rdlabo/ionic-theme-ios26/dist/css/ionic-theme-ios26.css';\n@import '@ionic/angular/css/palettes/dark.class.css';\n@import '@rdlabo/ionic-theme-ios26/dist/css/ionic-theme-ios26-dark-class.css';\n@import '@rdlabo/ionic-angular-photo-editor/css/ios26-header-button-color-scheme.css';\nUse the matching Always or System dark-mode import instead when appropriate. The photo-editor integration stylesheet must remain last so its local header scheme can override the ambient application scheme. Applications that do not use the iOS 26 theme should not import this optional stylesheet; they receive only the regular Ionic button foreground-color switch.
\n", "headings": [ { "id": "toolbar-color-scheme", @@ -272,7 +272,7 @@ export const PROJECT = { "file": "api.md", "section": "Reference", "path": "/projects/ionic-angular-photo-editor/docs/api", - "html": "Reference for the public entry points exported by @rdlabo/ionic-angular-photo-editor v22.0.0. Import components, services, and optional implementations from their dedicated secondary entry points.
| Import path | \nPrincipal exports | \n
|---|---|
@rdlabo/ionic-angular-photo-editor | \nConfiguration, shared types, PhotoLoadError | \n
@rdlabo/ionic-angular-photo-editor/editor | \nPhotoEditorPage | \n
@rdlabo/ionic-angular-photo-editor/editor/tui | \ncreateTuiImageEditor | \n
@rdlabo/ionic-angular-photo-editor/viewer | \nPhotoViewerPage | \n
@rdlabo/ionic-angular-photo-editor/file | \nPhotoFileService | \n
@rdlabo/ionic-angular-photo-editor/file/capacitor | \nloadCapacitorPhotoCamera | \n
function providePhotoEditor(config?: PhotoEditorConfig): EnvironmentProvidersRegisters application-wide photo-loading defaults and the optional editor and camera adapters.
constant PHOTO_EDITOR_CONFIGAngular injection token containing the resolved maxSize, labels, image-editor factory, and camera loader.
interface PhotoEditorConfig| Prop | \nType | \nDescription | \nDefault | \n
|---|---|---|---|
maxSize | \nnumber | \nLongest edge in pixels after resize. | \n1000 | \n
labels | \nPartial<PhotoFileLabels> | \nCamera, album, and cancel label overrides. | \nundefined | \n
createImageEditor | \nPhotoImageEditorFactory | \nAdapter used for editing and resizing. | \n\n |
loadCamera | \nPhotoCameraLoader | \nAdapter used for native camera and album selection. | \n\n |
class PhotoFileServiceImported from @rdlabo/ionic-angular-photo-editor/file. Selects and normalizes photos from browser and Capacitor sources.
| Member | \nType | \nDescription | \n
|---|---|---|
loadPhoto(options?) | \n(options?: PhotoLoadOptions) => Promise<string[]> | \nOpens the platform picker and returns normalized data URLs. | \n
interface PhotoLoadOptions| Prop | \nType | \nDescription | \nDefault | \n
|---|---|---|---|
limit | \nnumber | \nMaximum images for album and browser selection. | \n1 | \n
maxSize | \nnumber | \nLongest edge in pixels after resize. | \nConfigured maxSize or 1000 | \n
labels | \nPartial<PhotoFileLabels> | \nPer-request native action-sheet label overrides. | \nConfigured labels | \n
class PhotoLoadErrorTyped error for expected photo-selection failures. Its readonly code is a PhotoLoadErrorCode.
type alias PhotoLoadErrorCode'cancelled' | 'invalid-type' | 'unavailable'
interface PhotoFileLabels| Prop | \nType | \nDescription | \n
|---|---|---|
camera | \nstring | \nCamera source label. | \n
album | \nstring | \nAlbum source label. | \n
cancel | \nstring | \nCancel action label. | \n
component PhotoEditorPageImported from @rdlabo/ionic-angular-photo-editor/editor and presented through an Ionic modal.
| Input | \nType | \nDescription | \nDefault | \n
|---|---|---|---|
value | \nstring | \nImage URL or data URL. Required. | \n\n |
requireSquare | \nboolean | \nRequires square cropping before editing continues. | \nfalse | \n
toolbarColorScheme | \nPhotoToolbarColorScheme | \nToolbar appearance behind the header buttons. Required. | \n\n |
labels | \nPartial<PhotoEditorLabels> | \nEditor label overrides. | \nundefined | \n
interface PhotoEditorPropsThe modal componentProps contract. It contains the same value, requireSquare, toolbarColorScheme, and labels fields shown above.
interface PhotoEditorResult| Prop | \nType | \nDescription | \n
|---|---|---|
action | \n'save' | \nIdentifies a successful save. | \n
value | \nstring | \nData URL of the edited image. | \n
interface PhotoEditorLabelsString fields: save, close, back, apply, crop, rotate, cropCover, crop16x9, cropSquare, cropFree, filter, brightness, original, invert, sepia, vintage, blur, grayscale, sharpen, and emboss.
component PhotoViewerPageImported from @rdlabo/ionic-angular-photo-editor/viewer and presented through an Ionic modal.
| Input | \nType | \nDescription | \nDefault | \n
|---|---|---|---|
imageUrls | \nstring[] | \nImage URLs or data URLs. Required. | \n\n |
index | \nnumber | \nInitially selected image index. | \n0 | \n
isCircle | \nboolean | \nDisplays images as circles. | \nfalse | \n
enableDelete | \nboolean | \nDisplays the delete button. | \nfalse | \n
enableFooterSafeArea | \nboolean | \nAdds iOS footer safe-area padding. | \nfalse | \n
toolbarColorScheme | \nPhotoToolbarColorScheme | \nToolbar appearance behind the header buttons. Required. | \n\n |
imageAlt | \nstring | ((url: string, index: number) => string) | \nAccessible image alt text or resolver. | \n'' | \n
labels | \nPartial<PhotoViewerLabels> | \nViewer label overrides. | \nundefined | \n
interface PhotoViewerPropsThe modal componentProps contract. It contains the same fields shown for PhotoViewerPage.
interface PhotoViewerResult| Prop | \nType | \nDescription | \n
|---|---|---|
action | \n'delete' | \nIdentifies a delete request. | \n
index | \nnumber | \nIndex of the selected image. | \n
value | \nstring | \nURL or data URL at that index. | \n
interface PhotoViewerLabels| Prop | \nType | \nDescription | \n
|---|---|---|
close | \nstring | \nClose action label. | \n
delete | \nstring | \nDelete action label. | \n
type alias PhotoToolbarColorScheme'light' | 'dark'
function createTuiImageEditorImported from @rdlabo/ionic-angular-photo-editor/editor/tui. A PhotoImageEditorFactory that loads the TUI Image Editor implementation in a bundler-resolvable lazy chunk.
function loadCapacitorPhotoCameraImported from @rdlabo/ionic-angular-photo-editor/file/capacitor. A PhotoCameraLoader that loads the Capacitor Camera implementation in a bundler-resolvable lazy chunk.
type alias PhotoImageEditorFactory(host: Element, options: PhotoImageEditorOptions) => Promise<PhotoImageEditor>
interface PhotoImageEditorOptions| Prop | \nType | \nDescription | \n
|---|---|---|
cssMaxWidth | \nnumber | \nMaximum editor canvas width. | \n
cssMaxHeight | \nnumber | \nMaximum editor canvas height. | \n
interface PhotoCropRectRectangle with numeric left, top, width, and height fields.
interface PhotoImageEditorMinimal editor-adapter contract:
| Member | \nType | \n
|---|---|
applyFilter | \n(type: string, options?: Exclude<PhotoFilterOptions, null>) => Promise<unknown> | \n
crop | \n(rect: PhotoCropRect) => Promise<unknown> | \n
destroy | \n() => void | \n
getCropzoneRect | \n() => PhotoCropRect | \n
hasFilter | \n(type: string) => boolean | \n
loadImageFromFile | \n(file: File) => Promise<{ newWidth: number; newHeight: number }> | \n
removeFilter | \n(type: string) => Promise<unknown> | \n
rotate | \n(angle: number) => Promise<unknown> | \n
setCropzoneRect | \n(ratio?: number) => void | \n
startDrawingMode | \n(mode: string) => void | \n
stopDrawingMode | \n() => void | \n
toDataURL | \n(options?: { multiplier?: number }) => string | \n
type alias PhotoCameraLoader() => Promise<PhotoCameraAdapter>
interface PhotoCameraAdapter| Member | \nType | \nDescription | \n
|---|---|---|
getPhoto | \n(options: PhotoCameraOptions) => Promise<PhotoCameraImage> | \nCamera capture. | \n
pickImages | \n(options: PhotoCameraOptions) => Promise<{ photos: PhotoCameraImage[] }> | \nAlbum selection. | \n
interface PhotoCameraOptions| Prop | \nType | \nDescription | \n
|---|---|---|
quality | \nnumber | \nRequested image quality. | \n
width | \nnumber | \nRequested image width. | \n
limit? | \nnumber | \nOptional album selection limit. | \n
source? | \n'camera' | \nOptional camera-only source marker. | \n
interface PhotoCameraImage| Prop | \nType | \nDescription | \n
|---|---|---|
dataUrl? | \nstring | \nImage data URL. | \n
webPath? | \nstring | \nBrowser-accessible image URL. | \n
interface PhotoFilterRendered filter preview with name, type, option, data, width, and height.
type alias PhotoFilterOptions{ blur: number } | { brightness: number } | { noise: number } | { blocksize: number } | { color: string; distance: number; useAlpha?: boolean } | { mode: string; color: string; alpha?: number } | { maskObjId: number } | null
interface PhotoFilterPresetFilter menu preset with name, type, and option.
interface PhotoSizeTwo-dimensional pixel size with numeric width and height.
Reference for the public entry points exported by @rdlabo/ionic-angular-photo-editor v22.0.3. Import components, services, and optional implementations from their dedicated secondary entry points.
| Import path | \nPrincipal exports | \n
|---|---|
@rdlabo/ionic-angular-photo-editor | \nConfiguration, shared types, PhotoLoadError | \n
@rdlabo/ionic-angular-photo-editor/editor | \nPhotoEditorPage | \n
@rdlabo/ionic-angular-photo-editor/editor/tui | \ncreateTuiImageEditor | \n
@rdlabo/ionic-angular-photo-editor/viewer | \nPhotoViewerPage | \n
@rdlabo/ionic-angular-photo-editor/file | \nPhotoFileService | \n
@rdlabo/ionic-angular-photo-editor/file/capacitor | \nloadCapacitorPhotoCamera | \n
function providePhotoEditor(config?: PhotoEditorConfig): EnvironmentProvidersRegisters application-wide photo-loading defaults and the optional editor and camera adapters.
constant PHOTO_EDITOR_CONFIGAngular injection token containing the resolved maxSize, labels, image-editor factory, and camera loader.
interface PhotoEditorConfig| Prop | \nType | \nDescription | \nDefault | \n
|---|---|---|---|
maxSize | \nnumber | \nLongest edge in pixels after resize. | \n1000 | \n
labels | \nPartial<PhotoFileLabels> | \nCamera, album, and cancel label overrides. | \nundefined | \n
createImageEditor | \nPhotoImageEditorFactory | \nAdapter used for editing and resizing. | \n\n |
loadCamera | \nPhotoCameraLoader | \nAdapter used for native camera and album selection. | \n\n |
class PhotoFileServiceImported from @rdlabo/ionic-angular-photo-editor/file. Selects and normalizes photos from browser and Capacitor sources.
| Member | \nType | \nDescription | \n
|---|---|---|
loadPhoto(options?) | \n(options?: PhotoLoadOptions) => Promise<string[]> | \nOpens the platform picker and returns normalized data URLs. | \n
interface PhotoLoadOptions| Prop | \nType | \nDescription | \nDefault | \n
|---|---|---|---|
limit | \nnumber | \nMaximum images for album and browser selection. | \n1 | \n
maxSize | \nnumber | \nLongest edge in pixels after resize. | \nConfigured maxSize or 1000 | \n
labels | \nPartial<PhotoFileLabels> | \nPer-request native action-sheet label overrides. | \nConfigured labels | \n
class PhotoLoadErrorTyped error for expected photo-selection failures. Its readonly code is a PhotoLoadErrorCode.
type alias PhotoLoadErrorCode'cancelled' | 'invalid-type' | 'unavailable'
interface PhotoFileLabels| Prop | \nType | \nDescription | \n
|---|---|---|
camera | \nstring | \nCamera source label. | \n
album | \nstring | \nAlbum source label. | \n
cancel | \nstring | \nCancel action label. | \n
component PhotoEditorPageImported from @rdlabo/ionic-angular-photo-editor/editor and presented through an Ionic modal.
| Input | \nType | \nDescription | \nDefault | \n
|---|---|---|---|
value | \nstring | \nImage URL or data URL. Required. | \n\n |
requireSquare | \nboolean | \nRequires square cropping before editing continues. | \nfalse | \n
toolbarColorScheme | \nPhotoToolbarColorScheme | \nToolbar appearance behind the header buttons. Required. | \n\n |
labels | \nPartial<PhotoEditorLabels> | \nEditor label overrides. | \nundefined | \n
interface PhotoEditorPropsThe modal componentProps contract. It contains the same value, requireSquare, toolbarColorScheme, and labels fields shown above.
interface PhotoEditorResult| Prop | \nType | \nDescription | \n
|---|---|---|
action | \n'save' | \nIdentifies a successful save. | \n
value | \nstring | \nData URL of the edited image. | \n
interface PhotoEditorLabelsString fields: save, close, back, apply, crop, rotate, cropCover, crop16x9, cropSquare, cropFree, filter, brightness, original, invert, sepia, vintage, blur, grayscale, sharpen, and emboss.
component PhotoViewerPageImported from @rdlabo/ionic-angular-photo-editor/viewer and presented through an Ionic modal.
| Input | \nType | \nDescription | \nDefault | \n
|---|---|---|---|
imageUrls | \nstring[] | \nImage URLs or data URLs. Required. | \n\n |
index | \nnumber | \nInitially selected image index. | \n0 | \n
isCircle | \nboolean | \nDisplays images as circles. | \nfalse | \n
enableDelete | \nboolean | \nDisplays the delete button. | \nfalse | \n
enableFooterSafeArea | \nboolean | \nAdds iOS footer safe-area padding. | \nfalse | \n
toolbarColorScheme | \nPhotoToolbarColorScheme | \nToolbar appearance behind the header buttons. Required. | \n\n |
imageAlt | \nstring | ((url: string, index: number) => string) | \nAccessible image alt text or resolver. | \n'' | \n
labels | \nPartial<PhotoViewerLabels> | \nViewer label overrides. | \nundefined | \n
interface PhotoViewerPropsThe modal componentProps contract. It contains the same fields shown for PhotoViewerPage.
interface PhotoViewerResult| Prop | \nType | \nDescription | \n
|---|---|---|
action | \n'delete' | \nIdentifies a delete request. | \n
index | \nnumber | \nIndex of the selected image. | \n
value | \nstring | \nURL or data URL at that index. | \n
interface PhotoViewerLabels| Prop | \nType | \nDescription | \n
|---|---|---|
close | \nstring | \nClose action label. | \n
delete | \nstring | \nDelete action label. | \n
type alias PhotoToolbarColorScheme'light' | 'dark'
function createTuiImageEditorImported from @rdlabo/ionic-angular-photo-editor/editor/tui. A PhotoImageEditorFactory that loads the TUI Image Editor implementation in a bundler-resolvable lazy chunk.
function loadCapacitorPhotoCameraImported from @rdlabo/ionic-angular-photo-editor/file/capacitor. A PhotoCameraLoader that loads the Capacitor Camera implementation in a bundler-resolvable lazy chunk.
type alias PhotoImageEditorFactory(host: Element, options: PhotoImageEditorOptions) => Promise<PhotoImageEditor>
interface PhotoImageEditorOptions| Prop | \nType | \nDescription | \n
|---|---|---|
cssMaxWidth | \nnumber | \nMaximum editor canvas width. | \n
cssMaxHeight | \nnumber | \nMaximum editor canvas height. | \n
interface PhotoCropRectRectangle with numeric left, top, width, and height fields.
interface PhotoImageEditorMinimal editor-adapter contract:
| Member | \nType | \n
|---|---|
applyFilter | \n(type: string, options?: Exclude<PhotoFilterOptions, null>) => Promise<unknown> | \n
crop | \n(rect: PhotoCropRect) => Promise<unknown> | \n
destroy | \n() => void | \n
getCropzoneRect | \n() => PhotoCropRect | \n
hasFilter | \n(type: string) => boolean | \n
loadImageFromFile | \n(file: File) => Promise<{ newWidth: number; newHeight: number }> | \n
removeFilter | \n(type: string) => Promise<unknown> | \n
rotate | \n(angle: number) => Promise<unknown> | \n
setCropzoneRect | \n(ratio?: number) => void | \n
startDrawingMode | \n(mode: string) => void | \n
stopDrawingMode | \n() => void | \n
toDataURL | \n(options?: { multiplier?: number }) => string | \n
type alias PhotoCameraLoader() => Promise<PhotoCameraAdapter>
interface PhotoCameraAdapter| Member | \nType | \nDescription | \n
|---|---|---|
getPhoto | \n(options: PhotoCameraOptions) => Promise<PhotoCameraImage> | \nCamera capture. | \n
pickImages | \n(options: PhotoCameraOptions) => Promise<{ photos: PhotoCameraImage[] }> | \nAlbum selection. | \n
interface PhotoCameraOptions| Prop | \nType | \nDescription | \n
|---|---|---|
quality | \nnumber | \nRequested image quality. | \n
width | \nnumber | \nRequested image width. | \n
limit? | \nnumber | \nOptional album selection limit. | \n
source? | \n'camera' | \nOptional camera-only source marker. | \n
interface PhotoCameraImage| Prop | \nType | \nDescription | \n
|---|---|---|
dataUrl? | \nstring | \nImage data URL. | \n
webPath? | \nstring | \nBrowser-accessible image URL. | \n
interface PhotoFilterRendered filter preview with name, type, option, data, width, and height.
type alias PhotoFilterOptions{ blur: number } | { brightness: number } | { noise: number } | { blocksize: number } | { color: string; distance: number; useAlpha?: boolean } | { mode: string; color: string; alpha?: number } | { maskObjId: number } | null
interface PhotoFilterPresetFilter menu preset with name, type, and option.
interface PhotoSizeTwo-dimensional pixel size with numeric width and height.
Ionic Angularアプリ向けのPhoto Editor・Viewer Modal Pageです。Browserのfile選択に対応し、任意でCapacitorのCamera・Albumも使えます。
\nnpm install @rdlabo/ionic-angular-photo-editor tui-image-editor\nTUIのresize adapterを登録し(loadPhoto のresizeに必須)、fileを選んで返されたdata URLを表示します。
import { Component, inject, signal } from '@angular/core';\nimport type { ApplicationConfig } from '@angular/core';\nimport { IonButton, IonImg } from '@ionic/angular';\nimport { providePhotoEditor, PhotoLoadError } from '@rdlabo/ionic-angular-photo-editor';\nimport { createTuiImageEditor } from '@rdlabo/ionic-angular-photo-editor/editor/tui';\nimport { PhotoFileService } from '@rdlabo/ionic-angular-photo-editor/file';\n\nexport const appConfig: ApplicationConfig = {\n providers: [\n providePhotoEditor({\n maxSize: 1000,\n createImageEditor: createTuiImageEditor,\n }),\n ],\n};\n\n@Component({\n selector: 'app-photo-pick',\n imports: [IonButton, IonImg],\n template: `\n <ion-button type=\"button\" (click)=\"pick()\">Select photo</ion-button>\n @if (previewUrl()) {\n <ion-img [src]=\"previewUrl()\" alt=\"Selected photo\"></ion-img>\n }\n `,\n})\nexport class PhotoPickPage {\n private readonly photoFileService = inject(PhotoFileService);\n readonly previewUrl = signal('');\n\n async pick(): Promise<void> {\n try {\n const files = await this.photoFileService.loadPhoto({ limit: 1 });\n this.previewUrl.set(files[0] ?? '');\n } catch (error) {\n if (error instanceof PhotoLoadError && error.code === 'cancelled') {\n return;\n }\n throw error;\n }\n }\n}\nSelect photoをクリックして画像を選ぶと、previewが表示されます。NativeのCamera・Album設定は後段のPhotoFileServiceです。
\n追加する機能に合わせて依存をインストールします。
\n# viewer\nnpm install swiper\n\n# native camera and album selection\nnpm install @capacitor/camera\nネイティブカメラを使う場合は、カメラ権限とPhotoFileServiceのadapterを設定します。iOSアプリの対象はiOS/iPadOS 16.4以降です。
\n| Import path | \nExport | \n
|---|---|
@rdlabo/ionic-angular-photo-editor | \n型、providePhotoEditor、PHOTO_EDITOR_CONFIG、PhotoLoadError | \n
@rdlabo/ionic-angular-photo-editor/editor | \nPhotoEditorPage | \n
@rdlabo/ionic-angular-photo-editor/editor/tui | \nopt-inの createTuiImageEditor adapter | \n
@rdlabo/ionic-angular-photo-editor/viewer | \nPhotoViewerPage | \n
@rdlabo/ionic-angular-photo-editor/file | \nPhotoFileService | \n
@rdlabo/ionic-angular-photo-editor/file/capacitor | \nopt-inの loadCapacitorPhotoCamera adapter | \n
ComponentとServiceは、そのentry pointからだけimportしてください。共有型と設定はroot packageからimportします。
\n| 目的 | \nガイド | \n
|---|---|
| CameraまたはAlbumから写真を読み込む | \nPhotoFileService | \n
| Modalで切り抜き・編集する | \nPhoto Editor | \n
| Modalで画像を閲覧する | \nPhoto Viewer | \n
| Editorの色を上書きする | \nTheme | \n
| 以前のreleaseからupgradeする | \nMigration guide | \n
Ionic Angularアプリ向けのPhoto Editor・Viewer Modal Pageです。Browserのfile選択に対応し、任意でCapacitorのCamera・Albumも使えます。
\nnpm install @rdlabo/ionic-angular-photo-editor tui-image-editor\nTUIのresize adapterを登録し(loadPhoto のresizeに必須)、fileを選んで返されたdata URLを表示します。
import { Component, inject, signal } from '@angular/core';\nimport type { ApplicationConfig } from '@angular/core';\nimport { IonButton, IonImg } from '@ionic/angular';\nimport { providePhotoEditor, PhotoLoadError } from '@rdlabo/ionic-angular-photo-editor';\nimport { createTuiImageEditor } from '@rdlabo/ionic-angular-photo-editor/editor/tui';\nimport { PhotoFileService } from '@rdlabo/ionic-angular-photo-editor/file';\n\nexport const appConfig: ApplicationConfig = {\n providers: [\n providePhotoEditor({\n maxSize: 1000,\n createImageEditor: createTuiImageEditor,\n }),\n ],\n};\n\n@Component({\n selector: 'app-photo-pick',\n imports: [IonButton, IonImg],\n template: `\n <ion-button type=\"button\" (click)=\"pick()\">Select photo</ion-button>\n @if (previewUrl()) {\n <ion-img [src]=\"previewUrl()\" alt=\"Selected photo\"></ion-img>\n }\n `,\n})\nexport class PhotoPickPage {\n private readonly photoFileService = inject(PhotoFileService);\n readonly previewUrl = signal('');\n\n async pick(): Promise<void> {\n try {\n const files = await this.photoFileService.loadPhoto({ limit: 1 });\n this.previewUrl.set(files[0] ?? '');\n } catch (error) {\n if (error instanceof PhotoLoadError && error.code === 'cancelled') {\n return;\n }\n throw error;\n }\n }\n}\nSelect photoをクリックして画像を選ぶと、previewが表示されます。NativeのCamera・Album設定は後段のPhotoFileServiceです。
\n追加する機能に合わせて依存をインストールします。
\n# viewer\nnpm install swiper\n\n# native camera and album selection\nnpm install @capacitor/camera\nネイティブカメラを使う場合は、カメラ権限とPhotoFileServiceのadapterを設定します。iOSアプリの対象はiOS/iPadOS 16.4以降です。
\n| Import path | \nExport | \n
|---|---|
@rdlabo/ionic-angular-photo-editor | \n型、providePhotoEditor、PHOTO_EDITOR_CONFIG、PhotoLoadError | \n
@rdlabo/ionic-angular-photo-editor/editor | \nPhotoEditorPage | \n
@rdlabo/ionic-angular-photo-editor/editor/tui | \nopt-inの createTuiImageEditor adapter | \n
@rdlabo/ionic-angular-photo-editor/viewer | \nPhotoViewerPage | \n
@rdlabo/ionic-angular-photo-editor/file | \nPhotoFileService | \n
@rdlabo/ionic-angular-photo-editor/file/capacitor | \nopt-inの loadCapacitorPhotoCamera adapter | \n
ComponentとServiceは、そのentry pointからだけimportしてください。共有型と設定はroot packageからimportします。
\n| 目的 | \nガイド | \n
|---|---|
| CameraまたはAlbumから写真を読み込む | \nPhotoFileService | \n
| Modalで切り抜き・編集する | \nPhoto Editor | \n
| Modalで画像を閲覧する | \nPhoto Viewer | \n
| Editorの色を上書きする | \nTheme | \n
| 以前のreleaseからupgradeする | \nMigration guide | \n
Installationの後に、Editorの色を上書きします。
\nDefault colorはlibrary stylesheetで定義されています。CSS variableで上書きします。
\n:root {\n --ion-photo-editor-background: #2a2a2a;\n --ion-photo-editor-background-tint: #414141;\n\n --ion-photo-editor-color: #f0f0f0;\n --ion-photo-editor-color-tint: #dbdbdb;\n\n --ion-photo-editor-primary: #4d8dff;\n --ion-photo-editor-danger: #f24c58;\n --ion-photo-editor-success: #2dd55b;\n\n --ion-photo-editor-header-button-color-on-light: #222428;\n --ion-photo-editor-header-button-color-on-dark: #f4f5f8;\n}\nSource reference: core.scss
PhotoEditorPage と PhotoViewerPage では、Modalの componentProps に toolbarColorScheme: 'light' | 'dark' を指定する必要があります。暗色・黒色の ion-toolbar には dark、明色・白色のToolbarには light を選択してください。最終的なToolbarの外観はCSS、translucency、runtime theme overrideによって変わるため、Library側では確実に判定できません。利用側で明示的に選択する必要があります。
@rdlabo/ionic-theme-ios26 v3では、iOS 26 ThemeとDark Mode styleより後に、任意のintegration stylesheetをimportします。
@import '@rdlabo/ionic-theme-ios26/dist/css/ionic-theme-ios26.css';\n@import '@ionic/angular/css/palettes/dark.class.css';\n@import '@rdlabo/ionic-theme-ios26/dist/css/ionic-theme-ios26-dark-class.css';\n@import '@rdlabo/ionic-angular-photo-editor/css/ios26-header-button-color-scheme.css';\n必要に応じて、対応するAlwaysまたはSystem Dark Mode用のimportへ置き換えてください。Photo Editor integration stylesheetは、local header schemeがアプリ全体のschemeを上書きできるよう、必ず最後に読み込みます。iOS 26 Themeを使わないアプリでは、この任意stylesheetをimportしないでください。その場合は通常のIonic Button foreground color switchだけが適用されます。
\n", + "html": "Installationの後に、Editorの色を上書きします。
\nDefault colorはlibrary stylesheetで定義されています。CSS variableで上書きします。
\n:root {\n --ion-photo-editor-background: #2a2a2a;\n --ion-photo-editor-background-tint: #414141;\n\n --ion-photo-editor-color: #f0f0f0;\n --ion-photo-editor-color-tint: #dbdbdb;\n\n --ion-photo-editor-primary: #4d8dff;\n --ion-photo-editor-danger: #f24c58;\n --ion-photo-editor-success: #2dd55b;\n\n --ion-photo-editor-header-button-color-on-light: #222428;\n --ion-photo-editor-header-button-color-on-dark: #f4f5f8;\n}\nSource reference: core.scss
PhotoEditorPage と PhotoViewerPage では、Modalの componentProps に toolbarColorScheme: 'light' | 'dark' を指定する必要があります。暗色・黒色の ion-toolbar には dark、明色・白色のToolbarには light を選択してください。最終的なToolbarの外観はCSS、translucency、runtime theme overrideによって変わるため、Library側では確実に判定できません。利用側で明示的に選択する必要があります。
@rdlabo/ionic-theme-ios26 v3では、iOS 26 ThemeとDark Mode styleより後に、任意のintegration stylesheetをimportします。
@import '@rdlabo/ionic-theme-ios26/dist/css/ionic-theme-ios26.css';\n@import '@ionic/angular/css/palettes/dark.class.css';\n@import '@rdlabo/ionic-theme-ios26/dist/css/ionic-theme-ios26-dark-class.css';\n@import '@rdlabo/ionic-angular-photo-editor/css/ios26-header-button-color-scheme.css';\n必要に応じて、対応するAlwaysまたはSystem Dark Mode用のimportへ置き換えてください。Photo Editor integration stylesheetは、local header schemeがアプリ全体のschemeを上書きできるよう、必ず最後に読み込みます。iOS 26 Themeを使わないアプリでは、この任意stylesheetをimportしないでください。その場合は通常のIonic Button foreground color switchだけが適用されます。
\n", "headings": [ { "id": "toolbar-color-scheme", @@ -272,7 +272,7 @@ export const PROJECT = { "file": "api.md", "section": "リファレンス", "path": "/projects/ionic-angular-photo-editor/docs/api", - "html": "@rdlabo/ionic-angular-photo-editor v22.0.0が公開するpublic entry pointのリファレンスです。Component、Service、任意実装は、それぞれ専用のsecondary entry pointからimportしてください。
| Import path | \n主なexport | \n
|---|---|
@rdlabo/ionic-angular-photo-editor | \n設定、共有型、PhotoLoadError | \n
@rdlabo/ionic-angular-photo-editor/editor | \nPhotoEditorPage | \n
@rdlabo/ionic-angular-photo-editor/editor/tui | \ncreateTuiImageEditor | \n
@rdlabo/ionic-angular-photo-editor/viewer | \nPhotoViewerPage | \n
@rdlabo/ionic-angular-photo-editor/file | \nPhotoFileService | \n
@rdlabo/ionic-angular-photo-editor/file/capacitor | \nloadCapacitorPhotoCamera | \n
function providePhotoEditor(config?: PhotoEditorConfig): EnvironmentProvidersアプリ全体の写真読み込みdefaultと、任意のEditor・Camera adapterを登録します。
constant PHOTO_EDITOR_CONFIG解決済みの maxSize、label、Image Editor factory、Camera loaderを保持するAngular Injection Tokenです。
interface PhotoEditorConfig| Prop | \nType | \nDescription | \nDefault | \n
|---|---|---|---|
maxSize | \nnumber | \nResize後の長辺pixel数です。 | \n1000 | \n
labels | \nPartial<PhotoFileLabels> | \nCamera、Album、Cancel labelの上書きです。 | \nundefined | \n
createImageEditor | \nPhotoImageEditorFactory | \n編集・Resizeに使うadapterです。 | \n\n |
loadCamera | \nPhotoCameraLoader | \nNativeのCamera・Album選択に使うadapterです。 | \n\n |
class PhotoFileService@rdlabo/ionic-angular-photo-editor/file からimportします。Browser・Capacitorから写真を選択し、正規化します。
| Member | \nType | \nDescription | \n
|---|---|---|
loadPhoto(options?) | \n(options?: PhotoLoadOptions) => Promise<string[]> | \nPlatform Pickerを開き、正規化したData URLを返します。 | \n
interface PhotoLoadOptions| Prop | \nType | \nDescription | \nDefault | \n
|---|---|---|---|
limit | \nnumber | \nAlbum・Browserで選択する最大画像数です。 | \n1 | \n
maxSize | \nnumber | \nResize後の長辺pixel数です。 | \n設定済みの maxSize または 1000 | \n
labels | \nPartial<PhotoFileLabels> | \nRequest単位のNative Action Sheet label上書きです。 | \n設定済みのlabel | \n
class PhotoLoadError想定される写真選択エラーの型付きErrorです。Readonlyの code は PhotoLoadErrorCode です。
type alias PhotoLoadErrorCode'cancelled' | 'invalid-type' | 'unavailable'
interface PhotoFileLabels| Prop | \nType | \nDescription | \n
|---|---|---|
camera | \nstring | \nCamera選択labelです。 | \n
album | \nstring | \nAlbum選択labelです。 | \n
cancel | \nstring | \nCancel labelです。 | \n
component PhotoEditorPage@rdlabo/ionic-angular-photo-editor/editor からimportし、Ionic Modalで表示します。
| Input | \nType | \nDescription | \nDefault | \n
|---|---|---|---|
value | \nstring | \nImage URLまたはData URLです。必須です。 | \n\n |
requireSquare | \nboolean | \n編集を続ける前に正方形へのcropを必須にします。 | \nfalse | \n
toolbarColorScheme | \nPhotoToolbarColorScheme | \nHeader Button背後のToolbar外観です。必須です。 | \n\n |
labels | \nPartial<PhotoEditorLabels> | \nEditor labelを上書きします。 | \nundefined | \n
interface PhotoEditorPropsModalの componentProps contractです。上記の value、requireSquare、toolbarColorScheme、labels と同じfieldを持ちます。
interface PhotoEditorResult| Prop | \nType | \nDescription | \n
|---|---|---|
action | \n'save' | \nSave成功を示します。 | \n
value | \nstring | \n編集済み画像のData URLです。 | \n
interface PhotoEditorLabels文字列field: save、close、back、apply、crop、rotate、cropCover、crop16x9、cropSquare、cropFree、filter、brightness、original、invert、sepia、vintage、blur、grayscale、sharpen、emboss。
component PhotoViewerPage@rdlabo/ionic-angular-photo-editor/viewer からimportし、Ionic Modalで表示します。
| Input | \nType | \nDescription | \nDefault | \n
|---|---|---|---|
imageUrls | \nstring[] | \nImage URLまたはData URLです。必須です。 | \n\n |
index | \nnumber | \n最初に選択するImage indexです。 | \n0 | \n
isCircle | \nboolean | \n画像を円形で表示します。 | \nfalse | \n
enableDelete | \nboolean | \nDelete Buttonを表示します。 | \nfalse | \n
enableFooterSafeArea | \nboolean | \niOS FooterのSafe Area paddingを追加します。 | \nfalse | \n
toolbarColorScheme | \nPhotoToolbarColorScheme | \nHeader Button背後のToolbar外観です。必須です。 | \n\n |
imageAlt | \nstring | ((url: string, index: number) => string) | \nAccessibleな画像alt textまたはresolverです。 | \n'' | \n
labels | \nPartial<PhotoViewerLabels> | \nViewer labelを上書きします。 | \nundefined | \n
interface PhotoViewerPropsModalの componentProps contractです。PhotoViewerPage に示したものと同じfieldを持ちます。
interface PhotoViewerResult| Prop | \nType | \nDescription | \n
|---|---|---|
action | \n'delete' | \nDelete要求を示します。 | \n
index | \nnumber | \n選択した画像のindexです。 | \n
value | \nstring | \nそのindexのURLまたはData URLです。 | \n
interface PhotoViewerLabels| Prop | \nType | \nDescription | \n
|---|---|---|
close | \nstring | \nClose labelです。 | \n
delete | \nstring | \nDelete labelです。 | \n
type alias PhotoToolbarColorScheme'light' | 'dark'
function createTuiImageEditor@rdlabo/ionic-angular-photo-editor/editor/tui からimportします。TUI Image Editor実装をbundlerが解決できるLazy Chunkで読み込む PhotoImageEditorFactory です。
function loadCapacitorPhotoCamera@rdlabo/ionic-angular-photo-editor/file/capacitor からimportします。Capacitor Camera実装をbundlerが解決できるLazy Chunkで読み込む PhotoCameraLoader です。
type alias PhotoImageEditorFactory(host: Element, options: PhotoImageEditorOptions) => Promise<PhotoImageEditor>
interface PhotoImageEditorOptions| Prop | \nType | \nDescription | \n
|---|---|---|
cssMaxWidth | \nnumber | \nEditor Canvasの最大widthです。 | \n
cssMaxHeight | \nnumber | \nEditor Canvasの最大heightです。 | \n
interface PhotoCropRect数値の left、top、width、height fieldを持つRectangleです。
interface PhotoImageEditor最小限のEditor adapter contractです。
| Member | \nType | \n
|---|---|
applyFilter | \n(type: string, options?: Exclude<PhotoFilterOptions, null>) => Promise<unknown> | \n
crop | \n(rect: PhotoCropRect) => Promise<unknown> | \n
destroy | \n() => void | \n
getCropzoneRect | \n() => PhotoCropRect | \n
hasFilter | \n(type: string) => boolean | \n
loadImageFromFile | \n(file: File) => Promise<{ newWidth: number; newHeight: number }> | \n
removeFilter | \n(type: string) => Promise<unknown> | \n
rotate | \n(angle: number) => Promise<unknown> | \n
setCropzoneRect | \n(ratio?: number) => void | \n
startDrawingMode | \n(mode: string) => void | \n
stopDrawingMode | \n() => void | \n
toDataURL | \n(options?: { multiplier?: number }) => string | \n
type alias PhotoCameraLoader() => Promise<PhotoCameraAdapter>
interface PhotoCameraAdapter| Member | \nType | \nDescription | \n
|---|---|---|
getPhoto | \n(options: PhotoCameraOptions) => Promise<PhotoCameraImage> | \nCamera撮影。 | \n
pickImages | \n(options: PhotoCameraOptions) => Promise<{ photos: PhotoCameraImage[] }> | \nAlbum選択。 | \n
interface PhotoCameraOptions| Prop | \nType | \nDescription | \n
|---|---|---|
quality | \nnumber | \n要求するImage qualityです。 | \n
width | \nnumber | \n要求するImage widthです。 | \n
limit? | \nnumber | \n任意のAlbum選択上限です。 | \n
source? | \n'camera' | \n任意のCamera専用source markerです。 | \n
interface PhotoCameraImage| Prop | \nType | \nDescription | \n
|---|---|---|
dataUrl? | \nstring | \nImage Data URLです。 | \n
webPath? | \nstring | \nBrowserから参照できるImage URLです。 | \n
interface PhotoFiltername、type、option、data、width、height を持つrender済みFilter previewです。
type alias PhotoFilterOptions{ blur: number } | { brightness: number } | { noise: number } | { blocksize: number } | { color: string; distance: number; useAlpha?: boolean } | { mode: string; color: string; alpha?: number } | { maskObjId: number } | null
interface PhotoFilterPresetname、type、option を持つFilter Menu presetです。
interface PhotoSize数値の width と height を持つ2次元pixel sizeです。
@rdlabo/ionic-angular-photo-editor v22.0.3が公開するpublic entry pointのリファレンスです。Component、Service、任意実装は、それぞれ専用のsecondary entry pointからimportしてください。
| Import path | \n主なexport | \n
|---|---|
@rdlabo/ionic-angular-photo-editor | \n設定、共有型、PhotoLoadError | \n
@rdlabo/ionic-angular-photo-editor/editor | \nPhotoEditorPage | \n
@rdlabo/ionic-angular-photo-editor/editor/tui | \ncreateTuiImageEditor | \n
@rdlabo/ionic-angular-photo-editor/viewer | \nPhotoViewerPage | \n
@rdlabo/ionic-angular-photo-editor/file | \nPhotoFileService | \n
@rdlabo/ionic-angular-photo-editor/file/capacitor | \nloadCapacitorPhotoCamera | \n
function providePhotoEditor(config?: PhotoEditorConfig): EnvironmentProvidersアプリ全体の写真読み込みdefaultと、任意のEditor・Camera adapterを登録します。
constant PHOTO_EDITOR_CONFIG解決済みの maxSize、label、Image Editor factory、Camera loaderを保持するAngular Injection Tokenです。
interface PhotoEditorConfig| Prop | \nType | \nDescription | \nDefault | \n
|---|---|---|---|
maxSize | \nnumber | \nResize後の長辺pixel数です。 | \n1000 | \n
labels | \nPartial<PhotoFileLabels> | \nCamera、Album、Cancel labelの上書きです。 | \nundefined | \n
createImageEditor | \nPhotoImageEditorFactory | \n編集・Resizeに使うadapterです。 | \n\n |
loadCamera | \nPhotoCameraLoader | \nNativeのCamera・Album選択に使うadapterです。 | \n\n |
class PhotoFileService@rdlabo/ionic-angular-photo-editor/file からimportします。Browser・Capacitorから写真を選択し、正規化します。
| Member | \nType | \nDescription | \n
|---|---|---|
loadPhoto(options?) | \n(options?: PhotoLoadOptions) => Promise<string[]> | \nPlatform Pickerを開き、正規化したData URLを返します。 | \n
interface PhotoLoadOptions| Prop | \nType | \nDescription | \nDefault | \n
|---|---|---|---|
limit | \nnumber | \nAlbum・Browserで選択する最大画像数です。 | \n1 | \n
maxSize | \nnumber | \nResize後の長辺pixel数です。 | \n設定済みの maxSize または 1000 | \n
labels | \nPartial<PhotoFileLabels> | \nRequest単位のNative Action Sheet label上書きです。 | \n設定済みのlabel | \n
class PhotoLoadError想定される写真選択エラーの型付きErrorです。Readonlyの code は PhotoLoadErrorCode です。
type alias PhotoLoadErrorCode'cancelled' | 'invalid-type' | 'unavailable'
interface PhotoFileLabels| Prop | \nType | \nDescription | \n
|---|---|---|
camera | \nstring | \nCamera選択labelです。 | \n
album | \nstring | \nAlbum選択labelです。 | \n
cancel | \nstring | \nCancel labelです。 | \n
component PhotoEditorPage@rdlabo/ionic-angular-photo-editor/editor からimportし、Ionic Modalで表示します。
| Input | \nType | \nDescription | \nDefault | \n
|---|---|---|---|
value | \nstring | \nImage URLまたはData URLです。必須です。 | \n\n |
requireSquare | \nboolean | \n編集を続ける前に正方形へのcropを必須にします。 | \nfalse | \n
toolbarColorScheme | \nPhotoToolbarColorScheme | \nHeader Button背後のToolbar外観です。必須です。 | \n\n |
labels | \nPartial<PhotoEditorLabels> | \nEditor labelを上書きします。 | \nundefined | \n
interface PhotoEditorPropsModalの componentProps contractです。上記の value、requireSquare、toolbarColorScheme、labels と同じfieldを持ちます。
interface PhotoEditorResult| Prop | \nType | \nDescription | \n
|---|---|---|
action | \n'save' | \nSave成功を示します。 | \n
value | \nstring | \n編集済み画像のData URLです。 | \n
interface PhotoEditorLabels文字列field: save、close、back、apply、crop、rotate、cropCover、crop16x9、cropSquare、cropFree、filter、brightness、original、invert、sepia、vintage、blur、grayscale、sharpen、emboss。
component PhotoViewerPage@rdlabo/ionic-angular-photo-editor/viewer からimportし、Ionic Modalで表示します。
| Input | \nType | \nDescription | \nDefault | \n
|---|---|---|---|
imageUrls | \nstring[] | \nImage URLまたはData URLです。必須です。 | \n\n |
index | \nnumber | \n最初に選択するImage indexです。 | \n0 | \n
isCircle | \nboolean | \n画像を円形で表示します。 | \nfalse | \n
enableDelete | \nboolean | \nDelete Buttonを表示します。 | \nfalse | \n
enableFooterSafeArea | \nboolean | \niOS FooterのSafe Area paddingを追加します。 | \nfalse | \n
toolbarColorScheme | \nPhotoToolbarColorScheme | \nHeader Button背後のToolbar外観です。必須です。 | \n\n |
imageAlt | \nstring | ((url: string, index: number) => string) | \nAccessibleな画像alt textまたはresolverです。 | \n'' | \n
labels | \nPartial<PhotoViewerLabels> | \nViewer labelを上書きします。 | \nundefined | \n
interface PhotoViewerPropsModalの componentProps contractです。PhotoViewerPage に示したものと同じfieldを持ちます。
interface PhotoViewerResult| Prop | \nType | \nDescription | \n
|---|---|---|
action | \n'delete' | \nDelete要求を示します。 | \n
index | \nnumber | \n選択した画像のindexです。 | \n
value | \nstring | \nそのindexのURLまたはData URLです。 | \n
interface PhotoViewerLabels| Prop | \nType | \nDescription | \n
|---|---|---|
close | \nstring | \nClose labelです。 | \n
delete | \nstring | \nDelete labelです。 | \n
type alias PhotoToolbarColorScheme'light' | 'dark'
function createTuiImageEditor@rdlabo/ionic-angular-photo-editor/editor/tui からimportします。TUI Image Editor実装をbundlerが解決できるLazy Chunkで読み込む PhotoImageEditorFactory です。
function loadCapacitorPhotoCamera@rdlabo/ionic-angular-photo-editor/file/capacitor からimportします。Capacitor Camera実装をbundlerが解決できるLazy Chunkで読み込む PhotoCameraLoader です。
type alias PhotoImageEditorFactory(host: Element, options: PhotoImageEditorOptions) => Promise<PhotoImageEditor>
interface PhotoImageEditorOptions| Prop | \nType | \nDescription | \n
|---|---|---|
cssMaxWidth | \nnumber | \nEditor Canvasの最大widthです。 | \n
cssMaxHeight | \nnumber | \nEditor Canvasの最大heightです。 | \n
interface PhotoCropRect数値の left、top、width、height fieldを持つRectangleです。
interface PhotoImageEditor最小限のEditor adapter contractです。
| Member | \nType | \n
|---|---|
applyFilter | \n(type: string, options?: Exclude<PhotoFilterOptions, null>) => Promise<unknown> | \n
crop | \n(rect: PhotoCropRect) => Promise<unknown> | \n
destroy | \n() => void | \n
getCropzoneRect | \n() => PhotoCropRect | \n
hasFilter | \n(type: string) => boolean | \n
loadImageFromFile | \n(file: File) => Promise<{ newWidth: number; newHeight: number }> | \n
removeFilter | \n(type: string) => Promise<unknown> | \n
rotate | \n(angle: number) => Promise<unknown> | \n
setCropzoneRect | \n(ratio?: number) => void | \n
startDrawingMode | \n(mode: string) => void | \n
stopDrawingMode | \n() => void | \n
toDataURL | \n(options?: { multiplier?: number }) => string | \n
type alias PhotoCameraLoader() => Promise<PhotoCameraAdapter>
interface PhotoCameraAdapter| Member | \nType | \nDescription | \n
|---|---|---|
getPhoto | \n(options: PhotoCameraOptions) => Promise<PhotoCameraImage> | \nCamera撮影。 | \n
pickImages | \n(options: PhotoCameraOptions) => Promise<{ photos: PhotoCameraImage[] }> | \nAlbum選択。 | \n
interface PhotoCameraOptions| Prop | \nType | \nDescription | \n
|---|---|---|
quality | \nnumber | \n要求するImage qualityです。 | \n
width | \nnumber | \n要求するImage widthです。 | \n
limit? | \nnumber | \n任意のAlbum選択上限です。 | \n
source? | \n'camera' | \n任意のCamera専用source markerです。 | \n
interface PhotoCameraImage| Prop | \nType | \nDescription | \n
|---|---|---|
dataUrl? | \nstring | \nImage Data URLです。 | \n
webPath? | \nstring | \nBrowserから参照できるImage URLです。 | \n
interface PhotoFiltername、type、option、data、width、height を持つrender済みFilter previewです。
type alias PhotoFilterOptions{ blur: number } | { brightness: number } | { noise: number } | { blocksize: number } | { color: string; distance: number; useAlpha?: boolean } | { mode: string; color: string; alpha?: number } | { maskObjId: number } | null
interface PhotoFilterPresetname、type、option を持つFilter Menu presetです。
interface PhotoSize数値の width と height を持つ2次元pixel sizeです。
Attach scroll-aware headers to Ionic content. Call this after Installation. Import the package CSS globally first. Safe-area hidden headers and always-visible native headers are covered on Safe Area.
\nimport { Component } from '@angular/core';\nimport { IonContent, IonHeader, IonItem, IonLabel, IonList, IonTitle, IonToolbar } from '@ionic/angular';\nimport { ScrollHeaderDirective } from '@rdlabo/ionic-angular-scroll-header';\n\n@Component({\n selector: 'app-scroll-header',\n imports: [IonContent, IonHeader, IonToolbar, IonTitle, IonList, IonItem, IonLabel, ScrollHeaderDirective],\n template: `\n <ion-header class=\"hidden\">\n <ion-toolbar></ion-toolbar>\n </ion-header>\n <ion-content rdlaboScrollHeader>\n <ion-header>\n <ion-toolbar>\n <ion-title>Scroll header</ion-title>\n </ion-toolbar>\n </ion-header>\n <ion-list>\n @for (item of items; track item) {\n <ion-item>\n <ion-label>{{ item }}</ion-label>\n </ion-item>\n }\n </ion-list>\n </ion-content>\n `,\n})\nexport class ScrollHeaderPage {\n readonly items = Array.from({ length: 40 }, (_, index) => `Row ${index + 1}`);\n}\nScroll down — the content header hides. Scroll up — it returns. The outer ion-header.hidden reserves safe-area space; see Safe Area when you need a different header layout.
Attach scroll-aware headers to Ionic content. Call this after Installation. Import the package CSS globally first. Safe-area hidden headers and always-visible native headers are covered on Safe Area.
\nimport { Component } from '@angular/core';\nimport { IonContent, IonHeader, IonItem, IonLabel, IonList, IonTitle, IonToolbar } from '@ionic/angular';\nimport { ScrollHeaderDirective } from '@rdlabo/ionic-angular-scroll-header';\n\n@Component({\n selector: 'app-scroll-header',\n imports: [IonContent, IonHeader, IonToolbar, IonTitle, IonList, IonItem, IonLabel, ScrollHeaderDirective],\n template: `\n <ion-header class=\"hidden\">\n <ion-toolbar></ion-toolbar>\n </ion-header>\n <ion-content rdlaboScrollHeader>\n <ion-header>\n <ion-toolbar>\n <ion-title>Scroll header</ion-title>\n </ion-toolbar>\n </ion-header>\n <ion-list>\n @for (item of items; track item) {\n <ion-item>\n <ion-label>{{ item }}</ion-label>\n </ion-item>\n }\n </ion-list>\n </ion-content>\n `,\n})\nexport class ScrollHeaderPage {\n readonly items = Array.from({ length: 40 }, (_, index) => `Row ${index + 1}`);\n}\nScroll down — the content header hides. Scroll up — it returns. The outer ion-header.hidden reserves safe-area space; see Safe Area when you need a different header layout.
Extend an existing CDK virtual-scroll viewport with scroll-aware headers. Start from IonContent for the header/safe-area pattern, then replace the scroll host with a viewport. Call this after Installation.
\nimport { Component } from '@angular/core';\nimport { CdkFixedSizeVirtualScroll, CdkVirtualForOf, CdkVirtualScrollViewport } from '@angular/cdk/scrolling';\nimport { IonContent, IonHeader, IonTitle, IonToolbar } from '@ionic/angular';\nimport { VirtualScrollHeaderDirective } from '@rdlabo/ionic-angular-scroll-header';\n\n@Component({\n selector: 'app-virtual-scroll-header',\n imports: [\n IonContent,\n IonHeader,\n IonToolbar,\n IonTitle,\n CdkVirtualScrollViewport,\n CdkVirtualForOf,\n CdkFixedSizeVirtualScroll,\n VirtualScrollHeaderDirective,\n ],\n template: `\n <ion-header class=\"hidden\">\n <ion-toolbar></ion-toolbar>\n </ion-header>\n <ion-content rdlaboVirtualScrollHeader>\n <ion-header>\n <ion-toolbar>\n <ion-title>Virtual scroll header</ion-title>\n </ion-toolbar>\n </ion-header>\n <cdk-virtual-scroll-viewport\n minBufferPx=\"900\"\n maxBufferPx=\"1350\"\n [itemSize]=\"44\"\n class=\"ion-content-scroll-host\"\n >\n <div *cdkVirtualFor=\"let item of items; trackBy: trackByFn\" style=\"height: 44px\">\n {{ item }}\n </div>\n </cdk-virtual-scroll-viewport>\n </ion-content>\n `,\n})\nexport class VirtualScrollHeaderPage {\n readonly items = Array.from({ length: 80 }, (_, index) => `Row ${index + 1}`);\n trackByFn = (_: number, item: string) => item;\n}\nGive the viewport a definite height via the global CSS from Installation.
\nAddresses angular/components#27104 when an existing CDK viewport jumps back while scrolling.
\nAdd the directive to the imports of the component above, then add its attribute to the existing viewport. Keep the CDK imports and data from that example.
import { FixVirtualScrollElementDirective } from '@rdlabo/ionic-angular-scroll-header';\n\n<ion-content>\n <cdk-virtual-scroll-viewport\n rdlaboFixVirtualScrollElement\n minBufferPx=\"900\"\n maxBufferPx=\"1350\"\n [itemSize]=\"44\"\n class=\"ion-content-scroll-host\"\n >\n <div *cdkVirtualFor=\"let item of items; trackBy: trackByFn\" style=\"height: 44px\">\n {{ item }}\n </div>\n </cdk-virtual-scroll-viewport>\n</ion-content>\nExtend an existing CDK virtual-scroll viewport with scroll-aware headers. Start from IonContent for the header/safe-area pattern, then replace the scroll host with a viewport. Call this after Installation.
\nimport { Component } from '@angular/core';\nimport { CdkFixedSizeVirtualScroll, CdkVirtualForOf, CdkVirtualScrollViewport } from '@angular/cdk/scrolling';\nimport { IonContent, IonHeader, IonTitle, IonToolbar } from '@ionic/angular';\nimport { VirtualScrollHeaderDirective } from '@rdlabo/ionic-angular-scroll-header';\n\n@Component({\n selector: 'app-virtual-scroll-header',\n imports: [\n IonContent,\n IonHeader,\n IonToolbar,\n IonTitle,\n CdkVirtualScrollViewport,\n CdkVirtualForOf,\n CdkFixedSizeVirtualScroll,\n VirtualScrollHeaderDirective,\n ],\n template: `\n <ion-header class=\"hidden\">\n <ion-toolbar></ion-toolbar>\n </ion-header>\n <ion-content rdlaboVirtualScrollHeader>\n <ion-header>\n <ion-toolbar>\n <ion-title>Virtual scroll header</ion-title>\n </ion-toolbar>\n </ion-header>\n <cdk-virtual-scroll-viewport\n minBufferPx=\"900\"\n maxBufferPx=\"1350\"\n [itemSize]=\"44\"\n class=\"ion-content-scroll-host\"\n >\n <div *cdkVirtualFor=\"let item of items; trackBy: trackByFn\" style=\"height: 44px\">\n {{ item }}\n </div>\n </cdk-virtual-scroll-viewport>\n </ion-content>\n `,\n})\nexport class VirtualScrollHeaderPage {\n readonly items = Array.from({ length: 80 }, (_, index) => `Row ${index + 1}`);\n trackByFn = (_: number, item: string) => item;\n}\nGive the viewport a definite height via the global CSS from Installation.
\nAddresses angular/components#27104 when an existing CDK viewport jumps back while scrolling.
\nAdd the directive to the imports of the component above, then add its attribute to the existing viewport. Keep the CDK imports and data from that example.
import { FixVirtualScrollElementDirective } from '@rdlabo/ionic-angular-scroll-header';\n\n<ion-content>\n <cdk-virtual-scroll-viewport\n rdlaboFixVirtualScrollElement\n minBufferPx=\"900\"\n maxBufferPx=\"1350\"\n [itemSize]=\"44\"\n class=\"ion-content-scroll-host\"\n >\n <div *cdkVirtualFor=\"let item of items; trackBy: trackByFn\" style=\"height: 44px\">\n {{ item }}\n </div>\n </cdk-virtual-scroll-viewport>\n</ion-content>\nReference for the public standalone directives exported by @rdlabo/ionic-angular-scroll-header v22.0.0.
directive ScrollHeaderDirectiveConnects an ion-content scroll stream to a projected header element.
| Selector | \nContent child | \nDescription | \n
|---|---|---|
ion-content[rdlaboScrollHeader] | \nscrollHeader | \nHeader element that follows the IonContent scroll position. | \n
directive VirtualScrollHeaderDirectiveConnects a CDK virtual-scroll viewport to a projected header element and removes its subscription when destroyed.
| Selector | \nContent child | \nDescription | \n
|---|---|---|
ion-content[rdlaboVirtualScrollHeader] | \nvirtualScroll | \nCdkVirtualScrollViewport to observe. | \n
ion-content[rdlaboVirtualScrollHeader] | \nscrollHeader | \nHeader element that follows the virtual-scroll offset. | \n
directive FixVirtualScrollElementDirectiveApplies the viewport element correction used by the package's CDK virtual-scroll integration.
| Selector | \nDescription | \n
|---|---|
cdk-virtual-scroll-viewport[rdlaboFixVirtualScrollElement] | \nCorrects the virtual-scroll viewport element during initialization. | \n
Reference for the public standalone directives exported by @rdlabo/ionic-angular-scroll-header v22.0.3.
directive ScrollHeaderDirectiveConnects an ion-content scroll stream to a projected header element.
| Selector | \nContent child | \nDescription | \n
|---|---|---|
ion-content[rdlaboScrollHeader] | \nscrollHeader | \nHeader element that follows the IonContent scroll position. | \n
directive VirtualScrollHeaderDirectiveConnects a CDK virtual-scroll viewport to a projected header element and removes its subscription when destroyed.
| Selector | \nContent child | \nDescription | \n
|---|---|---|
ion-content[rdlaboVirtualScrollHeader] | \nvirtualScroll | \nCdkVirtualScrollViewport to observe. | \n
ion-content[rdlaboVirtualScrollHeader] | \nscrollHeader | \nHeader element that follows the virtual-scroll offset. | \n
directive FixVirtualScrollElementDirectiveApplies the viewport element correction used by the package's CDK virtual-scroll integration.
| Selector | \nDescription | \n
|---|---|
cdk-virtual-scroll-viewport[rdlaboFixVirtualScrollElement] | \nCorrects the virtual-scroll viewport element during initialization. | \n
Ionic Content に Scroll 連動 Header を付けます。インストール のあとで呼び出します。先にpackage CSSをグローバルへimportしてください。Safe Area用の非表示Headerと常時表示するNative Headerの使い分けは、Safe Areaガイドを参照してください。
\nimport { Component } from '@angular/core';\nimport { IonContent, IonHeader, IonItem, IonLabel, IonList, IonTitle, IonToolbar } from '@ionic/angular';\nimport { ScrollHeaderDirective } from '@rdlabo/ionic-angular-scroll-header';\n\n@Component({\n selector: 'app-scroll-header',\n imports: [IonContent, IonHeader, IonToolbar, IonTitle, IonList, IonItem, IonLabel, ScrollHeaderDirective],\n template: `\n <ion-header class=\"hidden\">\n <ion-toolbar></ion-toolbar>\n </ion-header>\n <ion-content rdlaboScrollHeader>\n <ion-header>\n <ion-toolbar>\n <ion-title>Scroll header</ion-title>\n </ion-toolbar>\n </ion-header>\n <ion-list>\n @for (item of items; track item) {\n <ion-item>\n <ion-label>{{ item }}</ion-label>\n </ion-item>\n }\n </ion-list>\n </ion-content>\n `,\n})\nexport class ScrollHeaderPage {\n readonly items = Array.from({ length: 40 }, (_, index) => `Row ${index + 1}`);\n}\n下へスクロールするとContent Headerが隠れ、上へスクロールすると戻ります。外側の ion-header.hidden はSafe Area用の余白を確保します。別のHeader構成は Safe Area を参照してください。
Ionic Content に Scroll 連動 Header を付けます。インストール のあとで呼び出します。先にpackage CSSをグローバルへimportしてください。Safe Area用の非表示Headerと常時表示するNative Headerの使い分けは、Safe Areaガイドを参照してください。
\nimport { Component } from '@angular/core';\nimport { IonContent, IonHeader, IonItem, IonLabel, IonList, IonTitle, IonToolbar } from '@ionic/angular';\nimport { ScrollHeaderDirective } from '@rdlabo/ionic-angular-scroll-header';\n\n@Component({\n selector: 'app-scroll-header',\n imports: [IonContent, IonHeader, IonToolbar, IonTitle, IonList, IonItem, IonLabel, ScrollHeaderDirective],\n template: `\n <ion-header class=\"hidden\">\n <ion-toolbar></ion-toolbar>\n </ion-header>\n <ion-content rdlaboScrollHeader>\n <ion-header>\n <ion-toolbar>\n <ion-title>Scroll header</ion-title>\n </ion-toolbar>\n </ion-header>\n <ion-list>\n @for (item of items; track item) {\n <ion-item>\n <ion-label>{{ item }}</ion-label>\n </ion-item>\n }\n </ion-list>\n </ion-content>\n `,\n})\nexport class ScrollHeaderPage {\n readonly items = Array.from({ length: 40 }, (_, index) => `Row ${index + 1}`);\n}\n下へスクロールするとContent Headerが隠れ、上へスクロールすると戻ります。外側の ion-header.hidden はSafe Area用の余白を確保します。別のHeader構成は Safe Area を参照してください。
既存のCDK Virtual Scroll Viewportへ、Scroll連動Headerを拡張します。Header/Safe Areaの形は IonContent を起点にし、Scroll HostをViewportへ置き換えます。インストール のあとで呼び出します。
\nimport { Component } from '@angular/core';\nimport { CdkFixedSizeVirtualScroll, CdkVirtualForOf, CdkVirtualScrollViewport } from '@angular/cdk/scrolling';\nimport { IonContent, IonHeader, IonTitle, IonToolbar } from '@ionic/angular';\nimport { VirtualScrollHeaderDirective } from '@rdlabo/ionic-angular-scroll-header';\n\n@Component({\n selector: 'app-virtual-scroll-header',\n imports: [\n IonContent,\n IonHeader,\n IonToolbar,\n IonTitle,\n CdkVirtualScrollViewport,\n CdkVirtualForOf,\n CdkFixedSizeVirtualScroll,\n VirtualScrollHeaderDirective,\n ],\n template: `\n <ion-header class=\"hidden\">\n <ion-toolbar></ion-toolbar>\n </ion-header>\n <ion-content rdlaboVirtualScrollHeader>\n <ion-header>\n <ion-toolbar>\n <ion-title>Virtual scroll header</ion-title>\n </ion-toolbar>\n </ion-header>\n <cdk-virtual-scroll-viewport\n minBufferPx=\"900\"\n maxBufferPx=\"1350\"\n [itemSize]=\"44\"\n class=\"ion-content-scroll-host\"\n >\n <div *cdkVirtualFor=\"let item of items; trackBy: trackByFn\" style=\"height: 44px\">\n {{ item }}\n </div>\n </cdk-virtual-scroll-viewport>\n </ion-content>\n `,\n})\nexport class VirtualScrollHeaderPage {\n readonly items = Array.from({ length: 80 }, (_, index) => `Row ${index + 1}`);\n trackByFn = (_: number, item: string) => item;\n}\nViewportには インストール のグローバルCSSで確定した高さを与えてください。
\n既存のCDK Viewportがスクロール中に先頭へ跳ね返る場合の対策です。angular/components#27104 を参照してください。
\n上のコンポーネントの imports にこのdirectiveを追加し、既存のviewportへ属性を付けます。CDKのimportsとデータはそのまま使います。
import { FixVirtualScrollElementDirective } from '@rdlabo/ionic-angular-scroll-header';\n\n<ion-content>\n <cdk-virtual-scroll-viewport\n rdlaboFixVirtualScrollElement\n minBufferPx=\"900\"\n maxBufferPx=\"1350\"\n [itemSize]=\"44\"\n class=\"ion-content-scroll-host\"\n >\n <div *cdkVirtualFor=\"let item of items; trackBy: trackByFn\" style=\"height: 44px\">\n {{ item }}\n </div>\n </cdk-virtual-scroll-viewport>\n</ion-content>\n既存のCDK Virtual Scroll Viewportへ、Scroll連動Headerを拡張します。Header/Safe Areaの形は IonContent を起点にし、Scroll HostをViewportへ置き換えます。インストール のあとで呼び出します。
\nimport { Component } from '@angular/core';\nimport { CdkFixedSizeVirtualScroll, CdkVirtualForOf, CdkVirtualScrollViewport } from '@angular/cdk/scrolling';\nimport { IonContent, IonHeader, IonTitle, IonToolbar } from '@ionic/angular';\nimport { VirtualScrollHeaderDirective } from '@rdlabo/ionic-angular-scroll-header';\n\n@Component({\n selector: 'app-virtual-scroll-header',\n imports: [\n IonContent,\n IonHeader,\n IonToolbar,\n IonTitle,\n CdkVirtualScrollViewport,\n CdkVirtualForOf,\n CdkFixedSizeVirtualScroll,\n VirtualScrollHeaderDirective,\n ],\n template: `\n <ion-header class=\"hidden\">\n <ion-toolbar></ion-toolbar>\n </ion-header>\n <ion-content rdlaboVirtualScrollHeader>\n <ion-header>\n <ion-toolbar>\n <ion-title>Virtual scroll header</ion-title>\n </ion-toolbar>\n </ion-header>\n <cdk-virtual-scroll-viewport\n minBufferPx=\"900\"\n maxBufferPx=\"1350\"\n [itemSize]=\"44\"\n class=\"ion-content-scroll-host\"\n >\n <div *cdkVirtualFor=\"let item of items; trackBy: trackByFn\" style=\"height: 44px\">\n {{ item }}\n </div>\n </cdk-virtual-scroll-viewport>\n </ion-content>\n `,\n})\nexport class VirtualScrollHeaderPage {\n readonly items = Array.from({ length: 80 }, (_, index) => `Row ${index + 1}`);\n trackByFn = (_: number, item: string) => item;\n}\nViewportには インストール のグローバルCSSで確定した高さを与えてください。
\n既存のCDK Viewportがスクロール中に先頭へ跳ね返る場合の対策です。angular/components#27104 を参照してください。
\n上のコンポーネントの imports にこのdirectiveを追加し、既存のviewportへ属性を付けます。CDKのimportsとデータはそのまま使います。
import { FixVirtualScrollElementDirective } from '@rdlabo/ionic-angular-scroll-header';\n\n<ion-content>\n <cdk-virtual-scroll-viewport\n rdlaboFixVirtualScrollElement\n minBufferPx=\"900\"\n maxBufferPx=\"1350\"\n [itemSize]=\"44\"\n class=\"ion-content-scroll-host\"\n >\n <div *cdkVirtualFor=\"let item of items; trackBy: trackByFn\" style=\"height: 44px\">\n {{ item }}\n </div>\n </cdk-virtual-scroll-viewport>\n</ion-content>\n@rdlabo/ionic-angular-scroll-header v22.0.0 が公開するstandalone directiveのリファレンスです。
directive ScrollHeaderDirectiveion-content のscroll streamと投影されたHeader elementを接続します。
| Selector | \nContent child | \nDescription | \n
|---|---|---|
ion-content[rdlaboScrollHeader] | \nscrollHeader | \nIonContentのscroll位置に追従するHeader elementです。 | \n
directive VirtualScrollHeaderDirectiveCDK Virtual Scroll viewportと投影されたHeader elementを接続し、破棄時にsubscriptionを解除します。
| Selector | \nContent child | \nDescription | \n
|---|---|---|
ion-content[rdlaboVirtualScrollHeader] | \nvirtualScroll | \n監視対象の CdkVirtualScrollViewport です。 | \n
ion-content[rdlaboVirtualScrollHeader] | \nscrollHeader | \nVirtual Scrollのoffsetに追従するHeader elementです。 | \n
directive FixVirtualScrollElementDirectivePackageのCDK Virtual Scroll連携で使用するviewport element補正を適用します。
| Selector | \nDescription | \n
|---|---|
cdk-virtual-scroll-viewport[rdlaboFixVirtualScrollElement] | \n初期化時にVirtual Scroll viewport elementを補正します。 | \n
@rdlabo/ionic-angular-scroll-header v22.0.3 が公開するstandalone directiveのリファレンスです。
directive ScrollHeaderDirectiveion-content のscroll streamと投影されたHeader elementを接続します。
| Selector | \nContent child | \nDescription | \n
|---|---|---|
ion-content[rdlaboScrollHeader] | \nscrollHeader | \nIonContentのscroll位置に追従するHeader elementです。 | \n
directive VirtualScrollHeaderDirectiveCDK Virtual Scroll viewportと投影されたHeader elementを接続し、破棄時にsubscriptionを解除します。
| Selector | \nContent child | \nDescription | \n
|---|---|---|
ion-content[rdlaboVirtualScrollHeader] | \nvirtualScroll | \n監視対象の CdkVirtualScrollViewport です。 | \n
ion-content[rdlaboVirtualScrollHeader] | \nscrollHeader | \nVirtual Scrollのoffsetに追従するHeader elementです。 | \n
directive FixVirtualScrollElementDirectivePackageのCDK Virtual Scroll連携で使用するviewport element補正を適用します。
| Selector | \nDescription | \n
|---|---|
cdk-virtual-scroll-viewport[rdlaboFixVirtualScrollElement] | \n初期化時にVirtual Scroll viewport elementを補正します。 | \n
Call this after Installation.
\n\n\nFor items whose variable heights are already known.
\n
Give the viewport a definite height in global CSS:
\ncdk-virtual-scroll-viewport {\n width: 100%;\n height: 320px;\n}\nimport { Component, computed, signal } from '@angular/core';\nimport { CdkVirtualForOf, CdkVirtualScrollViewport } from '@angular/cdk/scrolling';\nimport { CdkDynamicSizeVirtualScroll, itemDynamicSize } from '@rdlabo/ngx-cdk-scroll-strategies';\n\ntype Item = itemDynamicSize & { trackId: number };\n\n@Component({\n selector: 'app-scroll-simple',\n imports: [CdkVirtualScrollViewport, CdkVirtualForOf, CdkDynamicSizeVirtualScroll],\n template: `\n <cdk-virtual-scroll-viewport\n [itemDynamicSizes]=\"dynamicSize()\"\n minBufferPx=\"900\"\n maxBufferPx=\"1350\"\n >\n <div\n *cdkVirtualFor=\"let item of items(); trackBy: trackByFn\"\n class=\"dynamic-item\"\n [style.height.px]=\"item.itemSize\"\n >\n itemSize: {{ item.itemSize }}\n </div>\n </cdk-virtual-scroll-viewport>\n `,\n})\nexport class ScrollSimplePage {\n readonly items = signal<Item[]>(\n Array.from({ length: 20 }, (_, index) => ({\n trackId: index,\n itemSize: 40 + (index % 5) * 16,\n })),\n );\n readonly dynamicSize = computed<itemDynamicSize[]>(() =>\n this.items().map((item) => ({ trackId: item.trackId, itemSize: item.itemSize })),\n );\n trackByFn = (_: number, item: Item) => item.trackId;\n}\nRows use different heights so scrolling changes which sizes stay in view. Other than [itemDynamicSizes], it works the same way as @angular/cdk/scrolling.
Call this after Installation.
\n\n\nFor items whose variable heights are already known.
\n
Give the viewport a definite height in global CSS:
\ncdk-virtual-scroll-viewport {\n width: 100%;\n height: 320px;\n}\nimport { Component, computed, signal } from '@angular/core';\nimport { CdkVirtualForOf, CdkVirtualScrollViewport } from '@angular/cdk/scrolling';\nimport { CdkDynamicSizeVirtualScroll, itemDynamicSize } from '@rdlabo/ngx-cdk-scroll-strategies';\n\ntype Item = itemDynamicSize & { trackId: number };\n\n@Component({\n selector: 'app-scroll-simple',\n imports: [CdkVirtualScrollViewport, CdkVirtualForOf, CdkDynamicSizeVirtualScroll],\n template: `\n <cdk-virtual-scroll-viewport\n [itemDynamicSizes]=\"dynamicSize()\"\n minBufferPx=\"900\"\n maxBufferPx=\"1350\"\n >\n <div\n *cdkVirtualFor=\"let item of items(); trackBy: trackByFn\"\n class=\"dynamic-item\"\n [style.height.px]=\"item.itemSize\"\n >\n itemSize: {{ item.itemSize }}\n </div>\n </cdk-virtual-scroll-viewport>\n `,\n})\nexport class ScrollSimplePage {\n readonly items = signal<Item[]>(\n Array.from({ length: 20 }, (_, index) => ({\n trackId: index,\n itemSize: 40 + (index % 5) * 16,\n })),\n );\n readonly dynamicSize = computed<itemDynamicSize[]>(() =>\n this.items().map((item) => ({ trackId: item.trackId, itemSize: item.itemSize })),\n );\n trackByFn = (_: number, item: Item) => item.trackId;\n}\nRows use different heights so scrolling changes which sizes stay in view. Other than [itemDynamicSizes], it works the same way as @angular/cdk/scrolling.
Call this after Installation. Reuse the Simple Usage viewport, CDK imports, trackBy, and size-model rules. Replace known itemSize values with measured heights.
\n\nMeasure each scroll item as a separate component, then write the result into the size model that drives
\n[itemDynamicSizes].
Keep a measurement cache keyed by trackId. After the item renders, read its height and update the cache. A parent computed maps items to itemDynamicSize[], using the cache when present and a temporary estimate until the first measurement lands:
import { afterRenderEffect, Component, computed, ElementRef, inject, Injectable, input, signal, untracked } from '@angular/core';\nimport { CdkVirtualForOf, CdkVirtualScrollViewport } from '@angular/cdk/scrolling';\nimport { CdkDynamicSizeVirtualScroll, itemDynamicSize } from '@rdlabo/ngx-cdk-scroll-strategies';\n\ntype Row = { trackId: string; body: string };\ntype SizeCache = { trackId: string; itemSize: number };\n\n@Injectable()\nexport class RowSizeCache {\n readonly entries = signal<SizeCache[]>([]);\n}\n\n@Component({\n selector: 'app-measured-row',\n template: `<div class=\"row\">{{ item().body }}</div>`,\n styles: `:host { display: block; } .row { white-space: pre-wrap; padding: 12px; }`,\n})\nexport class MeasuredRow {\n private readonly el = inject(ElementRef<HTMLElement>);\n private readonly sizes = inject(RowSizeCache);\n readonly item = input.required<Row>();\n\n constructor() {\n afterRenderEffect((onCleanup) => {\n const trackId = this.item().trackId;\n const element = this.el.nativeElement;\n const measure = () => {\n const itemSize = Math.ceil(element.getBoundingClientRect().height);\n if (itemSize <= 0) return;\n untracked(() => this.sizes.entries.update((cache) => {\n const previous = cache.find((entry) => entry.trackId === trackId);\n if (previous?.itemSize === itemSize) return cache;\n return [...cache.filter((entry) => entry.trackId !== trackId), { trackId, itemSize }];\n }));\n };\n const observer = new ResizeObserver(measure);\n observer.observe(element);\n measure();\n onCleanup(() => observer.disconnect());\n });\n }\n}\n\n@Component({\n selector: 'app-scroll-advanced',\n providers: [RowSizeCache],\n styles: `cdk-virtual-scroll-viewport { height: 320px; width: 100%; }`,\n imports: [CdkVirtualScrollViewport, CdkVirtualForOf, CdkDynamicSizeVirtualScroll, MeasuredRow],\n template: `\n <cdk-virtual-scroll-viewport\n [itemDynamicSizes]=\"dynamicSize()\"\n minBufferPx=\"900\"\n maxBufferPx=\"1350\"\n >\n <app-measured-row\n *cdkVirtualFor=\"let item of items(); trackBy: trackByFn\"\n [item]=\"item\"\n />\n </cdk-virtual-scroll-viewport>\n `,\n})\nexport class ScrollAdvancedPage {\n private readonly sizes = inject(RowSizeCache);\n readonly items = signal<Row[]>(\n Array.from({ length: 20 }, (_, index) => ({\n trackId: String(index),\n body: `Row ${index + 1}\\n`.repeat(1 + (index % 4)),\n })),\n );\n readonly dynamicSize = computed<itemDynamicSize[]>(() =>\n this.items().map((item) => {\n const cached = this.sizes.entries().find((entry) => entry.trackId === item.trackId)?.itemSize;\n return {\n trackId: item.trackId,\n itemSize: cached ?? 80,\n source: cached === undefined ? 'temporary' : 'cache',\n };\n }),\n );\n trackByFn = (_: number, item: Row) => item.trackId;\n}\nEach itemSize must remain a finite number greater than zero. Until measurement arrives, pass a temporary positive estimate so lengths stay aligned; replace it when the cache updates. See the demo source for Infinite Scroll, refresher, and DynamicSizeVirtualScrollService helpers around this same measurement flow.
Call this after Installation. Reuse the Simple Usage viewport, CDK imports, trackBy, and size-model rules. Replace known itemSize values with measured heights.
\n\nMeasure each scroll item as a separate component, then write the result into the size model that drives
\n[itemDynamicSizes].
Keep a measurement cache keyed by trackId. After the item renders, read its height and update the cache. A parent computed maps items to itemDynamicSize[], using the cache when present and a temporary estimate until the first measurement lands:
import { afterRenderEffect, Component, computed, ElementRef, inject, Injectable, input, signal, untracked } from '@angular/core';\nimport { CdkVirtualForOf, CdkVirtualScrollViewport } from '@angular/cdk/scrolling';\nimport { CdkDynamicSizeVirtualScroll, itemDynamicSize } from '@rdlabo/ngx-cdk-scroll-strategies';\n\ntype Row = { trackId: string; body: string };\ntype SizeCache = { trackId: string; itemSize: number };\n\n@Injectable()\nexport class RowSizeCache {\n readonly entries = signal<SizeCache[]>([]);\n}\n\n@Component({\n selector: 'app-measured-row',\n template: `<div class=\"row\">{{ item().body }}</div>`,\n styles: `:host { display: block; } .row { white-space: pre-wrap; padding: 12px; }`,\n})\nexport class MeasuredRow {\n private readonly el = inject(ElementRef<HTMLElement>);\n private readonly sizes = inject(RowSizeCache);\n readonly item = input.required<Row>();\n\n constructor() {\n afterRenderEffect((onCleanup) => {\n const trackId = this.item().trackId;\n const element = this.el.nativeElement;\n const measure = () => {\n const itemSize = Math.ceil(element.getBoundingClientRect().height);\n if (itemSize <= 0) return;\n untracked(() => this.sizes.entries.update((cache) => {\n const previous = cache.find((entry) => entry.trackId === trackId);\n if (previous?.itemSize === itemSize) return cache;\n return [...cache.filter((entry) => entry.trackId !== trackId), { trackId, itemSize }];\n }));\n };\n const observer = new ResizeObserver(measure);\n observer.observe(element);\n measure();\n onCleanup(() => observer.disconnect());\n });\n }\n}\n\n@Component({\n selector: 'app-scroll-advanced',\n providers: [RowSizeCache],\n styles: `cdk-virtual-scroll-viewport { height: 320px; width: 100%; }`,\n imports: [CdkVirtualScrollViewport, CdkVirtualForOf, CdkDynamicSizeVirtualScroll, MeasuredRow],\n template: `\n <cdk-virtual-scroll-viewport\n [itemDynamicSizes]=\"dynamicSize()\"\n minBufferPx=\"900\"\n maxBufferPx=\"1350\"\n >\n <app-measured-row\n *cdkVirtualFor=\"let item of items(); trackBy: trackByFn\"\n [item]=\"item\"\n />\n </cdk-virtual-scroll-viewport>\n `,\n})\nexport class ScrollAdvancedPage {\n private readonly sizes = inject(RowSizeCache);\n readonly items = signal<Row[]>(\n Array.from({ length: 20 }, (_, index) => ({\n trackId: String(index),\n body: `Row ${index + 1}\\n`.repeat(1 + (index % 4)),\n })),\n );\n readonly dynamicSize = computed<itemDynamicSize[]>(() =>\n this.items().map((item) => {\n const cached = this.sizes.entries().find((entry) => entry.trackId === item.trackId)?.itemSize;\n return {\n trackId: item.trackId,\n itemSize: cached ?? 80,\n source: cached === undefined ? 'temporary' : 'cache',\n };\n }),\n );\n trackByFn = (_: number, item: Row) => item.trackId;\n}\nEach itemSize must remain a finite number greater than zero. Until measurement arrives, pass a temporary positive estimate so lengths stay aligned; replace it when the cache updates. See the demo source for Infinite Scroll, refresher, and DynamicSizeVirtualScrollService helpers around this same measurement flow.
Call this after Installation.
\n\n\nReuse the data model,
\ndynamicSizecomputed,trackBy, and viewport imports from Simple Usage. This page adds reverse layout CSS and[isReverse]=\"true\"for a chat-style list.
If reverse scroll, add isReverse directive to cdk-virtual-scroll-viewport tag.
<cdk-virtual-scroll-viewport\n [itemDynamicSizes]=\"dynamicSize()\"\n [isReverse]=\"true\"\n minBufferPx=\"900\"\n maxBufferPx=\"1350\"\n>\n <div class=\"reverse-items\">\n <div\n *cdkVirtualFor=\"let item of items(); trackBy: trackByFn\"\n class=\"dynamic-item\"\n [style.height.px]=\"item.itemSize\"\n >\n itemSize: {{ item.itemSize }}\n </div>\n </div>\n</cdk-virtual-scroll-viewport>\nAdd css to cdk-virtual-scroll-viewport.reverse-scroll at global css file like styles.css.
cdk-virtual-scroll-viewport {\n width: 100%;\n height: 320px;\n\n /* .reverse-scroll class is added from this directive. */\n &.reverse-scroll {\n display: flex;\n flex-direction: column-reverse;\n\n .cdk-virtual-scroll-content-wrapper {\n top: auto;\n bottom: 0;\n }\n }\n}\nAnd add item wrapper. div.reverse-items class is example. You can decide this.
div.reverse-items {\n height: 100%;\n display: flex;\n flex-direction: column-reverse;\n\n position: relative;\n bottom: 0;\n}\nIn Reverse Scroll, CdkVirtualScrollViewport's measureScrollOffset does not work. Please use the scrollOffset of this directive.\nhttps://github.com/rdlabo-dev/ionic-angular-library/blob/v22.0.0/projects/scroll-strategies/src/lib/dynamic-size-virtual-scroll-strategy.ts
\nThe reverse layout uses negative native scrollTop values. scrollToIndex() accepts a logical item index as usual and converts its cumulative offset to that native coordinate internally.
This package contains a Helper Service that simplifies development with Virtual Scroll.
\nimport { DynamicSizeVirtualScrollService } from '@rdlabo/ngx-cdk-scroll-strategies';\nDetail is here: https://github.com/rdlabo-dev/ionic-angular-library/blob/v22.0.0/projects/scroll-strategies/src/lib/dynamic-size-virtual-scroll.service.ts
\n", + "html": "Call this after Installation.
\n\n\nReuse the data model,
\ndynamicSizecomputed,trackBy, and viewport imports from Simple Usage. This page adds reverse layout CSS and[isReverse]=\"true\"for a chat-style list.
If reverse scroll, add isReverse directive to cdk-virtual-scroll-viewport tag.
<cdk-virtual-scroll-viewport\n [itemDynamicSizes]=\"dynamicSize()\"\n [isReverse]=\"true\"\n minBufferPx=\"900\"\n maxBufferPx=\"1350\"\n>\n <div class=\"reverse-items\">\n <div\n *cdkVirtualFor=\"let item of items(); trackBy: trackByFn\"\n class=\"dynamic-item\"\n [style.height.px]=\"item.itemSize\"\n >\n itemSize: {{ item.itemSize }}\n </div>\n </div>\n</cdk-virtual-scroll-viewport>\nAdd css to cdk-virtual-scroll-viewport.reverse-scroll at global css file like styles.css.
cdk-virtual-scroll-viewport {\n width: 100%;\n height: 320px;\n\n /* .reverse-scroll class is added from this directive. */\n &.reverse-scroll {\n display: flex;\n flex-direction: column-reverse;\n\n .cdk-virtual-scroll-content-wrapper {\n top: auto;\n bottom: 0;\n }\n }\n}\nAnd add item wrapper. div.reverse-items class is example. You can decide this.
div.reverse-items {\n height: 100%;\n display: flex;\n flex-direction: column-reverse;\n\n position: relative;\n bottom: 0;\n}\nIn Reverse Scroll, CdkVirtualScrollViewport's measureScrollOffset does not work. Please use the scrollOffset of this directive.\nhttps://github.com/rdlabo-dev/ionic-angular-library/blob/v22.0.3/projects/scroll-strategies/src/lib/dynamic-size-virtual-scroll-strategy.ts
\nThe reverse layout uses negative native scrollTop values. scrollToIndex() accepts a logical item index as usual and converts its cumulative offset to that native coordinate internally.
This package contains a Helper Service that simplifies development with Virtual Scroll.
\nimport { DynamicSizeVirtualScrollService } from '@rdlabo/ngx-cdk-scroll-strategies';\nDetail is here: https://github.com/rdlabo-dev/ionic-angular-library/blob/v22.0.3/projects/scroll-strategies/src/lib/dynamic-size-virtual-scroll.service.ts
\n", "headings": [ { "id": "optional", @@ -166,7 +166,7 @@ export const PROJECT = { "file": "api.md", "section": "Reference", "path": "/projects/ngx-cdk-scroll-strategies/docs/api", - "html": "Reference for the public API exported by @rdlabo/ngx-cdk-scroll-strategies v22.0.0.
directive CdkDynamicSizeVirtualScrollInstalls the dynamic-size strategy on a CDK virtual-scroll viewport.
| Input | \nType | \nDescription | \nDefault | \n
|---|---|---|---|
itemDynamicSizes | \nitemDynamicSize[] | \nExact size model for the list items. | \n[] | \n
minBufferPx | \nnumber | \nMinimum remaining buffer before rendering more items. | \n100 | \n
maxBufferPx | \nnumber | \nTarget buffer rendered when replenishing. | \n200 | \n
isReverse | \nboolean | \nEnables reverse virtual scrolling. | \nfalse | \n
scrollOffset | \nnumber | \nRead-only normalized offset for reverse scrolling. | \n\n |
class DynamicSizeVirtualScrollStrategyImplements Angular CDK VirtualScrollStrategy for item sizes known in advance.
| Member | \nType | \nDescription | \n
|---|---|---|
constructor | \n(itemSize, minBufferPx, maxBufferPx, isReverse) | \nCreates the strategy with its size model and buffer limits. | \n
updateItemAndBufferSize | \n(itemDynamicSize[], number, number, boolean) => void | \nReplaces the size model and buffer configuration. | \n
scrollToIndex | \n(index: number, behavior: ScrollBehavior) => void | \nScrolls to an item index. | \n
scrolledIndexChange | \nObservable<number> | \nEmits the currently scrolled index. | \n
measureScrollOffset | \nnumber | \nLast normalized scroll offset. | \n
class DynamicSizeVirtualScrollServiceProvides viewport lifecycle, item-height binding, refresh, and smooth-scroll helpers.
| Member | \nType | \nDescription | \n
|---|---|---|
onInit | \n(viewport, latestScrollOffset) => void | \nRestores viewport state. | \n
onDestroy | \n(viewport) => number | \nCaptures the offset for later restoration. | \n
getBindDynamicItemHeight | \n(sizes: Signal<itemDynamicSize[]>) => Signal<string[]> | \nConverts item sizes into CSS heights. | \n
refreshViewport | \n(viewport) => void | \nForces viewport dimensions to refresh. | \n
scrollToTopSmooth | \n(viewport) => Promise<void> | \nSmoothly scrolls to the top. | \n
scrollToPoint | \n(viewport, x, y, duration?) => Promise<void> | \nSmoothly scrolls to a point. | \n
function sumItemSize(dynamicSize: itemDynamicSize[], endIndex: number) => number
Returns the cumulative size of all items before endIndex.
function calculateItemCountForPixelDistance(dynamicSize: itemDynamicSize[], itemSizeRange: number, startIndex?: number, isReverse?: boolean) => number
Converts a pixel distance into an exact fractional item count.
function calcIndex(dynamicSize: itemDynamicSize[], itemSizeRange: number, startIndex?: number, isReverse?: boolean) => number
Legacy compatibility calculation. Use calculateItemCountForPixelDistance for continuous results.
interface itemDynamicSize| Prop | \nType | \nDescription | \n
|---|---|---|
itemSize | \nnumber | \nExact item size in pixels. | \n
| consumer metadata | \n`Record<string, string | \nnumber>` | \n
Reference for the public API exported by @rdlabo/ngx-cdk-scroll-strategies v22.0.3.
directive CdkDynamicSizeVirtualScrollInstalls the dynamic-size strategy on a CDK virtual-scroll viewport.
| Input | \nType | \nDescription | \nDefault | \n
|---|---|---|---|
itemDynamicSizes | \nitemDynamicSize[] | \nExact size model for the list items. | \n[] | \n
minBufferPx | \nnumber | \nMinimum remaining buffer before rendering more items. | \n100 | \n
maxBufferPx | \nnumber | \nTarget buffer rendered when replenishing. | \n200 | \n
isReverse | \nboolean | \nEnables reverse virtual scrolling. | \nfalse | \n
scrollOffset | \nnumber | \nRead-only normalized offset for reverse scrolling. | \n\n |
class DynamicSizeVirtualScrollStrategyImplements Angular CDK VirtualScrollStrategy for item sizes known in advance.
| Member | \nType | \nDescription | \n
|---|---|---|
constructor | \n(itemSize, minBufferPx, maxBufferPx, isReverse) | \nCreates the strategy with its size model and buffer limits. | \n
updateItemAndBufferSize | \n(itemDynamicSize[], number, number, boolean) => void | \nReplaces the size model and buffer configuration. | \n
scrollToIndex | \n(index: number, behavior: ScrollBehavior) => void | \nScrolls to an item index. | \n
scrolledIndexChange | \nObservable<number> | \nEmits the currently scrolled index. | \n
measureScrollOffset | \nnumber | \nLast normalized scroll offset. | \n
class DynamicSizeVirtualScrollServiceProvides viewport lifecycle, item-height binding, refresh, and smooth-scroll helpers.
| Member | \nType | \nDescription | \n
|---|---|---|
onInit | \n(viewport, latestScrollOffset) => void | \nRestores viewport state. | \n
onDestroy | \n(viewport) => number | \nCaptures the offset for later restoration. | \n
getBindDynamicItemHeight | \n(sizes: Signal<itemDynamicSize[]>) => Signal<string[]> | \nConverts item sizes into CSS heights. | \n
refreshViewport | \n(viewport) => void | \nForces viewport dimensions to refresh. | \n
scrollToTopSmooth | \n(viewport) => Promise<void> | \nSmoothly scrolls to the top. | \n
scrollToPoint | \n(viewport, x, y, duration?) => Promise<void> | \nSmoothly scrolls to a point. | \n
function sumItemSize(dynamicSize: itemDynamicSize[], endIndex: number) => number
Returns the cumulative size of all items before endIndex.
function calculateItemCountForPixelDistance(dynamicSize: itemDynamicSize[], itemSizeRange: number, startIndex?: number, isReverse?: boolean) => number
Converts a pixel distance into an exact fractional item count.
function calcIndex(dynamicSize: itemDynamicSize[], itemSizeRange: number, startIndex?: number, isReverse?: boolean) => number
Legacy compatibility calculation. Use calculateItemCountForPixelDistance for continuous results.
interface itemDynamicSize| Prop | \nType | \nDescription | \n
|---|---|---|
itemSize | \nnumber | \nExact item size in pixels. | \n
| consumer metadata | \n`Record<string, string | \nnumber>` | \n
インストール のあとで使います。
\n\n\n各Itemの可変Heightがすでに分かっている場合に使います。
\n
ViewportにはグローバルCSSで確定した高さを与えます。
\ncdk-virtual-scroll-viewport {\n width: 100%;\n height: 320px;\n}\nimport { Component, computed, signal } from '@angular/core';\nimport { CdkVirtualForOf, CdkVirtualScrollViewport } from '@angular/cdk/scrolling';\nimport { CdkDynamicSizeVirtualScroll, itemDynamicSize } from '@rdlabo/ngx-cdk-scroll-strategies';\n\ntype Item = itemDynamicSize & { trackId: number };\n\n@Component({\n selector: 'app-scroll-simple',\n imports: [CdkVirtualScrollViewport, CdkVirtualForOf, CdkDynamicSizeVirtualScroll],\n template: `\n <cdk-virtual-scroll-viewport\n [itemDynamicSizes]=\"dynamicSize()\"\n minBufferPx=\"900\"\n maxBufferPx=\"1350\"\n >\n <div\n *cdkVirtualFor=\"let item of items(); trackBy: trackByFn\"\n class=\"dynamic-item\"\n [style.height.px]=\"item.itemSize\"\n >\n itemSize: {{ item.itemSize }}\n </div>\n </cdk-virtual-scroll-viewport>\n `,\n})\nexport class ScrollSimplePage {\n readonly items = signal<Item[]>(\n Array.from({ length: 20 }, (_, index) => ({\n trackId: index,\n itemSize: 40 + (index % 5) * 16,\n })),\n );\n readonly dynamicSize = computed<itemDynamicSize[]>(() =>\n this.items().map((item) => ({ trackId: item.trackId, itemSize: item.itemSize })),\n );\n trackByFn = (_: number, item: Item) => item.trackId;\n}\n行ごとに高さが違うため、スクロールすると表示中のサイズが変わります。[itemDynamicSizes] 以外は @angular/cdk/scrolling と同じように動作します。
インストール のあとで使います。
\n\n\n各Itemの可変Heightがすでに分かっている場合に使います。
\n
ViewportにはグローバルCSSで確定した高さを与えます。
\ncdk-virtual-scroll-viewport {\n width: 100%;\n height: 320px;\n}\nimport { Component, computed, signal } from '@angular/core';\nimport { CdkVirtualForOf, CdkVirtualScrollViewport } from '@angular/cdk/scrolling';\nimport { CdkDynamicSizeVirtualScroll, itemDynamicSize } from '@rdlabo/ngx-cdk-scroll-strategies';\n\ntype Item = itemDynamicSize & { trackId: number };\n\n@Component({\n selector: 'app-scroll-simple',\n imports: [CdkVirtualScrollViewport, CdkVirtualForOf, CdkDynamicSizeVirtualScroll],\n template: `\n <cdk-virtual-scroll-viewport\n [itemDynamicSizes]=\"dynamicSize()\"\n minBufferPx=\"900\"\n maxBufferPx=\"1350\"\n >\n <div\n *cdkVirtualFor=\"let item of items(); trackBy: trackByFn\"\n class=\"dynamic-item\"\n [style.height.px]=\"item.itemSize\"\n >\n itemSize: {{ item.itemSize }}\n </div>\n </cdk-virtual-scroll-viewport>\n `,\n})\nexport class ScrollSimplePage {\n readonly items = signal<Item[]>(\n Array.from({ length: 20 }, (_, index) => ({\n trackId: index,\n itemSize: 40 + (index % 5) * 16,\n })),\n );\n readonly dynamicSize = computed<itemDynamicSize[]>(() =>\n this.items().map((item) => ({ trackId: item.trackId, itemSize: item.itemSize })),\n );\n trackByFn = (_: number, item: Item) => item.trackId;\n}\n行ごとに高さが違うため、スクロールすると表示中のサイズが変わります。[itemDynamicSizes] 以外は @angular/cdk/scrolling と同じように動作します。
インストール のあとで使います。シンプルな使い方 のViewport、CDK import、trackBy、サイズモデルの規則を引き継ぎ、既知の itemSize を計測値へ置き換えます。
\n\n各Scroll Itemを別Componentにして高さを測り、その結果を
\n[itemDynamicSizes]を駆動するサイズモデルへ書き戻します。
trackId をキーにした計測cacheを保持します。Itemの描画後に高さを読み、cacheを更新します。親の computed がItemを itemDynamicSize[] へ写像し、cacheがあればそれを使い、初回計測までは一時的な見積りを置きます。
import { afterRenderEffect, Component, computed, ElementRef, inject, Injectable, input, signal, untracked } from '@angular/core';\nimport { CdkVirtualForOf, CdkVirtualScrollViewport } from '@angular/cdk/scrolling';\nimport { CdkDynamicSizeVirtualScroll, itemDynamicSize } from '@rdlabo/ngx-cdk-scroll-strategies';\n\ntype Row = { trackId: string; body: string };\ntype SizeCache = { trackId: string; itemSize: number };\n\n@Injectable()\nexport class RowSizeCache {\n readonly entries = signal<SizeCache[]>([]);\n}\n\n@Component({\n selector: 'app-measured-row',\n template: `<div class=\"row\">{{ item().body }}</div>`,\n styles: `:host { display: block; } .row { white-space: pre-wrap; padding: 12px; }`,\n})\nexport class MeasuredRow {\n private readonly el = inject(ElementRef<HTMLElement>);\n private readonly sizes = inject(RowSizeCache);\n readonly item = input.required<Row>();\n\n constructor() {\n afterRenderEffect((onCleanup) => {\n const trackId = this.item().trackId;\n const element = this.el.nativeElement;\n const measure = () => {\n const itemSize = Math.ceil(element.getBoundingClientRect().height);\n if (itemSize <= 0) return;\n untracked(() => this.sizes.entries.update((cache) => {\n const previous = cache.find((entry) => entry.trackId === trackId);\n if (previous?.itemSize === itemSize) return cache;\n return [...cache.filter((entry) => entry.trackId !== trackId), { trackId, itemSize }];\n }));\n };\n const observer = new ResizeObserver(measure);\n observer.observe(element);\n measure();\n onCleanup(() => observer.disconnect());\n });\n }\n}\n\n@Component({\n selector: 'app-scroll-advanced',\n providers: [RowSizeCache],\n styles: `cdk-virtual-scroll-viewport { height: 320px; width: 100%; }`,\n imports: [CdkVirtualScrollViewport, CdkVirtualForOf, CdkDynamicSizeVirtualScroll, MeasuredRow],\n template: `\n <cdk-virtual-scroll-viewport\n [itemDynamicSizes]=\"dynamicSize()\"\n minBufferPx=\"900\"\n maxBufferPx=\"1350\"\n >\n <app-measured-row\n *cdkVirtualFor=\"let item of items(); trackBy: trackByFn\"\n [item]=\"item\"\n />\n </cdk-virtual-scroll-viewport>\n `,\n})\nexport class ScrollAdvancedPage {\n private readonly sizes = inject(RowSizeCache);\n readonly items = signal<Row[]>(\n Array.from({ length: 20 }, (_, index) => ({\n trackId: String(index),\n body: `Row ${index + 1}\\n`.repeat(1 + (index % 4)),\n })),\n );\n readonly dynamicSize = computed<itemDynamicSize[]>(() =>\n this.items().map((item) => {\n const cached = this.sizes.entries().find((entry) => entry.trackId === item.trackId)?.itemSize;\n return {\n trackId: item.trackId,\n itemSize: cached ?? 80,\n source: cached === undefined ? 'temporary' : 'cache',\n };\n }),\n );\n trackByFn = (_: number, item: Row) => item.trackId;\n}\n各 itemSize は 0 より大きい有限の数値のままにしてください。計測が来るまでは長さを揃えるために一時的な正の見積りを渡し、cache更新で置き換えます。この計測の流れの周辺にあるInfinite Scroll、Refresher、DynamicSizeVirtualScrollService の補助はdemo sourceを参照してください。
インストール のあとで使います。シンプルな使い方 のViewport、CDK import、trackBy、サイズモデルの規則を引き継ぎ、既知の itemSize を計測値へ置き換えます。
\n\n各Scroll Itemを別Componentにして高さを測り、その結果を
\n[itemDynamicSizes]を駆動するサイズモデルへ書き戻します。
trackId をキーにした計測cacheを保持します。Itemの描画後に高さを読み、cacheを更新します。親の computed がItemを itemDynamicSize[] へ写像し、cacheがあればそれを使い、初回計測までは一時的な見積りを置きます。
import { afterRenderEffect, Component, computed, ElementRef, inject, Injectable, input, signal, untracked } from '@angular/core';\nimport { CdkVirtualForOf, CdkVirtualScrollViewport } from '@angular/cdk/scrolling';\nimport { CdkDynamicSizeVirtualScroll, itemDynamicSize } from '@rdlabo/ngx-cdk-scroll-strategies';\n\ntype Row = { trackId: string; body: string };\ntype SizeCache = { trackId: string; itemSize: number };\n\n@Injectable()\nexport class RowSizeCache {\n readonly entries = signal<SizeCache[]>([]);\n}\n\n@Component({\n selector: 'app-measured-row',\n template: `<div class=\"row\">{{ item().body }}</div>`,\n styles: `:host { display: block; } .row { white-space: pre-wrap; padding: 12px; }`,\n})\nexport class MeasuredRow {\n private readonly el = inject(ElementRef<HTMLElement>);\n private readonly sizes = inject(RowSizeCache);\n readonly item = input.required<Row>();\n\n constructor() {\n afterRenderEffect((onCleanup) => {\n const trackId = this.item().trackId;\n const element = this.el.nativeElement;\n const measure = () => {\n const itemSize = Math.ceil(element.getBoundingClientRect().height);\n if (itemSize <= 0) return;\n untracked(() => this.sizes.entries.update((cache) => {\n const previous = cache.find((entry) => entry.trackId === trackId);\n if (previous?.itemSize === itemSize) return cache;\n return [...cache.filter((entry) => entry.trackId !== trackId), { trackId, itemSize }];\n }));\n };\n const observer = new ResizeObserver(measure);\n observer.observe(element);\n measure();\n onCleanup(() => observer.disconnect());\n });\n }\n}\n\n@Component({\n selector: 'app-scroll-advanced',\n providers: [RowSizeCache],\n styles: `cdk-virtual-scroll-viewport { height: 320px; width: 100%; }`,\n imports: [CdkVirtualScrollViewport, CdkVirtualForOf, CdkDynamicSizeVirtualScroll, MeasuredRow],\n template: `\n <cdk-virtual-scroll-viewport\n [itemDynamicSizes]=\"dynamicSize()\"\n minBufferPx=\"900\"\n maxBufferPx=\"1350\"\n >\n <app-measured-row\n *cdkVirtualFor=\"let item of items(); trackBy: trackByFn\"\n [item]=\"item\"\n />\n </cdk-virtual-scroll-viewport>\n `,\n})\nexport class ScrollAdvancedPage {\n private readonly sizes = inject(RowSizeCache);\n readonly items = signal<Row[]>(\n Array.from({ length: 20 }, (_, index) => ({\n trackId: String(index),\n body: `Row ${index + 1}\\n`.repeat(1 + (index % 4)),\n })),\n );\n readonly dynamicSize = computed<itemDynamicSize[]>(() =>\n this.items().map((item) => {\n const cached = this.sizes.entries().find((entry) => entry.trackId === item.trackId)?.itemSize;\n return {\n trackId: item.trackId,\n itemSize: cached ?? 80,\n source: cached === undefined ? 'temporary' : 'cache',\n };\n }),\n );\n trackByFn = (_: number, item: Row) => item.trackId;\n}\n各 itemSize は 0 より大きい有限の数値のままにしてください。計測が来るまでは長さを揃えるために一時的な正の見積りを渡し、cache更新で置き換えます。この計測の流れの周辺にあるInfinite Scroll、Refresher、DynamicSizeVirtualScrollService の補助はdemo sourceを参照してください。
インストール のあとで使います。
\n\n\nシンプルな使い方 のデータモデル、
\ndynamicSizecomputed、trackBy、Viewport importをそのまま使い、このページではChat型List向けにReverse Layout CSSと[isReverse]=\"true\"を追加します。
リバーススクロールの場合は、cdk-virtual-scroll-viewport タグに isReverse ディレクティブを追加します。
<cdk-virtual-scroll-viewport\n [itemDynamicSizes]=\"dynamicSize()\"\n [isReverse]=\"true\"\n minBufferPx=\"900\"\n maxBufferPx=\"1350\"\n>\n <div class=\"reverse-items\">\n <div\n *cdkVirtualFor=\"let item of items(); trackBy: trackByFn\"\n class=\"dynamic-item\"\n [style.height.px]=\"item.itemSize\"\n >\n itemSize: {{ item.itemSize }}\n </div>\n </div>\n</cdk-virtual-scroll-viewport>\nstyles.css のようなグローバル CSS ファイルで、cdk-virtual-scroll-viewport.reverse-scroll に CSS を追加します。
cdk-virtual-scroll-viewport {\n width: 100%;\n height: 320px;\n\n /* .reverse-scroll class is added from this directive. */\n &.reverse-scroll {\n display: flex;\n flex-direction: column-reverse;\n\n .cdk-virtual-scroll-content-wrapper {\n top: auto;\n bottom: 0;\n }\n }\n}\nさらにアイテムのラッパーを追加します。div.reverse-items クラスは一例です。自由に決めて構いません。
div.reverse-items {\n height: 100%;\n display: flex;\n flex-direction: column-reverse;\n\n position: relative;\n bottom: 0;\n}\nリバーススクロールでは、CdkVirtualScrollViewport の measureScrollOffset は動作しません。このディレクティブの scrollOffset を使ってください。\nhttps://github.com/rdlabo-dev/ionic-angular-library/blob/v22.0.0/projects/scroll-strategies/src/lib/dynamic-size-virtual-scroll-strategy.ts
\nリバースレイアウトはネイティブの負の scrollTop 値を使います。scrollToIndex() は通常どおり論理的なアイテムインデックスを受け取り、その累積オフセットを内部でネイティブ座標に変換します。
このパッケージには、Virtual Scroll での開発を簡単にする Helper Service が含まれています。
\nimport { DynamicSizeVirtualScrollService } from '@rdlabo/ngx-cdk-scroll-strategies';\nインストール のあとで使います。
\n\n\nシンプルな使い方 のデータモデル、
\ndynamicSizecomputed、trackBy、Viewport importをそのまま使い、このページではChat型List向けにReverse Layout CSSと[isReverse]=\"true\"を追加します。
リバーススクロールの場合は、cdk-virtual-scroll-viewport タグに isReverse ディレクティブを追加します。
<cdk-virtual-scroll-viewport\n [itemDynamicSizes]=\"dynamicSize()\"\n [isReverse]=\"true\"\n minBufferPx=\"900\"\n maxBufferPx=\"1350\"\n>\n <div class=\"reverse-items\">\n <div\n *cdkVirtualFor=\"let item of items(); trackBy: trackByFn\"\n class=\"dynamic-item\"\n [style.height.px]=\"item.itemSize\"\n >\n itemSize: {{ item.itemSize }}\n </div>\n </div>\n</cdk-virtual-scroll-viewport>\nstyles.css のようなグローバル CSS ファイルで、cdk-virtual-scroll-viewport.reverse-scroll に CSS を追加します。
cdk-virtual-scroll-viewport {\n width: 100%;\n height: 320px;\n\n /* .reverse-scroll class is added from this directive. */\n &.reverse-scroll {\n display: flex;\n flex-direction: column-reverse;\n\n .cdk-virtual-scroll-content-wrapper {\n top: auto;\n bottom: 0;\n }\n }\n}\nさらにアイテムのラッパーを追加します。div.reverse-items クラスは一例です。自由に決めて構いません。
div.reverse-items {\n height: 100%;\n display: flex;\n flex-direction: column-reverse;\n\n position: relative;\n bottom: 0;\n}\nリバーススクロールでは、CdkVirtualScrollViewport の measureScrollOffset は動作しません。このディレクティブの scrollOffset を使ってください。\nhttps://github.com/rdlabo-dev/ionic-angular-library/blob/v22.0.3/projects/scroll-strategies/src/lib/dynamic-size-virtual-scroll-strategy.ts
\nリバースレイアウトはネイティブの負の scrollTop 値を使います。scrollToIndex() は通常どおり論理的なアイテムインデックスを受け取り、その累積オフセットを内部でネイティブ座標に変換します。
このパッケージには、Virtual Scroll での開発を簡単にする Helper Service が含まれています。
\nimport { DynamicSizeVirtualScrollService } from '@rdlabo/ngx-cdk-scroll-strategies';\n@rdlabo/ngx-cdk-scroll-strategies v22.0.0 が公開するAPIのリファレンスです。
directive CdkDynamicSizeVirtualScrollCDK Virtual Scroll viewportにDynamic Size Strategyを設定します。
| Input | \nType | \nDescription | \nDefault | \n
|---|---|---|---|
itemDynamicSizes | \nitemDynamicSize[] | \nList itemの正確なsize modelです。 | \n[] | \n
minBufferPx | \nnumber | \nItemを追加描画するまでの最小bufferです。 | \n100 | \n
maxBufferPx | \nnumber | \n追加描画時に確保するbufferです。 | \n200 | \n
isReverse | \nboolean | \nReverse Virtual Scrollを有効にします。 | \nfalse | \n
scrollOffset | \nnumber | \nReverse Scroll向けに正規化したread-only offsetです。 | \n\n |
class DynamicSizeVirtualScrollStrategy事前にsizeが分かっているitem向けのAngular CDK VirtualScrollStrategy 実装です。
| Member | \nType | \nDescription | \n
|---|---|---|
constructor | \n(itemSize, minBufferPx, maxBufferPx, isReverse) | \nSize modelとbuffer設定を指定して生成します。 | \n
updateItemAndBufferSize | \n(itemDynamicSize[], number, number, boolean) => void | \nSize modelとbuffer設定を置き換えます。 | \n
scrollToIndex | \n(index: number, behavior: ScrollBehavior) => void | \n指定したitem indexへscrollします。 | \n
scrolledIndexChange | \nObservable<number> | \n現在のscroll indexを通知します。 | \n
measureScrollOffset | \nnumber | \n最後に正規化したscroll offsetです。 | \n
class DynamicSizeVirtualScrollServiceViewport lifecycle、item height binding、refresh、smooth scrollのhelperを提供します。
| Member | \nType | \nDescription | \n
|---|---|---|
onInit | \n(viewport, latestScrollOffset) => void | \nViewportの状態を復元します。 | \n
onDestroy | \n(viewport) => number | \n後で復元するoffsetを取得します。 | \n
getBindDynamicItemHeight | \n(sizes: Signal<itemDynamicSize[]>) => Signal<string[]> | \nItem sizeをCSS heightへ変換します。 | \n
refreshViewport | \n(viewport) => void | \nViewportの寸法を強制更新します。 | \n
scrollToTopSmooth | \n(viewport) => Promise<void> | \n先頭へsmooth scrollします。 | \n
scrollToPoint | \n(viewport, x, y, duration?) => Promise<void> | \n指定位置へsmooth scrollします。 | \n
function sumItemSize(dynamicSize: itemDynamicSize[], endIndex: number) => number
endIndex より前にある全itemの累積sizeを返します。
function calculateItemCountForPixelDistance(dynamicSize: itemDynamicSize[], itemSizeRange: number, startIndex?: number, isReverse?: boolean) => number
Pixel距離を正確な小数item数へ変換します。
function calcIndex(dynamicSize: itemDynamicSize[], itemSizeRange: number, startIndex?: number, isReverse?: boolean) => number
互換性維持用の旧計算です。連続値には calculateItemCountForPixelDistance を使います。
interface itemDynamicSize| Prop | \nType | \nDescription | \n
|---|---|---|
itemSize | \nnumber | \nPixel単位の正確なitem sizeです。 | \n
| consumer metadata | \n`Record<string, string | \nnumber>` | \n
@rdlabo/ngx-cdk-scroll-strategies v22.0.3 が公開するAPIのリファレンスです。
directive CdkDynamicSizeVirtualScrollCDK Virtual Scroll viewportにDynamic Size Strategyを設定します。
| Input | \nType | \nDescription | \nDefault | \n
|---|---|---|---|
itemDynamicSizes | \nitemDynamicSize[] | \nList itemの正確なsize modelです。 | \n[] | \n
minBufferPx | \nnumber | \nItemを追加描画するまでの最小bufferです。 | \n100 | \n
maxBufferPx | \nnumber | \n追加描画時に確保するbufferです。 | \n200 | \n
isReverse | \nboolean | \nReverse Virtual Scrollを有効にします。 | \nfalse | \n
scrollOffset | \nnumber | \nReverse Scroll向けに正規化したread-only offsetです。 | \n\n |
class DynamicSizeVirtualScrollStrategy事前にsizeが分かっているitem向けのAngular CDK VirtualScrollStrategy 実装です。
| Member | \nType | \nDescription | \n
|---|---|---|
constructor | \n(itemSize, minBufferPx, maxBufferPx, isReverse) | \nSize modelとbuffer設定を指定して生成します。 | \n
updateItemAndBufferSize | \n(itemDynamicSize[], number, number, boolean) => void | \nSize modelとbuffer設定を置き換えます。 | \n
scrollToIndex | \n(index: number, behavior: ScrollBehavior) => void | \n指定したitem indexへscrollします。 | \n
scrolledIndexChange | \nObservable<number> | \n現在のscroll indexを通知します。 | \n
measureScrollOffset | \nnumber | \n最後に正規化したscroll offsetです。 | \n
class DynamicSizeVirtualScrollServiceViewport lifecycle、item height binding、refresh、smooth scrollのhelperを提供します。
| Member | \nType | \nDescription | \n
|---|---|---|
onInit | \n(viewport, latestScrollOffset) => void | \nViewportの状態を復元します。 | \n
onDestroy | \n(viewport) => number | \n後で復元するoffsetを取得します。 | \n
getBindDynamicItemHeight | \n(sizes: Signal<itemDynamicSize[]>) => Signal<string[]> | \nItem sizeをCSS heightへ変換します。 | \n
refreshViewport | \n(viewport) => void | \nViewportの寸法を強制更新します。 | \n
scrollToTopSmooth | \n(viewport) => Promise<void> | \n先頭へsmooth scrollします。 | \n
scrollToPoint | \n(viewport, x, y, duration?) => Promise<void> | \n指定位置へsmooth scrollします。 | \n
function sumItemSize(dynamicSize: itemDynamicSize[], endIndex: number) => number
endIndex より前にある全itemの累積sizeを返します。
function calculateItemCountForPixelDistance(dynamicSize: itemDynamicSize[], itemSizeRange: number, startIndex?: number, isReverse?: boolean) => number
Pixel距離を正確な小数item数へ変換します。
function calcIndex(dynamicSize: itemDynamicSize[], itemSizeRange: number, startIndex?: number, isReverse?: boolean) => number
互換性維持用の旧計算です。連続値には calculateItemCountForPixelDistance を使います。
interface itemDynamicSize| Prop | \nType | \nDescription | \n
|---|---|---|
itemSize | \nnumber | \nPixel単位の正確なitem sizeです。 | \n
| consumer metadata | \n`Record<string, string | \nnumber>` | \n