Compare commits
85 Commits
vue-test
...
build-test
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba2a3b80c1 | ||
|
|
e29d8e6bae | ||
|
|
4ccc150edf | ||
|
|
fbada1d170 | ||
|
|
b2e40cdcb8 | ||
|
|
ebb9ae9c11 | ||
|
|
75ffeee933 | ||
|
|
da820b830e | ||
|
|
878eec6ea2 | ||
|
|
e96a1457a3 | ||
|
|
c794583abf | ||
|
|
204a861b27 | ||
|
|
dc1dd9c395 | ||
|
|
e5226016a0 | ||
|
|
2f99aeae6f | ||
|
|
4cf948fb47 | ||
|
|
5d3bf9818d | ||
|
|
16f39d96b7 | ||
|
|
bfd497f825 | ||
|
|
8d841b4225 | ||
|
|
ee6ba200d1 | ||
|
|
7ce1031c17 | ||
|
|
516b84475e | ||
|
|
bc51dd05cf | ||
|
|
ae6c353b51 | ||
|
|
ec07e70d99 | ||
|
|
fb23bf74d5 | ||
|
|
f6531fa2be | ||
|
|
150ea1a8ad | ||
|
|
92f1b8627a | ||
|
|
8ee23d20d5 | ||
|
|
8f66acdffb | ||
|
|
6f9ee6024b | ||
|
|
dc51b33db5 | ||
|
|
a17b963182 | ||
|
|
8f7d87c680 | ||
|
|
fc88613fef | ||
|
|
e886e3ff2f | ||
|
|
37290df7ef | ||
|
|
c680ad9988 | ||
|
|
ffa3073211 | ||
|
|
ec9228f54f | ||
|
|
f3799f332b | ||
|
|
90ce7f00fa | ||
|
|
77e2147733 | ||
|
|
ede9c6e3c8 | ||
|
|
e20acb98ac | ||
|
|
5573fde6a2 | ||
|
|
507d5ee1da | ||
|
|
e71e7a0690 | ||
|
|
7871b56ecc | ||
|
|
8c235fd30c | ||
|
|
65106ce21a | ||
|
|
e51deed21c | ||
|
|
e2cbc9c15f | ||
|
|
4847488090 | ||
|
|
fe3c3d500a | ||
|
|
1705d064cc | ||
|
|
357b8b2beb | ||
|
|
b757970d23 | ||
|
|
f9f5654ab0 | ||
|
|
60303aad23 | ||
|
|
a34188f7db | ||
|
|
52ed2bf637 | ||
|
|
1c1b567279 | ||
|
|
ddf630abfe | ||
|
|
fd36b953d6 | ||
|
|
4ad6df67f0 | ||
|
|
30c21aab3e | ||
|
|
0ae327f0e0 | ||
|
|
27c4d194c5 | ||
|
|
fcffa8f01b | ||
|
|
b5a4d37970 | ||
|
|
50e99b2180 | ||
|
|
11fd074972 | ||
|
|
0854a11a25 | ||
|
|
60630ccb42 | ||
|
|
6e79e1d179 | ||
|
|
eae8162d0d | ||
|
|
0e2797bd33 | ||
|
|
4a088d5d61 | ||
|
|
51c8dc82e9 | ||
|
|
8cd05d332e | ||
|
|
6b9d1a0c63 | ||
|
|
6438e3e919 |
9
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,4 +1,4 @@
|
||||
Issue number: #
|
||||
Issue number: resolves #
|
||||
|
||||
---------
|
||||
|
||||
@@ -21,7 +21,12 @@ Issue number: #
|
||||
- [ ] Yes
|
||||
- [ ] No
|
||||
|
||||
<!-- If this introduces a breaking change, please describe the impact and migration path for existing applications below. -->
|
||||
<!--
|
||||
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/.github/CONTRIBUTING.md#footer for more information.
|
||||
-->
|
||||
|
||||
|
||||
## Other information
|
||||
|
||||
9
.github/ionic-issue-bot.yml
vendored
@@ -1,10 +1,15 @@
|
||||
triage:
|
||||
label: triage
|
||||
label: "holiday triage"
|
||||
removeLabelWhenProjectAssigned: true
|
||||
dryRun: false
|
||||
|
||||
comment:
|
||||
labels:
|
||||
- label: "holiday triage"
|
||||
message: >
|
||||
Thanks for the issue! This issue has been labeled as `holiday triage`. With the winter holidays quickly approaching, much of the Ionic Team will soon be taking time off. During this time, issue triaging and PR review will be delayed until the team begins to return. After this period, we will work to ensure that all new issues are properly triaged and that new PRs are reviewed.
|
||||
In the meantime, please read our [Winter Holiday Triage Guide](https://github.com/ionic-team/ionic-framework/issues/22699) for information on how to ensure that your issue is triaged correctly.
|
||||
Thank you!
|
||||
- label: "help wanted"
|
||||
message: >
|
||||
This issue has been labeled as `help wanted`. This label is added to issues
|
||||
@@ -40,7 +45,7 @@ comment:
|
||||
|
||||
|
||||
If the requested feature is something you would find useful for your applications, please react to the original post with 👍 (`+1`). If you would like to provide an additional use case for the feature, please post a comment.
|
||||
|
||||
|
||||
|
||||
The team will review this feedback and make a final decision. Any decision will be posted on this thread, but please note that we may ultimately decide not to pursue this feature.
|
||||
|
||||
|
||||
15
.github/labeler.yml
vendored
@@ -6,16 +6,17 @@
|
||||
# https://github.com/actions/labeler
|
||||
|
||||
'package: core':
|
||||
- core/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: ['core/**/*']
|
||||
|
||||
'package: angular':
|
||||
- packages/angular/**/*
|
||||
- packages/angular-*/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: ['packages/angular/**/*', 'packages/angular-*/**/*']
|
||||
|
||||
'package: react':
|
||||
- packages/react/**/*
|
||||
- packages/react-*/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: ['packages/react/**/*', 'packages/react-*/**/*']
|
||||
|
||||
'package: vue':
|
||||
- packages/vue/**/*
|
||||
- packages/vue-*/**/*
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: ['packages/vue/**/*', 'packages/vue-*/**/*']
|
||||
|
||||
2
.github/workflows/label.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
triage:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/labeler@main
|
||||
- uses: actions/labeler@v5
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
sync-labels: true
|
||||
|
||||
6
.github/workflows/nightly.yml
vendored
@@ -1,11 +1,5 @@
|
||||
name: 'Ionic Nightly Build'
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Run every Monday-Friday
|
||||
# at 6:00 UTC (6:00 am UTC)
|
||||
- cron: '00 06 * * 1-5'
|
||||
|
||||
jobs:
|
||||
create-nightly-hash:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
29
.github/workflows/release.yml
vendored
@@ -69,16 +69,25 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
shell: bash
|
||||
# Lerna does not automatically bump versions
|
||||
# of Ionic dependencies that have changed,
|
||||
# so we do that here.
|
||||
- name: Bump Package Lock
|
||||
run: |
|
||||
lerna exec "npm install --package-lock-only"
|
||||
git add .
|
||||
git commit -m "chore(): update package lock files"
|
||||
git push
|
||||
shell: bash
|
||||
|
||||
update-package-lock:
|
||||
# This needs to run after finalize-release
|
||||
# because we also push to the repo in that
|
||||
# job. If these jobs ran in parallel then it is
|
||||
# possible for them to push at the same time.
|
||||
needs: [finalize-release]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Lerna does not automatically bump versions
|
||||
# of Ionic dependencies that have changed,
|
||||
# so we do that here.
|
||||
- name: Bump Package Lock
|
||||
run: |
|
||||
lerna exec "npm install --package-lock-only"
|
||||
git add .
|
||||
git commit -m "chore(): update package lock files"
|
||||
git push
|
||||
shell: bash
|
||||
|
||||
purge-cdn-cache:
|
||||
needs: [release-ionic]
|
||||
|
||||
88
CHANGELOG.md
@@ -3,6 +3,94 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [7.6.3](https://github.com/ionic-team/ionic-framework/compare/v7.6.2...v7.6.3) (2024-01-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **datetime:** selected today button renders correctly on ios ([#28740](https://github.com/ionic-team/ionic-framework/issues/28740)) ([2f99aea](https://github.com/ionic-team/ionic-framework/commit/2f99aeae6f71d5ffd1880f2c549227ecf71becf3))
|
||||
* **nav, router-outlet:** ios page transition does not cover menu on larger screens ([#28745](https://github.com/ionic-team/ionic-framework/issues/28745)) ([878eec6](https://github.com/ionic-team/ionic-framework/commit/878eec6ea21d76586466d01e13e5e842e69eaceb)), closes [#28737](https://github.com/ionic-team/ionic-framework/issues/28737)
|
||||
* **radio-group:** radio disabled prop can be undefined ([#28712](https://github.com/ionic-team/ionic-framework/issues/28712)) ([75ffeee](https://github.com/ionic-team/ionic-framework/commit/75ffeee933ae353d2601670178896116c81923e0)), closes [#28677](https://github.com/ionic-team/ionic-framework/issues/28677)
|
||||
* **refresher:** native ios refresher works on iPadOS ([#28620](https://github.com/ionic-team/ionic-framework/issues/28620)) ([e522601](https://github.com/ionic-team/ionic-framework/commit/e5226016a0f0b066a7bd7fc9997f905d3b87fbc4)), closes [#28617](https://github.com/ionic-team/ionic-framework/issues/28617)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.6.2](https://github.com/ionic-team/ionic-framework/compare/v7.6.1...v7.6.2) (2023-12-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **input, textarea, select:** reduce padding on slotted buttons ([#28676](https://github.com/ionic-team/ionic-framework/issues/28676)) ([516b844](https://github.com/ionic-team/ionic-framework/commit/516b84475e5d78060f35fa2c4821efc712536353))
|
||||
* **item:** label does not expand indefinitely ([#28700](https://github.com/ionic-team/ionic-framework/issues/28700)) ([bc51dd0](https://github.com/ionic-team/ionic-framework/commit/bc51dd05cf036656980de584d2367db46054f774))
|
||||
* **refresher:** mode property can be used in typescript ([#28717](https://github.com/ionic-team/ionic-framework/issues/28717)) ([7ce1031](https://github.com/ionic-team/ionic-framework/commit/7ce1031c177487649c2a698664ec98f10d9002b9)), closes [#28716](https://github.com/ionic-team/ionic-framework/issues/28716)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.6.1](https://github.com/ionic-team/ionic-framework/compare/v7.6.0...v7.6.1) (2023-12-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **datetime:** prefer wheel sets working value on confirmation ([#28520](https://github.com/ionic-team/ionic-framework/issues/28520)) ([e886e3f](https://github.com/ionic-team/ionic-framework/commit/e886e3ff2fcb8a3586a62881c5fc848f3074235d)), closes [#25839](https://github.com/ionic-team/ionic-framework/issues/25839)
|
||||
* **input, textarea:** clearOnInput ignores key modifiers ([#28639](https://github.com/ionic-team/ionic-framework/issues/28639)) ([8f7d87c](https://github.com/ionic-team/ionic-framework/commit/8f7d87c6803b1600a3ca21785df0e9bac49f74a3)), closes [#28633](https://github.com/ionic-team/ionic-framework/issues/28633)
|
||||
* **menu:** allow styling of the box shadow and transform when visible inside of a split pane ([#28691](https://github.com/ionic-team/ionic-framework/issues/28691)) ([8ee23d2](https://github.com/ionic-team/ionic-framework/commit/8ee23d20d5cc7419ce15f047b92d2f826d3eb681)), closes [#21530](https://github.com/ionic-team/ionic-framework/issues/21530)
|
||||
* **react:** avoid type collision with @types/react@18.2.43 and greater ([#28687](https://github.com/ionic-team/ionic-framework/issues/28687)) ([92f1b86](https://github.com/ionic-team/ionic-framework/commit/92f1b8627a240c93891205f75adcb5ce3d46596d))
|
||||
* **react:** replacing route uses new route direction and animation ([#28671](https://github.com/ionic-team/ionic-framework/issues/28671)) ([a17b963](https://github.com/ionic-team/ionic-framework/commit/a17b9631829c36c2daf1d5227f5afa69f99f8743)), closes [#24260](https://github.com/ionic-team/ionic-framework/issues/24260)
|
||||
* **react:** use custom animation when going back after a replace ([#28674](https://github.com/ionic-team/ionic-framework/issues/28674)) ([fc88613](https://github.com/ionic-team/ionic-framework/commit/fc88613fefa019a3b695a2c6e10c85cd3ce79ae8)), closes [#28673](https://github.com/ionic-team/ionic-framework/issues/28673)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [7.6.0](https://github.com/ionic-team/ionic-framework/compare/v7.5.8...v7.6.0) (2023-12-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular,vue:** range form value updates while dragging knob ([#28422](https://github.com/ionic-team/ionic-framework/issues/28422)) ([0854a11](https://github.com/ionic-team/ionic-framework/commit/0854a11a25759d0201eae66c96a62fe138d486f8)), closes [#28256](https://github.com/ionic-team/ionic-framework/issues/28256)
|
||||
* **animation:** add stronger types to Animation interface ([#28334](https://github.com/ionic-team/ionic-framework/issues/28334)) ([4a088d5](https://github.com/ionic-team/ionic-framework/commit/4a088d5d612ab0387064d388b37d46cdf15cf1ff))
|
||||
* **animation:** progressEnd coercion is reset before onFinish ([#28394](https://github.com/ionic-team/ionic-framework/issues/28394)) ([eae8162](https://github.com/ionic-team/ionic-framework/commit/eae8162d0dc2e0bd7a9d56a3662a8e5f5d142b72)), closes [#28393](https://github.com/ionic-team/ionic-framework/issues/28393)
|
||||
* **infinite-scroll:** remaining in threshold after ionInfinite can trigger event again on scroll ([#28569](https://github.com/ionic-team/ionic-framework/issues/28569)) ([8c235fd](https://github.com/ionic-team/ionic-framework/commit/8c235fd30c50f317de1f37f69068507aa0979068)), closes [#18071](https://github.com/ionic-team/ionic-framework/issues/18071)
|
||||
* **item:** allow item to grow when it is used in a flex container ([#28594](https://github.com/ionic-team/ionic-framework/issues/28594)) ([1c1b567](https://github.com/ionic-team/ionic-framework/commit/1c1b567279dee44da70bb9b90c129946c9043987))
|
||||
* **item:** wrap elements and label contents when the font size increases or the elements do not fit ([#28146](https://github.com/ionic-team/ionic-framework/issues/28146)) ([6438e3e](https://github.com/ionic-team/ionic-framework/commit/6438e3e919c665569b731a2d74fe1547b4f3c1cc))
|
||||
* **select:** do not collapse to width: 0 when placed in flex container ([#28631](https://github.com/ionic-team/ionic-framework/issues/28631)) ([e71e7a0](https://github.com/ionic-team/ionic-framework/commit/e71e7a069000db8738abc304758de64286817442))
|
||||
* **toast:** add swipeGesture to ToastOptions ([#28518](https://github.com/ionic-team/ionic-framework/issues/28518)) ([4ad6df6](https://github.com/ionic-team/ionic-framework/commit/4ad6df67f01cebce30d4da46c7541c4b14c5d4a4))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **checkbox:** add shadow part for label ([#28604](https://github.com/ionic-team/ionic-framework/issues/28604)) ([f9f5654](https://github.com/ionic-team/ionic-framework/commit/f9f5654ab0e920bf97089fbabfb9eedbcf6fe8ae)), closes [#28300](https://github.com/ionic-team/ionic-framework/issues/28300)
|
||||
* **input, textarea, select:** add start and end slots ([#28583](https://github.com/ionic-team/ionic-framework/issues/28583)) ([357b8b2](https://github.com/ionic-team/ionic-framework/commit/357b8b2beb29b95d53ef043af349067be1d32658)), closes [#26297](https://github.com/ionic-team/ionic-framework/issues/26297)
|
||||
* **radio-group:** add compareWith property ([#28452](https://github.com/ionic-team/ionic-framework/issues/28452)) ([0ae327f](https://github.com/ionic-team/ionic-framework/commit/0ae327f0e09cd97d705f2d3051c215034381e226))
|
||||
* **radio:** add shadow part for label ([#28607](https://github.com/ionic-team/ionic-framework/issues/28607)) ([b757970](https://github.com/ionic-team/ionic-framework/commit/b757970d23e87c59aa883ecb1bfa9b66bcae8de2)), closes [#28300](https://github.com/ionic-team/ionic-framework/issues/28300)
|
||||
* **range:** expose label wrapper as shadow part ([#28601](https://github.com/ionic-team/ionic-framework/issues/28601)) ([52ed2bf](https://github.com/ionic-team/ionic-framework/commit/52ed2bf63777c764f57bb4c3a5d4a127bff46c50))
|
||||
* **toast:** add swipe to dismiss functionality ([#28442](https://github.com/ionic-team/ionic-framework/issues/28442)) ([30c21aa](https://github.com/ionic-team/ionic-framework/commit/30c21aab3ed40d73c28e7d60d0952d8891b0a9d3)), closes [#21769](https://github.com/ionic-team/ionic-framework/issues/21769)
|
||||
* **toggle:** expose label wrapper as shadow part ([#28585](https://github.com/ionic-team/ionic-framework/issues/28585)) ([a34188f](https://github.com/ionic-team/ionic-framework/commit/a34188f7dbec4a16e4f2043ed3dc096e337725a7))
|
||||
|
||||
Note: Text inside of `ion-item` can now wrap to resolve accessibility issues related to readability. We recommend evaluating your application to account for text wrapping.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.8](https://github.com/ionic-team/ionic-framework/compare/v7.5.7...v7.5.8) (2023-12-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** add missing menu controller methods ([#28618](https://github.com/ionic-team/ionic-framework/issues/28618)) ([7871b56](https://github.com/ionic-team/ionic-framework/commit/7871b56eccfe63326b6dd4b56ade3b3afd444fce)), closes [#20053](https://github.com/ionic-team/ionic-framework/issues/20053)
|
||||
* **overlays:** trigger is configured on load ([#28526](https://github.com/ionic-team/ionic-framework/issues/28526)) ([a3cd204](https://github.com/ionic-team/ionic-framework/commit/a3cd204f616606ccffc35082655e55fdfb19fe28)), closes [#28524](https://github.com/ionic-team/ionic-framework/issues/28524)
|
||||
* **react:** router creates new view instances of parameterized routes ([#28616](https://github.com/ionic-team/ionic-framework/issues/28616)) ([1705d06](https://github.com/ionic-team/ionic-framework/commit/1705d064cc041e99f432a27207f3aab7fa62c778)), closes [#26524](https://github.com/ionic-team/ionic-framework/issues/26524)
|
||||
* **vue:** nav component accepts kebab-case component properties ([#28615](https://github.com/ionic-team/ionic-framework/issues/28615)) ([60303aa](https://github.com/ionic-team/ionic-framework/commit/60303aad23f823488afc8f8824e9c72e3ab86acc)), closes [#28611](https://github.com/ionic-team/ionic-framework/issues/28611)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.7](https://github.com/ionic-team/ionic-framework/compare/v7.5.6...v7.5.7) (2023-11-29)
|
||||
|
||||
|
||||
|
||||
@@ -3,6 +3,86 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [7.6.3](https://github.com/ionic-team/ionic-framework/compare/v7.6.2...v7.6.3) (2024-01-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **datetime:** selected today button renders correctly on ios ([#28740](https://github.com/ionic-team/ionic-framework/issues/28740)) ([2f99aea](https://github.com/ionic-team/ionic-framework/commit/2f99aeae6f71d5ffd1880f2c549227ecf71becf3))
|
||||
* **nav, router-outlet:** ios page transition does not cover menu on larger screens ([#28745](https://github.com/ionic-team/ionic-framework/issues/28745)) ([878eec6](https://github.com/ionic-team/ionic-framework/commit/878eec6ea21d76586466d01e13e5e842e69eaceb)), closes [#28737](https://github.com/ionic-team/ionic-framework/issues/28737)
|
||||
* **radio-group:** radio disabled prop can be undefined ([#28712](https://github.com/ionic-team/ionic-framework/issues/28712)) ([75ffeee](https://github.com/ionic-team/ionic-framework/commit/75ffeee933ae353d2601670178896116c81923e0)), closes [#28677](https://github.com/ionic-team/ionic-framework/issues/28677)
|
||||
* **refresher:** native ios refresher works on iPadOS ([#28620](https://github.com/ionic-team/ionic-framework/issues/28620)) ([e522601](https://github.com/ionic-team/ionic-framework/commit/e5226016a0f0b066a7bd7fc9997f905d3b87fbc4)), closes [#28617](https://github.com/ionic-team/ionic-framework/issues/28617)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.6.2](https://github.com/ionic-team/ionic-framework/compare/v7.6.1...v7.6.2) (2023-12-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **input, textarea, select:** reduce padding on slotted buttons ([#28676](https://github.com/ionic-team/ionic-framework/issues/28676)) ([516b844](https://github.com/ionic-team/ionic-framework/commit/516b84475e5d78060f35fa2c4821efc712536353))
|
||||
* **item:** label does not expand indefinitely ([#28700](https://github.com/ionic-team/ionic-framework/issues/28700)) ([bc51dd0](https://github.com/ionic-team/ionic-framework/commit/bc51dd05cf036656980de584d2367db46054f774))
|
||||
* **refresher:** mode property can be used in typescript ([#28717](https://github.com/ionic-team/ionic-framework/issues/28717)) ([7ce1031](https://github.com/ionic-team/ionic-framework/commit/7ce1031c177487649c2a698664ec98f10d9002b9)), closes [#28716](https://github.com/ionic-team/ionic-framework/issues/28716)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.6.1](https://github.com/ionic-team/ionic-framework/compare/v7.6.0...v7.6.1) (2023-12-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **datetime:** prefer wheel sets working value on confirmation ([#28520](https://github.com/ionic-team/ionic-framework/issues/28520)) ([e886e3f](https://github.com/ionic-team/ionic-framework/commit/e886e3ff2fcb8a3586a62881c5fc848f3074235d)), closes [#25839](https://github.com/ionic-team/ionic-framework/issues/25839)
|
||||
* **input, textarea:** clearOnInput ignores key modifiers ([#28639](https://github.com/ionic-team/ionic-framework/issues/28639)) ([8f7d87c](https://github.com/ionic-team/ionic-framework/commit/8f7d87c6803b1600a3ca21785df0e9bac49f74a3)), closes [#28633](https://github.com/ionic-team/ionic-framework/issues/28633)
|
||||
* **menu:** allow styling of the box shadow and transform when visible inside of a split pane ([#28691](https://github.com/ionic-team/ionic-framework/issues/28691)) ([8ee23d2](https://github.com/ionic-team/ionic-framework/commit/8ee23d20d5cc7419ce15f047b92d2f826d3eb681)), closes [#21530](https://github.com/ionic-team/ionic-framework/issues/21530)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [7.6.0](https://github.com/ionic-team/ionic-framework/compare/v7.5.8...v7.6.0) (2023-12-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular,vue:** range form value updates while dragging knob ([#28422](https://github.com/ionic-team/ionic-framework/issues/28422)) ([0854a11](https://github.com/ionic-team/ionic-framework/commit/0854a11a25759d0201eae66c96a62fe138d486f8)), closes [#28256](https://github.com/ionic-team/ionic-framework/issues/28256)
|
||||
* **animation:** add stronger types to Animation interface ([#28334](https://github.com/ionic-team/ionic-framework/issues/28334)) ([4a088d5](https://github.com/ionic-team/ionic-framework/commit/4a088d5d612ab0387064d388b37d46cdf15cf1ff))
|
||||
* **animation:** progressEnd coercion is reset before onFinish ([#28394](https://github.com/ionic-team/ionic-framework/issues/28394)) ([eae8162](https://github.com/ionic-team/ionic-framework/commit/eae8162d0dc2e0bd7a9d56a3662a8e5f5d142b72)), closes [#28393](https://github.com/ionic-team/ionic-framework/issues/28393)
|
||||
* **infinite-scroll:** remaining in threshold after ionInfinite can trigger event again on scroll ([#28569](https://github.com/ionic-team/ionic-framework/issues/28569)) ([8c235fd](https://github.com/ionic-team/ionic-framework/commit/8c235fd30c50f317de1f37f69068507aa0979068)), closes [#18071](https://github.com/ionic-team/ionic-framework/issues/18071)
|
||||
* **item:** allow item to grow when it is used in a flex container ([#28594](https://github.com/ionic-team/ionic-framework/issues/28594)) ([1c1b567](https://github.com/ionic-team/ionic-framework/commit/1c1b567279dee44da70bb9b90c129946c9043987))
|
||||
* **item:** wrap elements and label contents when the font size increases or the elements do not fit ([#28146](https://github.com/ionic-team/ionic-framework/issues/28146)) ([6438e3e](https://github.com/ionic-team/ionic-framework/commit/6438e3e919c665569b731a2d74fe1547b4f3c1cc))
|
||||
* **select:** do not collapse to width: 0 when placed in flex container ([#28631](https://github.com/ionic-team/ionic-framework/issues/28631)) ([e71e7a0](https://github.com/ionic-team/ionic-framework/commit/e71e7a069000db8738abc304758de64286817442))
|
||||
* **toast:** add swipeGesture to ToastOptions ([#28518](https://github.com/ionic-team/ionic-framework/issues/28518)) ([4ad6df6](https://github.com/ionic-team/ionic-framework/commit/4ad6df67f01cebce30d4da46c7541c4b14c5d4a4))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **checkbox:** add shadow part for label ([#28604](https://github.com/ionic-team/ionic-framework/issues/28604)) ([f9f5654](https://github.com/ionic-team/ionic-framework/commit/f9f5654ab0e920bf97089fbabfb9eedbcf6fe8ae)), closes [#28300](https://github.com/ionic-team/ionic-framework/issues/28300)
|
||||
* **input, textarea, select:** add start and end slots ([#28583](https://github.com/ionic-team/ionic-framework/issues/28583)) ([357b8b2](https://github.com/ionic-team/ionic-framework/commit/357b8b2beb29b95d53ef043af349067be1d32658)), closes [#26297](https://github.com/ionic-team/ionic-framework/issues/26297)
|
||||
* **radio-group:** add compareWith property ([#28452](https://github.com/ionic-team/ionic-framework/issues/28452)) ([0ae327f](https://github.com/ionic-team/ionic-framework/commit/0ae327f0e09cd97d705f2d3051c215034381e226))
|
||||
* **radio:** add shadow part for label ([#28607](https://github.com/ionic-team/ionic-framework/issues/28607)) ([b757970](https://github.com/ionic-team/ionic-framework/commit/b757970d23e87c59aa883ecb1bfa9b66bcae8de2)), closes [#28300](https://github.com/ionic-team/ionic-framework/issues/28300)
|
||||
* **range:** expose label wrapper as shadow part ([#28601](https://github.com/ionic-team/ionic-framework/issues/28601)) ([52ed2bf](https://github.com/ionic-team/ionic-framework/commit/52ed2bf63777c764f57bb4c3a5d4a127bff46c50))
|
||||
* **toast:** add swipe to dismiss functionality ([#28442](https://github.com/ionic-team/ionic-framework/issues/28442)) ([30c21aa](https://github.com/ionic-team/ionic-framework/commit/30c21aab3ed40d73c28e7d60d0952d8891b0a9d3)), closes [#21769](https://github.com/ionic-team/ionic-framework/issues/21769)
|
||||
* **toggle:** expose label wrapper as shadow part ([#28585](https://github.com/ionic-team/ionic-framework/issues/28585)) ([a34188f](https://github.com/ionic-team/ionic-framework/commit/a34188f7dbec4a16e4f2043ed3dc096e337725a7))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.8](https://github.com/ionic-team/ionic-framework/compare/v7.5.7...v7.5.8) (2023-12-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **overlays:** trigger is configured on load ([#28526](https://github.com/ionic-team/ionic-framework/issues/28526)) ([a3cd204](https://github.com/ionic-team/ionic-framework/commit/a3cd204f616606ccffc35082655e55fdfb19fe28)), closes [#28524](https://github.com/ionic-team/ionic-framework/issues/28524)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [7.5.7](https://github.com/ionic-team/ionic-framework/compare/v7.5.6...v7.5.7) (2023-11-29)
|
||||
|
||||
|
||||
|
||||
@@ -315,6 +315,7 @@ ion-checkbox,css-prop,--checkmark-width
|
||||
ion-checkbox,css-prop,--size
|
||||
ion-checkbox,css-prop,--transition
|
||||
ion-checkbox,part,container
|
||||
ion-checkbox,part,label
|
||||
ion-checkbox,part,mark
|
||||
|
||||
ion-chip,shadow
|
||||
@@ -1029,10 +1030,12 @@ ion-radio,css-prop,--color
|
||||
ion-radio,css-prop,--color-checked
|
||||
ion-radio,css-prop,--inner-border-radius
|
||||
ion-radio,part,container
|
||||
ion-radio,part,label
|
||||
ion-radio,part,mark
|
||||
|
||||
ion-radio-group,none
|
||||
ion-radio-group,prop,allowEmptySelection,boolean,false,false,false
|
||||
ion-radio-group,prop,compareWith,((currentValue: any, compareValue: any) => boolean) | null | string | undefined,undefined,false,false
|
||||
ion-radio-group,prop,name,string,this.inputId,false,false
|
||||
ion-radio-group,prop,value,any,undefined,false,false
|
||||
ion-radio-group,event,ionChange,RadioGroupChangeEventDetail<any>,true
|
||||
@@ -1076,6 +1079,7 @@ ion-range,css-prop,--pin-color
|
||||
ion-range,part,bar
|
||||
ion-range,part,bar-active
|
||||
ion-range,part,knob
|
||||
ion-range,part,label
|
||||
ion-range,part,pin
|
||||
ion-range,part,tick
|
||||
ion-range,part,tick-active
|
||||
@@ -1083,6 +1087,7 @@ ion-range,part,tick-active
|
||||
ion-refresher,none
|
||||
ion-refresher,prop,closeDuration,string,'280ms',false,false
|
||||
ion-refresher,prop,disabled,boolean,false,false,false
|
||||
ion-refresher,prop,mode,"ios" | "md",undefined,false,false
|
||||
ion-refresher,prop,pullFactor,number,1,false,false
|
||||
ion-refresher,prop,pullMax,number,this.pullMin + 60,false,false
|
||||
ion-refresher,prop,pullMin,number,60,false,false
|
||||
@@ -1446,6 +1451,7 @@ ion-toast,prop,message,IonicSafeString | string | undefined,undefined,false,fals
|
||||
ion-toast,prop,mode,"ios" | "md",undefined,false,false
|
||||
ion-toast,prop,position,"bottom" | "middle" | "top",'bottom',false,false
|
||||
ion-toast,prop,positionAnchor,HTMLElement | string | undefined,undefined,false,false
|
||||
ion-toast,prop,swipeGesture,"vertical" | undefined,undefined,false,false
|
||||
ion-toast,prop,translucent,boolean,false,false,false
|
||||
ion-toast,prop,trigger,string | undefined,undefined,false,false
|
||||
ion-toast,method,dismiss,dismiss(data?: any, role?: string) => Promise<boolean>
|
||||
@@ -1512,6 +1518,7 @@ ion-toggle,css-prop,--handle-width
|
||||
ion-toggle,css-prop,--track-background
|
||||
ion-toggle,css-prop,--track-background-checked
|
||||
ion-toggle,part,handle
|
||||
ion-toggle,part,label
|
||||
ion-toggle,part,track
|
||||
|
||||
ion-toolbar,shadow
|
||||
|
||||
88
core/package-lock.json
generated
@@ -1,23 +1,23 @@
|
||||
{
|
||||
"name": "@ionic/core",
|
||||
"version": "7.5.7",
|
||||
"version": "7.6.3",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@ionic/core",
|
||||
"version": "7.5.7",
|
||||
"version": "7.6.3",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@stencil/core": "^4.8.0",
|
||||
"ionicons": "^7.2.1",
|
||||
"@stencil/core": "^4.8.2",
|
||||
"ionicons": "^7.2.2",
|
||||
"tslib": "^2.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@axe-core/playwright": "^4.8.1",
|
||||
"@capacitor/core": "^5.5.1",
|
||||
"@axe-core/playwright": "^4.8.2",
|
||||
"@capacitor/core": "^5.6.0",
|
||||
"@capacitor/haptics": "^5.0.6",
|
||||
"@capacitor/keyboard": "^5.0.6",
|
||||
"@capacitor/keyboard": "^5.0.7",
|
||||
"@capacitor/status-bar": "^5.0.6",
|
||||
"@ionic/eslint-config": "^0.3.0",
|
||||
"@ionic/prettier-config": "^2.0.0",
|
||||
@@ -26,7 +26,7 @@
|
||||
"@rollup/plugin-virtual": "^2.0.3",
|
||||
"@stencil/angular-output-target": "^0.8.3",
|
||||
"@stencil/react-output-target": "^0.5.3",
|
||||
"@stencil/sass": "^3.0.7",
|
||||
"@stencil/sass": "^3.0.8",
|
||||
"@stencil/vue-output-target": "^0.8.7",
|
||||
"@types/jest": "^29.5.6",
|
||||
"@types/node": "^14.6.0",
|
||||
@@ -56,9 +56,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@axe-core/playwright": {
|
||||
"version": "4.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@axe-core/playwright/-/playwright-4.8.1.tgz",
|
||||
"integrity": "sha512-KC1X++UdRAwMLRvB+BIKFheyLHUnbJTL0t0Wbv6TJMozn2V2QyEtAcN6jyUiudtGiLUGhHCtj/eWorBnVZ4dAA==",
|
||||
"version": "4.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@axe-core/playwright/-/playwright-4.8.2.tgz",
|
||||
"integrity": "sha512-9KOhX2tNuvqn9DzpBNyqoqNKRZBrexeSiN9irQ0sEdq8zH13JnatepCJxobuXn4UopNy6iIpP4342beMiH+MSQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"axe-core": "~4.8.2"
|
||||
@@ -634,9 +634,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@capacitor/core": {
|
||||
"version": "5.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-5.5.1.tgz",
|
||||
"integrity": "sha512-VG6Iv8Q7ZAbvjodxpvjcSe0jfxUwZXnvjbi93ehuJ6eYP8U926qLSXyrT/DToZq+F6v/HyGyVgn3mrE/9jW2Tg==",
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-5.6.0.tgz",
|
||||
"integrity": "sha512-xJhCOUGPHw0QYDA3YH+CmL6qiV9DH4Ij3yPxSenymjrtLuXI197u9ddCZwGEwgVIkh9kGZBBKzsNkn89SZ2gdQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.1.0"
|
||||
@@ -652,9 +652,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@capacitor/keyboard": {
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@capacitor/keyboard/-/keyboard-5.0.6.tgz",
|
||||
"integrity": "sha512-9GewAa/y2Hwkdw/Be8MTdiAjrFZ7TPDKpR44M0Y/0QMnK+mBbgzcoZ/UkuumWv6e2F1IAI+VY5eYVQHDeZcRoA==",
|
||||
"version": "5.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@capacitor/keyboard/-/keyboard-5.0.7.tgz",
|
||||
"integrity": "sha512-+6lW8z2nXTM2NOG7D7pOasCfIGicz26+EeDRXIj5AtJibbjwtE1Q5GIY+qGHgzpmwOF0qmcrGJBz4zagDwUapg==",
|
||||
"dev": true,
|
||||
"peerDependencies": {
|
||||
"@capacitor/core": "^5.0.0"
|
||||
@@ -1825,9 +1825,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@stencil/core": {
|
||||
"version": "4.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.8.0.tgz",
|
||||
"integrity": "sha512-iNEaMiEt9oFZXSjZ7qkdlBpPTzWzBgWM7go8nI8a7V6ZOkmdVhhT0xGQD7OY13v5ZuDoIw5IsGvbIAGefhIhhQ==",
|
||||
"version": "4.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.8.2.tgz",
|
||||
"integrity": "sha512-KdZEAtz9VnqMtXOkf51+8mphyRt0fN/LYgtj5M8gnveGspG8KzoyTDzlWt0wsstWIsJJ21RA1yd3AgMMZiu3MA==",
|
||||
"bin": {
|
||||
"stencil": "bin/stencil"
|
||||
},
|
||||
@@ -1846,9 +1846,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@stencil/sass": {
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/sass/-/sass-3.0.7.tgz",
|
||||
"integrity": "sha512-HcBjrh2CJ6aJnkOrBNSVyf1+x3FnUneYFk44rcx/jDK6Lx7R4w0dXMEsIR5MXqtROYWonZt7WtR8wsM1vcD+6w==",
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/sass/-/sass-3.0.8.tgz",
|
||||
"integrity": "sha512-QJUG4Dr/b3wSizViwQXorrk1PJzxOsKkq5hSqtUHc3NNG3iomC4DQFYGeu15yNfoCDBtt4qkyHSCynsekQ8F6A==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12.0.0",
|
||||
@@ -5785,9 +5785,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/ionicons": {
|
||||
"version": "7.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ionicons/-/ionicons-7.2.1.tgz",
|
||||
"integrity": "sha512-2pvCM7DGVEtbbj48PJzQrCADCQrqjU1nUYX9l9PyEWz3ZfdnLdAouqwPxLdl8tbaF9cE7OZRSlyQD7oLOLnGoQ==",
|
||||
"version": "7.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ionicons/-/ionicons-7.2.2.tgz",
|
||||
"integrity": "sha512-I3iYIfc9Q9FRifWyFSwTAvbEABWlWY32i0sAVDDPGYnaIZVugkLCZFbEcrphW6ixVPg8tt1oLwalo/JJwbEqnA==",
|
||||
"dependencies": {
|
||||
"@stencil/core": "^4.0.3"
|
||||
}
|
||||
@@ -10899,9 +10899,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@axe-core/playwright": {
|
||||
"version": "4.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@axe-core/playwright/-/playwright-4.8.1.tgz",
|
||||
"integrity": "sha512-KC1X++UdRAwMLRvB+BIKFheyLHUnbJTL0t0Wbv6TJMozn2V2QyEtAcN6jyUiudtGiLUGhHCtj/eWorBnVZ4dAA==",
|
||||
"version": "4.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@axe-core/playwright/-/playwright-4.8.2.tgz",
|
||||
"integrity": "sha512-9KOhX2tNuvqn9DzpBNyqoqNKRZBrexeSiN9irQ0sEdq8zH13JnatepCJxobuXn4UopNy6iIpP4342beMiH+MSQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"axe-core": "~4.8.2"
|
||||
@@ -11324,9 +11324,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@capacitor/core": {
|
||||
"version": "5.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-5.5.1.tgz",
|
||||
"integrity": "sha512-VG6Iv8Q7ZAbvjodxpvjcSe0jfxUwZXnvjbi93ehuJ6eYP8U926qLSXyrT/DToZq+F6v/HyGyVgn3mrE/9jW2Tg==",
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@capacitor/core/-/core-5.6.0.tgz",
|
||||
"integrity": "sha512-xJhCOUGPHw0QYDA3YH+CmL6qiV9DH4Ij3yPxSenymjrtLuXI197u9ddCZwGEwgVIkh9kGZBBKzsNkn89SZ2gdQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "^2.1.0"
|
||||
@@ -11340,9 +11340,9 @@
|
||||
"requires": {}
|
||||
},
|
||||
"@capacitor/keyboard": {
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@capacitor/keyboard/-/keyboard-5.0.6.tgz",
|
||||
"integrity": "sha512-9GewAa/y2Hwkdw/Be8MTdiAjrFZ7TPDKpR44M0Y/0QMnK+mBbgzcoZ/UkuumWv6e2F1IAI+VY5eYVQHDeZcRoA==",
|
||||
"version": "5.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@capacitor/keyboard/-/keyboard-5.0.7.tgz",
|
||||
"integrity": "sha512-+6lW8z2nXTM2NOG7D7pOasCfIGicz26+EeDRXIj5AtJibbjwtE1Q5GIY+qGHgzpmwOF0qmcrGJBz4zagDwUapg==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
@@ -12184,9 +12184,9 @@
|
||||
"requires": {}
|
||||
},
|
||||
"@stencil/core": {
|
||||
"version": "4.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.8.0.tgz",
|
||||
"integrity": "sha512-iNEaMiEt9oFZXSjZ7qkdlBpPTzWzBgWM7go8nI8a7V6ZOkmdVhhT0xGQD7OY13v5ZuDoIw5IsGvbIAGefhIhhQ=="
|
||||
"version": "4.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-4.8.2.tgz",
|
||||
"integrity": "sha512-KdZEAtz9VnqMtXOkf51+8mphyRt0fN/LYgtj5M8gnveGspG8KzoyTDzlWt0wsstWIsJJ21RA1yd3AgMMZiu3MA=="
|
||||
},
|
||||
"@stencil/react-output-target": {
|
||||
"version": "0.5.3",
|
||||
@@ -12196,9 +12196,9 @@
|
||||
"requires": {}
|
||||
},
|
||||
"@stencil/sass": {
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/sass/-/sass-3.0.7.tgz",
|
||||
"integrity": "sha512-HcBjrh2CJ6aJnkOrBNSVyf1+x3FnUneYFk44rcx/jDK6Lx7R4w0dXMEsIR5MXqtROYWonZt7WtR8wsM1vcD+6w==",
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@stencil/sass/-/sass-3.0.8.tgz",
|
||||
"integrity": "sha512-QJUG4Dr/b3wSizViwQXorrk1PJzxOsKkq5hSqtUHc3NNG3iomC4DQFYGeu15yNfoCDBtt4qkyHSCynsekQ8F6A==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
@@ -15056,9 +15056,9 @@
|
||||
}
|
||||
},
|
||||
"ionicons": {
|
||||
"version": "7.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ionicons/-/ionicons-7.2.1.tgz",
|
||||
"integrity": "sha512-2pvCM7DGVEtbbj48PJzQrCADCQrqjU1nUYX9l9PyEWz3ZfdnLdAouqwPxLdl8tbaF9cE7OZRSlyQD7oLOLnGoQ==",
|
||||
"version": "7.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ionicons/-/ionicons-7.2.2.tgz",
|
||||
"integrity": "sha512-I3iYIfc9Q9FRifWyFSwTAvbEABWlWY32i0sAVDDPGYnaIZVugkLCZFbEcrphW6ixVPg8tt1oLwalo/JJwbEqnA==",
|
||||
"requires": {
|
||||
"@stencil/core": "^4.0.3"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@ionic/core",
|
||||
"version": "7.5.7",
|
||||
"version": "7.6.3",
|
||||
"description": "Base components for Ionic",
|
||||
"keywords": [
|
||||
"ionic",
|
||||
@@ -31,15 +31,15 @@
|
||||
"loader/"
|
||||
],
|
||||
"dependencies": {
|
||||
"@stencil/core": "^4.8.0",
|
||||
"ionicons": "^7.2.1",
|
||||
"@stencil/core": "^4.8.2",
|
||||
"ionicons": "^7.2.2",
|
||||
"tslib": "^2.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@axe-core/playwright": "^4.8.1",
|
||||
"@capacitor/core": "^5.5.1",
|
||||
"@axe-core/playwright": "^4.8.2",
|
||||
"@capacitor/core": "^5.6.0",
|
||||
"@capacitor/haptics": "^5.0.6",
|
||||
"@capacitor/keyboard": "^5.0.6",
|
||||
"@capacitor/keyboard": "^5.0.7",
|
||||
"@capacitor/status-bar": "^5.0.6",
|
||||
"@ionic/eslint-config": "^0.3.0",
|
||||
"@ionic/prettier-config": "^2.0.0",
|
||||
@@ -48,7 +48,7 @@
|
||||
"@rollup/plugin-virtual": "^2.0.3",
|
||||
"@stencil/angular-output-target": "^0.8.3",
|
||||
"@stencil/react-output-target": "^0.5.3",
|
||||
"@stencil/sass": "^3.0.7",
|
||||
"@stencil/sass": "^3.0.8",
|
||||
"@stencil/vue-output-target": "^0.8.7",
|
||||
"@types/jest": "^29.5.6",
|
||||
"@types/node": "^14.6.0",
|
||||
|
||||
21
core/setupJest.js
Normal file
@@ -0,0 +1,21 @@
|
||||
expect.extend({
|
||||
toHaveShadowPart(received, part) {
|
||||
if (typeof part !== 'string') {
|
||||
throw new Error('expected toHaveShadowPart to be called with a string of the CSS shadow part name');
|
||||
}
|
||||
|
||||
if (received.shadowRoot === null) {
|
||||
throw new Error('expected toHaveShadowPart to be called on an element with a shadow root');
|
||||
}
|
||||
|
||||
const shadowPart = received.shadowRoot.querySelector(`[part="${part}"]`);
|
||||
const pass = shadowPart !== null;
|
||||
|
||||
const message = `expected ${received.tagName.toLowerCase()} to have shadow part "${part}"`;
|
||||
|
||||
return {
|
||||
pass,
|
||||
message: () => message,
|
||||
};
|
||||
},
|
||||
});
|
||||
89
core/src/components.d.ts
vendored
@@ -27,7 +27,7 @@ import { PickerButton, PickerColumn } from "./components/picker/picker-interface
|
||||
import { PickerColumnItem } from "./components/picker-column-internal/picker-column-internal-interfaces";
|
||||
import { PickerInternalChangeEventDetail } from "./components/picker-internal/picker-internal-interfaces";
|
||||
import { PopoverSize, PositionAlign, PositionReference, PositionSide, TriggerAction } from "./components/popover/popover-interface";
|
||||
import { RadioGroupChangeEventDetail } from "./components/radio-group/radio-group-interface";
|
||||
import { RadioGroupChangeEventDetail, RadioGroupCompareFn } from "./components/radio-group/radio-group-interface";
|
||||
import { PinFormatter, RangeChangeEventDetail, RangeKnobMoveEndEventDetail, RangeKnobMoveStartEventDetail, RangeValue } from "./components/range/range-interface";
|
||||
import { RefresherEventDetail } from "./components/refresher/refresher-interface";
|
||||
import { ItemReorderEventDetail } from "./components/reorder-group/reorder-group-interface";
|
||||
@@ -39,7 +39,7 @@ import { SelectChangeEventDetail, SelectCompareFn, SelectInterface } from "./com
|
||||
import { SelectPopoverOption } from "./components/select-popover/select-popover-interface";
|
||||
import { TabBarChangedEventDetail, TabButtonClickEventDetail, TabButtonLayout } from "./components/tab-bar/tab-bar-interface";
|
||||
import { TextareaChangeEventDetail, TextareaInputEventDetail } from "./components/textarea/textarea-interface";
|
||||
import { ToastButton, ToastDismissOptions, ToastLayout, ToastPosition, ToastPresentOptions } from "./components/toast/toast-interface";
|
||||
import { ToastButton, ToastDismissOptions, ToastLayout, ToastPosition, ToastPresentOptions, ToastSwipeGestureDirection } from "./components/toast/toast-interface";
|
||||
import { ToggleChangeEventDetail } from "./components/toggle/toggle-interface";
|
||||
export { AccordionGroupChangeEventDetail } from "./components/accordion-group/accordion-group-interface";
|
||||
export { AnimationBuilder, AutocompleteTypes, Color, ComponentProps, ComponentRef, FrameworkDelegate, StyleEventDetail, TextFieldTypes } from "./interface";
|
||||
@@ -63,7 +63,7 @@ export { PickerButton, PickerColumn } from "./components/picker/picker-interface
|
||||
export { PickerColumnItem } from "./components/picker-column-internal/picker-column-internal-interfaces";
|
||||
export { PickerInternalChangeEventDetail } from "./components/picker-internal/picker-internal-interfaces";
|
||||
export { PopoverSize, PositionAlign, PositionReference, PositionSide, TriggerAction } from "./components/popover/popover-interface";
|
||||
export { RadioGroupChangeEventDetail } from "./components/radio-group/radio-group-interface";
|
||||
export { RadioGroupChangeEventDetail, RadioGroupCompareFn } from "./components/radio-group/radio-group-interface";
|
||||
export { PinFormatter, RangeChangeEventDetail, RangeKnobMoveEndEventDetail, RangeKnobMoveStartEventDetail, RangeValue } from "./components/range/range-interface";
|
||||
export { RefresherEventDetail } from "./components/refresher/refresher-interface";
|
||||
export { ItemReorderEventDetail } from "./components/reorder-group/reorder-group-interface";
|
||||
@@ -75,7 +75,7 @@ export { SelectChangeEventDetail, SelectCompareFn, SelectInterface } from "./com
|
||||
export { SelectPopoverOption } from "./components/select-popover/select-popover-interface";
|
||||
export { TabBarChangedEventDetail, TabButtonClickEventDetail, TabButtonLayout } from "./components/tab-bar/tab-bar-interface";
|
||||
export { TextareaChangeEventDetail, TextareaInputEventDetail } from "./components/textarea/textarea-interface";
|
||||
export { ToastButton, ToastDismissOptions, ToastLayout, ToastPosition, ToastPresentOptions } from "./components/toast/toast-interface";
|
||||
export { ToastButton, ToastDismissOptions, ToastLayout, ToastPosition, ToastPresentOptions, ToastSwipeGestureDirection } from "./components/toast/toast-interface";
|
||||
export { ToggleChangeEventDetail } from "./components/toggle/toggle-interface";
|
||||
export namespace Components {
|
||||
interface IonAccordion {
|
||||
@@ -943,7 +943,7 @@ export namespace Components {
|
||||
*/
|
||||
"size": 'cover' | 'fixed';
|
||||
/**
|
||||
* A callback used to format the header text that shows how many dates are selected. Only used if there are 0 or more than 1 selected (i.e. unused for exactly 1). By default, the header text is set to "numberOfDates days".
|
||||
* A callback used to format the header text that shows how many dates are selected. Only used if there are 0 or more than 1 selected (i.e. unused for exactly 1). By default, the header text is set to "numberOfDates days". See https://ionicframework.com/docs/troubleshooting/runtime#accessing-this if you need to access `this` from within the callback.
|
||||
*/
|
||||
"titleSelectedDatesFormatter"?: TitleSelectedDatesFormatter;
|
||||
/**
|
||||
@@ -1162,7 +1162,7 @@ export namespace Components {
|
||||
*/
|
||||
"autocorrect": 'on' | 'off';
|
||||
/**
|
||||
* This Boolean attribute lets you specify that a form control should have input focus when the page loads.
|
||||
* Sets the [`autofocus` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus) on the native input element. This may not be sufficient for the element to be focused on page load. See [managing focus](/docs/developing/managing-focus) for more information.
|
||||
*/
|
||||
"autofocus": boolean;
|
||||
/**
|
||||
@@ -1182,7 +1182,7 @@ export namespace Components {
|
||||
*/
|
||||
"counter": boolean;
|
||||
/**
|
||||
* A callback used to format the counter text. By default the counter text is set to "itemLength / maxLength".
|
||||
* A callback used to format the counter text. By default the counter text is set to "itemLength / maxLength". See https://ionicframework.com/docs/troubleshooting/runtime#accessing-this if you need to access `this` from within the callback.
|
||||
*/
|
||||
"counterFormatter"?: (inputLength: number, maxLength: number) => string;
|
||||
/**
|
||||
@@ -1274,7 +1274,7 @@ export namespace Components {
|
||||
*/
|
||||
"required": boolean;
|
||||
/**
|
||||
* Sets focus on the native `input` in `ion-input`. Use this method instead of the global `input.focus()`. Developers who wish to focus an input when a page enters should call `setFocus()` in the `ionViewDidEnter()` lifecycle method. Developers who wish to focus an input when an overlay is presented should call `setFocus` after `didPresent` has resolved.
|
||||
* Sets focus on the native `input` in `ion-input`. Use this method instead of the global `input.focus()`. Developers who wish to focus an input when a page enters should call `setFocus()` in the `ionViewDidEnter()` lifecycle method. Developers who wish to focus an input when an overlay is presented should call `setFocus` after `didPresent` has resolved. See [managing focus](/docs/developing/managing-focus) for more information.
|
||||
*/
|
||||
"setFocus": () => Promise<void>;
|
||||
/**
|
||||
@@ -1701,7 +1701,7 @@ export namespace Components {
|
||||
*/
|
||||
"breakpoints"?: number[];
|
||||
/**
|
||||
* Determines whether or not a modal can dismiss when calling the `dismiss` method. If the value is `true` or the value's function returns `true`, the modal will close when trying to dismiss. If the value is `false` or the value's function returns `false`, the modal will not close when trying to dismiss.
|
||||
* Determines whether or not a modal can dismiss when calling the `dismiss` method. If the value is `true` or the value's function returns `true`, the modal will close when trying to dismiss. If the value is `false` or the value's function returns `false`, the modal will not close when trying to dismiss. See https://ionicframework.com/docs/troubleshooting/runtime#accessing-this if you need to access `this` from within the callback.
|
||||
*/
|
||||
"canDismiss": boolean | ((data?: any, role?: string) => Promise<boolean>);
|
||||
/**
|
||||
@@ -2269,6 +2269,10 @@ export namespace Components {
|
||||
* If `true`, the radios can be deselected.
|
||||
*/
|
||||
"allowEmptySelection": boolean;
|
||||
/**
|
||||
* This property allows developers to specify a custom function or property name for comparing objects when determining the selected option in the ion-radio-group. When not specified, the default behavior will use strict equality (===) for comparison.
|
||||
*/
|
||||
"compareWith"?: string | RadioGroupCompareFn | null;
|
||||
/**
|
||||
* The name of the control, which is submitted with the form data.
|
||||
*/
|
||||
@@ -2332,7 +2336,7 @@ export namespace Components {
|
||||
*/
|
||||
"pin": boolean;
|
||||
/**
|
||||
* A callback used to format the pin text. By default the pin text is set to `Math.round(value)`.
|
||||
* A callback used to format the pin text. By default the pin text is set to `Math.round(value)`. See https://ionicframework.com/docs/troubleshooting/runtime#accessing-this if you need to access `this` from within the callback.
|
||||
*/
|
||||
"pinFormatter": PinFormatter;
|
||||
/**
|
||||
@@ -2373,6 +2377,10 @@ export namespace Components {
|
||||
* A number representing how far down the user has pulled. The number `0` represents the user hasn't pulled down at all. The number `1`, and anything greater than `1`, represents that the user has pulled far enough down that when they let go then the refresh will happen. If they let go and the number is less than `1`, then the refresh will not happen, and the content will return to it's original position.
|
||||
*/
|
||||
"getProgress": () => Promise<number>;
|
||||
/**
|
||||
* The mode determines which platform styles to use.
|
||||
*/
|
||||
"mode"?: "ios" | "md";
|
||||
/**
|
||||
* How much to multiply the pull speed by. To slow the pull animation down, pass a number less than `1`. To speed up the pull, pass a number greater than `1`. The default value is `1` which is equal to the speed of the cursor. If a negative value is passed in, the factor will be `1` instead. For example: If the value passed is `1.2` and the content is dragged by `10` pixels, instead of `10` pixels the content will be pulled by `12` pixels (an increase of 20 percent). If the value passed is `0.8`, the dragged amount will be `8` pixels, less than the amount the cursor has moved. Does not apply when the refresher content uses a spinner, enabling the native refresher.
|
||||
*/
|
||||
@@ -2601,7 +2609,7 @@ export namespace Components {
|
||||
*/
|
||||
"searchIcon"?: string;
|
||||
/**
|
||||
* Sets focus on the native `input` in `ion-searchbar`. Use this method instead of the global `input.focus()`. Developers who wish to focus an input when a page enters should call `setFocus()` in the `ionViewDidEnter()` lifecycle method. Developers who wish to focus an input when an overlay is presented should call `setFocus` after `didPresent` has resolved.
|
||||
* Sets focus on the native `input` in `ion-searchbar`. Use this method instead of the global `input.focus()`. Developers who wish to focus an input when a page enters should call `setFocus()` in the `ionViewDidEnter()` lifecycle method. Developers who wish to focus an input when an overlay is presented should call `setFocus` after `didPresent` has resolved. See [managing focus](/docs/developing/managing-focus) for more information.
|
||||
*/
|
||||
"setFocus": () => Promise<void>;
|
||||
/**
|
||||
@@ -2688,7 +2696,7 @@ export namespace Components {
|
||||
*/
|
||||
"color"?: Color;
|
||||
/**
|
||||
* A property name or function used to compare object values
|
||||
* This property allows developers to specify a custom function or property name for comparing objects when determining the selected option in the ion-select. When not specified, the default behavior will use strict equality (===) for comparison.
|
||||
*/
|
||||
"compareWith"?: string | SelectCompareFn | null;
|
||||
/**
|
||||
@@ -2950,7 +2958,7 @@ export namespace Components {
|
||||
*/
|
||||
"autocapitalize": string;
|
||||
/**
|
||||
* This Boolean attribute lets you specify that a form control should have input focus when the page loads.
|
||||
* Sets the [`autofocus` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus) on the native input element. This may not be sufficient for the element to be focused on page load. See [managing focus](/docs/developing/managing-focus) for more information.
|
||||
*/
|
||||
"autofocus": boolean;
|
||||
/**
|
||||
@@ -2970,7 +2978,7 @@ export namespace Components {
|
||||
*/
|
||||
"counter": boolean;
|
||||
/**
|
||||
* A callback used to format the counter text. By default the counter text is set to "itemLength / maxLength".
|
||||
* A callback used to format the counter text. By default the counter text is set to "itemLength / maxLength". See https://ionicframework.com/docs/troubleshooting/runtime#accessing-this if you need to access `this` from within the callback.
|
||||
*/
|
||||
"counterFormatter"?: (inputLength: number, maxLength: number) => string;
|
||||
/**
|
||||
@@ -3050,7 +3058,7 @@ export namespace Components {
|
||||
*/
|
||||
"rows"?: number;
|
||||
/**
|
||||
* Sets focus on the native `textarea` in `ion-textarea`. Use this method instead of the global `textarea.focus()`.
|
||||
* Sets focus on the native `textarea` in `ion-textarea`. Use this method instead of the global `textarea.focus()`. See [managing focus](/docs/developing/managing-focus) for more information.
|
||||
*/
|
||||
"setFocus": () => Promise<void>;
|
||||
/**
|
||||
@@ -3172,6 +3180,10 @@ export namespace Components {
|
||||
* Present the toast overlay after it has been created.
|
||||
*/
|
||||
"present": () => Promise<void>;
|
||||
/**
|
||||
* If set to 'vertical', the Toast can be dismissed with a swipe gesture. The swipe direction is determined by the value of the `position` property: `top`: The Toast can be swiped up to dismiss. `bottom`: The Toast can be swiped down to dismiss. `middle`: The Toast can be swiped up or down to dismiss.
|
||||
*/
|
||||
"swipeGesture"?: ToastSwipeGestureDirection;
|
||||
/**
|
||||
* If `true`, the toast will be translucent. Only applies when the mode is `"ios"` and the device supports [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility).
|
||||
*/
|
||||
@@ -3390,6 +3402,10 @@ export interface IonSelectCustomEvent<T> extends CustomEvent<T> {
|
||||
detail: T;
|
||||
target: HTMLIonSelectElement;
|
||||
}
|
||||
export interface IonSkeletonTextCustomEvent<T> extends CustomEvent<T> {
|
||||
detail: T;
|
||||
target: HTMLIonSkeletonTextElement;
|
||||
}
|
||||
export interface IonSplitPaneCustomEvent<T> extends CustomEvent<T> {
|
||||
detail: T;
|
||||
target: HTMLIonSplitPaneElement;
|
||||
@@ -4404,7 +4420,18 @@ declare global {
|
||||
prototype: HTMLIonSelectPopoverElement;
|
||||
new (): HTMLIonSelectPopoverElement;
|
||||
};
|
||||
interface HTMLIonSkeletonTextElementEventMap {
|
||||
"ionStyle": StyleEventDetail;
|
||||
}
|
||||
interface HTMLIonSkeletonTextElement extends Components.IonSkeletonText, HTMLStencilElement {
|
||||
addEventListener<K extends keyof HTMLIonSkeletonTextElementEventMap>(type: K, listener: (this: HTMLIonSkeletonTextElement, ev: IonSkeletonTextCustomEvent<HTMLIonSkeletonTextElementEventMap[K]>) => any, options?: boolean | AddEventListenerOptions): void;
|
||||
addEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
|
||||
addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
|
||||
addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
|
||||
removeEventListener<K extends keyof HTMLIonSkeletonTextElementEventMap>(type: K, listener: (this: HTMLIonSkeletonTextElement, ev: IonSkeletonTextCustomEvent<HTMLIonSkeletonTextElementEventMap[K]>) => any, options?: boolean | EventListenerOptions): void;
|
||||
removeEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: Document, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
|
||||
removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
|
||||
removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
|
||||
}
|
||||
var HTMLIonSkeletonTextElement: {
|
||||
prototype: HTMLIonSkeletonTextElement;
|
||||
@@ -5623,7 +5650,7 @@ declare namespace LocalJSX {
|
||||
*/
|
||||
"size"?: 'cover' | 'fixed';
|
||||
/**
|
||||
* A callback used to format the header text that shows how many dates are selected. Only used if there are 0 or more than 1 selected (i.e. unused for exactly 1). By default, the header text is set to "numberOfDates days".
|
||||
* A callback used to format the header text that shows how many dates are selected. Only used if there are 0 or more than 1 selected (i.e. unused for exactly 1). By default, the header text is set to "numberOfDates days". See https://ionicframework.com/docs/troubleshooting/runtime#accessing-this if you need to access `this` from within the callback.
|
||||
*/
|
||||
"titleSelectedDatesFormatter"?: TitleSelectedDatesFormatter;
|
||||
/**
|
||||
@@ -5854,7 +5881,7 @@ declare namespace LocalJSX {
|
||||
*/
|
||||
"autocorrect"?: 'on' | 'off';
|
||||
/**
|
||||
* This Boolean attribute lets you specify that a form control should have input focus when the page loads.
|
||||
* Sets the [`autofocus` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus) on the native input element. This may not be sufficient for the element to be focused on page load. See [managing focus](/docs/developing/managing-focus) for more information.
|
||||
*/
|
||||
"autofocus"?: boolean;
|
||||
/**
|
||||
@@ -5874,7 +5901,7 @@ declare namespace LocalJSX {
|
||||
*/
|
||||
"counter"?: boolean;
|
||||
/**
|
||||
* A callback used to format the counter text. By default the counter text is set to "itemLength / maxLength".
|
||||
* A callback used to format the counter text. By default the counter text is set to "itemLength / maxLength". See https://ionicframework.com/docs/troubleshooting/runtime#accessing-this if you need to access `this` from within the callback.
|
||||
*/
|
||||
"counterFormatter"?: (inputLength: number, maxLength: number) => string;
|
||||
/**
|
||||
@@ -6405,7 +6432,7 @@ declare namespace LocalJSX {
|
||||
*/
|
||||
"breakpoints"?: number[];
|
||||
/**
|
||||
* Determines whether or not a modal can dismiss when calling the `dismiss` method. If the value is `true` or the value's function returns `true`, the modal will close when trying to dismiss. If the value is `false` or the value's function returns `false`, the modal will not close when trying to dismiss.
|
||||
* Determines whether or not a modal can dismiss when calling the `dismiss` method. If the value is `true` or the value's function returns `true`, the modal will close when trying to dismiss. If the value is `false` or the value's function returns `false`, the modal will not close when trying to dismiss. See https://ionicframework.com/docs/troubleshooting/runtime#accessing-this if you need to access `this` from within the callback.
|
||||
*/
|
||||
"canDismiss"?: boolean | ((data?: any, role?: string) => Promise<boolean>);
|
||||
/**
|
||||
@@ -6934,6 +6961,10 @@ declare namespace LocalJSX {
|
||||
* If `true`, the radios can be deselected.
|
||||
*/
|
||||
"allowEmptySelection"?: boolean;
|
||||
/**
|
||||
* This property allows developers to specify a custom function or property name for comparing objects when determining the selected option in the ion-radio-group. When not specified, the default behavior will use strict equality (===) for comparison.
|
||||
*/
|
||||
"compareWith"?: string | RadioGroupCompareFn | null;
|
||||
/**
|
||||
* The name of the control, which is submitted with the form data.
|
||||
*/
|
||||
@@ -7033,7 +7064,7 @@ declare namespace LocalJSX {
|
||||
*/
|
||||
"pin"?: boolean;
|
||||
/**
|
||||
* A callback used to format the pin text. By default the pin text is set to `Math.round(value)`.
|
||||
* A callback used to format the pin text. By default the pin text is set to `Math.round(value)`. See https://ionicframework.com/docs/troubleshooting/runtime#accessing-this if you need to access `this` from within the callback.
|
||||
*/
|
||||
"pinFormatter"?: PinFormatter;
|
||||
/**
|
||||
@@ -7062,6 +7093,10 @@ declare namespace LocalJSX {
|
||||
* If `true`, the refresher will be hidden.
|
||||
*/
|
||||
"disabled"?: boolean;
|
||||
/**
|
||||
* The mode determines which platform styles to use.
|
||||
*/
|
||||
"mode"?: "ios" | "md";
|
||||
/**
|
||||
* Emitted while the user is pulling down the content and exposing the refresher.
|
||||
*/
|
||||
@@ -7416,7 +7451,7 @@ declare namespace LocalJSX {
|
||||
*/
|
||||
"color"?: Color;
|
||||
/**
|
||||
* A property name or function used to compare object values
|
||||
* This property allows developers to specify a custom function or property name for comparing objects when determining the selected option in the ion-select. When not specified, the default behavior will use strict equality (===) for comparison.
|
||||
*/
|
||||
"compareWith"?: string | SelectCompareFn | null;
|
||||
/**
|
||||
@@ -7553,6 +7588,10 @@ declare namespace LocalJSX {
|
||||
* If `true`, the skeleton text will animate.
|
||||
*/
|
||||
"animated"?: boolean;
|
||||
/**
|
||||
* Emitted when the styles change.
|
||||
*/
|
||||
"onIonStyle"?: (event: IonSkeletonTextCustomEvent<StyleEventDetail>) => void;
|
||||
}
|
||||
interface IonSpinner {
|
||||
/**
|
||||
@@ -7699,7 +7738,7 @@ declare namespace LocalJSX {
|
||||
*/
|
||||
"autocapitalize"?: string;
|
||||
/**
|
||||
* This Boolean attribute lets you specify that a form control should have input focus when the page loads.
|
||||
* Sets the [`autofocus` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus) on the native input element. This may not be sufficient for the element to be focused on page load. See [managing focus](/docs/developing/managing-focus) for more information.
|
||||
*/
|
||||
"autofocus"?: boolean;
|
||||
/**
|
||||
@@ -7719,7 +7758,7 @@ declare namespace LocalJSX {
|
||||
*/
|
||||
"counter"?: boolean;
|
||||
/**
|
||||
* A callback used to format the counter text. By default the counter text is set to "itemLength / maxLength".
|
||||
* A callback used to format the counter text. By default the counter text is set to "itemLength / maxLength". See https://ionicframework.com/docs/troubleshooting/runtime#accessing-this if you need to access `this` from within the callback.
|
||||
*/
|
||||
"counterFormatter"?: (inputLength: number, maxLength: number) => string;
|
||||
/**
|
||||
@@ -7951,6 +7990,10 @@ declare namespace LocalJSX {
|
||||
* The element to anchor the toast's position to. Can be set as a direct reference or the ID of the element. With `position="bottom"`, the toast will sit above the chosen element. With `position="top"`, the toast will sit below the chosen element. With `position="middle"`, the value of `positionAnchor` is ignored.
|
||||
*/
|
||||
"positionAnchor"?: HTMLElement | string;
|
||||
/**
|
||||
* If set to 'vertical', the Toast can be dismissed with a swipe gesture. The swipe direction is determined by the value of the `position` property: `top`: The Toast can be swiped up to dismiss. `bottom`: The Toast can be swiped down to dismiss. `middle`: The Toast can be swiped up or down to dismiss.
|
||||
*/
|
||||
"swipeGesture"?: ToastSwipeGestureDirection;
|
||||
/**
|
||||
* If `true`, the toast will be translucent. Only applies when the mode is `"ios"` and the device supports [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility).
|
||||
*/
|
||||
|
||||
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 5.7 KiB |
@@ -90,13 +90,6 @@
|
||||
}
|
||||
|
||||
.label-text-wrapper {
|
||||
/**
|
||||
* This ensures that double tapping this text
|
||||
* clicks the <label> and focuses the checkbox
|
||||
* when a screen reader is enabled.
|
||||
*/
|
||||
pointer-events: none;
|
||||
|
||||
text-overflow: ellipsis;
|
||||
|
||||
white-space: nowrap;
|
||||
@@ -173,7 +166,6 @@ input {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
|
||||
// Justify Content
|
||||
// ---------------------------------------------
|
||||
|
||||
@@ -200,7 +192,6 @@ input {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
||||
// Label Placement - Start
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
@@ -221,7 +212,6 @@ input {
|
||||
@include margin(null, $form-control-label-margin, null, 0);
|
||||
}
|
||||
|
||||
|
||||
// Label Placement - End
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
@@ -242,7 +232,6 @@ input {
|
||||
@include margin(null, 0, null, $form-control-label-margin);
|
||||
}
|
||||
|
||||
|
||||
// Label Placement - Fixed
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
@@ -317,7 +306,6 @@ input {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
||||
// Disabled Checkbox
|
||||
// ---------------------------------------------
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import type { CheckboxChangeEventDetail } from './checkbox-interface';
|
||||
* @slot - The label text to associate with the checkbox. Use the "labelPlacement" property to control where the label is placed relative to the checkbox.
|
||||
*
|
||||
* @part container - The container for the checkbox mark.
|
||||
* @part label - The label text describing the checkbox.
|
||||
* @part mark - The checkmark used to indicate the checked state.
|
||||
*/
|
||||
@Component({
|
||||
@@ -164,6 +165,8 @@ export class Checkbox implements ComponentInterface {
|
||||
private emitStyle() {
|
||||
const style: StyleEventDetail = {
|
||||
'interactive-disabled': this.disabled,
|
||||
// TODO(FW-3100): remove this
|
||||
legacy: !!this.legacy,
|
||||
};
|
||||
|
||||
// TODO(FW-3100): remove this
|
||||
@@ -282,6 +285,7 @@ export class Checkbox implements ComponentInterface {
|
||||
'label-text-wrapper': true,
|
||||
'label-text-wrapper-hidden': el.textContent === '',
|
||||
}}
|
||||
part="label"
|
||||
>
|
||||
<slot></slot>
|
||||
</div>
|
||||
|
||||
@@ -2,6 +2,23 @@ import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { Checkbox } from '../checkbox';
|
||||
|
||||
describe('ion-checkbox: shadow parts', () => {
|
||||
it('should render the checkbox with shadow parts', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Checkbox],
|
||||
html: `
|
||||
<ion-checkbox>Checkbox</ion-checkbox>
|
||||
`,
|
||||
});
|
||||
|
||||
const checkbox = page.body.querySelector('ion-checkbox')!;
|
||||
|
||||
expect(checkbox).toHaveShadowPart('container');
|
||||
expect(checkbox).toHaveShadowPart('label');
|
||||
expect(checkbox).toHaveShadowPart('mark');
|
||||
});
|
||||
});
|
||||
|
||||
describe('ion-checkbox: disabled', () => {
|
||||
it('clicking disabled checkbox should not toggle checked state', async () => {
|
||||
const page = await newSpecPage({
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
@import "./chip";
|
||||
@use "./chip" as b;
|
||||
@import "./chip.vars";
|
||||
|
||||
@include b.base;
|
||||
|
||||
:host {
|
||||
/**
|
||||
* Main content should be prioritized on iOS,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
@import "./chip";
|
||||
@import "./chip.vars";
|
||||
|
||||
:host {
|
||||
font-size: $chip-base-font-size-rem;
|
||||
@mixin md2 {
|
||||
:host {
|
||||
font-size: $chip-base-font-size-rem;
|
||||
}
|
||||
}
|
||||
|
||||
9
core/src/components/chip/chip.md2.flag.scss
Normal file
@@ -0,0 +1,9 @@
|
||||
@use 'chip.md.scss' as c;
|
||||
@use 'chip.scss' as b;
|
||||
|
||||
@use '../../utils/md-controller/index.scss' as controller;
|
||||
|
||||
@if controller.$md2 {
|
||||
@include b.base;
|
||||
@include c.md2;
|
||||
}
|
||||
9
core/src/components/chip/chip.md3.flag.scss
Normal file
@@ -0,0 +1,9 @@
|
||||
@use 'chip.md3.scss' as c;
|
||||
@use 'chip.scss' as b;
|
||||
|
||||
@use '../../utils/md-controller/index.scss' as controller;
|
||||
|
||||
@if controller.$md3 {
|
||||
@include b.base;
|
||||
@include c.md3;
|
||||
}
|
||||
7
core/src/components/chip/chip.md3.scss
Normal file
@@ -0,0 +1,7 @@
|
||||
@import "./chip.vars";
|
||||
|
||||
@mixin md3 {
|
||||
:host {
|
||||
background: green;
|
||||
}
|
||||
}
|
||||
@@ -1,156 +1,158 @@
|
||||
@import "../../themes/ionic.globals";
|
||||
@import "./chip.vars";
|
||||
|
||||
:host {
|
||||
/**
|
||||
* @prop --background: Background of the chip
|
||||
* @prop --color: Color of the chip
|
||||
*/
|
||||
--background: #{rgba($text-color-rgb, 0.12)};
|
||||
--color: #{rgba($text-color-rgb, 0.87)};
|
||||
@mixin base {
|
||||
:host {
|
||||
/**
|
||||
* @prop --background: Background of the chip
|
||||
* @prop --color: Color of the chip
|
||||
*/
|
||||
--background: #{rgba($text-color-rgb, 0.12)};
|
||||
--color: #{rgba($text-color-rgb, 0.87)};
|
||||
|
||||
@include border-radius(16px);
|
||||
@include font-smoothing();
|
||||
@include margin(4px);
|
||||
@include padding(6px, 12px);
|
||||
@include border-radius(16px);
|
||||
@include font-smoothing();
|
||||
@include margin(4px);
|
||||
@include padding(6px, 12px);
|
||||
|
||||
display: inline-flex;
|
||||
display: inline-flex;
|
||||
|
||||
position: relative;
|
||||
position: relative;
|
||||
|
||||
align-items: center;
|
||||
align-items: center;
|
||||
|
||||
min-height: 32px;
|
||||
min-height: 32px;
|
||||
|
||||
background: var(--background);
|
||||
color: var(--color);
|
||||
background: var(--background);
|
||||
color: var(--color);
|
||||
|
||||
font-family: $font-family-base;
|
||||
font-family: $font-family-base;
|
||||
|
||||
cursor: pointer;
|
||||
cursor: pointer;
|
||||
|
||||
overflow: hidden;
|
||||
overflow: hidden;
|
||||
|
||||
vertical-align: middle;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:host(.chip-disabled) {
|
||||
cursor: default;
|
||||
opacity: 0.4;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
// Chip Colors
|
||||
// ---------------------------------------------
|
||||
|
||||
:host(.ion-color) {
|
||||
background: current-color(base, 0.08);
|
||||
color: current-color(shade);
|
||||
}
|
||||
|
||||
:host(.ion-color:focus) {
|
||||
background: current-color(base, 0.12);
|
||||
}
|
||||
|
||||
:host(.ion-color.ion-activated) {
|
||||
background: current-color(base, 0.16);
|
||||
}
|
||||
|
||||
// Outline Chip
|
||||
// ---------------------------------------------
|
||||
|
||||
:host(.chip-outline) {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
:host(.chip-outline) {
|
||||
border-color: rgba($text-color-rgb, 0.32);
|
||||
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
:host(.chip-outline.ion-color) {
|
||||
border-color: current-color(base, 0.32);
|
||||
}
|
||||
|
||||
:host(.chip-outline:not(.ion-color):focus) {
|
||||
background: rgba($text-color-rgb, 0.04);
|
||||
}
|
||||
|
||||
:host(.chip-outline.ion-activated:not(.ion-color)) {
|
||||
background: rgba($text-color-rgb, 0.08);
|
||||
}
|
||||
|
||||
// Chip Icon
|
||||
// ---------------------------------------------
|
||||
|
||||
::slotted(ion-icon) {
|
||||
font-size: $chip-icon-size;
|
||||
}
|
||||
|
||||
:host(:not(.ion-color)) ::slotted(ion-icon) {
|
||||
color: rgba($text-color-rgb, 0.54);
|
||||
}
|
||||
|
||||
::slotted(ion-icon:first-child) {
|
||||
@include margin(-4px, 8px, -4px, -4px);
|
||||
}
|
||||
|
||||
::slotted(ion-icon:last-child) {
|
||||
@include margin(-4px, -4px, -4px, 8px);
|
||||
}
|
||||
|
||||
// Chip Avatar
|
||||
// ---------------------------------------------
|
||||
|
||||
::slotted(ion-avatar) {
|
||||
flex-shrink: 0;
|
||||
|
||||
width: $chip-avatar-size;
|
||||
height: $chip-avatar-size;
|
||||
}
|
||||
|
||||
::slotted(ion-avatar:first-child) {
|
||||
@include margin(-4px, 8px, -4px, -8px);
|
||||
}
|
||||
|
||||
::slotted(ion-avatar:last-child) {
|
||||
@include margin(-4px, -8px, -4px, 8px);
|
||||
}
|
||||
|
||||
// Chip: Focus
|
||||
// ---------------------------------------------
|
||||
|
||||
:host(:focus) {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
:host(:focus) {
|
||||
--background: #{rgba($text-color-rgb, 0.16)};
|
||||
}
|
||||
|
||||
// Chip: Activated
|
||||
// ---------------------------------------------
|
||||
|
||||
:host(.ion-activated) {
|
||||
--background: #{rgba($text-color-rgb, 0.2)};
|
||||
}
|
||||
|
||||
// Chip: Hover
|
||||
// ---------------------------------------------
|
||||
|
||||
@media (any-hover: hover) {
|
||||
:host(:hover) {
|
||||
--background: #{rgba($text-color-rgb, 0.16)};
|
||||
vertical-align: middle;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:host(.ion-color:hover) {
|
||||
:host(.chip-disabled) {
|
||||
cursor: default;
|
||||
opacity: 0.4;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
// Chip Colors
|
||||
// ---------------------------------------------
|
||||
|
||||
:host(.ion-color) {
|
||||
background: current-color(base, 0.08);
|
||||
color: current-color(shade);
|
||||
}
|
||||
|
||||
:host(.ion-color:focus) {
|
||||
background: current-color(base, 0.12);
|
||||
}
|
||||
|
||||
:host(.chip-outline:not(.ion-color):hover) {
|
||||
:host(.ion-color.ion-activated) {
|
||||
background: current-color(base, 0.16);
|
||||
}
|
||||
|
||||
// Outline Chip
|
||||
// ---------------------------------------------
|
||||
|
||||
:host(.chip-outline) {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
:host(.chip-outline) {
|
||||
border-color: rgba($text-color-rgb, 0.32);
|
||||
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
:host(.chip-outline.ion-color) {
|
||||
border-color: current-color(base, 0.32);
|
||||
}
|
||||
|
||||
:host(.chip-outline:not(.ion-color):focus) {
|
||||
background: rgba($text-color-rgb, 0.04);
|
||||
}
|
||||
|
||||
:host(.chip-outline.ion-activated:not(.ion-color)) {
|
||||
background: rgba($text-color-rgb, 0.08);
|
||||
}
|
||||
|
||||
// Chip Icon
|
||||
// ---------------------------------------------
|
||||
|
||||
::slotted(ion-icon) {
|
||||
font-size: $chip-icon-size;
|
||||
}
|
||||
|
||||
:host(:not(.ion-color)) ::slotted(ion-icon) {
|
||||
color: rgba($text-color-rgb, 0.54);
|
||||
}
|
||||
|
||||
::slotted(ion-icon:first-child) {
|
||||
@include margin(-4px, 8px, -4px, -4px);
|
||||
}
|
||||
|
||||
::slotted(ion-icon:last-child) {
|
||||
@include margin(-4px, -4px, -4px, 8px);
|
||||
}
|
||||
|
||||
// Chip Avatar
|
||||
// ---------------------------------------------
|
||||
|
||||
::slotted(ion-avatar) {
|
||||
flex-shrink: 0;
|
||||
|
||||
width: $chip-avatar-size;
|
||||
height: $chip-avatar-size;
|
||||
}
|
||||
|
||||
::slotted(ion-avatar:first-child) {
|
||||
@include margin(-4px, 8px, -4px, -8px);
|
||||
}
|
||||
|
||||
::slotted(ion-avatar:last-child) {
|
||||
@include margin(-4px, -8px, -4px, 8px);
|
||||
}
|
||||
|
||||
// Chip: Focus
|
||||
// ---------------------------------------------
|
||||
|
||||
:host(:focus) {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
:host(:focus) {
|
||||
--background: #{rgba($text-color-rgb, 0.16)};
|
||||
}
|
||||
|
||||
// Chip: Activated
|
||||
// ---------------------------------------------
|
||||
|
||||
:host(.ion-activated) {
|
||||
--background: #{rgba($text-color-rgb, 0.2)};
|
||||
}
|
||||
|
||||
// Chip: Hover
|
||||
// ---------------------------------------------
|
||||
|
||||
@media (any-hover: hover) {
|
||||
:host(:hover) {
|
||||
--background: #{rgba($text-color-rgb, 0.16)};
|
||||
}
|
||||
|
||||
:host(.ion-color:hover) {
|
||||
background: current-color(base, 0.12);
|
||||
}
|
||||
|
||||
:host(.chip-outline:not(.ion-color):hover) {
|
||||
background: rgba($text-color-rgb, 0.04);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,14 @@ import { createColorClasses } from '@utils/theme';
|
||||
import { getIonMode } from '../../global/ionic-global';
|
||||
import type { Color } from '../../interface';
|
||||
|
||||
/**
|
||||
styleUrls: {
|
||||
ios: 'chip.ios.scss',
|
||||
md: 'chip.md2.scss',
|
||||
md3: Build.isDev ? 'chip.md3.scss' : undefined
|
||||
},
|
||||
*/
|
||||
|
||||
/**
|
||||
* @virtualProp {"ios" | "md"} mode - The mode determines which platform styles to use.
|
||||
*/
|
||||
@@ -12,7 +20,8 @@ import type { Color } from '../../interface';
|
||||
tag: 'ion-chip',
|
||||
styleUrls: {
|
||||
ios: 'chip.ios.scss',
|
||||
md: 'chip.md.scss',
|
||||
md: 'chip.md2.flag.scss',
|
||||
md3: 'chip.md3.flag.scss'
|
||||
},
|
||||
shadow: true,
|
||||
})
|
||||
|
||||
@@ -23,82 +23,19 @@
|
||||
</ion-header>
|
||||
|
||||
<ion-content class="ion-padding" id="content" style="text-align: center">
|
||||
<h2>Basic Chips</h2>
|
||||
<h2>Default Chip</h2>
|
||||
<ion-chip>
|
||||
<ion-label>Default</ion-label>
|
||||
</ion-chip>
|
||||
<ion-chip style="border-radius: 4px">
|
||||
<ion-label>Border Radius</ion-label>
|
||||
</ion-chip>
|
||||
<ion-chip>
|
||||
<ion-icon name="checkmark-circle"></ion-icon>
|
||||
<ion-label>With Icon</ion-label>
|
||||
</ion-chip>
|
||||
<ion-chip>
|
||||
<ion-avatar>
|
||||
<img
|
||||
src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48cGF0aCBmaWxsPSIjYzVkYmZmIiBkPSJNMCAwaDUxMnY1MTJIMHoiLz48cGF0aCBkPSJNMjU2IDMwNGM2MS42IDAgMTEyLTUwLjQgMTEyLTExMlMzMTcuNiA4MCAyNTYgODBzLTExMiA1MC40LTExMiAxMTIgNTAuNCAxMTIgMTEyIDExMnptMCA0MGMtNzQuMiAwLTIyNCAzNy44LTIyNCAxMTJ2NTZoNDQ4di01NmMwLTc0LjItMTQ5LjgtMTEyLTIyNC0xMTJ6IiBmaWxsPSIjODJhZWZmIi8+PC9zdmc+"
|
||||
/>
|
||||
</ion-avatar>
|
||||
<ion-label>With Avatar</ion-label>
|
||||
</ion-chip>
|
||||
<ion-chip>
|
||||
<ion-avatar>
|
||||
<img
|
||||
src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48cGF0aCBmaWxsPSIjYzVkYmZmIiBkPSJNMCAwaDUxMnY1MTJIMHoiLz48cGF0aCBkPSJNMjU2IDMwNGM2MS42IDAgMTEyLTUwLjQgMTEyLTExMlMzMTcuNiA4MCAyNTYgODBzLTExMiA1MC40LTExMiAxMTIgNTAuNCAxMTIgMTEyIDExMnptMCA0MGMtNzQuMiAwLTIyNCAzNy44LTIyNCAxMTJ2NTZoNDQ4di01NmMwLTc0LjItMTQ5LjgtMTEyLTIyNC0xMTJ6IiBmaWxsPSIjODJhZWZmIi8+PC9zdmc+"
|
||||
/>
|
||||
</ion-avatar>
|
||||
<ion-label>With Icon and Avatar</ion-label>
|
||||
<ion-icon name="close-circle"></ion-icon>
|
||||
|
||||
<h2>MD2 Chip</h2>
|
||||
<ion-chip mode="md">
|
||||
<ion-label>MD2</ion-label>
|
||||
</ion-chip>
|
||||
|
||||
<h2>Color Chips</h2>
|
||||
<ion-chip color="primary">
|
||||
<ion-label>Primary</ion-label>
|
||||
</ion-chip>
|
||||
<ion-chip color="danger">
|
||||
<ion-label>Danger</ion-label>
|
||||
</ion-chip>
|
||||
<ion-chip color="tertiary">
|
||||
<ion-icon name="checkmark-circle"></ion-icon>
|
||||
<ion-label>Tertiary with Icon</ion-label>
|
||||
</ion-chip>
|
||||
<ion-chip color="primary">
|
||||
<ion-avatar>
|
||||
<img
|
||||
src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48cGF0aCBmaWxsPSIjYzVkYmZmIiBkPSJNMCAwaDUxMnY1MTJIMHoiLz48cGF0aCBkPSJNMjU2IDMwNGM2MS42IDAgMTEyLTUwLjQgMTEyLTExMlMzMTcuNiA4MCAyNTYgODBzLTExMiA1MC40LTExMiAxMTIgNTAuNCAxMTIgMTEyIDExMnptMCA0MGMtNzQuMiAwLTIyNCAzNy44LTIyNCAxMTJ2NTZoNDQ4di01NmMwLTc0LjItMTQ5LjgtMTEyLTIyNC0xMTJ6IiBmaWxsPSIjODJhZWZmIi8+PC9zdmc+"
|
||||
/>
|
||||
</ion-avatar>
|
||||
<ion-label>Primary with Avatar</ion-label>
|
||||
</ion-chip>
|
||||
<ion-chip color="success">
|
||||
<ion-label>Success with Icon</ion-label>
|
||||
<ion-icon name="calendar"></ion-icon>
|
||||
</ion-chip>
|
||||
|
||||
<h2>Outline Chips</h2>
|
||||
<ion-chip outline>
|
||||
<ion-label>Outline</ion-label>
|
||||
</ion-chip>
|
||||
<ion-chip outline color="danger">
|
||||
<ion-label>Danger Outline</ion-label>
|
||||
</ion-chip>
|
||||
<ion-chip outline color="secondary">
|
||||
<ion-icon name="checkmark-circle"></ion-icon>
|
||||
<ion-label>Secondary Outline with Icon</ion-label>
|
||||
</ion-chip>
|
||||
<ion-chip outline color="primary">
|
||||
<ion-label>Primary Outline with Avatar</ion-label>
|
||||
<ion-avatar>
|
||||
<img
|
||||
src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA1MTIgNTEyIj48cGF0aCBmaWxsPSIjYzVkYmZmIiBkPSJNMCAwaDUxMnY1MTJIMHoiLz48cGF0aCBkPSJNMjU2IDMwNGM2MS42IDAgMTEyLTUwLjQgMTEyLTExMlMzMTcuNiA4MCAyNTYgODBzLTExMiA1MC40LTExMiAxMTIgNTAuNCAxMTIgMTEyIDExMnptMCA0MGMtNzQuMiAwLTIyNCAzNy44LTIyNCAxMTJ2NTZoNDQ4di01NmMwLTc0LjItMTQ5LjgtMTEyLTIyNC0xMTJ6IiBmaWxsPSIjODJhZWZmIi8+PC9zdmc+"
|
||||
/>
|
||||
</ion-avatar>
|
||||
</ion-chip>
|
||||
<ion-chip outline>
|
||||
<ion-icon name="git-pull-request"></ion-icon>
|
||||
<ion-label>Outline with Icon and Avatar</ion-label>
|
||||
<ion-icon name="close-circle"></ion-icon>
|
||||
<h2>MD3 Chip</h2>
|
||||
<ion-chip mode="md3">
|
||||
<ion-label>MD3</ion-label>
|
||||
</ion-chip>
|
||||
</ion-content>
|
||||
</ion-app>
|
||||
|
||||
@@ -249,9 +249,11 @@
|
||||
|
||||
/**
|
||||
* Day that is selected and is today
|
||||
* should have white color.
|
||||
* should have base background color
|
||||
* with contrast text.
|
||||
*/
|
||||
:host .calendar-day.calendar-day-today.calendar-day-active {
|
||||
background: current-color(base);
|
||||
color: current-color(contrast);
|
||||
}
|
||||
|
||||
|
||||
@@ -267,6 +267,9 @@ ion-picker-column-internal {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
// TODO(FW-3547): the styles targeting ion-item
|
||||
// can be removed if we refactor datetime to
|
||||
// not use an ion-item
|
||||
:host .calendar-action-buttons ion-item,
|
||||
:host .calendar-action-buttons ion-button {
|
||||
--background: translucent;
|
||||
@@ -276,6 +279,11 @@ ion-picker-column-internal {
|
||||
display: flex;
|
||||
|
||||
align-items: center;
|
||||
|
||||
// Width is set to auto because it is set
|
||||
// to min-content elsewhere and we want to
|
||||
// prevent wrapping the label in datetime
|
||||
width: auto;
|
||||
}
|
||||
|
||||
:host .calendar-action-buttons ion-item ion-icon {
|
||||
|
||||
@@ -341,6 +341,9 @@ export class Datetime implements ComponentInterface {
|
||||
* dates are selected. Only used if there are 0 or more than 1
|
||||
* selected (i.e. unused for exactly 1). By default, the header
|
||||
* text is set to "numberOfDates days".
|
||||
*
|
||||
* See https://ionicframework.com/docs/troubleshooting/runtime#accessing-this
|
||||
* if you need to access `this` from within the callback.
|
||||
*/
|
||||
@Prop() titleSelectedDatesFormatter?: TitleSelectedDatesFormatter;
|
||||
|
||||
@@ -492,7 +495,7 @@ export class Datetime implements ComponentInterface {
|
||||
*/
|
||||
@Method()
|
||||
async confirm(closeOverlay = false) {
|
||||
const { isCalendarPicker, activeParts } = this;
|
||||
const { isCalendarPicker, activeParts, preferWheel, workingParts } = this;
|
||||
|
||||
/**
|
||||
* We only update the value if the presentation is not a calendar picker.
|
||||
@@ -500,7 +503,16 @@ export class Datetime implements ComponentInterface {
|
||||
if (activeParts !== undefined || !isCalendarPicker) {
|
||||
const activePartsIsArray = Array.isArray(activeParts);
|
||||
if (activePartsIsArray && activeParts.length === 0) {
|
||||
this.setValue(undefined);
|
||||
if (preferWheel) {
|
||||
/**
|
||||
* If the datetime is using a wheel picker, but the
|
||||
* active parts are empty, then the user has confirmed the
|
||||
* initial value (working parts) presented to them.
|
||||
*/
|
||||
this.setValue(convertDataToISO(workingParts));
|
||||
} else {
|
||||
this.setValue(undefined);
|
||||
}
|
||||
} else {
|
||||
this.setValue(convertDataToISO(activeParts));
|
||||
}
|
||||
@@ -1356,11 +1368,21 @@ export class Datetime implements ComponentInterface {
|
||||
const dayValues = (this.parsedDayValues = convertToArrayOfNumbers(this.dayValues));
|
||||
|
||||
const todayParts = (this.todayParts = parseDate(getToday())!);
|
||||
this.defaultParts = getClosestValidDate(todayParts, monthValues, dayValues, yearValues, hourValues, minuteValues);
|
||||
|
||||
this.processMinParts();
|
||||
this.processMaxParts();
|
||||
|
||||
this.defaultParts = getClosestValidDate({
|
||||
refParts: todayParts,
|
||||
monthValues,
|
||||
dayValues,
|
||||
yearValues,
|
||||
hourValues,
|
||||
minuteValues,
|
||||
minParts: this.minParts,
|
||||
maxParts: this.maxParts,
|
||||
});
|
||||
|
||||
this.processValue(this.value);
|
||||
|
||||
this.emitStyle();
|
||||
|
||||
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
@@ -458,6 +458,44 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
|
||||
});
|
||||
});
|
||||
|
||||
configs({ directions: ['ltr'] }).forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('datetime: today button rendering'), () => {
|
||||
test('should render today button correctly when selected', async ({ page }) => {
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'FW-5808',
|
||||
});
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-datetime presentation="date" value="2022-01-02"></ion-datetime>
|
||||
|
||||
<script>
|
||||
const mockToday = '2022-01-02T16:22';
|
||||
Date = class extends Date {
|
||||
constructor(...args) {
|
||||
if (args.length === 0) {
|
||||
super(mockToday)
|
||||
} else {
|
||||
super(...args);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const datetime = page.locator('ion-datetime');
|
||||
|
||||
await page.waitForSelector('.datetime-ready');
|
||||
|
||||
await expect(datetime.locator('.calendar-day-today')).toHaveScreenshot(
|
||||
screenshot(`datetime-today-calendar-button`)
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* The calendar day highlight does not render
|
||||
* on iOS and has the same behavior across directions.
|
||||
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 973 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 976 B |
|
After Width: | Height: | Size: 1.2 KiB |
@@ -16,6 +16,7 @@ import {
|
||||
subtractDays,
|
||||
addDays,
|
||||
validateParts,
|
||||
getClosestValidDate,
|
||||
} from '../utils/manipulation';
|
||||
|
||||
describe('addDays()', () => {
|
||||
@@ -558,3 +559,160 @@ describe('validateParts()', () => {
|
||||
).toEqual({ month: 1, day: 1, year: 2022, hour: 9, minute: 30 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('getClosestValidDate()', () => {
|
||||
it('should match a date with only month/day/year', () => {
|
||||
// October 10, 2023
|
||||
const refParts = { month: 10, day: 10, year: 2023 };
|
||||
// April 10, 2021
|
||||
const minParts = { month: 4, day: 10, year: 2021 };
|
||||
// September 14, 2021
|
||||
const maxParts = { month: 9, day: 14, year: 2021 };
|
||||
|
||||
// September 4, 2021
|
||||
const expected = { month: 9, day: 4, year: 2021, dayOfWeek: undefined };
|
||||
|
||||
expect(
|
||||
getClosestValidDate({
|
||||
refParts,
|
||||
monthValues: [2, 3, 7, 9, 10],
|
||||
dayValues: [4, 15, 25],
|
||||
yearValues: [2020, 2021, 2023],
|
||||
maxParts,
|
||||
minParts,
|
||||
})
|
||||
).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should match a date when the reference date is before the min', () => {
|
||||
// April 2, 2020 3:20 PM
|
||||
const refParts = { month: 4, day: 2, year: 2020, hour: 15, minute: 20 };
|
||||
// September 10, 2021 10:10 AM
|
||||
const minParts = { month: 9, day: 10, year: 2021, hour: 10, minute: 10 };
|
||||
// September 14, 2021 10:11 AM
|
||||
const maxParts = { month: 9, day: 14, year: 2021, hour: 10, minute: 11 };
|
||||
|
||||
// September 11, 2021 11:15 AM
|
||||
const expected = {
|
||||
year: 2021,
|
||||
day: 11,
|
||||
month: 9,
|
||||
hour: 11,
|
||||
minute: 15,
|
||||
ampm: 'am',
|
||||
dayOfWeek: undefined,
|
||||
};
|
||||
|
||||
expect(
|
||||
getClosestValidDate({
|
||||
refParts,
|
||||
monthValues: [4, 9, 11],
|
||||
dayValues: [11, 12, 13, 14],
|
||||
yearValues: [2020, 2021, 2023],
|
||||
hourValues: [9, 10, 11],
|
||||
minuteValues: [11, 12, 13, 14, 15],
|
||||
maxParts,
|
||||
minParts,
|
||||
})
|
||||
).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should match a date when the reference date is before the min', () => {
|
||||
// April 2, 2020 3:20 PM
|
||||
const refParts = { month: 4, day: 2, year: 2020, hour: 15, minute: 20 };
|
||||
// September 10, 2021 10:10 AM
|
||||
const minParts = { month: 9, day: 10, year: 2021, hour: 10, minute: 10 };
|
||||
// September 10, 2021 10:15 AM
|
||||
const maxParts = { month: 9, day: 10, year: 2021, hour: 10, minute: 15 };
|
||||
|
||||
// September 10, 2021 10:15 AM
|
||||
const expected = {
|
||||
month: 9,
|
||||
day: 10,
|
||||
year: 2021,
|
||||
hour: 10,
|
||||
minute: 15,
|
||||
ampm: 'am',
|
||||
dayOfWeek: undefined,
|
||||
};
|
||||
|
||||
expect(
|
||||
getClosestValidDate({
|
||||
refParts,
|
||||
monthValues: [4, 9, 11],
|
||||
dayValues: [10, 12, 13, 14],
|
||||
yearValues: [2020, 2021, 2023],
|
||||
hourValues: [9, 10, 11],
|
||||
minuteValues: [11, 12, 13, 14, 15],
|
||||
minParts,
|
||||
maxParts,
|
||||
})
|
||||
).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should only clamp minutes if within the same day and hour as min/max', () => {
|
||||
// April 2, 2020 9:16 AM
|
||||
const refParts = { month: 4, day: 2, year: 2020, hour: 9, minute: 16 };
|
||||
// September 10, 2021 10:10 AM
|
||||
const minParts = { month: 9, day: 10, year: 2021, hour: 10, minute: 10 };
|
||||
// September 10, 2021 11:15 AM
|
||||
const maxParts = { month: 9, day: 10, year: 2021, hour: 11, minute: 15 };
|
||||
|
||||
// September 10, 2021 10:16 AM
|
||||
const expected = {
|
||||
month: 9,
|
||||
day: 10,
|
||||
year: 2021,
|
||||
hour: 10,
|
||||
minute: 16,
|
||||
ampm: 'am',
|
||||
dayOfWeek: undefined,
|
||||
};
|
||||
|
||||
expect(
|
||||
getClosestValidDate({
|
||||
refParts,
|
||||
monthValues: [4, 9, 11],
|
||||
dayValues: [10, 12, 13, 14],
|
||||
yearValues: [2020, 2021, 2023],
|
||||
hourValues: [9, 10, 11],
|
||||
minuteValues: [10, 15, 16],
|
||||
minParts,
|
||||
maxParts,
|
||||
})
|
||||
).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should return the closest valid date after adjusting the allowed year', () => {
|
||||
// April 2, 2022 9:16 AM
|
||||
const refParts = { month: 4, day: 2, year: 2022, hour: 9, minute: 16 };
|
||||
// September 10, 2021 10:10 AM
|
||||
const minParts = { month: 9, day: 10, year: 2021, hour: 10, minute: 10 };
|
||||
// September 10, 2023 11:15 AM
|
||||
const maxParts = { month: 9, day: 10, year: 2023, hour: 11, minute: 15 };
|
||||
|
||||
// April 2, 2022 9:16 AM
|
||||
const expected = {
|
||||
month: 4,
|
||||
day: 2,
|
||||
year: 2022,
|
||||
hour: 9,
|
||||
minute: 16,
|
||||
ampm: 'am',
|
||||
dayOfWeek: undefined,
|
||||
};
|
||||
|
||||
expect(
|
||||
getClosestValidDate({
|
||||
refParts,
|
||||
monthValues: [4, 9, 11],
|
||||
dayValues: [2, 10, 12, 13, 14],
|
||||
yearValues: [2020, 2021, 2022, 2023],
|
||||
hourValues: [9, 10, 11],
|
||||
minuteValues: [10, 15, 16],
|
||||
minParts,
|
||||
maxParts,
|
||||
})
|
||||
).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { newSpecPage } from '@stencil/core/testing';
|
||||
|
||||
import { Datetime } from '../../datetime';
|
||||
|
||||
describe('datetime: preferWheel', () => {
|
||||
beforeEach(() => {
|
||||
const mockIntersectionObserver = jest.fn();
|
||||
mockIntersectionObserver.mockReturnValue({
|
||||
observe: () => null,
|
||||
unobserve: () => null,
|
||||
disconnect: () => null,
|
||||
});
|
||||
global.IntersectionObserver = mockIntersectionObserver;
|
||||
});
|
||||
|
||||
it('should select the working day when clicking the confirm button', async () => {
|
||||
const page = await newSpecPage({
|
||||
components: [Datetime],
|
||||
html: '<ion-datetime prefer-wheel="true" max="2021" show-default-buttons="true"></ion-datetime>',
|
||||
});
|
||||
|
||||
const datetime = page.body.querySelector<HTMLIonDatetimeElement>('ion-datetime')!;
|
||||
const confirmButton = datetime.shadowRoot!.querySelector<HTMLIonButtonElement>('#confirm-button')!;
|
||||
|
||||
confirmButton.click();
|
||||
|
||||
await page.waitForChanges();
|
||||
|
||||
expect(datetime.value).toBe('2021-12-31T23:59:00');
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { DatetimeParts } from '../datetime-interface';
|
||||
|
||||
import { isSameDay } from './comparison';
|
||||
import { isAfter, isBefore, isSameDay } from './comparison';
|
||||
import { getNumDaysInMonth } from './helpers';
|
||||
import { clampDate, parseAmPm } from './parse';
|
||||
|
||||
@@ -424,44 +424,137 @@ export const validateParts = (
|
||||
* Returns the closest date to refParts
|
||||
* that also meets the constraints of
|
||||
* the *Values params.
|
||||
* @param refParts The reference date
|
||||
* @param monthValues The allowed month values
|
||||
* @param dayValues The allowed day (of the month) values
|
||||
* @param yearValues The allowed year values
|
||||
* @param hourValues The allowed hour values
|
||||
* @param minuteValues The allowed minute values
|
||||
*/
|
||||
export const getClosestValidDate = (
|
||||
refParts: DatetimeParts,
|
||||
monthValues?: number[],
|
||||
dayValues?: number[],
|
||||
yearValues?: number[],
|
||||
hourValues?: number[],
|
||||
minuteValues?: number[]
|
||||
) => {
|
||||
export const getClosestValidDate = ({
|
||||
refParts,
|
||||
monthValues,
|
||||
dayValues,
|
||||
yearValues,
|
||||
hourValues,
|
||||
minuteValues,
|
||||
minParts,
|
||||
maxParts,
|
||||
}: {
|
||||
/**
|
||||
* The reference date
|
||||
*/
|
||||
refParts: DatetimeParts;
|
||||
/**
|
||||
* The allowed month values
|
||||
*/
|
||||
monthValues?: number[];
|
||||
/**
|
||||
* The allowed day (of the month) values
|
||||
*/
|
||||
dayValues?: number[];
|
||||
/**
|
||||
* The allowed year values
|
||||
*/
|
||||
yearValues?: number[];
|
||||
/**
|
||||
* The allowed hour values
|
||||
*/
|
||||
hourValues?: number[];
|
||||
/**
|
||||
* The allowed minute values
|
||||
*/
|
||||
minuteValues?: number[];
|
||||
/**
|
||||
* The minimum date that can be returned
|
||||
*/
|
||||
minParts?: DatetimeParts;
|
||||
/**
|
||||
* The maximum date that can be returned
|
||||
*/
|
||||
maxParts?: DatetimeParts;
|
||||
}) => {
|
||||
const { hour, minute, day, month, year } = refParts;
|
||||
const copyParts = { ...refParts, dayOfWeek: undefined };
|
||||
|
||||
if (yearValues !== undefined) {
|
||||
// Filters out years that are out of the min/max bounds
|
||||
const filteredYears = yearValues.filter((year) => {
|
||||
if (minParts !== undefined && year < minParts.year) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (maxParts !== undefined && year > maxParts.year) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
copyParts.year = findClosestValue(year, filteredYears);
|
||||
}
|
||||
|
||||
if (monthValues !== undefined) {
|
||||
copyParts.month = findClosestValue(month, monthValues);
|
||||
// Filters out months that are out of the min/max bounds
|
||||
const filteredMonths = monthValues.filter((month) => {
|
||||
if (minParts !== undefined && copyParts.year === minParts.year && month < minParts.month) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (maxParts !== undefined && copyParts.year === maxParts.year && month > maxParts.month) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
copyParts.month = findClosestValue(month, filteredMonths);
|
||||
}
|
||||
|
||||
// Day is nullable but cannot be undefined
|
||||
if (day !== null && dayValues !== undefined) {
|
||||
copyParts.day = findClosestValue(day, dayValues);
|
||||
}
|
||||
|
||||
if (yearValues !== undefined) {
|
||||
copyParts.year = findClosestValue(year, yearValues);
|
||||
// Filters out days that are out of the min/max bounds
|
||||
const filteredDays = dayValues.filter((day) => {
|
||||
if (minParts !== undefined && isBefore({ ...copyParts, day }, minParts)) {
|
||||
return false;
|
||||
}
|
||||
if (maxParts !== undefined && isAfter({ ...copyParts, day }, maxParts)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
copyParts.day = findClosestValue(day, filteredDays);
|
||||
}
|
||||
|
||||
if (hour !== undefined && hourValues !== undefined) {
|
||||
copyParts.hour = findClosestValue(hour, hourValues);
|
||||
// Filters out hours that are out of the min/max bounds
|
||||
const filteredHours = hourValues.filter((hour) => {
|
||||
if (minParts?.hour !== undefined && isSameDay(copyParts, minParts) && hour < minParts.hour) {
|
||||
return false;
|
||||
}
|
||||
if (maxParts?.hour !== undefined && isSameDay(copyParts, maxParts) && hour > maxParts.hour) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
copyParts.hour = findClosestValue(hour, filteredHours);
|
||||
copyParts.ampm = parseAmPm(copyParts.hour);
|
||||
}
|
||||
|
||||
if (minute !== undefined && minuteValues !== undefined) {
|
||||
copyParts.minute = findClosestValue(minute, minuteValues);
|
||||
// Filters out minutes that are out of the min/max bounds
|
||||
const filteredMinutes = minuteValues.filter((minute) => {
|
||||
if (
|
||||
minParts?.minute !== undefined &&
|
||||
isSameDay(copyParts, minParts) &&
|
||||
copyParts.hour === minParts.hour &&
|
||||
minute < minParts.minute
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
maxParts?.minute !== undefined &&
|
||||
isSameDay(copyParts, maxParts) &&
|
||||
copyParts.hour === maxParts.hour &&
|
||||
minute > maxParts.minute
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
copyParts.minute = findClosestValue(minute, filteredMinutes);
|
||||
}
|
||||
|
||||
return copyParts;
|
||||
|
||||
@@ -12,6 +12,14 @@ export class InfiniteScroll implements ComponentInterface {
|
||||
private thrPx = 0;
|
||||
private thrPc = 0;
|
||||
private scrollEl?: HTMLElement;
|
||||
|
||||
/**
|
||||
* didFire exists so that ionInfinite
|
||||
* does not fire multiple times if
|
||||
* users continue to scroll after
|
||||
* scrolling into the infinite
|
||||
* scroll threshold.
|
||||
*/
|
||||
private didFire = false;
|
||||
private isBusy = false;
|
||||
|
||||
@@ -127,8 +135,6 @@ export class InfiniteScroll implements ComponentInterface {
|
||||
this.ionInfinite.emit();
|
||||
return 3;
|
||||
}
|
||||
} else {
|
||||
this.didFire = false;
|
||||
}
|
||||
|
||||
return 4;
|
||||
@@ -190,10 +196,13 @@ export class InfiniteScroll implements ComponentInterface {
|
||||
writeTask(() => {
|
||||
scrollEl.scrollTop = newScrollTop;
|
||||
this.isBusy = false;
|
||||
this.didFire = false;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.didFire = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Infinite Scroll - Small DOM Update</title>
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"
|
||||
/>
|
||||
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet" />
|
||||
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet" />
|
||||
<script src="../../../../../scripts/testing/scripts.js"></script>
|
||||
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
|
||||
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
|
||||
<style>
|
||||
#list .item {
|
||||
width: 100%;
|
||||
border-bottom: 1px solid gray;
|
||||
|
||||
padding: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<ion-app>
|
||||
<ion-content class="ion-padding" id="content">
|
||||
<div id="list"></div>
|
||||
|
||||
<ion-infinite-scroll threshold="100px" id="infinite-scroll">
|
||||
<ion-infinite-scroll-content loading-spinner="crescent" loading-text="Loading more data...">
|
||||
</ion-infinite-scroll-content>
|
||||
</ion-infinite-scroll>
|
||||
</ion-content>
|
||||
</ion-app>
|
||||
|
||||
<script>
|
||||
const list = document.getElementById('list');
|
||||
const infiniteScroll = document.getElementById('infinite-scroll');
|
||||
|
||||
infiniteScroll.addEventListener('ionInfinite', () => {
|
||||
setTimeout(() => {
|
||||
appendItems();
|
||||
|
||||
infiniteScroll.complete();
|
||||
|
||||
// Custom event consumed in the e2e tests
|
||||
window.dispatchEvent(new CustomEvent('ionInfiniteComplete'));
|
||||
}, 500);
|
||||
});
|
||||
|
||||
function appendItems(count = 3) {
|
||||
for (var i = 0; i < count; i++) {
|
||||
const el = document.createElement('div');
|
||||
el.classList.add('item');
|
||||
el.textContent = `${1 + i}`;
|
||||
list.appendChild(el);
|
||||
}
|
||||
}
|
||||
appendItems(30);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,34 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
|
||||
test.describe(title('infinite-scroll: appending small amounts to dom'), () => {
|
||||
test('should load more after remaining in threshold', async ({ page }) => {
|
||||
await page.goto('/src/components/infinite-scroll/test/small-dom-update', config);
|
||||
|
||||
const ionInfiniteComplete = await page.spyOnEvent('ionInfiniteComplete');
|
||||
const content = page.locator('ion-content');
|
||||
const items = page.locator('#list .item');
|
||||
expect(await items.count()).toBe(30);
|
||||
|
||||
await content.evaluate((el: HTMLIonContentElement) => el.scrollToBottom(0));
|
||||
await ionInfiniteComplete.next();
|
||||
|
||||
/**
|
||||
* Even after appending we'll still be within
|
||||
* the infinite scroll's threshold
|
||||
*/
|
||||
expect(await items.count()).toBe(33);
|
||||
|
||||
await content.evaluate((el: HTMLIonContentElement) => el.scrollToBottom(0));
|
||||
await ionInfiniteComplete.next();
|
||||
|
||||
/**
|
||||
* Scrolling down again without leaving
|
||||
* the threshold should still trigger
|
||||
* infinite scroll again.
|
||||
*/
|
||||
expect(await items.count()).toBe(36);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -37,3 +37,25 @@
|
||||
:host(.input-disabled) {
|
||||
opacity: #{$input-ios-disabled-opacity};
|
||||
}
|
||||
|
||||
// Start/End Slots
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Slotted buttons have a lot of default padding that can
|
||||
* cause them to look misaligned from other pieces such
|
||||
* as the control's label, especially when using a clear
|
||||
* fill. We also make them circular to ensure that non-
|
||||
* clear buttons and the focus/hover state on clear ones
|
||||
* don't look too crowded.
|
||||
*/
|
||||
::slotted(ion-button[slot="start"].button-has-icon-only),
|
||||
::slotted(ion-button[slot="end"].button-has-icon-only) {
|
||||
--border-radius: 50%;
|
||||
--padding-start: 0;
|
||||
--padding-end: 0;
|
||||
--padding-top: 0;
|
||||
--padding-bottom: 0;
|
||||
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
|
||||
@@ -89,9 +89,7 @@
|
||||
/**
|
||||
* This makes the label sit above the input.
|
||||
*/
|
||||
:host(.has-focus.input-fill-outline.input-label-placement-floating) .label-text-wrapper,
|
||||
:host(.has-value.input-fill-outline.input-label-placement-floating) .label-text-wrapper,
|
||||
:host(.input-fill-outline.input-label-placement-stacked) .label-text-wrapper {
|
||||
:host(.label-floating.input-fill-outline) .label-text-wrapper {
|
||||
@include transform(translateY(-32%), scale(#{$form-control-label-stacked-scale}));
|
||||
@include margin(0);
|
||||
|
||||
@@ -216,8 +214,6 @@
|
||||
* the floating/stacked label. We simulate this "cut out"
|
||||
* by removing the top border from the notch fragment.
|
||||
*/
|
||||
:host(.has-focus.input-fill-outline.input-label-placement-floating) .input-outline-notch,
|
||||
:host(.has-value.input-fill-outline.input-label-placement-floating) .input-outline-notch,
|
||||
:host(.input-fill-outline.input-label-placement-stacked) .input-outline-notch {
|
||||
:host(.label-floating.input-fill-outline) .input-outline-notch {
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
@@ -117,3 +117,27 @@
|
||||
:host(.input-shape-round) {
|
||||
--border-radius: 16px;
|
||||
}
|
||||
|
||||
// Start/End Slots
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Slotted buttons have a lot of default padding that can
|
||||
* cause them to look misaligned from other pieces such
|
||||
* as the control's label, especially when using a clear
|
||||
* fill. We also make them circular to ensure that non-
|
||||
* clear buttons and the focus/hover state on clear ones
|
||||
* don't look too crowded.
|
||||
*/
|
||||
::slotted(ion-button[slot="start"].button-has-icon-only),
|
||||
::slotted(ion-button[slot="end"].button-has-icon-only) {
|
||||
--border-radius: 50%;
|
||||
--padding-start: 8px;
|
||||
--padding-end: 8px;
|
||||
--padding-top: 8px;
|
||||
--padding-bottom: 8px;
|
||||
|
||||
aspect-ratio: 1;
|
||||
|
||||
min-height: 40px;
|
||||
}
|
||||
|
||||
@@ -67,9 +67,7 @@
|
||||
// Input Label
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
:host(.input-fill-solid.input-label-placement-stacked) .label-text-wrapper,
|
||||
:host(.has-focus.input-fill-solid.input-label-placement-floating) .label-text-wrapper,
|
||||
:host(.has-value.input-fill-solid.input-label-placement-floating) .label-text-wrapper {
|
||||
:host(.label-floating.input-fill-solid.input-label-placement-floating) .label-text-wrapper {
|
||||
/**
|
||||
* Label text should not extend
|
||||
* beyond the bounds of the input.
|
||||
|
||||
@@ -259,15 +259,16 @@
|
||||
|
||||
// Input Has focus
|
||||
// --------------------------------------------------
|
||||
// When the input has focus, then the input cover should be hidden
|
||||
|
||||
:host(.has-focus) {
|
||||
// TODO FW-2764 Remove this
|
||||
:host(.has-focus.legacy-input) {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
:host(.has-focus) input,
|
||||
:host(.has-focus) a,
|
||||
:host(.has-focus) button {
|
||||
// TODO FW-2764 Remove this
|
||||
:host(.has-focus.legacy-input) input,
|
||||
:host(.has-focus.legacy-input) a,
|
||||
:host(.has-focus.legacy-input) button {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
@@ -322,6 +323,9 @@
|
||||
|
||||
flex-grow: 1;
|
||||
|
||||
// ensure start/end slot content is vertically centered
|
||||
align-items: center;
|
||||
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -641,9 +645,7 @@
|
||||
/**
|
||||
* This makes the label sit above the input.
|
||||
*/
|
||||
:host(.input-label-placement-stacked) .label-text-wrapper,
|
||||
:host(.has-focus.input-label-placement-floating) .label-text-wrapper,
|
||||
:host(.has-value.input-label-placement-floating) .label-text-wrapper {
|
||||
:host(.label-floating) .label-text-wrapper {
|
||||
@include transform(translateY(50%), scale(#{$form-control-label-stacked-scale}));
|
||||
|
||||
/**
|
||||
@@ -652,3 +654,16 @@
|
||||
*/
|
||||
max-width: calc(100% / #{$form-control-label-stacked-scale});
|
||||
}
|
||||
|
||||
// Start/End Slots
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
::slotted([slot="start"]) {
|
||||
margin-inline-end: $form-control-label-margin;
|
||||
margin-inline-start: 0;
|
||||
}
|
||||
|
||||
::slotted([slot="end"]) {
|
||||
margin-inline-start: $form-control-label-margin;
|
||||
margin-inline-end: 0;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ import { getCounterText } from './input.utils';
|
||||
* @virtualProp {"ios" | "md"} mode - The mode determines which platform styles to use.
|
||||
*
|
||||
* @slot label - The label text to associate with the input. Use the `labelPlacement` property to control where the label is placed relative to the input. Use this if you need to render a label with custom HTML. (EXPERIMENTAL)
|
||||
* @slot start - Content to display at the leading edge of the input. (EXPERIMENTAL)
|
||||
* @slot end - Content to display at the trailing edge of the input. (EXPERIMENTAL)
|
||||
*/
|
||||
@Component({
|
||||
tag: 'ion-input',
|
||||
@@ -95,7 +97,9 @@ export class Input implements ComponentInterface {
|
||||
@Prop() autocorrect: 'on' | 'off' = 'off';
|
||||
|
||||
/**
|
||||
* This Boolean attribute lets you specify that a form control should have input focus when the page loads.
|
||||
* Sets the [`autofocus` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus) on the native input element.
|
||||
*
|
||||
* This may not be sufficient for the element to be focused on page load. See [managing focus](/docs/developing/managing-focus) for more information.
|
||||
*/
|
||||
@Prop() autofocus = false;
|
||||
|
||||
@@ -117,6 +121,9 @@ export class Input implements ComponentInterface {
|
||||
/**
|
||||
* A callback used to format the counter text.
|
||||
* By default the counter text is set to "itemLength / maxLength".
|
||||
*
|
||||
* See https://ionicframework.com/docs/troubleshooting/runtime#accessing-this
|
||||
* if you need to access `this` from within the callback.
|
||||
*/
|
||||
@Prop() counterFormatter?: (inputLength: number, maxLength: number) => string;
|
||||
|
||||
@@ -369,7 +376,7 @@ export class Input implements ComponentInterface {
|
||||
const { el } = this;
|
||||
|
||||
this.legacyFormController = createLegacyFormController(el);
|
||||
this.slotMutationController = createSlotMutationController(el, 'label', () => forceUpdate(this));
|
||||
this.slotMutationController = createSlotMutationController(el, ['label', 'start', 'end'], () => forceUpdate(this));
|
||||
this.notchController = createNotchController(
|
||||
el,
|
||||
() => this.notchSpacerEl,
|
||||
@@ -424,6 +431,8 @@ export class Input implements ComponentInterface {
|
||||
*
|
||||
* Developers who wish to focus an input when an overlay is presented
|
||||
* should call `setFocus` after `didPresent` has resolved.
|
||||
*
|
||||
* See [managing focus](/docs/developing/managing-focus) for more information.
|
||||
*/
|
||||
@Method()
|
||||
async setFocus() {
|
||||
@@ -492,6 +501,8 @@ export class Input implements ComponentInterface {
|
||||
'has-value': this.hasValue(),
|
||||
'has-focus': this.hasFocus,
|
||||
'interactive-disabled': this.disabled,
|
||||
// TODO(FW-2764): remove this
|
||||
legacy: !!this.legacy,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -541,15 +552,37 @@ export class Input implements ComponentInterface {
|
||||
if (!this.shouldClearOnEdit()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* The following keys do not modify the
|
||||
* contents of the input. As a result, pressing
|
||||
* them should not edit the input.
|
||||
*
|
||||
* We can't check to see if the value of the input
|
||||
* was changed because we call checkClearOnEdit
|
||||
* in a keydown listener, and the key has not yet
|
||||
* been added to the input.
|
||||
*/
|
||||
const IGNORED_KEYS = ['Enter', 'Tab', 'Shift', 'Meta', 'Alt', 'Control'];
|
||||
const pressedIgnoredKey = IGNORED_KEYS.includes(ev.key);
|
||||
|
||||
/**
|
||||
* Clear the input if the control has not been previously cleared during focus.
|
||||
* Do not clear if the user hitting enter to submit a form.
|
||||
*/
|
||||
if (!this.didInputClearOnEdit && this.hasValue() && ev.key !== 'Enter' && ev.key !== 'Tab') {
|
||||
if (!this.didInputClearOnEdit && this.hasValue() && !pressedIgnoredKey) {
|
||||
this.value = '';
|
||||
this.emitInputChange(ev);
|
||||
}
|
||||
this.didInputClearOnEdit = true;
|
||||
|
||||
/**
|
||||
* Pressing an IGNORED_KEYS first and
|
||||
* then an allowed key will cause the input to not
|
||||
* be cleared.
|
||||
*/
|
||||
if (!pressedIgnoredKey) {
|
||||
this.didInputClearOnEdit = true;
|
||||
}
|
||||
}
|
||||
|
||||
private onCompositionStart = () => {
|
||||
@@ -695,18 +728,42 @@ export class Input implements ComponentInterface {
|
||||
}
|
||||
|
||||
private renderInput() {
|
||||
const { disabled, fill, readonly, shape, inputId, labelPlacement } = this;
|
||||
const { disabled, fill, readonly, shape, inputId, labelPlacement, el, hasFocus } = this;
|
||||
const mode = getIonMode(this);
|
||||
const value = this.getValue();
|
||||
const inItem = hostContext('ion-item', this.el);
|
||||
const shouldRenderHighlight = mode === 'md' && fill !== 'outline' && !inItem;
|
||||
|
||||
const hasValue = this.hasValue();
|
||||
const hasStartEndSlots = el.querySelector('[slot="start"], [slot="end"]') !== null;
|
||||
|
||||
/**
|
||||
* If the label is stacked, it should always sit above the input.
|
||||
* For floating labels, the label should move above the input if
|
||||
* the input has a value, is focused, or has anything in either
|
||||
* the start or end slot.
|
||||
*
|
||||
* If there is content in the start slot, the label would overlap
|
||||
* it if not forced to float. This is also applied to the end slot
|
||||
* because with the default or solid fills, the input is not
|
||||
* vertically centered in the container, but the label is. This
|
||||
* causes the slots and label to appear vertically offset from each
|
||||
* other when the label isn't floating above the input. This doesn't
|
||||
* apply to the outline fill, but this was not accounted for to keep
|
||||
* things consistent.
|
||||
*
|
||||
* TODO(FW-5592): Remove hasStartEndSlots condition
|
||||
*/
|
||||
const labelShouldFloat =
|
||||
labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
|
||||
|
||||
return (
|
||||
<Host
|
||||
class={createColorClasses(this.color, {
|
||||
[mode]: true,
|
||||
'has-value': this.hasValue(),
|
||||
'has-focus': this.hasFocus,
|
||||
'has-value': hasValue,
|
||||
'has-focus': hasFocus,
|
||||
'label-floating': labelShouldFloat,
|
||||
[`input-fill-${fill}`]: fill !== undefined,
|
||||
[`input-shape-${shape}`]: shape !== undefined,
|
||||
[`input-label-placement-${labelPlacement}`]: true,
|
||||
@@ -715,9 +772,16 @@ export class Input implements ComponentInterface {
|
||||
'input-disabled': disabled,
|
||||
})}
|
||||
>
|
||||
<label class="input-wrapper">
|
||||
{/**
|
||||
* htmlFor is needed so that clicking the label always focuses
|
||||
* the input. Otherwise, if the start slot has something
|
||||
* interactable, clicking the label would focus that instead
|
||||
* since it comes before the input in the DOM.
|
||||
*/}
|
||||
<label class="input-wrapper" htmlFor={inputId}>
|
||||
{this.renderLabelContainer()}
|
||||
<div class="native-wrapper">
|
||||
<slot name="start"></slot>
|
||||
<input
|
||||
class="native-input"
|
||||
ref={(input) => (this.nativeInput = input)}
|
||||
@@ -772,6 +836,7 @@ export class Input implements ComponentInterface {
|
||||
<ion-icon aria-hidden="true" icon={mode === 'ios' ? closeCircle : closeSharp}></ion-icon>
|
||||
</button>
|
||||
)}
|
||||
<slot name="end"></slot>
|
||||
</div>
|
||||
{shouldRenderHighlight && <div class="input-highlight"></div>}
|
||||
</label>
|
||||
|
||||
@@ -21,7 +21,13 @@
|
||||
<ion-input label="Email" label-placement="stacked" value="hi@ionic.io"></ion-input><br />
|
||||
<ion-input label="Email" label-placement="floating"></ion-input> <br />
|
||||
<ion-input label="Email" label-placement="floating" fill="outline" value="hi@ionic.io"></ion-input> <br />
|
||||
<ion-input label="Email" label-placement="floating" fill="solid" value="hi@ionic.io"></ion-input>
|
||||
<ion-input label="Email" label-placement="floating" fill="solid" value="hi@ionic.io"></ion-input><br />
|
||||
<ion-input label="Email" fill="solid" value="hi@ionic.io">
|
||||
<ion-icon slot="start" name="lock-closed" aria-hidden="true"></ion-icon>
|
||||
<ion-button slot="end" aria-label="button">
|
||||
<ion-icon slot="icon-only" name="lock-closed" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-input>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { test, configs } from '@utils/test/playwright';
|
||||
|
||||
const IGNORED_KEYS = ['Enter', 'Tab', 'Shift', 'Meta', 'Alt', 'Control'];
|
||||
|
||||
configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) => {
|
||||
test.describe(title('input: clearOnEdit'), () => {
|
||||
test('should clear when typed into', async ({ page }) => {
|
||||
@@ -16,28 +18,57 @@ configs({ modes: ['ios'], directions: ['ltr'] }).forEach(({ title, config }) =>
|
||||
await expect(input).toHaveJSProperty('value', 'h');
|
||||
});
|
||||
|
||||
test('should not clear when enter is pressed', async ({ page }) => {
|
||||
await page.setContent(`<ion-input value="abc" clear-on-edit="true" aria-label="input"></ion-input>`, config);
|
||||
test('should not clear the input if it does not have an initial value when typing', async ({ page }) => {
|
||||
await page.setContent(`<ion-input label="input" value="" clear-on-edit="true"></ion-input>`, config);
|
||||
|
||||
const input = page.locator('ion-input');
|
||||
await input.locator('input').focus();
|
||||
|
||||
await page.keyboard.press('Enter');
|
||||
await page.waitForChanges();
|
||||
await input.click();
|
||||
await input.type('hello world');
|
||||
|
||||
await expect(input).toHaveJSProperty('value', 'abc');
|
||||
await expect(input).toHaveJSProperty('value', 'hello world');
|
||||
});
|
||||
|
||||
test('should not clear when tab is pressed', async ({ page }) => {
|
||||
IGNORED_KEYS.forEach((ignoredKey: string) => {
|
||||
test(`should not clear when ${ignoredKey} is pressed`, async ({ page, skip }) => {
|
||||
skip.browser(
|
||||
(browserName: string) => browserName === 'firefox' && ignoredKey === 'Meta',
|
||||
'Firefox incorrectly adds "OS" to the input when pressing the Meta key on Linux'
|
||||
);
|
||||
await page.setContent(`<ion-input value="abc" clear-on-edit="true" aria-label="input"></ion-input>`, config);
|
||||
|
||||
const input = page.locator('ion-input');
|
||||
await input.locator('input').focus();
|
||||
|
||||
await page.keyboard.press(ignoredKey);
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(input).toHaveJSProperty('value', 'abc');
|
||||
});
|
||||
});
|
||||
|
||||
test('should clear after when pressing valid key after pressing ignored key', async ({ page }) => {
|
||||
test.info().annotations.push({
|
||||
type: 'issue',
|
||||
description: 'https://github.com/ionic-team/ionic-framework/issues/28633',
|
||||
});
|
||||
|
||||
await page.setContent(`<ion-input value="abc" clear-on-edit="true" aria-label="input"></ion-input>`, config);
|
||||
|
||||
const input = page.locator('ion-input');
|
||||
await input.locator('input').focus();
|
||||
|
||||
await page.keyboard.press('Tab');
|
||||
// ignored
|
||||
await page.keyboard.press('Shift');
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(input).toHaveJSProperty('value', 'abc');
|
||||
|
||||
// allowed
|
||||
await page.keyboard.press('a');
|
||||
await page.waitForChanges();
|
||||
|
||||
await expect(input).toHaveJSProperty('value', 'a');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<h2>Inactive</h2>
|
||||
<ion-item>
|
||||
<ion-label position="floating">Label</ion-label>
|
||||
<ion-input placeholder="Placeholder Text"></ion-input>
|
||||
<ion-input legacy="true" placeholder="Placeholder Text"></ion-input>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
<h2>Focused</h2>
|
||||
<ion-item class="item-has-focus">
|
||||
<ion-label position="floating">Label</ion-label>
|
||||
<ion-input placeholder="Placeholder Text"></ion-input>
|
||||
<ion-input legacy="true" placeholder="Placeholder Text"></ion-input>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
<h2>Activated</h2>
|
||||
<ion-item>
|
||||
<ion-label position="floating">Label</ion-label>
|
||||
<ion-input placeholder="Placeholder Text" value="Input Text"></ion-input>
|
||||
<ion-input legacy="true" placeholder="Placeholder Text" value="Input Text"></ion-input>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<h2>Hover</h2>
|
||||
<ion-item class="item-hovered">
|
||||
<ion-label position="floating">Label</ion-label>
|
||||
<ion-input></ion-input>
|
||||
<ion-input legacy="true"></ion-input>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
<h2>Disabled</h2>
|
||||
<ion-item>
|
||||
<ion-label position="floating">Label</ion-label>
|
||||
<ion-input disabled></ion-input>
|
||||
<ion-input legacy="true" disabled></ion-input>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
<h2>Toggle Placeholder</h2>
|
||||
<ion-item>
|
||||
<ion-label position="floating">Label</ion-label>
|
||||
<ion-input id="floatingToggle" type="password"></ion-input>
|
||||
<ion-input legacy="true" id="floatingToggle" type="password"></ion-input>
|
||||
<ion-button
|
||||
fill="clear"
|
||||
slot="end"
|
||||
@@ -89,7 +89,7 @@
|
||||
<h2>Inactive</h2>
|
||||
<ion-item>
|
||||
<ion-label position="stacked">Label</ion-label>
|
||||
<ion-input></ion-input>
|
||||
<ion-input legacy="true"></ion-input>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
<h2>Focused</h2>
|
||||
<ion-item class="item-has-focus">
|
||||
<ion-label position="stacked">Label</ion-label>
|
||||
<ion-input placeholder="Placeholder Text"></ion-input>
|
||||
<ion-input legacy="true" placeholder="Placeholder Text"></ion-input>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
@@ -105,7 +105,7 @@
|
||||
<h2>Activated</h2>
|
||||
<ion-item>
|
||||
<ion-label position="stacked">Label</ion-label>
|
||||
<ion-input placeholder="Placeholder Text" value="Input Text"></ion-input>
|
||||
<ion-input legacy="true" placeholder="Placeholder Text" value="Input Text"></ion-input>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
@@ -113,7 +113,7 @@
|
||||
<h2>Hover</h2>
|
||||
<ion-item class="item-hovered">
|
||||
<ion-label position="stacked">Label</ion-label>
|
||||
<ion-input></ion-input>
|
||||
<ion-input legacy="true"></ion-input>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
<h2>Disabled</h2>
|
||||
<ion-item>
|
||||
<ion-label position="stacked">Label</ion-label>
|
||||
<ion-input disabled></ion-input>
|
||||
<ion-input legacy="true" disabled></ion-input>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
<h2>Toggle Placeholder</h2>
|
||||
<ion-item>
|
||||
<ion-label position="stacked">Label</ion-label>
|
||||
<ion-input id="stackedToggle" type="password"></ion-input>
|
||||
<ion-input legacy="true" id="stackedToggle" type="password"></ion-input>
|
||||
<ion-button
|
||||
fill="clear"
|
||||
slot="end"
|
||||
|
||||
@@ -49,53 +49,125 @@
|
||||
</ion-header>
|
||||
|
||||
<ion-content id="content" class="ion-padding">
|
||||
<h1>Label Slot</h1>
|
||||
<div class="grid">
|
||||
<div class="grid-item">
|
||||
<h2>No Fill / Start</h2>
|
||||
<h2>No Fill / Start Label</h2>
|
||||
<ion-input label-placement="start" value="hi@ionic.io">
|
||||
<div slot="label">Email <span class="required">*</span></div>
|
||||
</ion-input>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>Solid / Start</h2>
|
||||
<h2>Solid / Start Label</h2>
|
||||
<ion-input label-placement="start" fill="solid" value="hi@ionic.io">
|
||||
<div slot="label">Email <span class="required">*</span></div>
|
||||
</ion-input>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>Outline / Start</h2>
|
||||
<h2>Outline / Start Label</h2>
|
||||
<ion-input label-placement="start" fill="outline" value="hi@ionic.io">
|
||||
<div slot="label">Email <span class="required">*</span></div>
|
||||
</ion-input>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>No Fill / Floating</h2>
|
||||
<h2>No Fill / Floating Label</h2>
|
||||
<ion-input label-placement="floating" value="hi@ionic.io">
|
||||
<div slot="label">Email <span class="required">*</span></div>
|
||||
</ion-input>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>Solid / Floating</h2>
|
||||
<h2>Solid / Floating Label</h2>
|
||||
<ion-input label-placement="floating" fill="solid" value="hi@ionic.io">
|
||||
<div slot="label">Email <span class="required">*</span></div>
|
||||
</ion-input>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>Outline / Floating</h2>
|
||||
<h2>Outline / Floating Label</h2>
|
||||
<ion-input label-placement="floating" fill="outline" value="hi@ionic.io">
|
||||
<div slot="label">Email <span class="required">*</span></div>
|
||||
</ion-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1>Start/End Slots</h1>
|
||||
<div class="grid">
|
||||
<div class="grid-item">
|
||||
<h2>No Fill / Start Label</h2>
|
||||
<ion-input label-placement="start" value="hi@ionic.io" label="Email">
|
||||
<ion-icon slot="start" name="lock-closed" aria-hidden="true"></ion-icon>
|
||||
<ion-button fill="clear" slot="end" aria-label="Show/hide password">
|
||||
<ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-input>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>Outline / Floating / Async</h2>
|
||||
<h2>Solid / Start Label</h2>
|
||||
<ion-input label-placement="start" fill="solid" value="hi@ionic.io" label="Email">
|
||||
<ion-icon slot="start" name="lock-closed" aria-hidden="true"></ion-icon>
|
||||
<ion-button fill="clear" slot="end" aria-label="Show/hide password">
|
||||
<ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-input>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>Outline / Start Label</h2>
|
||||
<ion-input label-placement="start" fill="outline" value="hi@ionic.io" label="Email">
|
||||
<ion-icon slot="start" name="lock-closed" aria-hidden="true"></ion-icon>
|
||||
<ion-button fill="clear" slot="end" aria-label="Show/hide password">
|
||||
<ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-input>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>No Fill / Floating Label</h2>
|
||||
<ion-input label-placement="floating" value="hi@ionic.io" label="Email">
|
||||
<ion-icon slot="start" name="lock-closed" aria-hidden="true"></ion-icon>
|
||||
<ion-button fill="clear" slot="end" aria-label="Show/hide password">
|
||||
<ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-input>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>Solid / Floating Label</h2>
|
||||
<ion-input label-placement="floating" fill="solid" value="hi@ionic.io" label="Email">
|
||||
<ion-icon slot="start" name="lock-closed" aria-hidden="true"></ion-icon>
|
||||
<ion-button fill="clear" slot="end" aria-label="Show/hide password">
|
||||
<ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-input>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>Outline / Floating Label</h2>
|
||||
<ion-input label-placement="floating" fill="outline" value="hi@ionic.io" label="Email">
|
||||
<ion-icon slot="start" name="lock-closed" aria-hidden="true"></ion-icon>
|
||||
<ion-button fill="clear" slot="end" aria-label="Show/hide password">
|
||||
<ion-icon slot="icon-only" name="eye" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h1>Async Slot Content</h1>
|
||||
<div class="grid">
|
||||
<div class="grid-item">
|
||||
<h2>Outline / Async Label</h2>
|
||||
<ion-input id="solid-async" label-placement="floating" fill="outline" value="hi@ionic.io"></ion-input>
|
||||
</div>
|
||||
|
||||
<div class="grid-item">
|
||||
<h2>Outline / Async Decorations</h2>
|
||||
<ion-input id="async-decorations" label-placement="floating" fill="outline" label="Email"></ion-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ion-button onclick="addSlot()">Add Slotted Content</ion-button>
|
||||
@@ -106,29 +178,65 @@
|
||||
|
||||
<script>
|
||||
const solidAsync = document.querySelector('#solid-async');
|
||||
const asyncDecos = document.querySelector('#async-decorations');
|
||||
|
||||
const getSlottedContent = () => {
|
||||
const getSlottedLabel = () => {
|
||||
return solidAsync.querySelector('[slot="label"]');
|
||||
};
|
||||
|
||||
const getSlottedStartDeco = () => {
|
||||
return asyncDecos.querySelector('[slot="start"]');
|
||||
};
|
||||
|
||||
const getSlottedEndDeco = () => {
|
||||
return asyncDecos.querySelector('[slot="end"]');
|
||||
};
|
||||
|
||||
const addSlot = () => {
|
||||
if (getSlottedContent() === null) {
|
||||
if (getSlottedLabel() === null) {
|
||||
const labelEl = document.createElement('div');
|
||||
labelEl.slot = 'label';
|
||||
labelEl.innerHTML = 'Email <span class="required">*</span>';
|
||||
|
||||
solidAsync.appendChild(labelEl);
|
||||
}
|
||||
|
||||
if (getSlottedStartDeco() === null) {
|
||||
const startEl = document.createElement('div');
|
||||
startEl.slot = 'start';
|
||||
startEl.innerHTML = 'Start';
|
||||
|
||||
asyncDecos.insertAdjacentElement('afterbegin', startEl);
|
||||
}
|
||||
|
||||
if (getSlottedEndDeco() === null) {
|
||||
const endEl = document.createElement('div');
|
||||
endEl.slot = 'end';
|
||||
endEl.innerHTML = 'End';
|
||||
|
||||
asyncDecos.insertAdjacentElement('beforeend', endEl);
|
||||
}
|
||||
};
|
||||
|
||||
const removeSlot = () => {
|
||||
if (getSlottedContent() !== null) {
|
||||
solidAsync.querySelector('[slot="label"]').remove();
|
||||
const slottedLabel = getSlottedLabel();
|
||||
if (slottedLabel !== null) {
|
||||
slottedLabel.remove();
|
||||
}
|
||||
|
||||
const slottedStartDeco = getSlottedStartDeco();
|
||||
if (slottedStartDeco !== null) {
|
||||
slottedStartDeco.remove();
|
||||
}
|
||||
|
||||
const slottedEndDeco = getSlottedEndDeco();
|
||||
if (slottedEndDeco !== null) {
|
||||
slottedEndDeco.remove();
|
||||
}
|
||||
};
|
||||
|
||||
const updateSlot = () => {
|
||||
const slottedContent = getSlottedContent();
|
||||
const slottedContent = getSlottedLabel();
|
||||
|
||||
if (slottedContent !== null) {
|
||||
slottedContent.textContent = 'This is my really really really long text';
|
||||
|
||||
72
core/src/components/input/test/slot/input.e2e.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { expect } from '@playwright/test';
|
||||
import { configs, test } from '@utils/test/playwright';
|
||||
|
||||
configs().forEach(({ title, screenshot, config }) => {
|
||||
test.describe(title('input: start and end slots (visual checks)'), () => {
|
||||
test('should not have visual regressions with a start-positioned label', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-input label-placement="start" fill="solid" value="100" label="Weight" clear-input="true">
|
||||
<ion-icon slot="start" name="barbell" aria-hidden="true"></ion-icon>
|
||||
<ion-button slot="end" aria-label="Show/hide password">
|
||||
<ion-icon slot="icon-only" name="lock-closed" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-input>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const input = page.locator('ion-input');
|
||||
await expect(input).toHaveScreenshot(screenshot(`input-slots-label-start`));
|
||||
});
|
||||
|
||||
test('should not have visual regressions with a floating label', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-input label-placement="floating" fill="solid" value="100" label="Weight" clear-input="true">
|
||||
<ion-icon slot="start" name="barbell" aria-hidden="true"></ion-icon>
|
||||
<ion-button slot="end" aria-label="Show/hide password">
|
||||
<ion-icon slot="icon-only" name="lock-closed" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-input>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const input = page.locator('ion-input');
|
||||
await expect(input).toHaveScreenshot(screenshot(`input-slots-label-floating`));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => {
|
||||
test.describe(title('input: start and end slots (functionality checks)'), () => {
|
||||
test('should raise floating label when there is content in the start slot', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-input label-placement="floating" fill="solid" label="Weight">
|
||||
<ion-icon slot="start" name="barbell" aria-hidden="true"></ion-icon>
|
||||
</ion-input>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const input = page.locator('ion-input');
|
||||
await expect(input).toHaveClass(/label-floating/);
|
||||
});
|
||||
|
||||
test('should raise floating label when there is content in the end slot', async ({ page }) => {
|
||||
await page.setContent(
|
||||
`
|
||||
<ion-input label-placement="floating" fill="solid" label="Weight">
|
||||
<ion-icon slot="end" name="barbell" aria-hidden="true"></ion-icon>
|
||||
</ion-input>
|
||||
`,
|
||||
config
|
||||
);
|
||||
|
||||
const input = page.locator('ion-input');
|
||||
await expect(input).toHaveClass(/label-floating/);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
@@ -51,6 +51,15 @@
|
||||
* @prop --highlight-color-valid: The color of the highlight on the item when valid. Only applies to inputs and textareas using the legacy form syntax. DEPRECATED: Highlights can be styled on `ion-input` or `ion-textarea` when using the modern form syntax.
|
||||
* @prop --highlight-color-invalid: The color of the highlight on the item when invalid. Only applies to inputs and textareas using the legacy form syntax. DEPRECATED: Highlights can be styled on `ion-input` or `ion-textarea` when using the modern form syntax.
|
||||
*/
|
||||
|
||||
/**
|
||||
* We change the minimum width as the
|
||||
* font size changes. Using a fixed minimum
|
||||
* width means that fewer and fewer characters
|
||||
* can be displayed in the same space as the
|
||||
* text grows.
|
||||
*/
|
||||
--inner-min-width: 4rem;
|
||||
--border-radius: 0px;
|
||||
--border-width: 0px;
|
||||
--border-style: solid;
|
||||
@@ -221,6 +230,11 @@
|
||||
display: flex;
|
||||
position: relative;
|
||||
|
||||
// Flex wrap is required here in order to wrap
|
||||
// the start slot + .item-inner content that
|
||||
// doesn't fit on the same line
|
||||
flex-wrap: wrap;
|
||||
|
||||
align-items: inherit;
|
||||
justify-content: inherit;
|
||||
|
||||
@@ -238,9 +252,15 @@
|
||||
background: var(--background);
|
||||
|
||||
overflow: inherit;
|
||||
box-sizing: border-box;
|
||||
|
||||
z-index: 1;
|
||||
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
// TODO(FW-5289): remove
|
||||
:host(.item-legacy) .item-native {
|
||||
flex-wrap: unset;
|
||||
}
|
||||
|
||||
.item-native::-moz-focus-inner {
|
||||
@@ -287,11 +307,34 @@ button, a {
|
||||
// This is required to work with an inset highlight
|
||||
position: relative;
|
||||
|
||||
flex: 1;
|
||||
// This flex property is required in order to make
|
||||
// the elements wrap when there is a slotted start
|
||||
// element and a label
|
||||
flex: 1 0 0;
|
||||
|
||||
flex-direction: inherit;
|
||||
|
||||
// Flex wrap is required here in order to wrap
|
||||
// .input-wrapper content + the end slot that
|
||||
// doesn't fit on the same line
|
||||
flex-wrap: wrap;
|
||||
|
||||
align-items: inherit;
|
||||
align-self: stretch;
|
||||
|
||||
/**
|
||||
* The min-width defines when the
|
||||
* content in the default slot should
|
||||
* stop wrapping/truncating within its own
|
||||
* container. At this point the entire
|
||||
* container will wrap to the next line.
|
||||
*/
|
||||
min-width: var(--inner-min-width);
|
||||
|
||||
// Max width must be set to 100%, otherwise the
|
||||
// elements will overflow this container instead
|
||||
// of wrapping
|
||||
max-width: 100%;
|
||||
min-height: inherit;
|
||||
|
||||
border-width: var(--inner-border-width);
|
||||
@@ -303,6 +346,14 @@ button, a {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
// TODO(FW-5289): remove
|
||||
:host(.item-legacy) .item-inner {
|
||||
flex: 1;
|
||||
|
||||
flex-wrap: unset;
|
||||
|
||||
max-width: unset;
|
||||
}
|
||||
|
||||
// Item Bottom
|
||||
// --------------------------------------------------
|
||||
@@ -370,6 +421,18 @@ button, a {
|
||||
|
||||
::slotted(ion-label:not([slot="end"])) {
|
||||
flex: 1;
|
||||
|
||||
// Setting width to min-content allows the label to
|
||||
// shrink and wrap its text instead of moving to its
|
||||
// own row if there are slotted elements next to it
|
||||
width: min-content;
|
||||
|
||||
/**
|
||||
* We allow labels in the default
|
||||
* slot to grow. However, we do not
|
||||
* want them to grow indefinitely.
|
||||
*/
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
// Item Input
|
||||
@@ -382,18 +445,41 @@ button, a {
|
||||
.input-wrapper {
|
||||
display: flex;
|
||||
|
||||
flex: 1;
|
||||
// This flex property is required in order to keep
|
||||
// the label from shrinking when there are wide
|
||||
// elements next to it
|
||||
flex: 1 0 auto;
|
||||
|
||||
flex-direction: inherit;
|
||||
|
||||
// Flex wrap is required here in order to wrap
|
||||
// content in the default slot (such as a label
|
||||
// and a button) that doesn't fit on the same line
|
||||
flex-wrap: wrap;
|
||||
|
||||
align-items: inherit;
|
||||
align-self: stretch;
|
||||
|
||||
// Max width must be set to 100%, otherwise the
|
||||
// elements will overflow this container instead
|
||||
// of wrapping
|
||||
max-width: 100%;
|
||||
|
||||
text-overflow: ellipsis;
|
||||
|
||||
overflow: inherit;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
// TODO(FW-5289): remove
|
||||
:host(.item-legacy) .input-wrapper {
|
||||
flex: 1;
|
||||
|
||||
flex-wrap: unset;
|
||||
|
||||
max-width: unset;
|
||||
}
|
||||
|
||||
:host(.item-label-stacked),
|
||||
:host(.item-label-floating) {
|
||||
align-items: start;
|
||||
|
||||
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 14 KiB |
@@ -43,11 +43,13 @@
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label> Single line text that should have ellipses when it doesn't all fit in the item</ion-label>
|
||||
<ion-label class="ion-text-nowrap">
|
||||
Single line text that should have ellipses when it doesn't all fit in the item
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ion-item lines="none">
|
||||
<ion-label> Single line item with no lines</ion-label>
|
||||
<ion-label>Single line item with no lines</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
@@ -57,21 +59,21 @@
|
||||
</ion-item>
|
||||
|
||||
<ion-item color="secondary">
|
||||
<ion-label class="ion-text-wrap">
|
||||
<ion-label>
|
||||
<h1>H1 Title Text</h1>
|
||||
<p>Paragraph line 1</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label class="ion-text-wrap">
|
||||
<ion-label>
|
||||
<h2>H2 Title Text</h2>
|
||||
<p>Paragraph line 1</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label class="ion-text-wrap">
|
||||
<ion-label>
|
||||
<ion-text color="primary">
|
||||
<h3>H3 Title Text</h3>
|
||||
</ion-text>
|
||||
@@ -83,7 +85,7 @@
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label class="ion-text-wrap">
|
||||
<ion-label>
|
||||
<h4>H4 Title Text</h4>
|
||||
<p>Paragraph line 1</p>
|
||||
<p>Paragraph line 2</p>
|
||||
@@ -92,7 +94,7 @@
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label> Item using inner ion-label </ion-label>
|
||||
<ion-label>Item using inner ion-label </ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ion-item class="overflow-visible">
|
||||
|
||||
@@ -31,7 +31,7 @@ configs().forEach(({ title, screenshot, config }) => {
|
||||
</ion-item>
|
||||
|
||||
<ion-item>
|
||||
<ion-label> Single line text that should have ellipses when it doesn't all fit in the item</ion-label>
|
||||
<ion-label class="ion-text-nowrap"> Single line text that should have ellipses when it doesn't all fit in the item</ion-label>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
`,
|
||||
|
||||
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 79 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 64 KiB |