Compare commits

..

28 Commits

Author SHA1 Message Date
David Lourenço
7c11d2b2ab Added separated variables for size of slots in Toolbar 2026-02-25 09:53:59 +00:00
Susmita Bhowmik
5cc8adb1d5 fix(chip): scale slotted icon size by chip size (#30961)
Issue number: resolves internal

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->
When an icon was placed within a chip, the icon size was the same
regardless of the chip size.

## What is the new behavior?
Scales the size of the icon based on design specifications.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!--
  If this introduces a breaking change:
1. Describe the impact and migration path for existing applications
below.
  2. Update the BREAKING.md file with the breaking change.
3. Add "BREAKING CHANGE: [...]" to the commit description when merging.
See
https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer
for more information.
-->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->
2026-02-20 08:09:19 -07:00
rug
a6016f85bd fix(textarea): respect rows prop by adjusting min-height in the ionic theme (#30957)
Issue number: resolves internal

---------

## What is the current behavior?
- The `rows` attribute is not respected in `ion-textarea` when lower
than 3. Some paddings and margins were applied in incorrect places in
the `ionic` theme.

## What is the new behavior?
- The ionic theme has the following changes:
- `.textarea-size-*` classes stopped forcing the `min-height` allowing
`rows` to adjust the height
- Added an end-to-end test file `textarea.e2e.ts` to verify that the
`rows` attribute is respected across different values, sizes, and with
auto-grow enabled.
- Introduced a new HTML test page (`index.html`) for visual/manual
testing of `ion-textarea` with different `rows` and `size` combinations,
and with auto-grow enabled.

## Does this introduce a breaking change?
- [ ] Yes
- [x] No

## Other information

- [Textarea:
Autogrow](https://ionic-framework-git-rou-12443-v4-ionic1.vercel.app/src/components/textarea/test/autogrow?ionic:theme=ionic)
- [Textarea:
Basic](https://ionic-framework-git-rou-12443-v4-ionic1.vercel.app/src/components/textarea/test/basic?ionic:theme=ionic)
- [Textarea:
Rows](https://ionic-framework-git-rou-12443-v4-ionic1.vercel.app/src/components/textarea/test/rows?ionic:theme=ionic)
- [Textarea:
Size](https://ionic-framework-git-rou-12443-v4-ionic1.vercel.app/src/components/textarea/test/size?ionic:theme=ionic)
- [Textarea:
Shape](https://ionic-framework-git-rou-12443-v4-ionic1.vercel.app/src/components/textarea/test/shape?ionic:theme=ionic)

---------

Co-authored-by: ionitron <hi@ionicframework.com>
Co-authored-by: Maria Hutt <thetaPC@users.noreply.github.com>
2026-02-20 10:14:15 +01:00
Brandy Smith
177a6ea134 feat(input-otp): convert to a form associated shadow component #30785 (#30884)
Issue number: internal

---------

## What is the current behavior?
Input Otp uses `scoped` encapsulation. This causes issues with CSP compatibility and is inconsistent with our goal of having all components use Shadow DOM.

## What is the new behavior?
- Converts `ion-input-otp` to `shadow` with `formAssociated: true`
- Adds shadow parts for inner elements

## Does this introduce a breaking change?
- [x] Yes
- [ ] No

BREAKING CHANGE:

Input Otp has been converted to use [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM).

If you were targeting the internals of `ion-input-otp` in your CSS, you will need to target the `group`, `container`, `native`, `separator` or `description` [Shadow Parts](https://ionicframework.com/docs/theming/css-shadow-parts) instead, or use the provided CSS Variables.

---------

Co-authored-by: Brandy Smith <6577830+brandyscarney@users.noreply.github.com>
2026-02-05 13:54:05 -05:00
Brandy Smith
1d36021c6f feat(textarea): convert to a form associated shadow component (#30785)
Issue number: internal

---------

## What is the current behavior?
Textarea uses `scoped` encapsulation. This causes issues with CSP compatibility and is inconsistent with our goal of having all components use Shadow DOM.

## What is the new behavior?
- Converts `ion-textarea` to `shadow` with `formAssociated: true`
- Adds shadow parts for inner elements
- Adds and updates existing e2e tests in core for textarea
- Updated Angular test app to target textarea shadowRoot and updated lazy forms test to include textarea (standalone already has these)
- Updated React & Vue test apps to target textarea shadowRoot and added validation tests
- Improves focus behavior inside of a popover so that it is no longer required to tab twice to get to the textarea in any browser

## Does this introduce a breaking change?
- [x] Yes
- [ ] No

BREAKING CHANGE:

Textarea has been converted to use [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM).

If you were targeting the internals of `ion-textarea` in your CSS, you will need to target the `container`, `label`, `native`, `supporting-text`, `helper-text`, `error-text`, `counter`, or `bottom` [Shadow Parts](https://ionicframework.com/docs/theming/css-shadow-parts) instead, or use the provided CSS Variables.

---------

Co-authored-by: Brandy Smith <6577830+brandyscarney@users.noreply.github.com>
2026-02-05 13:35:39 -05:00
Brandy Smith
89f3b1f956 fix(datetime): remove ion-buttons to fix CSP violations (#30770)
Issue number: N/A

---------

## What is the current behavior?
The Datetime component breaks CSP rules due to the following:

1. Datetime is using `ion-buttons` which uses `scoped` encapsulation.
2. When using the `ionic` theme, it imports Phosphor icons as data URIs, which are blocked by `connect-src 'self' blob:`.

## What is the new behavior?
- Remove all usages of `ion-buttons`, removing the dependency on a scoped component
- Updates the styling to account for the removal of `ion-buttons`
- Updates `ion-button` to change its styles based on being inside of an `ion-datetime`

Phosphor icons have not been removed because there is a workaround for it and we will remove its usage across all components in future work.

## Does this introduce a breaking change?

- [x] Yes
- [ ] No

This shouldn't cause breaking changes but because we are no longer recommending users use `ion-buttons` with custom buttons I am marking it as a breaking change.

BREAKING CHANGE:

The `ion-buttons` component has been removed from the internal implementation of `ion-datetime` and is no longer required when passing custom buttons to the `slot="buttons"`. When providing custom buttons, use a `div` element instead of `ion-buttons`. While existing code using `ion-buttons` may continue to work visually, future updates to the `ion-buttons` component may cause any styles you rely on to break.

---------

Co-authored-by: Brandy Smith <6577830+brandyscarney@users.noreply.github.com>
2026-02-05 13:17:25 -05:00
Brandy Smith
4aaece0bec feat(card): convert card-content to shadow DOM (#30759)
Issue number: internal

---------

## What is the current behavior?
Card content has no encapsulation.

## What is the new behavior?
Converted `ion-card-content` to Shadow DOM which improves consistency among components & CSP compatibility.

## Does this introduce a breaking change?

- [x] Yes
- [ ] No

BREAKING CHANGE:

The `ion-card-content` component has been updated to Shadow DOM. With this update, all card-related components now use Shadow DOM for style encapsulation. There should not be any breaking changes related to targeting inner elements since `ion-card-content` does not have any internal elements of its own. However, some user styles may break due to the removal of the `card-content-{mode}` class or changes in selector specificity.

The default styles for heading elements inside `ion-card-content` have been removed. If you need custom styling for headings, you can add your own CSS targeting these elements.

For example:

```css
ion-card-content h1 {
  margin-top: 0;
  margin-bottom: 2px;

  font-size: 1.5rem;
}

ion-card-content h2 {
  margin-top: 2px;
  margin-bottom: 2px;

  font-size: 1rem;
}

ion-card-content h3,
ion-card-content h4,
ion-card-content h5,
ion-card-content h6 {
  margin-top: 2px;
  margin-bottom: 2px;

  font-size: 0.875rem;
}
```

---------

Co-authored-by: Brandy Smith <6577830+brandyscarney@users.noreply.github.com>
2026-02-05 12:17:42 -05:00
Giuliana Silva
17b8468b04 style(segment-button): add and update color to slotted children for improved styling consistency (#30905)
Issue number: resolves internal

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->
- Previously in iOS, when using something different than the `ion-label`
to add a text inside the `segment-button`, the default color attributed
to it was blue.
<img width="357" height="103" alt="image"
src="https://github.com/user-attachments/assets/2196e548-f0b7-4e7f-b093-e58c88b2a6cb"
/>

- In Android, the expected color is not attributed either, but it
defaults to black instead of blue.

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->
- Now, all the slotted elements will respect the color defined by the
`color` CSS variable.

## Does this introduce a breaking change?
- [ ] Yes
- [x] No

<!--
  If this introduces a breaking change:
1. Describe the impact and migration path for existing applications
below.
  2. Update the BREAKING.md file with the breaking change.
3. Add "BREAKING CHANGE: [...]" to the commit description when merging.
See
https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer
for more information.
-->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->
2026-01-07 09:33:25 +00:00
José Rio
abb950638b fix(checkbox, toggle): uniform helper text style for ionic theme (#30893)
Issue number: resolves #

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
Helper and Error text style it's not uniform between elements.

## What is the new behavior?
* Changed the typography for `.checkbox-bottom` and `.toggle-bottom` to
use `globals.$ion-body-sm-medium` for a more consistent appearance with
other components.
[[1]](diffhunk://#diff-796ffc72b6171910c25d5aad085bc245095c38f104d55e253e06282702205066L116-R116)
[[2]](diffhunk://#diff-ad6128c5d08f42a970eab3a8c7081eaad590bd4ebbcd19ec4e6110a6f7977040L59-R70)
* Updated the color of `.input-bottom .helper-text` and `.input-bottom
.counter` to use `globals.$ion-text-subtlest` instead of
`globals.$ion-primitives-neutral-800`, and `.input-bottom .error-text`
to use `globals.$ion-text-danger`.
* Standardized `.textarea-bottom .error-text` to use
`globals.$ion-text-danger` and reordered the CSS for clarity, while
`.textarea-bottom .helper-text` and `.textarea-bottom .counter` use
`globals.$ion-text-subtlest`.
* Updated `.toggle-bottom .error-text` to use `globals.$ion-text-danger`
and `.toggle-bottom .helper-text` to use `globals.$ion-text-subtlest`
for improved semantic clarity and consistency.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!--
  If this introduces a breaking change:
1. Describe the impact and migration path for existing applications
below.
  2. Update the BREAKING.md file with the breaking change.
3. Add "BREAKING CHANGE: [...]" to the commit description when merging.
See
https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer
for more information.
-->

---------

Co-authored-by: ionitron <hi@ionicframework.com>
2025-12-30 19:28:41 +00:00
João Ferreira
6c74618a07 fix(segment-button): correct color for button childs (#30891)
Issue number: internal


<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->


## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->
- guarantee that every child of segment-button has color: primary-color
when checked;

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!--
  If this introduces a breaking change:
1. Describe the impact and migration path for existing applications
below.
  2. Update the BREAKING.md file with the breaking change.
3. Add "BREAKING CHANGE: [...]" to the commit description when merging.
See
https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer
for more information.
-->

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

Sample:
https://ionic-framework-git-rou-12439-ionic1.vercel.app/src/components/segment/test/basic?ionic:theme=ionic
2025-12-26 17:46:40 +00:00
Shane
68b66947d3 release-8.7.15 (#30888)
v8.7.15
2025-12-23 12:18:50 -08:00
ShaneK
5b9a0bbc4d Merge branch 'main' into fix/merge-next-12-23 2025-12-23 12:04:38 -08:00
Shane
72826edf9a merge release-8.7.15 (#30886)
v8.7.15

---------

Co-authored-by: ionitron <hi@ionicframework.com>
2025-12-23 11:14:57 -08:00
ionitron
4360e39a58 chore(): update package lock files 2025-12-23 18:55:54 +00:00
ionitron
622d62a3f4 v8.7.15 2025-12-23 18:55:19 +00:00
Shane
12ede4b79c fix(input-password-toggle): improve screen reader announcements (#30885)
Issue number: resolves internal

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?

The `ion-input-password-toggle` button uses `role="switch"` with
`aria-checked`, causing screen readers like VoiceOver to announce both a
state ("On/Off") and an action ("Show/Hide password"). This results in
confusing, redundant output such as "On, Hide Password" or "Off, Show
Password".

## What is the new behavior?

The password toggle button now uses `aria-pressed` instead of
`role="switch"` with `aria-checked`. Screen readers announce the
action-based label ("Show password" or "Hide password") along with the
pressed state, and properly announce state changes when the button is
activated.

## Does this introduce a breaking change?

- [ ] Yes
- [X] No

<!--
  If this introduces a breaking change:
1. Describe the impact and migration path for existing applications
below.
  2. Update the BREAKING.md file with the breaking change.
3. Add "BREAKING CHANGE: [...]" to the commit description when merging.
See
https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer
for more information.
-->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

[Old
Preview](https://ionic-framework-git-main-ionic1.vercel.app/src/components/input-password-toggle/test/basic)

[New
Preview](https://ionic-framework-git-fw-6920-ionic1.vercel.app/src/components/input-password-toggle/test/basic)

Current dev build:
```
8.7.15-dev.11766421552.180757ca
```
2025-12-22 17:24:32 +00:00
Israel de la Barrera
f83b000530 fix(header): show iOS condense header when app is in MD mode (#30690)
Issue number: resolves #29929

---------

## What is the current behavior?
When forcing `mode=ios` in a collapsible header,
`.header-collapse-condense` would still be applied from the
`header.md.scss` file, leaving the collapsible header always hidden.

## What is the new behavior?
When forcing `mode=ios` in a collapsible header, the
`.header-collapse-condense` styles from the `header.md.scss` file won't
be applied, and the collapsible header will be visible.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No


## Other information
Something worth mentioning is that this behavior only appears after
initial load: if the route is loaded refreshing the page, the header
will appear and work correctly, but navigating forth and back will apply
both the .ios and .md style files.

I showcase this with a modal because It'll always display the broken
hehavior.

| Before | After |
|--------|-------|
| <video
src="https://github.com/user-attachments/assets/1307ee9f-452a-4b00-877d-0b8e360d3bf7">
| <video
src="https://github.com/user-attachments/assets/f9ee3851-ce94-4a27-9947-37aa1f5433b9">
|

---------

Co-authored-by: ShaneK <shane@shanessite.net>
2025-12-22 17:21:16 +00:00
Pedro Lourenço
e227fd904e refactor(checkbox): update according to design (#30882)
Issue number: resolves #

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
The checkbox component does not currently match our UX designs for the
ionic theme.

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->
- Changed typography to use correct design token.


## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!--
  If this introduces a breaking change:
1. Describe the impact and migration path for existing applications
below.
  2. Update the BREAKING.md file with the breaking change.
3. Add "BREAKING CHANGE: [...]" to the commit description when merging.
See
https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer
for more information.
-->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->
2025-12-22 15:54:43 +00:00
Brandy Smith
3b60a1d68a fix(modal): dismiss top-most overlay when multiple IDs match (#30883)
Issue number: resolves #30030

---------

## What is the current behavior?
When modals are presented one after another with matching IDs and then
dismissed by ID it will dismiss the first presented modal.

## What is the new behavior?
- When modals are presented one after another with matching IDs and then
dismissed by ID it will dismiss the last (top-most) presented modal.
- Added e2e tests to verify this behavior works the same as the default
dismiss (not passing an ID).

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

## Other information

[Modal: Dismiss
Behavior](https://ionic-framework-git-fw-7016-ionic1.vercel.app/src/components/modal/test/dismiss-behavior)

---------

Co-authored-by: Brandy Smith <6577830+brandyscarney@users.noreply.github.com>
2025-12-19 19:03:04 +00:00
Maria Hutt
5bb5cc6385 refactor(core): use Capacitor safe-area CSS variables on older WebViews (#30866)
Issue number: internal

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->

The safe area variables are only reliant on `env` variables that are
provided by devices.

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

Capacitor 8 has released [safe area variable
fallbacks](https://capacitorjs.com/docs/apis/system-bars#android-note)
to provide consistent behaviors with older Android devices:

> Due to a [bug](https://issues.chromium.org/issues/40699457) in some
older versions of Android WebView (< 140), correct safe area values are
not available via the safe-area-inset-x CSS env variables. This plugin
will inject the correct inset values into a new CSS variable(s) named
--safe-area-inset-x that you can use as a fallback in your frontend
styles.

- Updated safe area variables to use the fallbacks provided by
Capacitor.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!--
  If this introduces a breaking change:
1. Describe the impact and migration path for existing applications
below.
  2. Update the BREAKING.md file with the breaking change.
3. Add "BREAKING CHANGE: [...]" to the commit description when merging.
See
https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer
for more information.
-->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

Changes to the [core
file](2ee52d77c8/core/src/css/core.scss (L253-L260))
will be done on `main`.

---------

Co-authored-by: Brandy Smith <6577830+brandyscarney@users.noreply.github.com>
Co-authored-by: ionitron <hi@ionicframework.com>
2025-12-18 13:41:03 -05:00
Maria Hutt
8573bf8083 fix(core): use Capacitor safe-area CSS variables on older WebViews (#30865)
Issue number: internal

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->

The safe area variables are only reliant on `env` variables that are
provided by devices.

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

Capacitor 8 has released [safe area variable
fallbacks](https://capacitorjs.com/docs/apis/system-bars#android-note)
to provide consistent behaviors with older Android devices:

> Due to a [bug](https://issues.chromium.org/issues/40699457) in some
older versions of Android WebView (< 140), correct safe area values are
not available via the safe-area-inset-x CSS env variables. This plugin
will inject the correct inset values into a new CSS variable(s) named
--safe-area-inset-x that you can use as a fallback in your frontend
styles.

- Updated safe area variables to use the fallbacks provided by
Capacitor.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!--
  If this introduces a breaking change:
1. Describe the impact and migration path for existing applications
below.
  2. Update the BREAKING.md file with the breaking change.
3. Add "BREAKING CHANGE: [...]" to the commit description when merging.
See
https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer
for more information.
-->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

Dev build: `8.7.13-dev.11765920447.1a01ab8b`

---------

Co-authored-by: Brandy Smith <brandyscarney@users.noreply.github.com>
2025-12-18 18:16:56 +00:00
Bernardo Cardoso
1b21e0748a fix(core): add fallback handler for hardware back button when no router is present (#30878)
Issue number: internal

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

This pull request enhances the hardware back button functionality to
ensure a consistent user experience, especially when no custom handlers
are registered. The main improvement is the addition of a fallback
handler that triggers the default browser back navigation when no other
handlers are present.

**Hardware Back Button Improvements:**

* Added a fallback handler in `startHardwareBackButton` that navigates
back in browser history (`win?.history.back()`) if no custom handlers
are registered, ensuring the hardware back button always performs a
meaningful action.
* Introduced a constant `FALLBACK_BACK_BUTTON_PRIORITY` with a value of
`-1` to manage the priority of the fallback handler.

**Code Consistency:**

* Moved the import of `win` from `@utils/browser` to group it with other
imports for consistency.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!--
  If this introduces a breaking change:
1. Describe the impact and migration path for existing applications
below.
  2. Update the BREAKING.md file with the breaking change.
3. Add "BREAKING CHANGE: [...]" to the commit description when merging.
See
https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer
for more information.
-->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

---------

Co-authored-by: ShaneK <shane@shanessite.net>
2025-12-18 16:21:11 +00:00
Brandy Smith
34bcf95481 chore(): update next from main (#30881) 2025-12-17 13:57:04 -05:00
Brandy Smith
18eacab8fb Merge branch 'main' into chore/update-next-from-main 2025-12-17 13:25:59 -05:00
Brandy Smith
2c6fac9060 merge release-8.7.14 (#30879)
v8.7.14

---------

Co-authored-by: ionitron <hi@ionicframework.com>
2025-12-17 13:22:41 -05:00
ionitron
b9fdfab667 chore(): update package lock files 2025-12-17 17:51:54 +00:00
ionitron
f7af5d3ca5 v8.7.14 2025-12-17 17:50:44 +00:00
Shane
03fb422bfa fix(tabs): select correct tab when routes have similar prefixes (#30863)
Issue number: resolves #30448 

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?

When using ion-tabs with routes that share a common prefix (e.g.,
`/home`, `/home2`, `/home3`), navigating to `/home2` incorrectly
highlights the `/home` tab. This occurs because the tab matching logic
uses `pathname.startsWith(href)`, which causes `/home2` to match `/home`
since `/home2` starts with `/home`.

## What is the new behavior?

Tab selection now uses path segment matching instead of simple prefix
matching. A tab's href will only match if the pathname is an exact match
OR starts with the href followed by a / (for nested routes). This
ensures /home2 no longer incorrectly matches /home, while still allowing
/home/details to correctly match the /home tab.

## Does this introduce a breaking change?

- [ ] Yes
- [X] No

<!--
  If this introduces a breaking change:
1. Describe the impact and migration path for existing applications
below.
  2. Update the BREAKING.md file with the breaking change.
3. Add "BREAKING CHANGE: [...]" to the commit description when merging.
See
https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer
for more information.
-->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

Current dev build:
```
8.7.13-dev.11765486444.14025098
```

---------

Co-authored-by: Maria Hutt <thetaPC@users.noreply.github.com>
Co-authored-by: Brandy Smith <brandyscarney@users.noreply.github.com>
2025-12-17 00:11:10 +00:00
1010 changed files with 5642 additions and 9094 deletions

View File

@@ -29,4 +29,4 @@ runs:
with:
name: ionic-core
output: core/CoreBuild.zip
paths: core/dist core/components core/src/foundations core/css core/themes core/hydrate core/loader core/src/components.d.ts
paths: core/dist core/components core/src/foundations core/css core/hydrate core/loader core/src/components.d.ts

View File

@@ -33,4 +33,4 @@ runs:
output: core/CoreBuild.zip
# Include generated proxy files from Stencil output targets so
# framework builds can detect when they need to be updated
paths: core/dist core/components core/src/foundations core/css core/themes core/hydrate core/loader core/src/components.d.ts core/api.txt packages/angular/src/directives/proxies.ts packages/angular/src/directives/proxies-list.ts packages/angular/standalone/src/directives/proxies.ts packages/vue/src/proxies.ts packages/react/src/components/proxies.ts packages/react/src/components/inner-proxies.ts packages/react/src/components/routing-proxies.ts
paths: core/dist core/components core/src/foundations core/css core/hydrate core/loader core/src/components.d.ts core/api.txt packages/angular/src/directives/proxies.ts packages/angular/src/directives/proxies-list.ts packages/angular/standalone/src/directives/proxies.ts packages/vue/src/proxies.ts packages/react/src/components/proxies.ts packages/react/src/components/inner-proxies.ts packages/react/src/components/routing-proxies.ts

1
.gitignore vendored
View File

@@ -23,7 +23,6 @@ temp/
core/theme-builder/
core/test-components/
core/css/
core/themes/
$RECYCLE.BIN/
.DS_Store

View File

@@ -18,7 +18,11 @@ This is a comprehensive list of the breaking changes introduced in the major ver
- [Button](#version-9x-button)
- [Card](#version-9x-card)
- [Chip](#version-9x-chip)
- [Datetime](#version-9x-datetime)
- [Grid](#version-9x-grid)
- [Input Otp](#version-9x-input-otp)
- [Radio Group](#version-9x-radio-group)
- [Textarea](#version-9x-textarea)
<h2 id="version-9x-components">Components</h2>
@@ -28,25 +32,52 @@ This is a comprehensive list of the breaking changes introduced in the major ver
<h4 id="version-9x-card">Card</h4>
- The `border-radius` of the `ios` and `md` card now defaults to `14px` and `12px` instead of `8px` and `4px`, respectively, in accordance with the iOS and Material Design 3 guidelines. To revert to the previous appearance, set the `shape` to `"soft"`, or override the `--border-radius` CSS variable to specify a different value.
- **ion-card**: The `border-radius` of the `ios` and `md` card now defaults to `14px` and `12px` instead of `8px` and `4px`, respectively, in accordance with the iOS and Material Design 3 guidelines. To revert to the previous appearance, set the `shape` to `"soft"`, or override the `--border-radius` CSS variable to specify a different value.
- **ion-card-content**: The `ion-card-content` component has been updated to Shadow DOM. With this update, all card-related components now use Shadow DOM for style encapsulation. The default styles for heading elements inside `ion-card-content` have been removed. If you need custom styling for headings, you can add your own CSS targeting these elements. For example:
```css
ion-card-content h1 {
margin-top: 0;
margin-bottom: 2px;
font-size: 1.5rem;
}
ion-card-content h2 {
margin-top: 2px;
margin-bottom: 2px;
font-size: 1rem;
}
ion-card-content h3,
ion-card-content h4,
ion-card-content h5,
ion-card-content h6 {
margin-top: 2px;
margin-bottom: 2px;
font-size: 0.875rem;
}
```
<h4 id="version-9x-chip">Chip</h4>
- The `border-radius` of the `ios` and `md` chip now defaults to `10px` and `8px`, respectively, instead of `16px` in accordance with the iOS and Material Design 3 guidelines. To revert to the previous appearance, set the `shape` to `"round"`, or override the `--border-radius` CSS variable to specify a different value.
<h4 id="version-9x-datetime">Datetime</h4>
- The `ion-buttons` component has been removed from the internal implementation of `ion-datetime` and is no longer required when passing custom buttons to the `slot="buttons"`. When providing custom buttons, use a `div` element instead of `ion-buttons`. While existing code using `ion-buttons` may continue to work visually, future updates to the `ion-buttons` component may cause any styles you rely on to break.
<h4 id="version-9x-grid">Grid</h4>
- The properties `pull` and `push` have been deprecated and no longer work. A similar look can be achieved with the newly added property `order`.
<h4 id="version-9x-radio-group">Radio Group</h4>
- Converted `ion-radio-group` to use [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM).<br/>
If you were targeting the internals of `ion-radio-group` in your CSS, you will need to target the `supporting-text`, `helper-text` or `error-text` [Shadow Parts](https://ionicframework.com/docs/theming/css-shadow-parts) instead, or use the provided CSS Variables.<br/>
Additionally, the `radio-group-wrapper` div element has been removed, causing slotted elements to be direct children of the `ion-radio-group`.
<h5>Example 1: Swap two columns</h5>
**Version up to 8.x**
```html
<ion-grid>
<ion-row>
@@ -56,7 +87,9 @@ Additionally, the `radio-group-wrapper` div element has been removed, causing sl
</ion-row>
</ion-grid>
```
**Version 9.x+**
```html
<ion-grid>
<ion-row>
@@ -68,9 +101,11 @@ Additionally, the `radio-group-wrapper` div element has been removed, causing sl
```
<h5>Example 2: Reorder columns with specific sizes</h5>
To reorder two columns where column 1 has `size="9" push="3"` and column 2 has `size="3" pull="9"`:
**Version up to 8.x**
```html
<ion-grid>
<ion-row>
@@ -79,7 +114,9 @@ To reorder two columns where column 1 has `size="9" push="3"` and column 2 has `
</ion-row>
</ion-grid>
```
**Version 9.x+**
```html
<ion-grid>
<ion-row>
@@ -88,7 +125,9 @@ To reorder two columns where column 1 has `size="9" push="3"` and column 2 has `
</ion-row>
</ion-grid>
```
<h5>Example 3: Push</h5>
```html
<ion-grid>
<ion-row>
@@ -101,7 +140,9 @@ To reorder two columns where column 1 has `size="9" push="3"` and column 2 has `
</ion-row>
</ion-grid>
```
**Version 9.x+**
```html
<ion-grid>
<ion-row>
@@ -116,6 +157,7 @@ To reorder two columns where column 1 has `size="9" push="3"` and column 2 has `
```
<h5>Example 4: Push and Pull</h5>
```html
<ion-grid>
<ion-row>
@@ -128,7 +170,9 @@ To reorder two columns where column 1 has `size="9" push="3"` and column 2 has `
</ion-row>
</ion-grid>
```
**Version 9.x+**
```html
<ion-grid>
<ion-row>
@@ -140,4 +184,24 @@ To reorder two columns where column 1 has `size="9" push="3"` and column 2 has `
</ion-col>
</ion-row>
</ion-grid>
```
```
<h4 id="version-9x-input-otp">Input Otp</h4>
Converted `ion-input-otp` to use [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM).
If you were targeting the internals of `ion-input-otp` in your CSS, you will need to target the `group`, `container`, `native`, `separator` or `description` [Shadow Parts](https://ionicframework.com/docs/theming/css-shadow-parts) instead, or use the provided CSS Variables.
<h4 id="version-9x-radio-group">Radio Group</h4>
Converted `ion-radio-group` to use [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM).
If you were targeting the internals of `ion-radio-group` in your CSS, you will need to target the `supporting-text`, `helper-text` or `error-text` [Shadow Parts](https://ionicframework.com/docs/theming/css-shadow-parts) instead, or use the provided CSS Variables.
Additionally, the `radio-group-wrapper` div element has been removed, causing slotted elements to be direct children of the `ion-radio-group`.
<h4 id="version-9x-textarea">Textarea</h4>
Converted `ion-textarea` to use [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM).
If you were targeting the internals of `ion-textarea` in your CSS, you will need to target the `wrapper`, `container`, `label`, `native`, `supporting-text`, `helper-text`, `error-text`, `counter`, or `bottom` [Shadow Parts](https://ionicframework.com/docs/theming/css-shadow-parts) instead, or use the provided CSS Variables.

View File

@@ -3,6 +3,31 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [8.7.15](https://github.com/ionic-team/ionic-framework/compare/v8.7.14...v8.7.15) (2025-12-23)
### Bug Fixes
* **core:** use Capacitor safe-area CSS variables on older WebViews ([#30865](https://github.com/ionic-team/ionic-framework/issues/30865)) ([8573bf8](https://github.com/ionic-team/ionic-framework/commit/8573bf8083f75eda13c954a56731a6aac8ca5724))
* **header:** show iOS condense header when app is in MD mode ([#30690](https://github.com/ionic-team/ionic-framework/issues/30690)) ([f83b000](https://github.com/ionic-team/ionic-framework/commit/f83b0005309400d674e43c497bdffbcb9d2c4d94)), closes [#29929](https://github.com/ionic-team/ionic-framework/issues/29929)
* **input-password-toggle:** improve screen reader announcements ([#30885](https://github.com/ionic-team/ionic-framework/issues/30885)) ([12ede4b](https://github.com/ionic-team/ionic-framework/commit/12ede4b79c8d5cffc2b014c7c8a0d2ef1d3bf90d))
* **modal:** dismiss top-most overlay when multiple IDs match ([#30883](https://github.com/ionic-team/ionic-framework/issues/30883)) ([3b60a1d](https://github.com/ionic-team/ionic-framework/commit/3b60a1d68a1df1606ffee0bde7db7a206bac404a)), closes [#30030](https://github.com/ionic-team/ionic-framework/issues/30030)
## [8.7.14](https://github.com/ionic-team/ionic-framework/compare/v8.7.13...v8.7.14) (2025-12-17)
### Bug Fixes
* **tabs:** select correct tab when routes have similar prefixes ([#30863](https://github.com/ionic-team/ionic-framework/issues/30863)) ([03fb422](https://github.com/ionic-team/ionic-framework/commit/03fb422bfa775e3e9dd695ea1857fa88d4245ecd)), closes [#30448](https://github.com/ionic-team/ionic-framework/issues/30448)
## [8.7.13](https://github.com/ionic-team/ionic-framework/compare/v8.7.12...v8.7.13) (2025-12-13)
**Note:** Version bump only for package ionic-framework

View File

@@ -3,6 +3,28 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [8.7.15](https://github.com/ionic-team/ionic-framework/compare/v8.7.14...v8.7.15) (2025-12-23)
### Bug Fixes
* **core:** use Capacitor safe-area CSS variables on older WebViews ([#30865](https://github.com/ionic-team/ionic-framework/issues/30865)) ([8573bf8](https://github.com/ionic-team/ionic-framework/commit/8573bf8083f75eda13c954a56731a6aac8ca5724))
* **header:** show iOS condense header when app is in MD mode ([#30690](https://github.com/ionic-team/ionic-framework/issues/30690)) ([f83b000](https://github.com/ionic-team/ionic-framework/commit/f83b0005309400d674e43c497bdffbcb9d2c4d94)), closes [#29929](https://github.com/ionic-team/ionic-framework/issues/29929)
* **input-password-toggle:** improve screen reader announcements ([#30885](https://github.com/ionic-team/ionic-framework/issues/30885)) ([12ede4b](https://github.com/ionic-team/ionic-framework/commit/12ede4b79c8d5cffc2b014c7c8a0d2ef1d3bf90d))
* **modal:** dismiss top-most overlay when multiple IDs match ([#30883](https://github.com/ionic-team/ionic-framework/issues/30883)) ([3b60a1d](https://github.com/ionic-team/ionic-framework/commit/3b60a1d68a1df1606ffee0bde7db7a206bac404a)), closes [#30030](https://github.com/ionic-team/ionic-framework/issues/30030)
## [8.7.14](https://github.com/ionic-team/ionic-framework/compare/v8.7.13...v8.7.14) (2025-12-17)
**Note:** Version bump only for package @ionic/core
## [8.7.13](https://github.com/ionic-team/ionic-framework/compare/v8.7.12...v8.7.13) (2025-12-13)
**Note:** Version bump only for package @ionic/core

View File

@@ -504,7 +504,7 @@ ion-card,css-prop,--color,ios
ion-card,css-prop,--color,md
ion-card,part,native
ion-card-content,none
ion-card-content,shadow
ion-card-content,prop,mode,"ios" | "md",undefined,false,false
ion-card-content,prop,theme,"ios" | "md" | "ionic",undefined,false,false
@@ -596,17 +596,23 @@ ion-checkbox,part,supporting-text
ion-chip,shadow
ion-chip,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record<never, never> | undefined,undefined,false,true
ion-chip,prop,disabled,boolean,false,false,false
ion-chip,prop,fill,"outline" | "solid" | undefined,undefined,false,false
ion-chip,prop,hue,"bold" | "subtle" | undefined,undefined,false,false
ion-chip,prop,hue,"bold" | "subtle" | undefined,'subtle',false,false
ion-chip,prop,mode,"ios" | "md",undefined,false,false
ion-chip,prop,outline,boolean,false,false,false
ion-chip,prop,shape,"rectangular" | "round" | "soft" | undefined,undefined,false,false
ion-chip,prop,size,"large" | "small" | undefined,undefined,false,false
ion-chip,css-prop,--border-radius
ion-chip,css-prop,--color
ion-chip,css-prop,--focus-ring-color
ion-chip,css-prop,--focus-ring-style
ion-chip,css-prop,--focus-ring-width
ion-chip,prop,theme,"ios" | "md" | "ionic",undefined,false,false
ion-chip,css-prop,--background,ionic
ion-chip,css-prop,--background,ios
ion-chip,css-prop,--background,md
ion-chip,css-prop,--border-radius,ionic
ion-chip,css-prop,--border-radius,ios
ion-chip,css-prop,--border-radius,md
ion-chip,css-prop,--color,ionic
ion-chip,css-prop,--color,ios
ion-chip,css-prop,--color,md
ion-chip,css-prop,--focus-ring-color,ionic
ion-chip,css-prop,--focus-ring-width,ionic
ion-col,shadow
ion-col,prop,mode,"ios" | "md",undefined,false,false
@@ -1029,18 +1035,20 @@ ion-input,css-prop,--placeholder-opacity,ionic
ion-input,css-prop,--placeholder-opacity,ios
ion-input,css-prop,--placeholder-opacity,md
ion-input-otp,scoped
ion-input-otp,shadow
ion-input-otp,prop,autocapitalize,string,'off',false,false
ion-input-otp,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record<never, never> | undefined,undefined,false,true
ion-input-otp,prop,disabled,boolean,false,false,true
ion-input-otp,prop,fill,"outline" | "solid" | undefined,'outline',false,false
ion-input-otp,prop,inputmode,"decimal" | "email" | "none" | "numeric" | "search" | "tel" | "text" | "url" | undefined,undefined,false,false
ion-input-otp,prop,length,number,4,false,false
ion-input-otp,prop,mode,"ios" | "md",undefined,false,false
ion-input-otp,prop,pattern,string | undefined,undefined,false,false
ion-input-otp,prop,readonly,boolean,false,false,true
ion-input-otp,prop,separators,number[] | string | undefined,undefined,false,false
ion-input-otp,prop,shape,"rectangular" | "round" | "soft",'round',false,false
ion-input-otp,prop,size,"large" | "medium" | "small",'medium',false,false
ion-input-otp,prop,theme,"ios" | "md" | "ionic",undefined,false,false
ion-input-otp,prop,type,"number" | "text",'number',false,false
ion-input-otp,prop,value,null | number | string | undefined,'',false,false
ion-input-otp,method,setFocus,setFocus(index?: number) => Promise<void>
@@ -1121,6 +1129,11 @@ ion-input-otp,css-prop,--separator-width,md
ion-input-otp,css-prop,--width,ionic
ion-input-otp,css-prop,--width,ios
ion-input-otp,css-prop,--width,md
ion-input-otp,part,container
ion-input-otp,part,description
ion-input-otp,part,group
ion-input-otp,part,native
ion-input-otp,part,separator
ion-input-password-toggle,shadow
ion-input-password-toggle,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record<never, never> | undefined,undefined,false,true
@@ -1244,16 +1257,37 @@ ion-item-divider,shadow
ion-item-divider,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "secondary" | "success" | "tertiary" | "warning" | string & Record<never, never> | undefined,undefined,false,true
ion-item-divider,prop,mode,"ios" | "md",undefined,false,false
ion-item-divider,prop,sticky,boolean,false,false,false
ion-item-divider,css-prop,--background
ion-item-divider,css-prop,--color
ion-item-divider,css-prop,--inner-padding-bottom
ion-item-divider,css-prop,--inner-padding-end
ion-item-divider,css-prop,--inner-padding-start
ion-item-divider,css-prop,--inner-padding-top
ion-item-divider,css-prop,--padding-bottom
ion-item-divider,css-prop,--padding-end
ion-item-divider,css-prop,--padding-start
ion-item-divider,css-prop,--padding-top
ion-item-divider,prop,theme,"ios" | "md" | "ionic",undefined,false,false
ion-item-divider,css-prop,--background,ionic
ion-item-divider,css-prop,--background,ios
ion-item-divider,css-prop,--background,md
ion-item-divider,css-prop,--color,ionic
ion-item-divider,css-prop,--color,ios
ion-item-divider,css-prop,--color,md
ion-item-divider,css-prop,--inner-padding-bottom,ionic
ion-item-divider,css-prop,--inner-padding-bottom,ios
ion-item-divider,css-prop,--inner-padding-bottom,md
ion-item-divider,css-prop,--inner-padding-end,ionic
ion-item-divider,css-prop,--inner-padding-end,ios
ion-item-divider,css-prop,--inner-padding-end,md
ion-item-divider,css-prop,--inner-padding-start,ionic
ion-item-divider,css-prop,--inner-padding-start,ios
ion-item-divider,css-prop,--inner-padding-start,md
ion-item-divider,css-prop,--inner-padding-top,ionic
ion-item-divider,css-prop,--inner-padding-top,ios
ion-item-divider,css-prop,--inner-padding-top,md
ion-item-divider,css-prop,--padding-bottom,ionic
ion-item-divider,css-prop,--padding-bottom,ios
ion-item-divider,css-prop,--padding-bottom,md
ion-item-divider,css-prop,--padding-end,ionic
ion-item-divider,css-prop,--padding-end,ios
ion-item-divider,css-prop,--padding-end,md
ion-item-divider,css-prop,--padding-start,ionic
ion-item-divider,css-prop,--padding-start,ios
ion-item-divider,css-prop,--padding-start,md
ion-item-divider,css-prop,--padding-top,ionic
ion-item-divider,css-prop,--padding-top,ios
ion-item-divider,css-prop,--padding-top,md
ion-item-group,none
ion-item-group,prop,mode,"ios" | "md",undefined,false,false
@@ -2399,7 +2433,7 @@ ion-text,prop,color,"danger" | "dark" | "light" | "medium" | "primary" | "second
ion-text,prop,mode,"ios" | "md",undefined,false,false
ion-text,prop,theme,"ios" | "md" | "ionic",undefined,false,false
ion-textarea,scoped
ion-textarea,shadow
ion-textarea,prop,autoGrow,boolean,false,false,true
ion-textarea,prop,autocapitalize,string,'none',false,false
ion-textarea,prop,autofocus,boolean,false,false,false
@@ -2423,7 +2457,7 @@ ion-textarea,prop,mode,"ios" | "md",undefined,false,false
ion-textarea,prop,name,string,this.inputId,false,false
ion-textarea,prop,placeholder,string | undefined,undefined,false,false
ion-textarea,prop,readonly,boolean,false,false,false
ion-textarea,prop,required,boolean,false,false,false
ion-textarea,prop,required,boolean,false,false,true
ion-textarea,prop,rows,number | undefined,undefined,false,false
ion-textarea,prop,shape,"rectangular" | "round" | "soft" | undefined,undefined,false,false
ion-textarea,prop,size,"large" | "medium" | "small" | undefined,'medium',false,false
@@ -2491,6 +2525,15 @@ ion-textarea,css-prop,--placeholder-font-weight,md
ion-textarea,css-prop,--placeholder-opacity,ionic
ion-textarea,css-prop,--placeholder-opacity,ios
ion-textarea,css-prop,--placeholder-opacity,md
ion-textarea,part,bottom
ion-textarea,part,container
ion-textarea,part,counter
ion-textarea,part,error-text
ion-textarea,part,helper-text
ion-textarea,part,label
ion-textarea,part,native
ion-textarea,part,supporting-text
ion-textarea,part,wrapper
ion-thumbnail,shadow
ion-thumbnail,prop,mode,"ios" | "md",undefined,false,false

489
core/package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@ionic/core",
"version": "8.7.13",
"version": "8.7.15",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@ionic/core",
"version": "8.7.13",
"version": "8.7.15",
"license": "MIT",
"dependencies": {
"@phosphor-icons/core": "^2.1.1",
@@ -37,7 +37,6 @@
"chalk": "^5.3.0",
"clean-css-cli": "^5.6.1",
"domino": "^2.1.6",
"esbuild": "^0.27.0",
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-custom-rules": "file:custom-rules",
@@ -773,448 +772,6 @@
"sisteransi": "^1.0.5"
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.0.tgz",
"integrity": "sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.0.tgz",
"integrity": "sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.0.tgz",
"integrity": "sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.0.tgz",
"integrity": "sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.0.tgz",
"integrity": "sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.0.tgz",
"integrity": "sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.0.tgz",
"integrity": "sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.0.tgz",
"integrity": "sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.0.tgz",
"integrity": "sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.0.tgz",
"integrity": "sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.0.tgz",
"integrity": "sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.0.tgz",
"integrity": "sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.0.tgz",
"integrity": "sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg==",
"cpu": [
"mips64el"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.0.tgz",
"integrity": "sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.0.tgz",
"integrity": "sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.0.tgz",
"integrity": "sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.0.tgz",
"integrity": "sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.0.tgz",
"integrity": "sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.0.tgz",
"integrity": "sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.0.tgz",
"integrity": "sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.0.tgz",
"integrity": "sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openharmony-arm64": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.0.tgz",
"integrity": "sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openharmony"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.0.tgz",
"integrity": "sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.0.tgz",
"integrity": "sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.0.tgz",
"integrity": "sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.0.tgz",
"integrity": "sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@eslint-community/eslint-utils": {
"version": "4.4.0",
"dev": true,
@@ -5045,48 +4602,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/esbuild": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.0.tgz",
"integrity": "sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.27.0",
"@esbuild/android-arm": "0.27.0",
"@esbuild/android-arm64": "0.27.0",
"@esbuild/android-x64": "0.27.0",
"@esbuild/darwin-arm64": "0.27.0",
"@esbuild/darwin-x64": "0.27.0",
"@esbuild/freebsd-arm64": "0.27.0",
"@esbuild/freebsd-x64": "0.27.0",
"@esbuild/linux-arm": "0.27.0",
"@esbuild/linux-arm64": "0.27.0",
"@esbuild/linux-ia32": "0.27.0",
"@esbuild/linux-loong64": "0.27.0",
"@esbuild/linux-mips64el": "0.27.0",
"@esbuild/linux-ppc64": "0.27.0",
"@esbuild/linux-riscv64": "0.27.0",
"@esbuild/linux-s390x": "0.27.0",
"@esbuild/linux-x64": "0.27.0",
"@esbuild/netbsd-arm64": "0.27.0",
"@esbuild/netbsd-x64": "0.27.0",
"@esbuild/openbsd-arm64": "0.27.0",
"@esbuild/openbsd-x64": "0.27.0",
"@esbuild/openharmony-arm64": "0.27.0",
"@esbuild/sunos-x64": "0.27.0",
"@esbuild/win32-arm64": "0.27.0",
"@esbuild/win32-ia32": "0.27.0",
"@esbuild/win32-x64": "0.27.0"
}
},
"node_modules/escalade": {
"version": "3.1.1",
"dev": true,

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/core",
"version": "8.7.13",
"version": "8.7.15",
"description": "Base components for Ionic",
"engines": {
"node": ">= 16"
@@ -62,7 +62,6 @@
"chalk": "^5.3.0",
"clean-css-cli": "^5.6.1",
"domino": "^2.1.6",
"esbuild": "^0.27.0",
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-custom-rules": "file:custom-rules",
@@ -80,12 +79,11 @@
"stylelint-order": "^4.1.0"
},
"scripts": {
"build": "npm run clean && npm run build.css && npm run build.themes && stencil build --es5 --docs-json dist/docs.json",
"build": "npm run clean && npm run build.css && stencil build --es5 --docs-json dist/docs.json",
"build.css": "npm run css.sass && npm run css.minify",
"build.debug": "npm run clean && stencil build --debug",
"build.docs.json": "stencil build --docs-json dist/docs.json",
"build.tokens": "npx build.tokens --dest src/foundations/ --root=false --scss=true --scss-file=ionic.vars.scss --scss-prefix=ion --utilities=false && npm run prettier.tokens",
"build.themes": "esbuild src/themes/*/*.tokens.ts --bundle --format=esm --platform=browser --target=es2017 --outdir=themes --outbase=src/themes && esbuild src/utils/theme.ts --bundle --format=esm --platform=browser --target=es2017 --outfile=themes/utils/theme.js",
"clean": "node scripts/clean.js",
"css.minify": "cleancss -O2 -o ./css/ionic.bundle.css ./css/ionic.bundle.css",
"css.sass": "sass --embed-sources --style compressed src/css:./css",
@@ -99,7 +97,7 @@
"prerender.e2e": "node scripts/testing/prerender.js",
"prettier": "prettier \"./src/**/*.{html,ts,tsx,js,jsx,scss}\"",
"prettier.tokens": "prettier \"./src/foundations/*.{scss, html}\" --write --cache",
"start": "npm run build.css && npm run build.themes && stencil build --dev --watch --serve",
"start": "npm run build.css && stencil build --dev --watch --serve",
"test": "npm run test.spec && npm run test.e2e",
"test.spec": "stencil test --spec --max-workers=2",
"test.e2e": "npx playwright test",

View File

@@ -4,8 +4,7 @@ const path = require('path');
const cleanDirs = [
'dist',
'css',
'themes'
'css'
];
cleanDirs.forEach(dir => {

View File

@@ -14,17 +14,14 @@
* The following URL parameters are supported:
* - `rtl`: Set to `true` to enable right-to-left directionality.
* - `ionic:_testing`: Set to `true` to identify testing environments.
* - `ionic:theme`: Set to `ionic`, `ios`, or `md` to load a specific
* theme. Defaults to `md`.
* - `ionic:theme`: Set to `ios` or `md` to load a specific theme.
* Defaults to `md`.
* - `palette`: Set to `light`, `dark`, `high-contrast`, or
* `high-contrast-dark` to load a specific palette. Defaults to `light`.
*/
const DEFAULT_THEME = 'md';
const DEFAULT_PALETTE = 'light';
(function() {
/**
* The `rtl` param is used to set the directionality of the
* document. This can be `true` or `false`.
@@ -52,16 +49,45 @@ const DEFAULT_PALETTE = 'light';
}
/**
* The `theme` param is used to load a specific theme.
* This can be `ionic`, `ios`, or `md`. Default to `md` for tests.
* The `palette` param is used to load a specific palette
* for the theme.
* The dark class will load the dark palette automatically
* if no palette is specified through the URL.
*
* Values can be `light`, `dark`, `high-contrast`,
* or `high-contrast-dark`. Default to `light` for tests.
*/
const themeQuery = window.location.search.match(/ionic:theme=([a-z0-9]+)/i);
const themeHash = window.location.hash.match(/ionic:theme=([a-z0-9]+)/i);
const themeAttr = document.documentElement.getAttribute('theme');
const themeName = themeQuery?.[1] || themeHash?.[1] || themeAttr || DEFAULT_THEME;
const validPalettes = ['light', 'dark', 'high-contrast', 'high-contrast-dark'];
const paletteQuery = window.location.search.match(/palette=([a-z-]+)/);
const paletteHash = window.location.hash.match(/palette=([a-z-]+)/);
const darkClass = document.body?.classList.contains('ion-palette-dark') ? 'dark' : null;
const highContrastClass = document.body?.classList.contains('ion-palette-high-contrast') ? 'high-contrast' : null;
const highContrastDarkClass = darkClass && highContrastClass ? 'high-contrast-dark' : null;
let paletteName = paletteQuery?.[1] || paletteHash?.[1] || highContrastDarkClass || darkClass || highContrastClass || 'light';
if (!validPalettes.includes(paletteName)) {
console.warn(`Invalid palette name: '${paletteName}'. Falling back to 'light' palette.`);
paletteName = 'light';
}
if (paletteName !== 'light') {
const linkTag = document.createElement('link');
linkTag.setAttribute('rel', 'stylesheet');
linkTag.setAttribute('type', 'text/css');
linkTag.setAttribute('href', `/css/palettes/${paletteName}.always.css`);
document.head.appendChild(linkTag);
}
/**
* The `ionic` theme uses a different stylesheet than the `iOS` and `md` themes.
* This is to ensure that the `ionic` theme is loaded when the `ionic:theme=ionic`
* or when the HTML tag has the `theme="ionic"` attribute. This is useful for
* the snapshot tests, where the `ionic` theme is not loaded by default.
*/
const themeQuery = window.location.search.match(/ionic:theme=([a-z]+)/);
const themeAttr = document.documentElement.getAttribute('theme');
// TODO(): Remove this when the tokens are working for all components
// and the themes all use the same bundle
if ((themeQuery && themeQuery[1] === 'ionic') || themeAttr === 'ionic') {
const ionicThemeLinkTag = document.querySelector('link[href*="css/ionic/bundle.ionic.css"]');
@@ -73,136 +99,21 @@ const DEFAULT_PALETTE = 'light';
document.head.appendChild(linkTag);
}
const utilsBundleLinkTag = document.querySelector('link[href*="css/utils.bundle.css"]');
if (!utilsBundleLinkTag) {
const linkTag = document.createElement('link');
linkTag.setAttribute('rel', 'stylesheet');
linkTag.setAttribute('type', 'text/css');
linkTag.setAttribute('href', '/css/utils.bundle.css');
document.head.appendChild(linkTag);
}
const defaultThemeLinkTag = document.querySelector('link[href*="css/ionic.bundle.css"]');
if (defaultThemeLinkTag) {
defaultThemeLinkTag.remove();
}
}
/**
* The `palette` param is used to load a specific palette
* for the theme.
* The dark class will load the dark palette automatically
* if no palette is specified through the URL.
*
* Values can be `light`, `dark`, `high-contrast`,
* or `high-contrast-dark`. Default to `light` for tests.
*/
const validPalettes = ['light', 'dark', 'high-contrast', 'high-contrast-dark'];
const configDarkMode = window.Ionic?.config?.customTheme?.palette?.dark?.enabled === 'always' ? 'dark' : null;
const configHighContrastMode = window.Ionic?.config?.customTheme?.palette?.highContrast?.enabled === 'always' ? 'high-contrast' : null;
const configHighContrastDarkMode = window.Ionic?.config?.customTheme?.palette?.highContrastDark?.enabled === 'always' ? 'high-contrast-dark' : null;
/**
* Ensure window.Ionic.config is defined before importing 'testing/scripts'
* in the test HTML to properly initialize the palette configuration below.
*
* Example:
* <script>
* window.Ionic = { config: { customTheme: { palette: { ... } } } };
* </script>
* <script src="testing/scripts.js"></script>
*/
const configPalette = configDarkMode || configHighContrastMode || configHighContrastDarkMode;
const paletteQuery = window.location.search.match(/palette=([a-z-]+)/);
const paletteHash = window.location.hash.match(/palette=([a-z-]+)/);
const darkClass = document.body?.classList.contains('ion-palette-dark') ? 'dark' : null;
const highContrastClass = document.body?.classList.contains('ion-palette-high-contrast') ? 'high-contrast' : null;
const highContrastDarkClass = darkClass && highContrastClass ? 'high-contrast-dark' : null;
const paletteClass = highContrastDarkClass || highContrastClass || darkClass;
let paletteName = configPalette || paletteQuery?.[1] || paletteHash?.[1] || paletteClass || DEFAULT_PALETTE;
if (!validPalettes.includes(paletteName)) {
console.warn(`Invalid palette name: '${paletteName}'. Falling back to 'light' palette.`);
paletteName = DEFAULT_PALETTE;
}
// Load theme tokens if the theme is valid
const validThemes = ['ionic', 'ios', 'md'];
if (themeName && validThemes.includes(themeName)) {
loadThemeTokens(themeName, paletteName);
} else if(themeName) {
console.warn(
`Unsupported theme "${themeName}". Supported themes are: ${validThemes.join(', ')}. Defaulting to ${DEFAULT_THEME}.`
);
}
async function loadThemeTokens(themeName, paletteName) {
try {
// Store existing theme set from the app initialization
const customTheme = window.Ionic?.config?.customTheme;
// Load the default tokens for the theme
const defaultTokens = await import(`/themes/${themeName}/default.tokens.js`);
let theme = defaultTokens.defaultTheme;
// Merge with existing theme to preserve any customizations
if (customTheme) {
theme = {
...theme,
...customTheme,
palette: {
...theme.palette,
...customTheme.palette,
},
};
}
// If a specific palette is requested, modify the palette structure
// to set the enabled property to 'always'
// TODO(FW-4004): Implement dark palette
if (paletteName === 'dark' && theme.palette?.dark) {
theme.palette.dark.enabled = 'always';
// TODO(FW-4005): Implement high contrast palette
} else if (paletteName === 'high-contrast' && theme.palette?.highContrast) {
theme.palette.highContrast.enabled = 'always';
// TODO(FW-4005): Implement high contrast dark palette
} else if (paletteName === 'high-contrast-dark' && theme.palette?.highContrastDark) {
theme.palette.highContrastDark.enabled = 'always';
}
if (window.Ionic?.config?.set) {
/**
* New Page Load after Initial App Load or Playwright Test:
*
* If the Config instance exists, we must use the
* `set()` method. This ensures the internal private Map inside
* the `Config` class is updated with the loaded theme tokens.
* Without this, components would read 'undefined' or 'base'
* values from the stale Map when trying to access them through
* methods like `config.get()`.
*/
window.Ionic.config.set('customTheme', theme);
} else {
/**
* App Initialization or Browser Refresh:
*
* If the Config instance doesn't exist yet,
* we attach the theme to the global Ionic object. The `initialize()`
* method in `ionic-global.ts` will later merge this into the new
* `Config` instance via `config.reset()`.
*/
window.Ionic = window.Ionic || {};
window.Ionic.config = window.Ionic.config || {};
window.Ionic.config.customTheme = theme;
}
/**
* Re-applying the global theme is critical for Playwright tests.
* Even if the config is set, the CSS variables for the specific theme
* (e.g., md or ios) must be force-injected into the document head to
* ensure visual assertions pass correctly.
*/
if (window.Ionic?.config?.get && window.Ionic?.config?.set) {
const themeModule = await import('/themes/utils/theme.js');
themeModule.applyGlobalTheme(theme);
themeModule.applyComponentsTheme(theme);
}
} catch (error) {
console.error(`Failed to load theme tokens for ${themeName}:`, error);
}
}
window.Ionic = window.Ionic || {};
window.Ionic.config = window.Ionic.config || {};

View File

File diff suppressed because one or more lines are too long

View File

@@ -600,7 +600,7 @@ export namespace Components {
*/
"expand"?: 'full' | 'block';
/**
* Set to `"clear"` for a transparent button that resembles a flat button, to `"outline"` for a transparent button with a border, or to `"solid"` for a button with a filled background. The default fill is `"solid"` except inside of a toolbar, where the default is `"clear"`.
* Set to `"clear"` for a transparent button that resembles a flat button, to `"outline"` for a transparent button with a border, or to `"solid"` for a button with a filled background. The default fill is `"solid"` except when inside of a buttons or datetime component, where the default fill is `"clear"`.
*/
"fill"?: 'clear' | 'outline' | 'solid' | 'default';
/**
@@ -872,11 +872,8 @@ export namespace Components {
*/
"disabled": boolean;
/**
* The fill for the chip. Set to `"outline"` for a chip with a border and background. Set to `"solid"` for a chip with a background. Defaults to `"solid"`.
*/
"fill"?: 'outline' | 'solid';
/**
* Set to `"bold"` for a chip with vibrant, bold colors or to `"subtle"` for a chip with muted, subtle colors. Defaults to `"subtle"`.
* 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';
/**
@@ -885,18 +882,21 @@ export namespace Components {
"mode"?: "ios" | "md";
/**
* Display an outline style button.
* @deprecated - Use fill="outline" instead
* @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"`.
* 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.
*/
"shape"?: 'soft' | 'round' | 'rectangular';
/**
* Set to `"small"` for a chip with less height and padding. Defaults to `"large"`.
* Set to `"small"` for a chip with less height and padding. Defaults to `"large"` for the ionic theme, and undefined for all other themes.
*/
"size"?: 'small' | 'large';
/**
* The theme determines the visual appearance of the component.
*/
"theme"?: "ios" | "md" | "ionic";
}
interface IonCol {
/**
@@ -1779,6 +1779,10 @@ export namespace Components {
* @default 4
*/
"length": number;
/**
* The mode determines the platform behaviors of the component.
*/
"mode"?: "ios" | "md";
/**
* A regex pattern string for allowed characters. Defaults based on type. For numbers (`type="number"`): `"[\p{N}]"` For text (`type="text"`): `"[\p{L}\p{N}]"`
*/
@@ -1807,6 +1811,10 @@ export namespace Components {
* @default 'medium'
*/
"size": 'small' | 'medium' | 'large';
/**
* The theme determines the visual appearance of the component.
*/
"theme"?: "ios" | "md" | "ionic";
/**
* The type of input allowed in the input boxes.
* @default 'number'
@@ -1920,6 +1928,10 @@ export namespace Components {
* @default false
*/
"sticky": boolean;
/**
* The theme determines the visual appearance of the component.
*/
"theme"?: "ios" | "md" | "ionic";
}
interface IonItemGroup {
/**
@@ -6551,7 +6563,7 @@ declare namespace LocalJSX {
*/
"expand"?: 'full' | 'block';
/**
* Set to `"clear"` for a transparent button that resembles a flat button, to `"outline"` for a transparent button with a border, or to `"solid"` for a button with a filled background. The default fill is `"solid"` except inside of a toolbar, where the default is `"clear"`.
* Set to `"clear"` for a transparent button that resembles a flat button, to `"outline"` for a transparent button with a border, or to `"solid"` for a button with a filled background. The default fill is `"solid"` except when inside of a buttons or datetime component, where the default fill is `"clear"`.
*/
"fill"?: 'clear' | 'outline' | 'solid' | 'default';
/**
@@ -6842,11 +6854,8 @@ declare namespace LocalJSX {
*/
"disabled"?: boolean;
/**
* The fill for the chip. Set to `"outline"` for a chip with a border and background. Set to `"solid"` for a chip with a background. Defaults to `"solid"`.
*/
"fill"?: 'outline' | 'solid';
/**
* Set to `"bold"` for a chip with vibrant, bold colors or to `"subtle"` for a chip with muted, subtle colors. Defaults to `"subtle"`.
* 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';
/**
@@ -6855,18 +6864,21 @@ declare namespace LocalJSX {
"mode"?: "ios" | "md";
/**
* Display an outline style button.
* @deprecated - Use fill="outline" instead
* @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"`.
* 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.
*/
"shape"?: 'soft' | 'round' | 'rectangular';
/**
* Set to `"small"` for a chip with less height and padding. Defaults to `"large"`.
* Set to `"small"` for a chip with less height and padding. Defaults to `"large"` for the ionic theme, and undefined for all other themes.
*/
"size"?: 'small' | 'large';
/**
* The theme determines the visual appearance of the component.
*/
"theme"?: "ios" | "md" | "ionic";
}
interface IonCol {
/**
@@ -7758,6 +7770,10 @@ declare namespace LocalJSX {
* @default 4
*/
"length"?: number;
/**
* The mode determines the platform behaviors of the component.
*/
"mode"?: "ios" | "md";
/**
* Emitted when the input group loses focus.
*/
@@ -7801,6 +7817,10 @@ declare namespace LocalJSX {
* @default 'medium'
*/
"size"?: 'small' | 'medium' | 'large';
/**
* The theme determines the visual appearance of the component.
*/
"theme"?: "ios" | "md" | "ionic";
/**
* The type of input allowed in the input boxes.
* @default 'number'
@@ -7914,6 +7934,10 @@ declare namespace LocalJSX {
* @default false
*/
"sticky"?: boolean;
/**
* The theme determines the visual appearance of the component.
*/
"theme"?: "ios" | "md" | "ionic";
}
interface IonItemGroup {
/**

View File

@@ -9,8 +9,8 @@ configs({ directions: ['ltr'], modes: ['ionic-md'] }).forEach(({ config, screens
`
<style>
/* Background styles to show the border radius */
.ionic {
--ion-background-color: #ccc7c7;
:root {
--background: #ccc7c7;
}
</style>
<ion-accordion-group>
@@ -47,8 +47,8 @@ configs({ directions: ['ltr'], modes: ['ionic-md'] }).forEach(({ config, screens
`
<style>
/* Background styles to show the border radius */
.ionic {
--ion-background-color: #ccc7c7;
:root {
--background: #ccc7c7;
}
</style>
<ion-accordion-group value="first">
@@ -87,8 +87,8 @@ configs({ directions: ['ltr'], modes: ['ionic-md'] }).forEach(({ config, screens
`
<style>
/* Background styles to show the border radius */
.ionic {
--ion-background-color: #ccc7c7;
:root {
--background: #ccc7c7;
}
</style>
<ion-accordion-group expand="inset">
@@ -125,8 +125,8 @@ configs({ directions: ['ltr'], modes: ['ionic-md'] }).forEach(({ config, screens
`
<style>
/* Background styles to show the border radius */
.ionic {
--ion-background-color: #ccc7c7;
:root {
--background: #ccc7c7;
}
</style>
<ion-accordion-group value="first" expand="inset">

View File

@@ -9,8 +9,8 @@ configs({ directions: ['ltr'], modes: ['ionic-md'] }).forEach(({ config, screens
`
<style>
/* Background styles to show the border radius */
.ionic {
--ion-background-color: #222;
:root {
--background: #222;
}
</style>

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -9,8 +9,8 @@ configs({ directions: ['ltr'], modes: ['ionic-md'] }).forEach(({ config, screens
`
<style>
/* Background styles to show the border radius */
.ionic {
--ion-background-color: #222;
:root {
--background: #222;
}
</style>

View File

@@ -5,7 +5,7 @@ import type { E2EPage } from '@utils/test/playwright';
/**
* Safe area tests only check top and bottom edges. RTL checks are not required here.
*/
configs({ directions: ['ltr'] }).forEach(({ config, title, screenshot }) => {
configs({ directions: ['ltr'], modes: ['ios', 'md', 'ionic-md'] }).forEach(({ config, title, screenshot }) => {
test.describe(title('app: safe-area'), () => {
const testOverlay = async (page: E2EPage, trigger: string, event: string, screenshotModifier: string) => {
const presentEvent = await page.spyOnEvent(event);
@@ -18,20 +18,66 @@ configs({ directions: ['ltr'] }).forEach(({ config, title, screenshot }) => {
await expect(page).toHaveScreenshot(screenshot(`app-${screenshotModifier}-diff`));
};
test.beforeEach(async ({ page }) => {
await page.goto(`/src/components/app/test/safe-area`, config);
});
test('should not have visual regressions with action sheet', async ({ page }) => {
await testOverlay(page, '#show-action-sheet', 'ionActionSheetDidPresent', 'action-sheet');
test.describe(title('Ionic safe area variables'), () => {
test.beforeEach(async ({ page }) => {
const htmlTag = page.locator('html');
const hasSafeAreaClass = await htmlTag.evaluate((el) => el.classList.contains('safe-area'));
expect(hasSafeAreaClass).toBe(true);
});
test('should not have visual regressions with action sheet', async ({ page }) => {
await testOverlay(page, '#show-action-sheet', 'ionActionSheetDidPresent', 'action-sheet');
});
test('should not have visual regressions with menu', async ({ page }) => {
await testOverlay(page, '#show-menu', 'ionDidOpen', 'menu');
});
test('should not have visual regressions with picker', async ({ page }) => {
await testOverlay(page, '#show-picker', 'ionPickerDidPresent', 'picker');
});
test('should not have visual regressions with toast', async ({ page }) => {
await testOverlay(page, '#show-toast', 'ionToastDidPresent', 'toast');
});
});
test('should not have visual regressions with menu', async ({ page }) => {
await testOverlay(page, '#show-menu', 'ionDidOpen', 'menu');
});
test('should not have visual regressions with picker', async ({ page }) => {
await testOverlay(page, '#show-picker', 'ionPickerDidPresent', 'picker');
});
test('should not have visual regressions with toast', async ({ page }) => {
await testOverlay(page, '#show-toast', 'ionToastDidPresent', 'toast');
test.describe(title('Capacitor safe area variables'), () => {
test('should use safe-area-inset vars when safe-area class is not defined', async ({ page }) => {
await page.evaluate(() => {
const html = document.documentElement;
// Remove the safe area class
html.classList.remove('safe-area');
// Set the safe area inset variables
html.style.setProperty('--safe-area-inset-top', '10px');
html.style.setProperty('--safe-area-inset-bottom', '20px');
html.style.setProperty('--safe-area-inset-left', '30px');
html.style.setProperty('--safe-area-inset-right', '40px');
});
const top = await page.evaluate(() =>
getComputedStyle(document.documentElement).getPropertyValue('--ion-safe-area-top').trim()
);
const bottom = await page.evaluate(() =>
getComputedStyle(document.documentElement).getPropertyValue('--ion-safe-area-bottom').trim()
);
const left = await page.evaluate(() =>
getComputedStyle(document.documentElement).getPropertyValue('--ion-safe-area-left').trim()
);
const right = await page.evaluate(() =>
getComputedStyle(document.documentElement).getPropertyValue('--ion-safe-area-right').trim()
);
expect(top).toBe('10px');
expect(bottom).toBe('20px');
expect(left).toBe('30px');
expect(right).toBe('40px');
});
});
});
});

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.4 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: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -3,8 +3,8 @@ import type { ComponentInterface } from '@stencil/core';
import { Component, Element, Host, Prop, h } from '@stencil/core';
import type { ButtonInterface } from '@utils/element-interface';
import type { Attributes } from '@utils/helpers';
import { inheritAriaAttributes, openURL } from '@utils/helpers';
import { createColorClasses, hostContext } from '@utils/theme';
import { inheritAriaAttributes } from '@utils/helpers';
import { createColorClasses, hostContext, openURL } from '@utils/theme';
import { arrowBackSharp, chevronBack } from 'ionicons/icons';
import { config } from '../../global/config';

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 65 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

After

Width:  |  Height:  |  Size: 85 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 85 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 65 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

After

Width:  |  Height:  |  Size: 86 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 85 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 46 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 6.0 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: 19 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 22 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: 19 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 324 B

After

Width:  |  Height:  |  Size: 316 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 357 B

After

Width:  |  Height:  |  Size: 356 B

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 324 B

After

Width:  |  Height:  |  Size: 316 B

View File

@@ -3,8 +3,8 @@ import dotsThreeRegular from '@phosphor-icons/core/assets/regular/dots-three.svg
import type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Component, Element, Event, Host, Prop, h } from '@stencil/core';
import type { Attributes } from '@utils/helpers';
import { inheritAriaAttributes, openURL } from '@utils/helpers';
import { createColorClasses, hostContext } from '@utils/theme';
import { inheritAriaAttributes } from '@utils/helpers';
import { createColorClasses, hostContext, openURL } from '@utils/theme';
import { chevronForwardOutline, ellipsisHorizontal } from 'ionicons/icons';
import { config } from '../../global/config';

View File

@@ -334,3 +334,12 @@
@include globals.position(null, globals.$ion-space-200, globals.$ion-space-200, null);
}
}
// Button in Datetime
// --------------------------------------------------
:host(.in-datetime) {
@include globals.typography(globals.$ion-body-action-md);
min-height: globals.$ion-space-1200;
}

View File

@@ -293,3 +293,16 @@
background: var(--ion-toolbar-color, var(--color));
color: #{var(--ion-toolbar-background, var(--background), ion-color(primary, contrast))};
}
// Button in Datetime
// --------------------------------------------------
:host(.in-datetime) {
--padding-top: 3px;
--padding-bottom: 3px;
--padding-start: 5px;
--padding-end: 5px;
font-size: dynamic-font-clamp(1, 17px, 1.24);
font-weight: 400;
}

View File

@@ -243,3 +243,20 @@
background: var(--ion-toolbar-background, var(--color));
color: #{var(--ion-toolbar-color, var(--background), ion-color(primary, contrast))};
}
// Button in Datetime
// --------------------------------------------------
:host(.in-datetime) {
--padding-top: 3px;
--padding-bottom: 3px;
--padding-start: 8px;
--padding-end: 8px;
--box-shadow: none;
}
:host(.in-datetime.button-clear) {
--background-activated: transparent;
--background-focused: currentColor;
--background-hover: currentColor;
}

View File

@@ -59,3 +59,12 @@
:host ::slotted(ion-badge[vertical]:not(:empty)) {
@include globals.padding(2px);
}
// Button in Datetime
// --------------------------------------------------
:host(.in-datetime) {
@include globals.margin(0px, 2px, 0px, 2px);
min-height: 32px;
}

View File

@@ -2,9 +2,9 @@ import type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Component, Element, Event, Host, Prop, Watch, State, forceUpdate, h } from '@stencil/core';
import type { AnchorInterface, ButtonInterface } from '@utils/element-interface';
import type { Attributes } from '@utils/helpers';
import { inheritAriaAttributes, hasShadowDom, openURL } from '@utils/helpers';
import { inheritAriaAttributes, hasShadowDom } from '@utils/helpers';
import { printIonWarning } from '@utils/logging';
import { createColorClasses, hostContext } from '@utils/theme';
import { createColorClasses, hostContext, openURL } from '@utils/theme';
import { getIonTheme, getIonMode } from '../../global/ionic-global';
import type { AnimationBuilder, Color } from '../../interface';
@@ -31,9 +31,10 @@ import type { RouterDirection } from '../router/utils/interface';
shadow: true,
})
export class Button implements ComponentInterface, AnchorInterface, ButtonInterface {
private inDatetime = false;
private inItem = false;
private inListHeader = false;
private inToolbar = false;
private inButtons = false;
private formButtonEl: HTMLButtonElement | null = null;
private formEl: HTMLFormElement | null = null;
private inheritedAttributes: Attributes = {};
@@ -78,7 +79,8 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
/**
* Set to `"clear"` for a transparent button that resembles a flat button, to `"outline"`
* for a transparent button with a border, or to `"solid"` for a button with a filled background.
* The default fill is `"solid"` except inside of a toolbar, where the default is `"clear"`.
* The default fill is `"solid"` except when inside of a buttons or datetime component, where
* the default fill is `"clear"`.
*/
@Prop({ reflect: true, mutable: true }) fill?: 'clear' | 'outline' | 'solid' | 'default';
@@ -174,6 +176,7 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
*/
@Watch('aria-checked')
@Watch('aria-label')
@Watch('aria-pressed')
onAriaChanged(newValue: string, _oldValue: string, propName: string) {
this.inheritedAttributes = {
...this.inheritedAttributes,
@@ -216,9 +219,10 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
}
componentWillLoad() {
this.inToolbar = !!this.el.closest('ion-buttons');
this.inListHeader = !!this.el.closest('ion-list-header');
this.inItem = !!this.el.closest('ion-item') || !!this.el.closest('ion-item-divider');
this.inDatetime = hostContext('ion-datetime', this.el);
this.inButtons = hostContext('ion-buttons', this.el);
this.inListHeader = hostContext('ion-list-header', this.el);
this.inItem = hostContext('ion-item', this.el) || hostContext('ion-item-divider', this.el);
this.inheritedAttributes = inheritAriaAttributes(this.el);
}
@@ -233,9 +237,11 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
private get rippleType() {
const hasClearFill = this.fill === undefined || this.fill === 'clear';
// If the button is in a toolbar, has a clear fill (which is the default)
// and only has an icon we use the unbounded "circular" ripple effect
if (hasClearFill && this.hasIconOnly && this.inToolbar) {
// Use the unbounded "circular" ripple effect if it:
// - Has a clear fill (the default)
// - Only has an icon and
// - Is inside of buttons (used in a toolbar) or a datetime
if (hasClearFill && this.hasIconOnly && (this.inButtons || this.inDatetime)) {
return 'unbounded';
}
@@ -400,7 +406,7 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
};
let fill = this.fill;
if (fill === undefined) {
fill = this.inToolbar || this.inListHeader ? 'clear' : 'solid';
fill = this.inDatetime || this.inButtons || this.inListHeader ? 'clear' : 'solid';
}
/**
@@ -426,9 +432,10 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
[`${buttonType}-${shape}`]: true,
[`${buttonType}-${fill}`]: true,
[`${buttonType}-strong`]: strong,
'in-datetime': this.inDatetime,
'in-toolbar': hostContext('ion-toolbar', this.el),
'in-toolbar-color': hostContext('ion-toolbar[color]', this.el),
'in-buttons': hostContext('ion-buttons', this.el),
'in-buttons': this.inButtons,
'button-has-icon-only': hasIconOnly,
'button-has-badge': hasBadge,
'button-disabled': disabled,

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 101 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB

After

Width:  |  Height:  |  Size: 136 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

After

Width:  |  Height:  |  Size: 130 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

After

Width:  |  Height:  |  Size: 102 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 137 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 KiB

After

Width:  |  Height:  |  Size: 131 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 95 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

After

Width:  |  Height:  |  Size: 128 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

After

Width:  |  Height:  |  Size: 126 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 95 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

After

Width:  |  Height:  |  Size: 128 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

After

Width:  |  Height:  |  Size: 126 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

After

Width:  |  Height:  |  Size: 125 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 161 KiB

After

Width:  |  Height:  |  Size: 169 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

After

Width:  |  Height:  |  Size: 158 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 99 KiB

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