Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
These libraries is a collection of components and services that are useful for developing Ionic Angular applications.
These libraries are a collection of components and services for Ionic Angular applications.

Documentation: [Ionic Angular Kit](https://docs.rdlabo.dev/projects/ionic-angular-kit) · [Photo Editor](https://docs.rdlabo.dev/projects/ionic-angular-photo-editor) · [Scroll Header](https://docs.rdlabo.dev/projects/ionic-angular-scroll-header) · [Scroll Strategies](https://docs.rdlabo.dev/projects/ngx-cdk-scroll-strategies)

Migration guide: [Angular 21–22 and Ionic 9](docs/migration.md)
Upgrading from an earlier release? Read the [library migration guide](docs/migration.md).

[Demo site is here.](https://rdlabo-ionic-angular-library.netlify.app/)

Expand All @@ -15,16 +15,16 @@ Migration guide: [Angular 21–22 and Ionic 9](docs/migration.md)
| v19 | v8 | 19.x.x |
| v18 | v8 | 2.x.x |

Ionic 9 native applications require Capacitor 7 or later. See the [migration guide](docs/migration.md) before upgrading an existing application.
The compatibility table describes package requirements, not application-framework migration steps. Follow the upstream Angular, Ionic, and Capacitor guides separately, then apply this repository's [library-specific migrations](docs/migration.md).

## packages

| package name | description | path |
| ----------------------------------- | ------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------- |
| @rdlabo/ionic-angular-kit | Auth guards, Firebase flows, storage, overlay, HTTP interceptor, and other fleet helpers. | [/projects/kit](https://github.com/rdlabo-dev/ionic-angular-library/tree/main/projects/kit#readme) |
| @rdlabo/ionic-angular-photo-editor | This is a photo editor and viewer for modal page of Ionic Angular project using Capacitor. | [/project/photo-editor](https://github.com/rdlabo-dev/ionic-angular-library/tree/main/projects/photo-editor#readme) |
| @rdlabo/ionic-angular-scroll-header | This is directive for scroll with Header. | [/project/scroll-header](https://github.com/rdlabo-dev/ionic-angular-library/tree/main/projects/scroll-header#readme) |
| @rdlabo/ngx-cdk-scroll-strategies | This is directive for virtual scroll of dynamic item size. | [/project/scroll-strategies](https://github.com/rdlabo-dev/ionic-angular-library/tree/main/projects/scroll-strategies#readme) |
| package name | description | path |
| ----------------------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| @rdlabo/ionic-angular-kit | Auth guards, Firebase flows, storage, overlays, HTTP, and shared utilities. | [/projects/kit](https://github.com/rdlabo-dev/ionic-angular-library/tree/main/projects/kit#readme) |
| @rdlabo/ionic-angular-photo-editor | Photo editor/viewer modals and camera or album file loading. | [/projects/photo-editor](https://github.com/rdlabo-dev/ionic-angular-library/tree/main/projects/photo-editor#readme) |
| @rdlabo/ionic-angular-scroll-header | Directives for scroll-linked Ionic headers. | [/projects/scroll-header](https://github.com/rdlabo-dev/ionic-angular-library/tree/main/projects/scroll-header#readme) |
| @rdlabo/ngx-cdk-scroll-strategies | Dynamic-size virtual scroll strategies for Angular CDK. | [/projects/scroll-strategies](https://github.com/rdlabo-dev/ionic-angular-library/tree/main/projects/scroll-strategies#readme) |

### Release

Expand Down
115 changes: 69 additions & 46 deletions docs/migration.md
Original file line number Diff line number Diff line change
@@ -1,75 +1,98 @@
# Migration guide
# Library migration guide

## Angular 21–22 and Ionic 9
This guide covers consumer-facing changes in the packages published from this repository. It does not replace the upstream [Angular update guide](https://angular.dev/update-guide), [Ionic breaking-change guide](https://github.com/ionic-team/ionic-framework/blob/main/BREAKING.md), or [Capacitor upgrade guides](https://capacitorjs.com/docs/updating/overview).

The 22.x package line supports Angular 21 and 22 with Ionic 9. Ionic 9 requires Angular 18 or later; native applications also require Capacitor 7 or later.
## v21 to v22

Read the upstream [Ionic 9 breaking changes](https://github.com/ionic-team/ionic-framework/blob/main/BREAKING.md#version-9x) and the [Angular version compatibility table](https://angular.dev/reference/versions) before upgrading your application.
### Compatibility requirements

### 1. Update dependencies
Version 22 supports Angular 21 and 22. The kit, photo-editor, and scroll-header packages require Ionic 9; scroll-strategies requires only Angular and Angular CDK. Native kit and photo-editor features require Capacitor 7 or 8. Upgrade the host application with the upstream tools first, then update only the rdlabo packages it uses. Keep rdlabo packages on the same release line when an application uses more than one of them.

Use matching Angular major versions throughout the application. Angular 22 uses TypeScript 6.0 and requires a supported Node.js release.
While v22 is available under the npm `beta` dist-tag, install the prerelease with:

```bash
npx ng update @angular/core@22 @angular/cli@22
npx @ionic/migrate
npm install @rdlabo/ionic-angular-kit@beta \
@rdlabo/ionic-angular-photo-editor@beta \
@rdlabo/ionic-angular-scroll-header@beta \
@rdlabo/ngx-cdk-scroll-strategies@beta
```

`@ionic/migrate` is the recommended path for Ionic applications: it updates the Ionic packages and applies the available source migrations. Review its changes together with the manual audit below. The migrator does not support Angular library workspaces such as this repository, so library maintainers must apply the Ionic changes manually.
After stable v22 is published, use `@^22` instead of `@beta`. Omit packages that the application does not use. Release maintainers must update this prerelease instruction when promoting v22 to npm `latest`.

For a native application, upgrade Capacitor separately and follow its migration guide:
### @rdlabo/ionic-angular-photo-editor

```bash
npm install @capacitor/core@^8 @capacitor/ios@^8 @capacitor/android@^8
npm install --save-dev @capacitor/cli@^8
npx cap migrate
```
#### Choose the header button scheme

Capacitor 7 and 8 are supported by these libraries. Keep all Capacitor core and plugin packages on compatible major versions.
`PhotoEditorPage` and `PhotoViewerPage` now require `headerButtonColorScheme`. The library cannot infer the final `ion-toolbar` appearance after application CSS, translucency, and runtime theme overrides are applied.

### 2. Update Ionic Angular imports
Use `dark` for a dark or black toolbar and `light` for a light or white toolbar. Define typed props before passing them to Ionic because `ModalController` does not enforce the component's input types.

Ionic 9 exports standalone components and providers from `@ionic/angular` by default. Replace imports from the old standalone entry point:
```typescript
import { PhotoEditorPage, PhotoEditorProps } from '@rdlabo/ionic-angular-photo-editor';

```ts
// Before
import { IonContent, ModalController, provideIonicAngular } from '@ionic/angular/standalone';
const componentProps = {
value,
headerButtonColorScheme: 'dark',
} satisfies PhotoEditorProps;

// After
import { IonContent, ModalController, provideIonicAngular } from '@ionic/angular';
await modalController.create({
component: PhotoEditorPage,
componentProps,
});
```

If the application intentionally uses lazy-loaded Ionic wrappers, import those wrappers from `@ionic/angular/lazy`. `IonicModule` still works in Ionic 9 but is deprecated; new and migrated applications should use `provideIonicAngular()`.
```typescript
import { PhotoViewerPage, PhotoViewerProps } from '@rdlabo/ionic-angular-photo-editor';

### 3. Check change detection
const componentProps = {
imageUrls,
headerButtonColorScheme: 'dark',
} satisfies PhotoViewerProps;

Angular 21 is zoneless by default. Angular 22 additionally defaults components without an explicit strategy to `OnPush`.
await modalController.create({
component: PhotoViewerPage,
componentProps,
});
```

Prefer signals for state changed after asynchronous work such as overlay dismissal, timers, RxJS subscriptions, or platform events. Otherwise call `ChangeDetectorRef.markForCheck()`. Run the Angular update migrations so existing components retain their intended change-detection behavior.
`PhotoViewerProps.imageUrls` is now correctly declared as required. It was already a required component input, so ensure every viewer invocation supplies it.

### 4. Audit Ionic 9 component changes
#### Update direct-template selectors

Check application templates, styles, and tests for the following Ionic 9 changes:
The public components now use package-prefixed selectors:

- Replace `autocorrect="on"` or `autocorrect="off"` on `ion-input` and `ion-searchbar` with a boolean property binding, or omit it for the default `false` value.
- Replace legacy picker components and controller APIs with the inline `ion-picker` component.
- Set `handleBehavior="none"` on sheet modals only when the handle must retain its previous inert behavior; the new default is `cycle`.
- Use `ion-router-outlet` for URL-based routing. `ion-nav` now manages only an imperative, URL-less navigation stack.
- Do not rely on `ion-select` emitting `ionChange` when a confirmed value did not change. Use dismissal events when confirmation itself matters.
- Do not rely on the `selected` dismissal role from the `ion-select` action-sheet interface; listen for `ionChange` when the selected value changes.
- Check floating labels on `ion-input`, `ion-select`, and `ion-textarea` with slotted content. Slotted content alone no longer causes the label to float.
- Review custom selectors and shadow-part styles for `ion-input`, `ion-select`, and `ion-textarea`, whose internal structures changed.
- Review Material Design textarea layouts: the new minimum height is 72px.
| Before | After |
| -------------------- | ----------------------- |
| `<app-editor-image>` | `<rdlabo-photo-editor>` |
| `<app-photo-image>` | `<rdlabo-photo-viewer>` |

### 5. Verify the application
No selector update is needed when presenting `PhotoEditorPage` or `PhotoViewerPage` by component class through `ModalController`.

Build and test both web and native targets after updating:
#### Optional iOS 26 theme integration

```bash
npm run lint
npm test
npm run build
npx cap sync
Applications using `@rdlabo/ionic-theme-ios26` v3 can import the photo-editor adapter after the Ionic and iOS 26 theme styles:

```scss
@import '@rdlabo/ionic-theme-ios26/dist/css/ionic-theme-ios26.css';
@import '@ionic/angular/css/palettes/dark.class.css';
@import '@rdlabo/ionic-theme-ios26/dist/css/ionic-theme-ios26-dark-class.css';
@import '@rdlabo/ionic-angular-photo-editor/css/ios26-header-button-color-scheme.css';
```

Test modal sheets, form controls, select overlays, virtual scrolling, navigation gestures, and state updates that occur after asynchronous callbacks on each supported platform.
Do not import the adapter when the application does not use the iOS 26 theme. See the photo editor [theme guide](../projects/photo-editor/docs/theme.md) for color overrides and alternative dark-mode imports.

### @rdlabo/ionic-angular-kit

Version 22 changes the supported host framework range but does not rename or remove kit public APIs. After satisfying the compatibility requirements, existing kit imports and provider configuration remain valid.

### @rdlabo/ionic-angular-scroll-header

Version 22 has no package-specific API migration. Existing `rdlaboScrollHeader`, `rdlaboVirtualScrollHeader`, and `rdlaboFixVirtualScrollElement` usages remain valid after the dependency update.

### @rdlabo/ngx-cdk-scroll-strategies

Version 22 has no package-specific API migration. Existing dynamic-size virtual-scroll configuration remains valid after the dependency update.

### Verification

After applying the relevant package migrations, run the host application's normal checks and exercise the affected UI on each supported platform. For photo-editor, verify both editor and viewer modals against every toolbar color used by the application.
59 changes: 59 additions & 0 deletions e2e/photo-editor-ios26.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { readFileSync } from 'node:fs';
import { createRequire } from 'node:module';
import { expect, test } from '@playwright/test';

const IOS26_CSS = 'node_modules/@rdlabo/ionic-theme-ios26/dist/css/ionic-theme-ios26.css';
const IOS26_DARK_CLASS_CSS = 'node_modules/@rdlabo/ionic-theme-ios26/dist/css/ionic-theme-ios26-dark-class.css';
const IONIC_DARK_CLASS_CSS = 'node_modules/@ionic/angular/css/palettes/dark.class.css';
const requireFromWorkspace = createRequire(`${process.cwd()}/package.json`);
const PHOTO_EDITOR_IOS26_CSS = requireFromWorkspace.resolve('@rdlabo/ionic-angular-photo-editor/css/ios26-header-button-color-scheme.css');

test('exports a small adapter scoped to photo editor headers', () => {
const css = readFileSync(PHOTO_EDITOR_IOS26_CSS, 'utf8');
const selectors = css.split('\n').filter((line) => line.endsWith(' {') && !line.trimStart().startsWith('@'));

expect(Buffer.byteLength(css)).toBeLessThan(15_000);
expect(selectors.length).toBeGreaterThan(0);
expect(selectors.every((selector) => selector.includes('photo-editor-header-buttons-'))).toBe(true);
});

test.describe('Photo editor iOS 26 header button color scheme', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/main/photo-editor?ionic:mode=ios');
await page.addStyleTag({ path: IOS26_CSS });
await page.addStyleTag({ path: IONIC_DARK_CLASS_CSS });
await page.addStyleTag({ path: IOS26_DARK_CLASS_CSS });
await page.addStyleTag({ path: PHOTO_EDITOR_IOS26_CSS });
});

test('forces light buttons inside an ambient dark palette', async ({ page }) => {
await page.locator('html').evaluate((element) => element.classList.add('ion-palette-dark'));
await page.getByText('Launch Photo Editor', { exact: true }).click();

const header = page.locator('ion-modal rdlabo-photo-editor ion-header');
await header.evaluate((element) => {
element.classList.remove('photo-editor-header-buttons-dark');
element.classList.add('photo-editor-header-buttons-light');
(element as HTMLElement).style.colorScheme = 'light';
});
const button = header.locator('ion-buttons[slot="start"] ion-button');
await button.evaluate((element) => element.classList.add('ion-activated'));

await expect(header).toHaveCSS('--ios26-glass-background-rgb', '255, 255, 255');
await expect(header).toHaveCSS('--ion-text-color-rgb', '0, 0, 0');
await expect(button.locator('..')).toHaveCSS('backdrop-filter', 'blur(2px) saturate(3.6)');
});

test('forces dark viewer buttons inside an ambient light palette', async ({ page }) => {
await page.locator('html').evaluate((element) => element.classList.remove('ion-palette-dark'));
await page.getByText('Launch Photo Viewer', { exact: true }).click();

const header = page.locator('ion-modal rdlabo-photo-viewer ion-header');
const button = header.locator('ion-buttons[slot="start"] ion-button');
await button.evaluate((element) => element.classList.add('ion-activated'));

await expect(header).toHaveCSS('--ios26-glass-background-rgb', '62, 62, 62');
await expect(header).toHaveCSS('--ion-text-color-rgb', '255, 255, 255');
await expect(button.locator('..')).toHaveCSS('backdrop-filter', 'blur(7px) saturate(1.8)');
});
});
14 changes: 13 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"cap": "npm run build -- --configuration production && npx cap copy",
"fmt": "prettier --parser typescript --write \"./**/*.ts\" && prettier --parser angular --write \"./**/*.html\"",
"prebuild:kit": "ng build kit --configuration=production",
"prebuild:photo-editor": "ng build photo-editor --configuration=production",
"prebuild:photo-editor": "sass --no-source-map --load-path=node_modules projects/photo-editor/src/assets/ios26-header-button-color-scheme.scss projects/photo-editor/css/ios26-header-button-color-scheme.css && ng build photo-editor --configuration=production",
"prebuild:scroll-header": "sass projects/scroll-header/src/assets/scroll-header.directive.scss projects/scroll-header/css/scroll-header.directive.css && ng build scroll-header --configuration=production",
"prebuild:scroll-strategies": "ng build scroll-strategies --configuration=production",
"prebuild": "npm run prebuild:kit && npm run prebuild:photo-editor && npm run prebuild:scroll-header && npm run prebuild:scroll-strategies",
Expand Down Expand Up @@ -81,6 +81,7 @@
"@playwright/test": "^1.57.0",
"@rdlabo/capacitor-brotherprint": "^8.1.1",
"@rdlabo/eslint-plugin-rules": "^21.2.6",
"@rdlabo/ionic-theme-ios26": "^3.0.0",
"angular-eslint": "^22.1.0",
"child_process": "^1.0.2",
"dom-to-image-more": "^3.10.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export class DemoPhotoEditorPage {
componentProps: {
requireSquare: false,
value: photoData,
headerButtonColorScheme: 'dark',
},
});
await modal.present();
Expand All @@ -88,6 +89,7 @@ export class DemoPhotoEditorPage {
index: 1,
isCircle: false,
enableDelete: true,
headerButtonColorScheme: 'dark',
labels: {
delete: 'Delete',
},
Expand Down
Loading