Compare commits

..

4 Commits

Author SHA1 Message Date
Brandy Smith
360fce73d4 chore: grab theme changes from next 2025-08-13 10:46:59 -04:00
Maria Hutt
8c21980a70 feat(theme): more changes 2025-08-12 15:50:23 -04:00
Maria Hutt
3c6bb9fb00 feat(core): component level theming 2025-08-12 15:50:11 -04:00
Maria Hutt
a130a4a63a feat(core): separate mode and theme 2025-08-12 15:48:22 -04:00
221 changed files with 2466 additions and 3903 deletions

View File

@@ -56,6 +56,14 @@ closeAndLock:
bug reports and feature requests. Please use our [forum](https://forum.ionicframework.com) for questions about the framework.
Thank you for using Ionic!
- label: "ionitron: appflow"
message: >
Thanks for the issue! This issue appears to be related to Ionic Appflow. We use this issue tracker exclusively for
bug reports and feature requests. Please use the [Ionic Appflow Support Forum](https://ionic.zendesk.com/hc/en-us/requests/new)
to report this issue.
Thank you for using Ionic!
- label: "ionitron: missing template"
message: >
@@ -137,6 +145,65 @@ noReproduction:
lock: true
dryRun: false
wrongRepo:
repos:
- label: "ionitron: capacitor"
repo: capacitor
message: >
Thanks for the issue! We use this issue tracker exclusively for bug reports and feature requests
associated with the Ionic Framework. It appears that this issue is associated with Capacitor.
I am moving this issue to the Capacitor repository. Please track this issue over there.
Thank you for using Ionic!
- label: "ionitron: v3"
repo: ionic-v3
message: >
Thanks for the issue! We have moved the source code and issues for Ionic 3 into a separate repository.
I am moving this issue to the repository for Ionic 3. Please track this issue over there.
Thank you for using Ionic!
- label: "ionitron: cli"
repo: ionic-cli
message: >
Thanks for the issue! We use this issue tracker exclusively for bug reports and feature requests
associated with the Ionic Framework. It appears that this issue is associated with the Ionic CLI.
I am moving this issue to the Ionic CLI repository. Please track this issue over there.
Thank you for using Ionic!
- label: "ionitron: docs"
repo: ionic-docs
message: >
Thanks for the issue! We use this issue tracker exclusively for bug reports and feature requests
associated with the Ionic Framework. It appears that this issue is associated with the Ionic Documentation.
I am moving this issue to the Ionic Docs repository. Please track this issue over there.
Thank you for using Ionic!
- label: "ionitron: stencil"
repo: stencil
message: >
Thanks for the issue! We use this issue tracker exclusively for bug reports and feature requests
associated with the Ionic Framework. It appears that this issue is associated with Stencil.
I am moving this issue to the Stencil repository. Please track this issue over there.
Thank you for using Ionic!
- label: "ionitron: native"
repo: ionic-native
message: >
Thanks for the issue! We use this issue tracker exclusively for bug reports and feature requests
associated with the Ionic Framework. It appears that this issue is associated with Ionic Native.
I am moving this issue to the Ionic Native repository. Please track this issue over there.
Thank you for using Ionic!
close: true
lock: true
dryRun: false
screenshot:
appId: 18001
checkName: "build"

View File

@@ -3,7 +3,7 @@ description: 'Build Ionic Angular Server'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v5
- uses: actions/setup-node@v4
with:
node-version: 22.x
- uses: ./.github/workflows/actions/download-archive

View File

@@ -3,7 +3,7 @@ description: 'Build Ionic Angular'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v5
- uses: actions/setup-node@v4
with:
node-version: 22.x
- uses: ./.github/workflows/actions/download-archive

View File

@@ -9,7 +9,7 @@ runs:
using: 'composite'
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
- uses: actions/setup-node@v4
with:
node-version: 22.x

View File

@@ -9,7 +9,7 @@ runs:
using: 'composite'
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
- uses: actions/setup-node@v4
with:
node-version: 22.x
- name: Install Dependencies

View File

@@ -3,7 +3,7 @@ description: 'Build Ionic React Router'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v5
- uses: actions/setup-node@v4
with:
node-version: 22.x
- uses: ./.github/workflows/actions/download-archive

View File

@@ -3,7 +3,7 @@ description: 'Build Ionic React'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v5
- uses: actions/setup-node@v4
with:
node-version: 22.x
- uses: ./.github/workflows/actions/download-archive

View File

@@ -3,7 +3,7 @@ description: 'Builds Ionic Vue Router'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v5
- uses: actions/setup-node@v4
with:
node-version: 22.x
- uses: ./.github/workflows/actions/download-archive

View File

@@ -3,7 +3,7 @@ description: 'Build Ionic Vue'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v5
- uses: actions/setup-node@v4
with:
node-version: 22.x
- uses: ./.github/workflows/actions/download-archive

View File

@@ -19,7 +19,7 @@ inputs:
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v5
- uses: actions/setup-node@v4
with:
node-version: 22.x
# Provenance requires npm 9.5.0+

View File

@@ -6,7 +6,7 @@ inputs:
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v5
- uses: actions/setup-node@v4
with:
node-version: 22.x
- uses: ./.github/workflows/actions/download-archive

View File

@@ -3,7 +3,7 @@ description: 'Test Core Clean Build'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v5
- uses: actions/setup-node@v4
with:
node-version: 22.x

View File

@@ -3,7 +3,7 @@ description: 'Test Core Lint'
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v5
- uses: actions/setup-node@v4
with:
node-version: 22.x
- name: Install Dependencies

View File

@@ -13,7 +13,7 @@ inputs:
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v5
- uses: actions/setup-node@v4
with:
node-version: 22.x
- uses: ./.github/workflows/actions/download-archive

View File

@@ -6,7 +6,7 @@ inputs:
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v5
- uses: actions/setup-node@v4
with:
node-version: 22.x
- name: Install Dependencies

View File

@@ -6,7 +6,7 @@ inputs:
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v5
- uses: actions/setup-node@v4
with:
node-version: 22.x
- uses: ./.github/workflows/actions/download-archive

View File

@@ -6,7 +6,7 @@ inputs:
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v5
- uses: actions/setup-node@v4
with:
node-version: 22.x
- uses: ./.github/workflows/actions/download-archive

View File

@@ -6,7 +6,7 @@ inputs:
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v5
- uses: actions/setup-node@v4
with:
node-version: 22.x
- uses: ./.github/workflows/actions/download-archive

View File

@@ -7,7 +7,7 @@ on:
runs:
using: 'composite'
steps:
- uses: actions/setup-node@v5
- uses: actions/setup-node@v4
with:
node-version: 22.x
- uses: actions/download-artifact@v5

View File

@@ -13,6 +13,6 @@ jobs:
- name: 'Auto-assign issue'
uses: pozil/auto-assign-issue@39c06395cbac76e79afc4ad4e5c5c6db6ecfdd2e # v2.2.0
with:
assignees: brandyscarney, thetaPC, ShaneK
assignees: brandyscarney, ShaneK
numOfAssignee: 1
allowSelfAssign: false

View File

@@ -12,7 +12,7 @@ jobs:
if: |
!contains(github.event.pull_request.title, 'release') &&
!contains(github.event.pull_request.title, 'chore')
uses: amannn/action-semantic-pull-request@v6
uses: amannn/action-semantic-pull-request@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:

View File

@@ -13,7 +13,7 @@ jobs:
triage:
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v6
- uses: actions/labeler@v5
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
sync-labels: true

View File

@@ -3,43 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [8.7.5](https://github.com/ionic-team/ionic-framework/compare/v8.7.4...v8.7.5) (2025-09-24)
### Bug Fixes
* **modal:** allow sheet modals to skip focus trap ([#30689](https://github.com/ionic-team/ionic-framework/issues/30689)) ([a40d957](https://github.com/ionic-team/ionic-framework/commit/a40d957ad9c1897af365a91b45b00228a00d614c)), closes [#30684](https://github.com/ionic-team/ionic-framework/issues/30684)
* **vue:** emit component-specific overlay events ([#30688](https://github.com/ionic-team/ionic-framework/issues/30688)) ([024d090](https://github.com/ionic-team/ionic-framework/commit/024d090122548e26ec2cdcfae4637dde8f288278)), closes [#30641](https://github.com/ionic-team/ionic-framework/issues/30641)
## [8.7.4](https://github.com/ionic-team/ionic-framework/compare/v8.7.3...v8.7.4) (2025-09-17)
### Bug Fixes
* **input:** improve error text accessibility ([#30635](https://github.com/ionic-team/ionic-framework/issues/30635)) ([c339bc3](https://github.com/ionic-team/ionic-framework/commit/c339bc36827b62ef871325869a9a5db9b17ac785))
* **overlays,picker:** remove invalid aria-hidden attribute ([#30563](https://github.com/ionic-team/ionic-framework/issues/30563)) ([49f96d7](https://github.com/ionic-team/ionic-framework/commit/49f96d7f1e9050a95e3e33a821c0467ecc0bed64)), closes [#30040](https://github.com/ionic-team/ionic-framework/issues/30040)
* **segment-view:** scroll and select the right item when the component is in RTL context; ([#30675](https://github.com/ionic-team/ionic-framework/issues/30675)) ([66f517d](https://github.com/ionic-team/ionic-framework/commit/66f517d5b2154fff00b294a78f4107f057a580c6)), closes [#30079](https://github.com/ionic-team/ionic-framework/issues/30079)
## [8.7.3](https://github.com/ionic-team/ionic-framework/compare/v8.7.2...v8.7.3) (2025-08-20)
### Bug Fixes
* **checkbox:** add aria attributes to ignore checkbox icon ([#30633](https://github.com/ionic-team/ionic-framework/issues/30633)) ([e9e6605](https://github.com/ionic-team/ionic-framework/commit/e9e6605862a05a46d26c26a144ed1cf22133a2b7)), closes [#30231](https://github.com/ionic-team/ionic-framework/issues/30231)
* **refresher:** prevent focus-related scroll jumps on refresh ([#30636](https://github.com/ionic-team/ionic-framework/issues/30636)) ([1899b49](https://github.com/ionic-team/ionic-framework/commit/1899b49d252abc6003f763cea8db2a51efa941ec))
## [8.7.2](https://github.com/ionic-team/ionic-framework/compare/v8.7.1...v8.7.2) (2025-08-06)

View File

@@ -3,42 +3,6 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [8.7.5](https://github.com/ionic-team/ionic-framework/compare/v8.7.4...v8.7.5) (2025-09-24)
### Bug Fixes
* **modal:** allow sheet modals to skip focus trap ([#30689](https://github.com/ionic-team/ionic-framework/issues/30689)) ([a40d957](https://github.com/ionic-team/ionic-framework/commit/a40d957ad9c1897af365a91b45b00228a00d614c)), closes [#30684](https://github.com/ionic-team/ionic-framework/issues/30684)
## [8.7.4](https://github.com/ionic-team/ionic-framework/compare/v8.7.3...v8.7.4) (2025-09-17)
### Bug Fixes
* **input:** improve error text accessibility ([#30635](https://github.com/ionic-team/ionic-framework/issues/30635)) ([c339bc3](https://github.com/ionic-team/ionic-framework/commit/c339bc36827b62ef871325869a9a5db9b17ac785))
* **overlays,picker:** remove invalid aria-hidden attribute ([#30563](https://github.com/ionic-team/ionic-framework/issues/30563)) ([49f96d7](https://github.com/ionic-team/ionic-framework/commit/49f96d7f1e9050a95e3e33a821c0467ecc0bed64)), closes [#30040](https://github.com/ionic-team/ionic-framework/issues/30040)
* **segment-view:** scroll and select the right item when the component is in RTL context; ([#30675](https://github.com/ionic-team/ionic-framework/issues/30675)) ([66f517d](https://github.com/ionic-team/ionic-framework/commit/66f517d5b2154fff00b294a78f4107f057a580c6)), closes [#30079](https://github.com/ionic-team/ionic-framework/issues/30079)
## [8.7.3](https://github.com/ionic-team/ionic-framework/compare/v8.7.2...v8.7.3) (2025-08-20)
### Bug Fixes
* **checkbox:** add aria attributes to ignore checkbox icon ([#30633](https://github.com/ionic-team/ionic-framework/issues/30633)) ([e9e6605](https://github.com/ionic-team/ionic-framework/commit/e9e6605862a05a46d26c26a144ed1cf22133a2b7)), closes [#30231](https://github.com/ionic-team/ionic-framework/issues/30231)
* **refresher:** prevent focus-related scroll jumps on refresh ([#30636](https://github.com/ionic-team/ionic-framework/issues/30636)) ([1899b49](https://github.com/ionic-team/ionic-framework/commit/1899b49d252abc6003f763cea8db2a51efa941ec))
## [8.7.2](https://github.com/ionic-team/ionic-framework/compare/v8.7.1...v8.7.2) (2025-08-06)

View File

@@ -1,5 +1,5 @@
# Get Playwright
FROM mcr.microsoft.com/playwright:v1.55.1
FROM mcr.microsoft.com/playwright:v1.54.2
# Set the working directory
WORKDIR /ionic

View File

@@ -1205,6 +1205,37 @@ ion-modal,part,backdrop
ion-modal,part,content
ion-modal,part,handle
ion-my-chip,shadow
ion-my-chip,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record<never, never> | undefined,undefined,false,true
ion-my-chip,prop,disabled,boolean,false,false,false
ion-my-chip,prop,hue,"bold" | "subtle" | undefined,'subtle',false,false
ion-my-chip,prop,mode,"ios" | "md",undefined,false,false
ion-my-chip,prop,outline,boolean,false,false,false
ion-my-chip,prop,shape,"rectangular" | "round" | "soft" | undefined,'soft',false,false
ion-my-chip,prop,size,"large" | "small" | undefined,'small',false,false
ion-my-chip,prop,theme,"ios" | "md" | "ionic",undefined,false,false
ion-my-chip,css-prop,--ion-chip-border-width
ion-my-chip,css-prop,--ion-chip-focus-ring-color
ion-my-chip,css-prop,--ion-chip-focus-ring-width
ion-my-chip,css-prop,--ion-chip-font-weight
ion-my-chip,css-prop,--ion-chip-gap
ion-my-chip,css-prop,--ion-chip-hue-bold-bg
ion-my-chip,css-prop,--ion-chip-hue-bold-border-color
ion-my-chip,css-prop,--ion-chip-hue-bold-color
ion-my-chip,css-prop,--ion-chip-hue-subtle-bg
ion-my-chip,css-prop,--ion-chip-hue-subtle-border-color
ion-my-chip,css-prop,--ion-chip-hue-subtle-color
ion-my-chip,css-prop,--ion-chip-line-height
ion-my-chip,css-prop,--ion-chip-padding-horizontal
ion-my-chip,css-prop,--ion-chip-padding-vertical
ion-my-chip,css-prop,--ion-chip-shape-rectangular-border-radius
ion-my-chip,css-prop,--ion-chip-shape-round-border-radius
ion-my-chip,css-prop,--ion-chip-shape-soft-border-radius
ion-my-chip,css-prop,--ion-chip-size-large-font-size
ion-my-chip,css-prop,--ion-chip-size-large-min-height
ion-my-chip,css-prop,--ion-chip-size-small-font-size
ion-my-chip,css-prop,--ion-chip-size-small-min-height
ion-nav,shadow
ion-nav,prop,animated,boolean,true,false,false
ion-nav,prop,animation,((baseEl: any, opts?: any) => Animation) | undefined,undefined,false,false
@@ -1231,6 +1262,7 @@ ion-nav,event,ionNavWillChange,void,false
ion-nav-link,none
ion-nav-link,prop,component,Function | HTMLElement | ViewController | null | string | undefined,undefined,false,false
ion-nav-link,prop,componentProps,undefined | { [key: string]: any; },undefined,false,false
ion-nav-link,prop,mode,"ios" | "md",undefined,false,false
ion-nav-link,prop,routerAnimation,((baseEl: any, opts?: any) => Animation) | undefined,undefined,false,false
ion-nav-link,prop,routerDirection,"back" | "forward" | "root",'forward',false,false

98
core/package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@ionic/core",
"version": "8.7.5",
"version": "8.7.2",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/core",
"version": "8.7.5",
"version": "8.7.2",
"license": "MIT",
"dependencies": {
"@stencil/core": "4.36.2",
@@ -22,7 +22,7 @@
"@clack/prompts": "^0.11.0",
"@ionic/eslint-config": "^0.3.0",
"@ionic/prettier-config": "^2.0.0",
"@playwright/test": "^1.55.1",
"@playwright/test": "^1.54.2",
"@rollup/plugin-node-resolve": "^8.4.0",
"@rollup/plugin-virtual": "^2.0.3",
"@stencil/angular-output-target": "^0.10.0",
@@ -663,9 +663,9 @@
"dev": true
},
"node_modules/@capacitor/core": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-7.4.3.tgz",
"integrity": "sha512-wCWr8fQ9Wxn0466vPg7nMn0tivbNVjNy1yL4GvDSIZuZx7UpU2HeVGNe9QjN/quEd+YLRFeKEBLBw619VqUiNg==",
"version": "7.4.2",
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-7.4.2.tgz",
"integrity": "sha512-akCf9A1FUR8AWTtmgGjHEq6LmGsjA2U7igaJ9PxiCBfyxKqlDbuGHrlNdpvHEjV5tUPH3KYtkze6gtFcNKPU9A==",
"dev": true,
"dependencies": {
"tslib": "^2.1.0"
@@ -681,18 +681,18 @@
}
},
"node_modules/@capacitor/keyboard": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/@capacitor/keyboard/-/keyboard-7.0.3.tgz",
"integrity": "sha512-BIBKjmky5rOYNhvYhNeDi0MMvjwYZ6YF9JoCYcGKvKY+XLJKtezsEL78XfOlgWZBkbfR8uq3tzktY6PqgoYLKA==",
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@capacitor/keyboard/-/keyboard-7.0.2.tgz",
"integrity": "sha512-9We5BY1mu+QWOReDukr+6HxA4Bh0mKBU0txFtwXJdjBohttMYWJzB+dQf4oHrX8odiU2Cm/BfDdAU2wV06Cyig==",
"dev": true,
"peerDependencies": {
"@capacitor/core": ">=7.0.0"
}
},
"node_modules/@capacitor/status-bar": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/@capacitor/status-bar/-/status-bar-7.0.3.tgz",
"integrity": "sha512-JyRpVnKwHij9hgPWolF6PK+HT3e2HSPjN11/h2OmKxq8GAdPGARFLv+97eZl0pvuvm0Kka/LpiLb5whXISBg7Q==",
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@capacitor/status-bar/-/status-bar-7.0.2.tgz",
"integrity": "sha512-fYYkkdzCbQV+MjZVnaQTFl5I4bddnFW8ZrPVxDjNoGVPTUG7H58Ij1+NcuNxHLXjJvZOoZeYJ3w3I16Wb2zssw==",
"dev": true,
"peerDependencies": {
"@capacitor/core": ">=7.0.0"
@@ -1715,12 +1715,12 @@
}
},
"node_modules/@playwright/test": {
"version": "1.55.1",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.55.1.tgz",
"integrity": "sha512-IVAh/nOJaw6W9g+RJVlIQJ6gSiER+ae6mKQ5CX1bERzQgbC1VSeBlwdvczT7pxb0GWiyrxH4TGKbMfDb4Sq/ig==",
"version": "1.54.2",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.54.2.tgz",
"integrity": "sha512-A+znathYxPf+72riFd1r1ovOLqsIIB0jKIoPjyK2kqEIe30/6jF6BC7QNluHuwUmsD2tv1XZVugN8GqfTMOxsA==",
"dev": true,
"dependencies": {
"playwright": "1.55.1"
"playwright": "1.54.2"
},
"bin": {
"playwright": "cli.js"
@@ -3474,9 +3474,9 @@
]
},
"node_modules/chalk": {
"version": "5.6.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
"integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.5.0.tgz",
"integrity": "sha512-1tm8DTaJhPBG3bIkVeZt1iZM9GfSX2lzOeDVZH9R9ffRHpmHvxZ/QhgQH/aDTkswQVt+YHdXAdS/In/30OjCbg==",
"dev": true,
"engines": {
"node": "^12.17.0 || ^14.13 || >=16.0.0"
@@ -8593,12 +8593,12 @@
}
},
"node_modules/playwright": {
"version": "1.55.1",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.55.1.tgz",
"integrity": "sha512-cJW4Xd/G3v5ovXtJJ52MAOclqeac9S/aGGgRzLabuF8TnIb6xHvMzKIa6JmrRzUkeXJgfL1MhukP0NK6l39h3A==",
"version": "1.54.2",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.54.2.tgz",
"integrity": "sha512-Hu/BMoA1NAdRUuulyvQC0pEqZ4vQbGfn8f7wPXcnqQmM+zct9UliKxsIkLNmz/ku7LElUNqmaiv1TG/aL5ACsw==",
"dev": true,
"dependencies": {
"playwright-core": "1.55.1"
"playwright-core": "1.54.2"
},
"bin": {
"playwright": "cli.js"
@@ -8611,9 +8611,9 @@
}
},
"node_modules/playwright-core": {
"version": "1.55.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.1.tgz",
"integrity": "sha512-Z6Mh9mkwX+zxSlHqdr5AOcJnfp+xUWLCt9uKV18fhzA8eyxUd8NUWzAjxUh55RZKSYwDGX0cfaySdhZJGMoJ+w==",
"version": "1.54.2",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.54.2.tgz",
"integrity": "sha512-n5r4HFbMmWsB4twG7tJLDN9gmBUeSPcsBZiWSE4DnYz9mJMAFqr2ID7+eGC9kpEnxExJ1epttwR59LEWCk8mtA==",
"dev": true,
"bin": {
"playwright-core": "cli.js"
@@ -11102,9 +11102,9 @@
"dev": true
},
"@capacitor/core": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-7.4.3.tgz",
"integrity": "sha512-wCWr8fQ9Wxn0466vPg7nMn0tivbNVjNy1yL4GvDSIZuZx7UpU2HeVGNe9QjN/quEd+YLRFeKEBLBw619VqUiNg==",
"version": "7.4.2",
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-7.4.2.tgz",
"integrity": "sha512-akCf9A1FUR8AWTtmgGjHEq6LmGsjA2U7igaJ9PxiCBfyxKqlDbuGHrlNdpvHEjV5tUPH3KYtkze6gtFcNKPU9A==",
"dev": true,
"requires": {
"tslib": "^2.1.0"
@@ -11118,16 +11118,16 @@
"requires": {}
},
"@capacitor/keyboard": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/@capacitor/keyboard/-/keyboard-7.0.3.tgz",
"integrity": "sha512-BIBKjmky5rOYNhvYhNeDi0MMvjwYZ6YF9JoCYcGKvKY+XLJKtezsEL78XfOlgWZBkbfR8uq3tzktY6PqgoYLKA==",
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@capacitor/keyboard/-/keyboard-7.0.2.tgz",
"integrity": "sha512-9We5BY1mu+QWOReDukr+6HxA4Bh0mKBU0txFtwXJdjBohttMYWJzB+dQf4oHrX8odiU2Cm/BfDdAU2wV06Cyig==",
"dev": true,
"requires": {}
},
"@capacitor/status-bar": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/@capacitor/status-bar/-/status-bar-7.0.3.tgz",
"integrity": "sha512-JyRpVnKwHij9hgPWolF6PK+HT3e2HSPjN11/h2OmKxq8GAdPGARFLv+97eZl0pvuvm0Kka/LpiLb5whXISBg7Q==",
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@capacitor/status-bar/-/status-bar-7.0.2.tgz",
"integrity": "sha512-fYYkkdzCbQV+MjZVnaQTFl5I4bddnFW8ZrPVxDjNoGVPTUG7H58Ij1+NcuNxHLXjJvZOoZeYJ3w3I16Wb2zssw==",
"dev": true,
"requires": {}
},
@@ -11863,12 +11863,12 @@
}
},
"@playwright/test": {
"version": "1.55.1",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.55.1.tgz",
"integrity": "sha512-IVAh/nOJaw6W9g+RJVlIQJ6gSiER+ae6mKQ5CX1bERzQgbC1VSeBlwdvczT7pxb0GWiyrxH4TGKbMfDb4Sq/ig==",
"version": "1.54.2",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.54.2.tgz",
"integrity": "sha512-A+znathYxPf+72riFd1r1ovOLqsIIB0jKIoPjyK2kqEIe30/6jF6BC7QNluHuwUmsD2tv1XZVugN8GqfTMOxsA==",
"dev": true,
"requires": {
"playwright": "1.55.1"
"playwright": "1.54.2"
}
},
"@rollup/plugin-node-resolve": {
@@ -13076,9 +13076,9 @@
"dev": true
},
"chalk": {
"version": "5.6.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
"integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.5.0.tgz",
"integrity": "sha512-1tm8DTaJhPBG3bIkVeZt1iZM9GfSX2lzOeDVZH9R9ffRHpmHvxZ/QhgQH/aDTkswQVt+YHdXAdS/In/30OjCbg==",
"dev": true
},
"chalk-template": {
@@ -16812,19 +16812,19 @@
}
},
"playwright": {
"version": "1.55.1",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.55.1.tgz",
"integrity": "sha512-cJW4Xd/G3v5ovXtJJ52MAOclqeac9S/aGGgRzLabuF8TnIb6xHvMzKIa6JmrRzUkeXJgfL1MhukP0NK6l39h3A==",
"version": "1.54.2",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.54.2.tgz",
"integrity": "sha512-Hu/BMoA1NAdRUuulyvQC0pEqZ4vQbGfn8f7wPXcnqQmM+zct9UliKxsIkLNmz/ku7LElUNqmaiv1TG/aL5ACsw==",
"dev": true,
"requires": {
"fsevents": "2.3.2",
"playwright-core": "1.55.1"
"playwright-core": "1.54.2"
}
},
"playwright-core": {
"version": "1.55.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.1.tgz",
"integrity": "sha512-Z6Mh9mkwX+zxSlHqdr5AOcJnfp+xUWLCt9uKV18fhzA8eyxUd8NUWzAjxUh55RZKSYwDGX0cfaySdhZJGMoJ+w==",
"version": "1.54.2",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.54.2.tgz",
"integrity": "sha512-n5r4HFbMmWsB4twG7tJLDN9gmBUeSPcsBZiWSE4DnYz9mJMAFqr2ID7+eGC9kpEnxExJ1epttwR59LEWCk8mtA==",
"dev": true
},
"postcss": {

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/core",
"version": "8.7.5",
"version": "8.7.2",
"description": "Base components for Ionic",
"keywords": [
"ionic",
@@ -44,7 +44,7 @@
"@clack/prompts": "^0.11.0",
"@ionic/eslint-config": "^0.3.0",
"@ionic/prettier-config": "^2.0.0",
"@playwright/test": "^1.55.1",
"@playwright/test": "^1.54.2",
"@rollup/plugin-node-resolve": "^8.4.0",
"@rollup/plugin-virtual": "^2.0.3",
"@stencil/angular-output-target": "^0.10.0",

View File

@@ -2072,6 +2072,45 @@ export namespace Components {
*/
"trigger": string | undefined;
}
interface IonMyChip {
/**
* The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics).
*/
"color"?: Color;
/**
* If `true`, the user cannot interact with the chip.
* @default false
*/
"disabled": boolean;
/**
* Set to `"bold"` for a chip with vibrant, bold colors or to `"subtle"` for a chip with muted, subtle colors. Only applies to the `ionic` theme.
* @default 'subtle'
*/
"hue"?: 'bold' | 'subtle';
/**
* The mode determines the platform behaviors of the component.
*/
"mode"?: "ios" | "md";
/**
* Display an outline style button.
* @default false
*/
"outline": boolean;
/**
* Set to `"soft"` for a chip with slightly rounded corners, `"round"` for a chip with fully rounded corners, or `"rectangular"` for a chip without rounded corners. Defaults to `"round"` for the `"ionic"` theme and `"soft"` for all other themes.
* @default 'soft'
*/
"shape"?: 'soft' | 'round' | 'rectangular';
/**
* Set to `"small"` for a chip with less height and padding. Defaults to `"large"` for the ionic theme, and undefined for all other themes.
* @default 'small'
*/
"size"?: 'small' | 'large';
/**
* The theme determines the visual appearance of the component.
*/
"theme"?: "ios" | "md" | "ionic";
}
interface IonNav {
/**
* If `true`, the nav should animate the transition of components.
@@ -2208,6 +2247,10 @@ export namespace Components {
* Data you want to pass to the component as props. Only used if the `"routerDirection"` is `"forward"` or `"root"`.
*/
"componentProps"?: ComponentProps;
/**
* The mode determines the platform behaviors of the component.
*/
"mode"?: "ios" | "md";
/**
* The transition animation when navigating to another page.
*/
@@ -4535,6 +4578,12 @@ declare global {
prototype: HTMLIonModalElement;
new (): HTMLIonModalElement;
};
interface HTMLIonMyChipElement extends Components.IonMyChip, HTMLStencilElement {
}
var HTMLIonMyChipElement: {
prototype: HTMLIonMyChipElement;
new (): HTMLIonMyChipElement;
};
interface HTMLIonNavElementEventMap {
"ionNavWillLoad": void;
"ionNavWillChange": void;
@@ -5236,6 +5285,7 @@ declare global {
"ion-menu-button": HTMLIonMenuButtonElement;
"ion-menu-toggle": HTMLIonMenuToggleElement;
"ion-modal": HTMLIonModalElement;
"ion-my-chip": HTMLIonMyChipElement;
"ion-nav": HTMLIonNavElement;
"ion-nav-link": HTMLIonNavLinkElement;
"ion-note": HTMLIonNoteElement;
@@ -7375,6 +7425,45 @@ declare namespace LocalJSX {
*/
"trigger"?: string | undefined;
}
interface IonMyChip {
/**
* The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics).
*/
"color"?: Color;
/**
* If `true`, the user cannot interact with the chip.
* @default false
*/
"disabled"?: boolean;
/**
* Set to `"bold"` for a chip with vibrant, bold colors or to `"subtle"` for a chip with muted, subtle colors. Only applies to the `ionic` theme.
* @default 'subtle'
*/
"hue"?: 'bold' | 'subtle';
/**
* The mode determines the platform behaviors of the component.
*/
"mode"?: "ios" | "md";
/**
* Display an outline style button.
* @default false
*/
"outline"?: boolean;
/**
* Set to `"soft"` for a chip with slightly rounded corners, `"round"` for a chip with fully rounded corners, or `"rectangular"` for a chip without rounded corners. Defaults to `"round"` for the `"ionic"` theme and `"soft"` for all other themes.
* @default 'soft'
*/
"shape"?: 'soft' | 'round' | 'rectangular';
/**
* Set to `"small"` for a chip with less height and padding. Defaults to `"large"` for the ionic theme, and undefined for all other themes.
* @default 'small'
*/
"size"?: 'small' | 'large';
/**
* The theme determines the visual appearance of the component.
*/
"theme"?: "ios" | "md" | "ionic";
}
interface IonNav {
/**
* If `true`, the nav should animate the transition of components.
@@ -7420,6 +7509,10 @@ declare namespace LocalJSX {
* Data you want to pass to the component as props. Only used if the `"routerDirection"` is `"forward"` or `"root"`.
*/
"componentProps"?: ComponentProps;
/**
* The mode determines the platform behaviors of the component.
*/
"mode"?: "ios" | "md";
/**
* The transition animation when navigating to another page.
*/
@@ -9166,6 +9259,7 @@ declare namespace LocalJSX {
"ion-menu-button": IonMenuButton;
"ion-menu-toggle": IonMenuToggle;
"ion-modal": IonModal;
"ion-my-chip": IonMyChip;
"ion-nav": IonNav;
"ion-nav-link": IonNavLink;
"ion-note": IonNote;
@@ -9269,6 +9363,7 @@ declare module "@stencil/core" {
"ion-menu-button": LocalJSX.IonMenuButton & JSXBase.HTMLAttributes<HTMLIonMenuButtonElement>;
"ion-menu-toggle": LocalJSX.IonMenuToggle & JSXBase.HTMLAttributes<HTMLIonMenuToggleElement>;
"ion-modal": LocalJSX.IonModal & JSXBase.HTMLAttributes<HTMLIonModalElement>;
"ion-my-chip": LocalJSX.IonMyChip & JSXBase.HTMLAttributes<HTMLIonMyChipElement>;
"ion-nav": LocalJSX.IonNav & JSXBase.HTMLAttributes<HTMLIonNavElement>;
"ion-nav-link": LocalJSX.IonNavLink & JSXBase.HTMLAttributes<HTMLIonNavLinkElement>;
"ion-note": LocalJSX.IonNote & JSXBase.HTMLAttributes<HTMLIonNoteElement>;

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 33 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1018 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 968 B

After

Width:  |  Height:  |  Size: 1004 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 33 KiB

View File

@@ -328,7 +328,7 @@ export class Checkbox implements ComponentInterface {
{this.renderHintText()}
</div>
<div class="native-wrapper">
<svg class="checkbox-icon" viewBox="0 0 24 24" part="container" aria-hidden="true">
<svg class="checkbox-icon" viewBox="0 0 24 24" part="container">
{path}
</svg>
</div>

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@@ -3,7 +3,6 @@ import { Component, Element, Event, Host, Method, Prop, State, Watch, h, writeTa
import { startFocusVisible } from '@utils/focus-visible';
import { getElementRoot, raf, renderHiddenInput } from '@utils/helpers';
import { printIonError, printIonWarning } from '@utils/logging';
import { FOCUS_TRAP_DISABLE_CLASS } from '@utils/overlays';
import { isRTL } from '@utils/rtl';
import { createColorClasses } from '@utils/theme';
import { caretDownSharp, caretUpSharp, chevronBack, chevronDown, chevronForward } from 'ionicons/icons';
@@ -1599,7 +1598,7 @@ export class Datetime implements ComponentInterface {
forcePresentation === 'time-date'
? [this.renderTimePickerColumns(forcePresentation), this.renderDatePickerColumns(forcePresentation)]
: [this.renderDatePickerColumns(forcePresentation), this.renderTimePickerColumns(forcePresentation)];
return <ion-picker class={FOCUS_TRAP_DISABLE_CLASS}>{renderArray}</ion-picker>;
return <ion-picker>{renderArray}</ion-picker>;
}
private renderDatePickerColumns(forcePresentation: string) {

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -79,15 +79,8 @@ export class Input implements ComponentInterface {
*/
@State() hasFocus = false;
/**
* Track validation state for proper aria-live announcements
*/
@State() isInvalid = false;
@Element() el!: HTMLIonInputElement;
private validationObserver?: MutationObserver;
/**
* The color to use from your application's color palette.
* Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`.
@@ -403,16 +396,6 @@ export class Input implements ComponentInterface {
};
}
/**
* Checks if the input is in an invalid state based on Ionic validation classes
*/
private checkInvalidState(): boolean {
const hasIonTouched = this.el.classList.contains('ion-touched');
const hasIonInvalid = this.el.classList.contains('ion-invalid');
return hasIonTouched && hasIonInvalid;
}
connectedCallback() {
const { el } = this;
@@ -423,26 +406,6 @@ export class Input implements ComponentInterface {
() => this.labelSlot
);
// Watch for class changes to update validation state
if (Build.isBrowser && typeof MutationObserver !== 'undefined') {
this.validationObserver = new MutationObserver(() => {
const newIsInvalid = this.checkInvalidState();
if (this.isInvalid !== newIsInvalid) {
this.isInvalid = newIsInvalid;
// Force a re-render to update aria-describedby immediately
forceUpdate(this);
}
});
this.validationObserver.observe(el, {
attributes: true,
attributeFilter: ['class'],
});
}
// Always set initial state
this.isInvalid = this.checkInvalidState();
this.debounceChanged();
if (Build.isBrowser) {
document.dispatchEvent(
@@ -488,12 +451,6 @@ export class Input implements ComponentInterface {
this.notchController.destroy();
this.notchController = undefined;
}
// Clean up validation observer to prevent memory leaks
if (this.validationObserver) {
this.validationObserver.disconnect();
this.validationObserver = undefined;
}
}
/**
@@ -669,22 +626,22 @@ export class Input implements ComponentInterface {
* Renders the helper text or error text values
*/
private renderHintText() {
const { helperText, errorText, helperTextId, errorTextId, isInvalid } = this;
const { helperText, errorText, helperTextId, errorTextId } = this;
return [
<div id={helperTextId} class="helper-text" aria-live="polite">
{!isInvalid ? helperText : null}
<div id={helperTextId} class="helper-text">
{helperText}
</div>,
<div id={errorTextId} class="error-text" role="alert">
{isInvalid ? errorText : null}
<div id={errorTextId} class="error-text">
{errorText}
</div>,
];
}
private getHintTextID(): string | undefined {
const { isInvalid, helperText, errorText, helperTextId, errorTextId } = this;
const { el, helperText, errorText, helperTextId, errorTextId } = this;
if (isInvalid && errorText) {
if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
return errorTextId;
}
@@ -907,7 +864,7 @@ export class Input implements ComponentInterface {
onCompositionstart={this.onCompositionStart}
onCompositionend={this.onCompositionEnd}
aria-describedby={this.getHintTextID()}
aria-invalid={this.isInvalid ? 'true' : undefined}
aria-invalid={this.getHintTextID() === this.errorTextId}
{...this.inheritedAttributes}
/>
{this.clearInput && !readonly && !disabled && (

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@@ -1,284 +0,0 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>Input - Validation</title>
<meta
name="viewport"
content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
<style>
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
grid-row-gap: 20px;
grid-column-gap: 20px;
}
h2 {
font-size: 12px;
font-weight: normal;
color: var(--ion-color-step-600);
margin-top: 10px;
margin-bottom: 5px;
}
.validation-info {
margin: 20px;
padding: 10px;
background: var(--ion-color-light);
border-radius: 4px;
}
</style>
</head>
<body>
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Input - Validation Test</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<div class="validation-info">
<h2>Screen Reader Testing Instructions:</h2>
<ol>
<li>Enable your screen reader (VoiceOver, NVDA, JAWS, etc.)</li>
<li>Tab through the form fields</li>
<li>When you tab away from an empty required field, the error should be announced immediately</li>
<li>The error text should be announced BEFORE the next field is announced</li>
<li>Test in Chrome, Safari, and Firefox to verify consistent behavior</li>
</ol>
</div>
<div class="grid">
<div>
<h2>Required Email Field</h2>
<ion-input
id="email-input"
type="email"
label="Email"
label-placement="floating"
fill="outline"
placeholder="Enter your email"
helper-text="We'll never share your email"
error-text="Please enter a valid email address"
required
></ion-input>
</div>
<div>
<h2>Required Name Field</h2>
<ion-input
id="name-input"
type="text"
label="Full Name"
label-placement="floating"
fill="outline"
placeholder="Enter your full name"
helper-text="First and last name"
error-text="Name is required"
required
></ion-input>
</div>
<div>
<h2>Phone Number (Pattern Validation)</h2>
<ion-input
id="phone-input"
type="tel"
label="Phone"
label-placement="floating"
fill="outline"
placeholder="(555) 555-5555"
pattern="^\(\d{3}\) \d{3}-\d{4}$"
helper-text="Format: (555) 555-5555"
error-text="Please enter a valid phone number"
required
></ion-input>
</div>
<div>
<h2>Password (Min Length)</h2>
<ion-input
id="password-input"
type="password"
label="Password"
label-placement="floating"
fill="outline"
placeholder="Enter password"
minlength="8"
helper-text="At least 8 characters"
error-text="Password must be at least 8 characters"
required
></ion-input>
</div>
<div>
<h2>Age (Number Range)</h2>
<ion-input
id="age-input"
type="number"
label="Age"
label-placement="floating"
fill="outline"
placeholder="Enter your age"
min="18"
max="120"
helper-text="Must be 18 or older"
error-text="Please enter a valid age (18-120)"
required
></ion-input>
</div>
<div>
<h2>Optional Field (No Validation)</h2>
<ion-input
id="optional-input"
type="text"
label="Optional Info"
label-placement="floating"
fill="outline"
placeholder="This field is optional"
helper-text="You can skip this field"
></ion-input>
</div>
</div>
<div class="ion-padding">
<ion-button id="submit-btn" expand="block" disabled>Submit Form</ion-button>
<ion-button id="reset-btn" expand="block" fill="outline">Reset Form</ion-button>
</div>
</ion-content>
</ion-app>
<script>
// Simple validation logic
const inputs = document.querySelectorAll('ion-input');
const submitBtn = document.getElementById('submit-btn');
const resetBtn = document.getElementById('reset-btn');
// Track which fields have been touched
const touchedFields = new Set();
// Validation functions
const validators = {
'email-input': (value) => {
if (!value) return false;
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
},
'name-input': (value) => {
return value && value.trim().length > 0;
},
'phone-input': (value) => {
if (!value) return false;
return /^\(\d{3}\) \d{3}-\d{4}$/.test(value);
},
'password-input': (value) => {
return value && value.length >= 8;
},
'age-input': (value) => {
if (!value) return false;
const age = parseInt(value);
return age >= 18 && age <= 120;
},
'optional-input': () => true, // Always valid
};
function validateField(input) {
const inputId = input.id;
const value = input.value;
const isValid = validators[inputId] ? validators[inputId](value) : true;
// Only show validation state if field has been touched
if (touchedFields.has(inputId)) {
if (isValid) {
input.classList.remove('ion-invalid');
input.classList.add('ion-valid');
} else {
input.classList.remove('ion-valid');
input.classList.add('ion-invalid');
}
input.classList.add('ion-touched');
}
return isValid;
}
function validateForm() {
let allValid = true;
inputs.forEach((input) => {
if (input.id !== 'optional-input') {
const isValid = validateField(input);
if (!isValid) {
allValid = false;
}
}
});
submitBtn.disabled = !allValid;
return allValid;
}
// Add event listeners
inputs.forEach((input) => {
// Mark as touched on blur
input.addEventListener('ionBlur', (e) => {
touchedFields.add(input.id);
validateField(input);
validateForm();
const isInvalid = input.classList.contains('ion-invalid');
if (isInvalid) {
console.log('Field marked invalid:', input.label, input.errorText);
}
});
// Validate on input
input.addEventListener('ionInput', (e) => {
if (touchedFields.has(input.id)) {
validateField(input);
validateForm();
}
});
// Also validate on focus loss via native blur
input.addEventListener('focusout', (e) => {
// Small delay to ensure Ionic's classes are updated
setTimeout(() => {
touchedFields.add(input.id);
validateField(input);
validateForm();
}, 10);
});
});
// Reset button
resetBtn.addEventListener('click', () => {
inputs.forEach((input) => {
input.value = '';
input.classList.remove('ion-valid', 'ion-invalid', 'ion-touched');
});
touchedFields.clear();
submitBtn.disabled = true;
});
// Submit button
submitBtn.addEventListener('click', () => {
if (validateForm()) {
alert('Form submitted successfully!');
}
});
// Initial setup
validateForm();
</script>
</body>
</html>

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@@ -418,9 +418,6 @@ export class Menu implements ComponentInterface, MenuI {
*/
@Method()
setOpen(shouldOpen: boolean, animated = true, role?: string): Promise<boolean> {
// Blur the active element to prevent it from being kept focused inside an element that will be set with aria-hidden="true"
(document.activeElement as HTMLElement)?.blur();
return menuController._setOpen(this, shouldOpen, animated, role);
}

View File

@@ -95,16 +95,6 @@ export const createSheetGesture = (
const contentAnimation = animation.childAnimations.find((ani) => ani.id === 'contentAnimation');
const enableBackdrop = () => {
// Respect explicit opt-out of focus trapping/backdrop interactions
// If focusTrap is false or showBackdrop is false, do not enable the backdrop or re-enable focus trap
const el = baseEl as HTMLIonModalElement & { focusTrap?: boolean; showBackdrop?: boolean };
const focusTrapAttr = el.getAttribute?.('focus-trap');
const showBackdropAttr = el.getAttribute?.('show-backdrop');
const focusTrapDisabled = el.focusTrap === false || focusTrapAttr === 'false';
const backdropDisabled = el.showBackdrop === false || showBackdropAttr === 'false';
if (focusTrapDisabled || backdropDisabled) {
return;
}
baseEl.style.setProperty('pointer-events', 'auto');
backdropEl.style.setProperty('pointer-events', 'auto');
@@ -245,12 +235,7 @@ export const createSheetGesture = (
* ion-backdrop and .modal-wrapper always have pointer-events: auto
* applied, so the modal content can still be interacted with.
*/
const modalEl = baseEl as HTMLIonModalElement & { focusTrap?: boolean; showBackdrop?: boolean };
const focusTrapAttr = modalEl.getAttribute?.('focus-trap');
const showBackdropAttr = modalEl.getAttribute?.('show-backdrop');
const focusTrapDisabled = modalEl.focusTrap === false || focusTrapAttr === 'false';
const backdropDisabled = modalEl.showBackdrop === false || showBackdropAttr === 'false';
const shouldEnableBackdrop = currentBreakpoint > backdropBreakpoint && !focusTrapDisabled && !backdropDisabled;
const shouldEnableBackdrop = currentBreakpoint > backdropBreakpoint;
if (shouldEnableBackdrop) {
enableBackdrop();
} else {
@@ -597,16 +582,7 @@ export const createSheetGesture = (
* Backdrop should become enabled
* after the backdropBreakpoint value
*/
const modalEl = baseEl as HTMLIonModalElement & {
focusTrap?: boolean;
showBackdrop?: boolean;
};
const focusTrapAttr = modalEl.getAttribute?.('focus-trap');
const showBackdropAttr = modalEl.getAttribute?.('show-backdrop');
const focusTrapDisabled = modalEl.focusTrap === false || focusTrapAttr === 'false';
const backdropDisabled = modalEl.showBackdrop === false || showBackdropAttr === 'false';
const shouldEnableBackdrop =
currentBreakpoint > backdropBreakpoint && !focusTrapDisabled && !backdropDisabled;
const shouldEnableBackdrop = currentBreakpoint > backdropBreakpoint;
if (shouldEnableBackdrop) {
enableBackdrop();
} else {

View File

@@ -1237,7 +1237,6 @@ export class Modal implements ComponentInterface, OverlayInterface {
const isHandleCycle = handleBehavior === 'cycle';
const isSheetModalWithHandle = isSheetModal && showHandle;
const focusTrapAttr = this.el.getAttribute('focus-trap');
return (
<Host
no-router
@@ -1254,7 +1253,7 @@ export class Modal implements ComponentInterface, OverlayInterface {
[`modal-sheet`]: isSheetModal,
[`modal-no-expand-scroll`]: isSheetModal && !expandToScroll,
'overlay-hidden': true,
[FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false || focusTrapAttr === 'false',
[FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false,
...getClassMap(this.cssClass),
}}
onIonBackdropTap={this.onBackdropTap}

View File

@@ -28,18 +28,6 @@ describe('modal: focus trap', () => {
expect(modal.classList.contains(FOCUS_TRAP_DISABLE_CLASS)).toBe(true);
});
it('should set the focus trap class when disabled via attribute string', async () => {
const page = await newSpecPage({
components: [Modal],
html: `
<ion-modal focus-trap="false"></ion-modal>
`,
});
const modal = page.body.querySelector('ion-modal')!;
expect(modal.classList.contains(FOCUS_TRAP_DISABLE_CLASS)).toBe(true);
});
it('should not set the focus trap class by default', async () => {
const page = await newSpecPage({
components: [Modal],

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

@@ -0,0 +1,187 @@
@import "../../themes/ionic.functions.color";
@import "../../themes/ionic.mixins";
:host {
/**
* @prop --ion-chip-focus-ring-color: Color of the focus ring
* @prop --ion-chip-focus-ring-width: Width of the focus ring
* @prop --ion-chip-padding-horizontal: Padding top and bottom of the chip
* @prop --ion-chip-padding-vertical: Padding start and end of the chip
* @prop --ion-chip-font-weight: Font weight of the chip
* @prop --ion-chip-line-height: Line height of the chip
* @prop --ion-chip-gap: Gap between the chip and the text
* @prop --ion-chip-border-width: Border width of the chip
*
* @prop --ion-chip-shape-soft-border-radius: Border radius of the chip for the soft shape
* @prop --ion-chip-shape-round-border-radius: Border radius of the chip for the round shape
* @prop --ion-chip-shape-rectangular-border-radius: Border radius of the chip for the rectangular shape
*
* @prop --ion-chip-size-small-min-height: Minimum height of the chip for the small size
* @prop --ion-chip-size-large-min-height: Minimum height of the chip for the large size
* @prop --ion-chip-size-small-font-size: Font size of the chip for the small size
* @prop --ion-chip-size-large-font-size: Font size of the chip for the large size
*
* @prop --ion-chip-hue-subtle-bg: Background of the chip for the subtle hue
* @prop --ion-chip-hue-subtle-color: Color of the chip for the subtle hue
* @prop --ion-chip-hue-subtle-border-color: Border color of the chip for the subtle hue
* @prop --ion-chip-hue-bold-bg: Background of the chip for the bold hue
* @prop --ion-chip-hue-bold-color: Color of the chip for the bold hue
* @prop --ion-chip-hue-bold-border-color: Border color of the chip for the bold hue
*/
--ion-chip-focus-ring-color: var(--ion-color-blue-50);
--ion-chip-focus-ring-width: var(--ion-spacing-xs);
--ion-chip-padding-horizontal: var(--ion-spacing-xs);
--ion-chip-padding-vertical: var(--ion-spacing-sm);
--ion-chip-font-weight: var(--ion-font-weights-normal);
--ion-chip-line-height: var(--ion-line-heights-md);
--ion-chip-gap: var(--ion-spacing-xs);
--ion-chip-border-width: var(--ion-spacing-xxs);
--ion-chip-shape-soft-border-radius: var(--ion-radii-lg);
--ion-chip-shape-round-border-radius: var(--ion-radii-xxl);
--ion-chip-shape-rectangular-border-radius: var(--ion-radii-none);
--ion-chip-size-small-min-height: var(--ion-scaling-600);
--ion-chip-size-large-min-height: var(--ion-scaling-900);
--ion-chip-size-small-font-size: var(--ion-font-sizes-sm-rem);
--ion-chip-size-large-font-size: var(--ion-font-sizes-lg-rem);
--ion-chip-hue-subtle-bg: var(--ion-color-gray-100);
--ion-chip-hue-subtle-color: var(--ion-color-gray-800);
--ion-chip-hue-subtle-border-color: var(--ion-color-gray-300);
--ion-chip-hue-bold-bg: var(--ion-color-gray-700);
--ion-chip-hue-bold-color: var(--ion-color-white);
--ion-chip-hue-bold-border-color: var(--ion-color-gray-800);
@include font-smoothing();
@include padding(var(--ion-chip-padding-horizontal), var(--ion-chip-padding-vertical));
@include border-radius(var(--ion-chip-border-radius));
display: inline-flex;
position: relative;
align-items: center;
justify-content: center;
background: var(--ion-chip-background);
color: var(--ion-chip-color);
cursor: pointer;
font-weight: var(--ion-chip-font-weight);
line-height: var(--ion-chip-line-height);
gap: var(--ion-chip-gap);
overflow: hidden;
box-sizing: border-box;
vertical-align: middle;
}
:host(.chip-disabled) {
cursor: default;
pointer-events: none;
}
// Outline Chip
// ---------------------------------------------
:host(.chip-outline) {
border-width: var(--ion-chip-border-width);
border-style: solid; // Do we need to use a variable here? ionic uses tokens but all themes use solid so it's not really necessary
}
// Chip Shapes
// ---------------------------------------------
:host(.chip-soft) {
--ion-chip-border-radius: var(--ion-chip-shape-soft-border-radius);
}
:host(.chip-round) {
--ion-chip-border-radius: var(--ion-chip-shape-round-border-radius);
}
:host(.chip-rectangular) {
--ion-chip-border-radius: var(--ion-chip-shape-rectangular-border-radius);
}
// Size
// ---------------------------------------------
:host(.chip-small) {
min-height: var(--ion-chip-size-small-min-height);
font-size: var(--ion-chip-size-small-font-size);
}
:host(.chip-large) {
min-height: var(--ion-chip-size-large-min-height);
font-size: var(--ion-chip-size-large-font-size);
}
// Subtle Chip
// ---------------------------------------------
:host(.chip-subtle) {
--ion-chip-background: var(--ion-chip-hue-subtle-bg);
--ion-chip-color: var(--ion-chip-hue-subtle-color);
}
:host(.chip-outline.chip-subtle) {
border-color: var(--ion-chip-hue-subtle-border-color);
}
// Bold Chip
// ---------------------------------------------
:host(.chip-bold) {
--ion-chip-background: var(--ion-chip-hue-bold-bg);
--ion-chip-color: var(--ion-chip-hue-bold-color);
}
:host(.chip-outline.chip-bold) {
border-color: var(--ion-chip-hue-bold-border-color);
}
// Chip Colors
// ---------------------------------------------
// Subtle
:host(.chip-subtle.ion-color) {
background: current-color(
base,
$subtle: true
); // these don't work because we need to update the function to use the new color system
color: current-color(
contrast,
$subtle: true
); // these don't work because we need to update the function to use the new color system
}
:host(.chip-subtle.chip-outline.ion-color) {
border-color: current-color(
shade,
$subtle: true
); // these don't work because we need to update the function to use the new color system
}
// Bold
:host(.chip-bold.ion-color) {
background: current-color(
base
); // these don't work because we need to update the function to use the new color system
color: current-color(
contrast
); // these don't work because we need to update the function to use the new color system
}
:host(.chip-bold.chip-outline.ion-color) {
border-color: current-color(
shade
); // these don't work because we need to update the function to use the new color system
}

View File

@@ -0,0 +1,113 @@
import type { ComponentInterface } from '@stencil/core';
import { Component, Host, Prop, h, Element } from '@stencil/core';
import { createColorClasses } from '@utils/theme';
import { getIonMode, getIonCustomTheme } from '../../global/ionic-global';
import type { Color } from '../../interface';
import { generateCSSVars } from '../../themes/base/generate-css-vars';
/**
* @virtualProp {"ios" | "md"} mode - The mode determines the platform behaviors of the component.
* @virtualProp {"ios" | "md" | "ionic"} theme - The theme determines the visual appearance of the component.
*/
@Component({
tag: 'ion-my-chip',
styleUrl: 'my-chip.base.scss',
shadow: true,
})
export class MyChip implements ComponentInterface {
private isThemed = false;
@Element() el!: HTMLElement;
/**
* The color to use from your application's color palette.
* Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`.
* For more information on colors, see [theming](/docs/theming/basics).
*/
@Prop({ reflect: true }) color?: Color;
/**
* Display an outline style button.
*/
@Prop() outline = false;
/**
* If `true`, the user cannot interact with the chip.
*/
@Prop() disabled = false;
/**
* Set to `"bold"` for a chip with vibrant, bold colors or to `"subtle"` for
* a chip with muted, subtle colors.
*
* Only applies to the `ionic` theme.
*/
// THOUGHT: Comparing to ChakraUI, they use variants (aka fills) for this
// maybe we should consider using variants: solid, outline, subtle
// they use solid for bold solid background, no outline, has states
// subtle for light background, no outline, has states
// outline for outline style, no background, has states
// surface for light background, with outline, has states
// ghost for no background, no outline, has states
// plain for no background, no outline, no states
@Prop() hue?: 'bold' | 'subtle' = 'subtle';
/**
* Set to `"soft"` for a chip with slightly rounded corners, `"round"` for a chip with fully
* rounded corners, or `"rectangular"` for a chip without rounded corners.
* Defaults to `"round"` for the `"ionic"` theme and `"soft"` for all other themes.
*/
@Prop() shape?: 'soft' | 'round' | 'rectangular' = 'soft';
/**
* Set to `"small"` for a chip with less height and padding.
*
* Defaults to `"large"` for the ionic theme, and undefined for all other themes.
*/
@Prop() size?: 'small' | 'large' = 'small';
componentWillLoad() {
const myCustomTheme = getIonCustomTheme();
const componentTheme = myCustomTheme.components['IonChip'];
// check if componentTheme is not an empty object or undefined
if (componentTheme !== undefined || Object.keys(componentTheme).length > 0) {
this.isThemed = true;
// apply a style tag to this component
const style = document.createElement('style');
style.innerHTML = [generateCSSVars(componentTheme, '--ion-chip-', ':host(.chip-themed)')].join('\n\n');
// Attach to Shadow Root if available, otherwise Light DOM
const root = this.el.shadowRoot ?? this.el;
root.appendChild(style);
}
}
render() {
const { hue, shape, size } = this;
const mode = getIonMode(this);
return (
<Host
aria-disabled={this.disabled ? 'true' : null}
class={createColorClasses(this.color, {
// Needed to properly add the custom styles else
// the styles would be ignored due to styling order
'chip-themed': this.isThemed,
[`chip-${shape}`]: shape !== undefined,
'chip-outline': this.outline,
'chip-disabled': this.disabled,
'ion-activatable': true,
'ion-focusable': !this.disabled,
[`chip-${size}`]: size !== undefined,
[`chip-${hue}`]: hue !== undefined,
})}
>
<slot></slot>
{mode === 'md' && <ion-ripple-effect></ion-ripple-effect>}
</Host>
);
}
}

View File

@@ -0,0 +1,95 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8" />
<title>MyChip - Basic</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
/>
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
</head>
<body>
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>MyChip - Basic</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding" id="content" style="text-align: center">
<h2>My Chip</h2>
<p>
This is a POC, not all the features are working as expected. For example, the color property still needs to be
worked on.
</p>
<p>You can verify that the variables are being generated correctly by inspecting the root element.</p>
<ion-my-chip>default</ion-my-chip>
<ion-my-chip outline>outline</ion-my-chip>
<ion-my-chip size="large">large</ion-my-chip>
<ion-my-chip hue="bold">bold</ion-my-chip>
<ion-my-chip hue="bold" outline>bold with outline</ion-my-chip>
<ion-my-chip color="primary">primary, subtle hue</ion-my-chip>
<ion-my-chip color="primary" hue="bold">primary, bold hue</ion-my-chip>
<ion-my-chip color="secondary">secondary, subtle hue</ion-my-chip>
<ion-my-chip color="secondary" hue="bold">secondary, bold hue</ion-my-chip>
</ion-content>
</ion-app>
<script>
window.Ionic = {
config: {
customTheme: {
palette: {
light: {
color: {
primary: {
bold: {
base: 'pink',
constrast: 'black',
},
},
secondary: {
bold: {
base: '#510aa8',
},
},
// gray: {
// 800: 'pink',
// }
},
},
},
components: {
IonChip: {
hue: {
subtle: {
bg: 'red',
color: 'white',
borderColor: 'black'
},
bold: {
bg: 'blue',
color: 'white',
borderColor: 'black'
}
}
},
},
},
},
};
</script>
</body>
</html>

View File

@@ -7,6 +7,9 @@ import type { RouterDirection } from '../router/utils/interface';
import { navLink } from './nav-link-utils';
/**
* @virtualProp {"ios" | "md"} mode - The mode determines the platform behaviors of the component.
*/
@Component({
tag: 'ion-nav-link',
})

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 43 KiB

View File

@@ -3,7 +3,7 @@
// Picker Column
// --------------------------------------------------
.picker-column-option-button {
button {
@include padding(0);
@include margin(0);
@@ -40,6 +40,6 @@
opacity: 0.4;
}
:host(.option-disabled) .picker-column-option-button {
:host(.option-disabled) button {
cursor: default;
}

View File

@@ -124,9 +124,9 @@ export class PickerColumnOption implements ComponentInterface {
['option-disabled']: disabled,
})}
>
<div class={'picker-column-option-button'} role="button" aria-label={ariaLabel} onClick={() => this.onClick()}>
<button tabindex="-1" aria-label={ariaLabel} disabled={disabled} onClick={() => this.onClick()}>
<slot></slot>
</div>
</button>
</Host>
);
}

View File

@@ -10,7 +10,7 @@ configs({ directions: ['ltr'] }).forEach(({ config, title }) => {
test('should not have accessibility violations', async ({ page }) => {
await page.goto(`/src/components/picker-column-option/test/a11y`, config);
const results = await new AxeBuilder({ page }).disableRules('color-contrast').analyze();
const results = await new AxeBuilder({ page }).analyze();
expect(results.violations).toEqual([]);
});

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Some files were not shown because too many files have changed in this diff Show More