Compare commits

...

139 Commits

Author SHA1 Message Date
Liam DeBeasi
a0ff24b5d6 5.6.1 2021-03-17 12:59:08 -04:00
Brandy Carney
c8a3999da1 fix(label): properly float labels for non-input items (#23060) 2021-03-17 12:32:50 -04:00
Liam DeBeasi
67617fbc0f fix(refresher): progressEnd no longer errors when pulling quickly in MD native refresher (#23056) 2021-03-16 11:28:10 -04:00
Liam DeBeasi
c877061a32 fix(item): detail icon is no longer announced by screen readers (#23055)
resolves #23054
2021-03-16 09:59:30 -04:00
Liam DeBeasi
2f54bc1469 fix(vue): passing params as props are correctly updated when switching pages (#23049)
resolves #23043
2021-03-16 09:31:45 -04:00
Liam DeBeasi
2a253a1d33 fix(virtual-scroll): allow null in items prop (#23047)
Co-authored-by: Robert Rhoades <studioromeo@users.noreply.github.com>
2021-03-15 12:16:55 -04:00
Victor Berchet
4ce62b26a8 fix(router): simplify param merging (#22999) 2021-03-15 11:45:56 -04:00
Liam DeBeasi
e4bf052794 fix(): overlays now present correctly when using custom elements build (#23039)
resolves #23029
2021-03-15 09:13:23 -04:00
Evgeniy
630848ae5c chore(): remove duplicate --color definition in ion-item (#23030) 2021-03-10 09:56:03 -05:00
Liam DeBeasi
f94e618a7b fix(react): only pass tab event props from IonTabs to IonTabBar if defined (#23024)
resolves #23023
2021-03-05 16:03:14 -05:00
Liam DeBeasi
de58238333 merge release-5.6.0
5.6.0
2021-03-04 14:14:24 -05:00
Liam DeBeasi
1a2fd2fb6e 5.6.0 2021-03-04 13:54:38 -05:00
Liam DeBeasi
a1f95264e7 chore(): remove core/components from separate package release (#23018) 2021-03-04 13:38:09 -05:00
Brandy Carney
e256d3f09f feat(progress): add parts for more design customization (#22938)
resolves #20062 fixes #21820
2021-03-04 12:48:52 -05:00
Brandy Carney
e828a9a693 docs(github): update component guide to include switch (#22994)
adds a notice of webkit bug with VoiceOver
2021-03-03 14:10:05 -05:00
Liam DeBeasi
dcf5317310 docs(modal): remove incorrect ion-page note for react (#23009) 2021-03-03 10:46:29 -05:00
Liam DeBeasi
f022464cf8 docs(menu, split-pane): update contentId docs to be more clear (#23008) 2021-03-03 10:40:39 -05:00
Liam DeBeasi
38eb378d66 chore(): update web driver (#23010) 2021-03-03 10:06:28 -05:00
Liam DeBeasi
a2763afe8e fix(vue): correctly remove active state from tab button when navigating away from tab (#23000)
resolves #22597
2021-03-03 09:26:46 -05:00
Liam DeBeasi
943e3f6ae3 fix(react, vue): tab buttons no longer throw an error if href is undefined (#22998)
resolves #22997
2021-03-02 10:05:59 -05:00
Brandy Carney
9282aa6871 fix(label): only show placeholder with floating label when focused (#22958)
fixes #17571
2021-03-01 15:36:29 -05:00
Ely Lucas
b83e00934e feat(react): add react hooks to control overlay components (#22484) 2021-03-01 11:34:13 -05:00
Liam DeBeasi
dd1c8dbf3b feat(vue): add composition API ionic lifecycle hooks (#22970)
resolves #22769
2021-03-01 10:35:25 -05:00
Liam DeBeasi
9486e51f6d merge release-5.5.5
Release 5.5.5
2021-02-26 14:51:05 -05:00
Liam DeBeasi
fb631742af chore(): run build 2021-02-26 14:26:06 -05:00
Liam DeBeasi
0101a5fce2 5.5.5 2021-02-26 14:10:48 -05:00
Liam DeBeasi
06d4c8e6f1 fix(vue): account for event name changes in vue 3.0.6+ 2021-02-26 14:10:42 -05:00
Liam DeBeasi
cd8ffd82a0 fix(react, vue): navigating using ion-back-button now selects correct page (#22974)
resolves #22830
2021-02-25 13:18:20 -05:00
Dominik Geng
b6b2714d70 fix(progress-bar): use correct theme colors in dark mode (#22965)
resolves #20098

Co-authored-by: Liam DeBeasi <liamdebeasi@users.noreply.github.com>
2021-02-24 16:40:40 -05:00
Liam DeBeasi
8c1646a105 fix(progress-bar): add width transition (#22964) 2021-02-24 11:27:18 -05:00
Liam DeBeasi
68a9b80053 fix(vue): prevent race conditions when opening overlays (#22883)
resolves #22880
2021-02-23 14:42:38 -05:00
Liam DeBeasi
26285bbc91 fix(progress-bar): use correct theme colors in dark mode (#22957)
resolves #20098

Co-authored-by: Dominik Geng <domske@users.noreply.github.com>
2021-02-23 13:13:38 -05:00
Hans Krywalsky
215eb5d4ef feat(searchbar): add showClearIcon property (#22759)
resolves #22738

Co-authored-by: Liam DeBeasi <liamdebeasi@icloud.com>
2021-02-23 12:34:42 -05:00
Liam DeBeasi
677d55ebe4 test(): update theming test with latest dark mode values (#22956) 2021-02-23 12:34:04 -05:00
Liam DeBeasi
a4c38aca88 chore(): update package-lock to account for npm 7.5.3 bug fix (#22963) 2021-02-23 11:41:51 -05:00
Liam DeBeasi
5300dcc693 fix(header): collapsed toolbar is no longer incorrectly shown when using ion-refresher (#22937)
resolves #22829
2021-02-18 08:53:49 -05:00
Ely Lucas
b064fdebef fix(react): onIonTabsWillChange and onIonTabsDidChange event handlers are now properly bound to IonTabs (#22233)
Co-authored-by: Hansu Kim <khsily@users.noreply.github.com>
2021-02-11 14:50:07 -05:00
Yohann CINTRE
c05476b88e fix(refresher): add correct dark mode styles (#22639)
resolves #22637
2021-02-11 12:31:41 -05:00
Adam Bradley
0de75afbef feat(custom-elements): add experimental custom elements build (#22863)
Co-authored-by: Liam DeBeasi <liamdebeasi@icloud.com>
2021-02-11 12:08:00 -05:00
Liam DeBeasi
19d63f6243 fix(a11y): improve support for ids with special characters when getting label element (#22680)
resolves #22678
2021-02-11 11:34:03 -05:00
aleemosman
343f855147 chore(): update code of conduct (#22619)
Co-authored-by: Brandy Carney <brandyscarney@users.noreply.github.com>
2021-02-11 09:35:02 -05:00
Liam DeBeasi
da1b7a0e7a fix(react): IonRouterOutlet now respects animated={false} prop (#22905)
resolves #22903
2021-02-10 15:05:04 -05:00
Liam DeBeasi
3a0465e7d6 fix(radio-group): pressing space no longer jumps screen to bottom of page (#22892)
resolves #22716
2021-02-09 12:56:58 -05:00
Liam DeBeasi
8cb2ea6804 test(): update modal dark theme, fix styles test (#22884) 2021-02-05 12:55:57 -05:00
Liam DeBeasi
d46e074fb1 merge release-5.5.4
5.5.4
2021-02-04 11:50:39 -05:00
Liam DeBeasi
cb2c9b63e8 5.5.4 2021-02-04 11:13:52 -05:00
Liam DeBeasi
ff0f1da9f1 fix(vue): ionChange events now propagate correctly (#22872)
resolves #22870
2021-02-04 11:00:46 -05:00
Mike Hartington
487349f02a fix(schematics): update ngAdd schematic (#22858)
* fix(schematics): update ngAdd schematic
Closes https://github.com/ionic-team/angular-toolkit/issues/415
Closes https://github.com/ionic-team/angular-toolkit/issues/419

* Update angular/src/schematics/utils/config.ts

Co-authored-by: Liam DeBeasi <liamdebeasi@users.noreply.github.com>

* Update angular/src/schematics/add/index.ts

Co-authored-by: Liam DeBeasi <liamdebeasi@users.noreply.github.com>

Co-authored-by: Liam DeBeasi <liamdebeasi@users.noreply.github.com>
2021-02-02 16:44:33 -05:00
Liam DeBeasi
b6b2d34fd4 fix(app): keyboard no longer hides when using contenteditable (#22857)
resolves #22856
2021-02-02 13:19:19 -05:00
Liam DeBeasi
1a5accc5f7 fix(select): class on component now indicates when select is open (#22846)
resolves #22801
2021-02-01 13:02:04 -05:00
Liam DeBeasi
f7d4c21b64 fix(): scroll assist no longer prevents first click event from firing (#22845)
resolves #21871
2021-02-01 13:00:33 -05:00
Adam Bradley
fa515eb79b refactor(react): look up tabs also by property key (#22823)
When IonTabBar is wrapped by a functional component or fragment, then IonTabs is unable to find it. This is a rare case, but adding the ability for a function component to manually state that is is an IonTabBar. Same for IonTabButtons.
2021-02-01 11:31:33 -06:00
Liam DeBeasi
697bcf3420 merge release-5.5.3
5.5.3
2021-01-28 12:38:23 -05:00
Liam DeBeasi
bc17e24a03 5.5.3 2021-01-28 12:17:55 -05:00
Liam DeBeasi
ab1fc8f231 fix(react): add improved method for not unmounting component until overlay is dismissed (#22813)
resolves #22761
2021-01-22 12:48:47 -05:00
Liam DeBeasi
2a3ce9a74e fix(vue): improve path matching with tabs (#22807)
resolves #22519
2021-01-22 10:19:38 -05:00
Liam DeBeasi
e2d8e5c4dc fix(refresher): correctly detect spinner when using native refresher (#22800)
resolves #22706
2021-01-20 17:18:36 -05:00
Simon
7ecae2e4cb fix(vue): output commonjs format for node environments (#22766) 2021-01-20 16:20:44 -05:00
Liam DeBeasi
14e8441706 fix(react): do not unmount overlay inner component until overlay is dismissed (#22763)
resolves #22761
2021-01-14 10:53:39 -05:00
Liam DeBeasi
e1d6627bf0 fix(vue): ensure v-model value is properly synced before ionChange event (#22749)
resolves #22610
2021-01-13 11:22:27 -05:00
Liam DeBeasi
348c50b7ea fix(title): only add large title transition when using collapsible header (#22762)
resolves #22760
2021-01-13 10:12:04 -05:00
Liam DeBeasi
9e9a372497 fix(react, vue): do not show back button when replacing to root page (#22750)
resolves #22528
2021-01-13 10:10:44 -05:00
Liam DeBeasi
64719f49f9 fix(vue): improve v-model binding sync between vue wrappers and web components (#22745)
resolves #22731
2021-01-08 12:06:45 -05:00
Liam DeBeasi
3d6ac1382e fix(vue): all ionic vue components can now use router link (#22743) 2021-01-07 15:52:06 -05:00
Liam DeBeasi
9a02ec8402 chore(): end holiday triage mode (#22741) 2021-01-07 12:20:08 -05:00
Liam DeBeasi
fbb777ab5a test(datetime): improve reliability of focus trap test (#22742) 2021-01-07 11:37:19 -05:00
Liam DeBeasi
67b0853b28 chore(): start holiday triage mode (#22700) 2020-12-18 17:32:02 -05:00
Liam DeBeasi
e3a05bfeb5 fix(vue): correctly determine leaving view when transitioning to a new instance of a previous page (#22655)
resolves #22654 resolves #22658
2020-12-16 16:45:28 -05:00
Liam DeBeasi
5c27dd8032 fix(vue): tab bar is now correctly hidden when keyboard is open (#22687) 2020-12-16 16:43:38 -05:00
Amith Mihiranga
b768181307 docs(input): add available autocapitalize property options (#21886)
Co-authored-by: Brandy Carney <brandy@ionic.io>
2020-12-16 09:53:00 -05:00
Ely Lucas
f891f66708 fix(react): improve view matching logic (#22569) 2020-12-15 11:03:08 -05:00
Ely Lucas
a01bdb8c8d fix(react): adding dynamic class to ion-page no longer hides component (#22666)
resolves #22631
2020-12-15 11:02:06 -05:00
Ken Sodemann
09a5ed6a0d docs(tab-button): use an href in vue sample (#22674) 2020-12-14 16:14:42 -05:00
Liam DeBeasi
39bbb99f75 chore(vue): import animation types from @ionic/vue instead of @ionic/core (#22677) 2020-12-14 14:39:41 -05:00
Óscar Carretero
11a59133d9 test(item): fix disabled test script path (#22660) 2020-12-10 17:00:32 -05:00
Liam DeBeasi
1ef112ef12 merge release-5.5.2
5.5.2
2020-12-09 16:03:47 -05:00
Liam DeBeasi
78740d0dfc 5.5.2 2020-12-09 15:38:27 -05:00
Liam DeBeasi
94518c9387 chore(react): relax peer dependency requirements (#22645)
resolves #22644
2020-12-08 16:17:16 -05:00
Ely Lucas
31f45cdcc9 fix(react): setting a ref should allow other props to be passed in, closes #22609 2020-12-08 13:45:24 -07:00
Liam DeBeasi
61cf0c534e fix(vue): adding non tab button elements inside ion-tab-bar no longer causes errors (#22643)
resolves #22642
2020-12-08 12:49:13 -05:00
Liam DeBeasi
8d5ed47a28 fix(refresher): clean up old css if calling refresh method before native refresher is setup (#22640)
resolves #22636
2020-12-08 10:16:29 -05:00
Liam DeBeasi
a94e2a87fb fix(vue): query strings are now correctly handled when navigating back (#22615)
resolves #22517
2020-12-07 15:17:31 -05:00
Liam DeBeasi
818e387fe8 fix(button): allow aria-label to be inherited to inner button (#22632)
resolves #22629
2020-12-07 12:31:05 -05:00
Augusto Amaral
ff39d40255 docs(vue): fix menu usage example (#22624) 2020-12-07 11:42:18 -05:00
Ely Lucas
91aaaab71a chore(react): adding prettier and formating files 2020-12-07 09:31:33 -07:00
Liam DeBeasi
01afdc42e5 fix(vue): pass in correct route to props function (#22605)
resolves #22602
2020-12-07 10:33:22 -05:00
Ely Lucas
36939e10ae fix(react): android hardware button should invoke browser back 2020-12-07 08:33:00 -07:00
Liam DeBeasi
5ed73cdf4d fix(refresher): refresher correctly detects native refresher when shown asynchronously (#22623)
resolves #22616
2020-12-03 14:58:12 -05:00
Augusto Amaral
0be79fe82b docs(vue): fix skeleton-text usage example (#22596) 2020-12-03 12:52:13 -05:00
Liam DeBeasi
216f51b12a fix(vue): correctly handle navigation failures (#22621)
resolves #22591
2020-12-03 12:44:16 -05:00
Liam DeBeasi
dc9faa6a0f fix(android): setting hardwareBackButton: false in config now disabled default webview behavior (#22555)
resolves #18237
2020-12-02 15:44:34 -05:00
Liam DeBeasi
4f4f31b65e fix(vue): correctly remove old view when replacing route (#22566)
resolves #22492
2020-11-30 11:00:14 -05:00
Liam DeBeasi
9d04c127e8 fix(vue): swipe back gesture is properly disabled when swipeBackEnabled config is false (#22568)
resolves #22567
2020-11-30 10:59:00 -05:00
Liam DeBeasi
181fc59ab7 chore(): add note on 5.5.1 upgrading (#22578) 2020-11-25 18:47:52 -05:00
Liam DeBeasi
53ec9cff5e merge release-5.5.1
5.5.1
2020-11-25 18:25:50 -05:00
Liam DeBeasi
d61456c46d 5.5.1 2020-11-25 17:17:52 -05:00
Brandy Carney
07868354aa fix(a11y): only preventDefault for the onClick event (#22573) 2020-11-25 16:56:51 -05:00
Ely Lucas
5275332e43 fix(react): eliminate use of deprecated findDOMNode, resolves #20972 2020-11-25 14:21:12 -07:00
Brandy Carney
ea52db66f0 fix(select): improvements for announcing placeholder and value on screenreaders (#22556)
- Hides select text from screen readers so it isn't announced twice (Android talkback needs this)
- Adds the placeholder text to be announced if there is no value
- Don't add a comma if there is no value/placeholder (NVDA speech viewer)
- Don't announce alert label twice
2020-11-25 15:47:14 -05:00
Liam DeBeasi
c45c8d5564 fix(checkbox): click events on ion-item now fire properly (#22561)
resolves #22557
2020-11-24 14:31:24 -05:00
Brandy Carney
afcc46e1cc fix(radio): properly announce radios on screen readers and resolve axe errors (#22507) 2020-11-24 11:43:59 -05:00
Liam DeBeasi
4e23aad3d9 fix(vue): testing a routerLink-capable component no longer warns of missing router dependency (#22532)
resolves #22506
2020-11-23 14:17:52 -05:00
Liam DeBeasi
a664ccbde9 chore(): fix @types/react to avoid 16.14.1 release (#22554) 2020-11-23 13:37:22 -05:00
Liam DeBeasi
8002114e72 fix(vue): onBeforeRouteLeave and onBeforeRouteUpdate hooks now fire properly (#22542)
resolves #22540
2020-11-23 10:22:03 -05:00
Brandy Carney
04b874e32a fix(select): fix a11y issues with axe and screen readers (#22494)
fixes #21552 fixes #21548
2020-11-20 17:18:51 -05:00
Yohann CINTRE
c727419350 docs(select): fix broken link to select-option (#22539) 2020-11-20 12:20:07 -05:00
Liam DeBeasi
e1e23bc1a2 docs(modal): include note on using ion-page when wrapping modal component (#22534) 2020-11-19 15:31:01 -05:00
Liam DeBeasi
cdc2fb652f fix(vue): tabs correctly fire lifecycle events (#22479)
resolves #22466
2020-11-19 15:30:32 -05:00
Liam DeBeasi
bb519b8724 chore(): update "needs reproduction" wording for clarity (#22520) 2020-11-19 09:46:58 -05:00
Liam DeBeasi
1956f98968 fix(router): navigation guards now fire when navigating to a page with params (#22521)
resolves #22516
2020-11-18 14:36:49 -05:00
Liam DeBeasi
3ed44993e1 merge release-5.5.0
5.5.0
2020-11-18 14:33:55 -05:00
Liam DeBeasi
f4ecc32c14 5.5.0 2020-11-18 13:02:47 -05:00
Liam DeBeasi
d5eb3a44e6 test(angular): fix e2e tests (#22522) 2020-11-18 12:25:30 -05:00
Liam DeBeasi
33768e1d0c docs(tabs): add correct vue examples (#22510) 2020-11-17 11:02:28 -05:00
Liam DeBeasi
ce4a381d4f chore(): re-add es5 check (#22504)
This reverts commit 0eb643d563.
2020-11-16 12:54:59 -05:00
Liam DeBeasi
2d878fc4f6 fix(backdrop): nvda no longer incorrectly announces backdrop (#22481)
resolves #22102
2020-11-16 12:15:33 -05:00
Takuma Kira
65bc99577c feat(segment): add swipeGesture property to allow for disabling of the swipe gesture (#22087)
resolves #22048
2020-11-13 12:38:23 -05:00
Liam DeBeasi
abad12fbdb fix(input): title attribute is automatically inherited (#22493)
resolves #22055
2020-11-13 12:15:33 -05:00
Liam DeBeasi
d77a9d57ec perf(ios): move content to stacking context while preserving position: fixed behavior (#22489)
resolves #22473
2020-11-12 16:02:12 -05:00
Brandy Carney
813611a61b fix(toggle): use a native input to fix a11y issues with axe and screen readers (#22477)
fixes #22011
references #21552
2020-11-12 15:29:05 -05:00
Liam DeBeasi
96d6012071 merge release-5.4.4
Release 5.4.4
2020-11-12 13:23:18 -05:00
Brandy Carney
7214a8401b fix(checkbox): use a native input to fix a11y issues with axe and screen readers (#22402)
fixes #21644
fixes #20517
fixes #17796
2020-11-12 11:25:33 -05:00
Liam DeBeasi
bf3f3bb3dc test(): disable treeshake test 2020-11-12 10:39:10 -05:00
Liam DeBeasi
3a6fcc7d8b 5.4.4 2020-11-12 09:51:48 -05:00
Liam DeBeasi
62dd16a5ee fix(build): add missing es5 output 2020-11-12 09:49:49 -05:00
Liam DeBeasi
0956f8bc55 fix(vue): correctly pass route props to components (#22476)
resolves #22472
2020-11-11 12:12:12 -05:00
Liam DeBeasi
a4a64530ff fix(refresher): ios native refresher now works in side menu (#22449) 2020-11-10 12:07:38 -05:00
Liam DeBeasi
e17c822bfb fix(vue): tab bar now works with slot="top" (#22461)
resolves #22456
2020-11-10 10:07:31 -05:00
Michael Tintiuc
f5b0299729 feat(vue): composition api lifecycle methods (#22241) 2020-11-10 09:14:24 -05:00
Daniele
1267945480 docs(checkbox): update example for vue 3 syntax (#22457) 2020-11-09 11:01:34 -05:00
Stian Øvrevåge
907cc7b159 docs(vue): fix label typo (#22451) 2020-11-09 09:57:38 -05:00
Liam DeBeasi
e76f79d054 feat(vue): vetur support (#22403) 2020-11-06 14:25:23 -05:00
Liam DeBeasi
f130fb2b17 merge release-5.4.3
Release 5.4.3
2020-11-06 12:53:17 -05:00
Liam DeBeasi
6b817f26b0 fix(refresher): refresher gesture now works in side menu (#22446)
resolves #20832
2020-11-06 12:16:45 -05:00
Liam DeBeasi
c356526520 5.4.3 2020-11-06 12:13:28 -05:00
Liam DeBeasi
caa3afa613 fix(): add missing vendor prefixes to css 2020-11-06 12:13:14 -05:00
Hans Krywalsky
0a0cbd8f2a feat(chip): add disabled property (#20658)
resolves #19510
2020-11-05 14:18:24 -05:00
Piotr Tomiak
639314ab21 feat(vue): web-types support (#22428)
resolves #19522
2020-11-05 13:54:31 -05:00
Liam DeBeasi
7512c90241 merge release-5.4.2
Release 5.4.2
2020-11-05 13:51:25 -05:00
426 changed files with 25891 additions and 18958 deletions

View File

@@ -185,7 +185,7 @@ jobs:
- attach_workspace:
at: /tmp/workspace
- run:
command: npm install --legacy-peer-deps
command: npm install
working_directory: /tmp/workspace/packages/vue
- run:
command: sudo npm link
@@ -208,13 +208,7 @@ jobs:
- attach_workspace:
at: /tmp/workspace
- run:
command: npm install --legacy-peer-deps
working_directory: /tmp/workspace/packages/vue-router
- run:
command: sudo npm link
working_directory: /tmp/workspace/core
- run:
command: sudo npm link @ionic/core
command: npm install
working_directory: /tmp/workspace/packages/vue-router
- run:
command: sudo npm link
@@ -442,8 +436,11 @@ workflows:
requires: [puppeteer-dependencies, build-core]
- test-core-spec:
requires: [build-core]
- test-core-treeshake:
requires: [build-core]
# Adam requested we skip this test for now
# since it is failing on ES5 code which
# will be removed in Ionic Framework v6
#- test-core-treeshake:
# requires: [build-core]
- test-core-screenshot:
requires: [build-core]
filters:

View File

@@ -9,6 +9,9 @@
* [Ripple Effect](#ripple-effect)
* [Example Components](#example-components)
* [References](#references)
- [Accessibility](#accessibility)
* [Checkbox](#checkbox)
* [Switch](#switch)
- [Rendering Anchor or Button](#rendering-anchor-or-button)
* [Example Components](#example-components-1)
* [Component Structure](#component-structure-1)
@@ -362,6 +365,265 @@ ion-ripple-effect {
- [iOS Buttons](https://developer.apple.com/design/human-interface-guidelines/ios/controls/buttons/)
## Accessibility
### Checkbox
#### Example Components
- [ion-checkbox](https://github.com/ionic-team/ionic/tree/master/core/src/components/checkbox)
#### VoiceOver
In order for VoiceOver to work properly with a checkbox component there must be a native `input` with `type="checkbox"`, and `aria-checked` and `role="checkbox"` **must** be on the host element. The `aria-hidden` attribute needs to be added if the checkbox is disabled, preventing iOS users from selecting it:
```tsx
render() {
const { checked, disabled } = this;
return (
<Host
aria-checked={`${checked}`}
aria-hidden={disabled ? 'true' : null}
role="checkbox"
>
<input
type="checkbox"
/>
...
</Host>
);
}
```
#### NVDA
It is required to have `aria-checked` on the native input for checked to read properly and `disabled` to prevent tabbing to the input:
```tsx
render() {
const { checked, disabled } = this;
return (
<Host
aria-checked={`${checked}`}
aria-hidden={disabled ? 'true' : null}
role="checkbox"
>
<input
type="checkbox"
aria-checked={`${checked}`}
disabled={disabled}
/>
...
</Host>
);
}
```
#### Labels
A helper function has been created to get the proper `aria-label` for the checkbox. This can be imported as `getAriaLabel` like the following:
```tsx
const { label, labelId, labelText } = getAriaLabel(el, inputId);
```
where `el` and `inputId` are the following:
```tsx
export class Checkbox implements ComponentInterface {
private inputId = `ion-cb-${checkboxIds++}`;
@Element() el!: HTMLElement;
...
}
```
This can then be added to the `Host` like the following:
```tsx
<Host
aria-labelledby={label ? labelId : null}
aria-checked={`${checked}`}
aria-hidden={disabled ? 'true' : null}
role="checkbox"
>
```
In addition to that, the checkbox input should have a label added:
```tsx
<Host
aria-labelledby={label ? labelId : null}
aria-checked={`${checked}`}
aria-hidden={disabled ? 'true' : null}
role="checkbox"
>
<label htmlFor={inputId}>
{labelText}
</label>
<input
type="checkbox"
aria-checked={`${checked}`}
disabled={disabled}
id={inputId}
/>
```
#### Hidden Input
A helper function to render a hidden input has been added, it can be added in the `render`:
```tsx
renderHiddenInput(true, el, name, (checked ? value : ''), disabled);
```
> This is required for the checkbox to work with forms.
#### Known Issues
When using VoiceOver on macOS, Chrome will announce the following when you are focused on a checkbox:
```
currently on a checkbox inside of a checkbox
```
This is a compromise we have to make in order for it to work with the other screen readers & Safari.
### Switch
#### Example Components
- [ion-toggle](https://github.com/ionic-team/ionic/tree/master/core/src/components/toggle)
#### Voiceover
In order for VoiceOver to work properly with a switch component there must be a native `input` with `type="checkbox"` and `role="switch"`, and `aria-checked` and `role="switch"` **must** be on the host element. The `aria-hidden` attribute needs to be added if the switch is disabled, preventing iOS users from selecting it:
```tsx
render() {
const { checked, disabled } = this;
return (
<Host
aria-checked={`${checked}`}
aria-hidden={disabled ? 'true' : null}
role="switch"
>
<input
type="checkbox"
role="switch"
/>
...
</Host>
);
}
```
#### NVDA
It is required to have `aria-checked` on the native input for checked to read properly and `disabled` to prevent tabbing to the input:
```tsx
render() {
const { checked, disabled } = this;
return (
<Host
aria-checked={`${checked}`}
aria-hidden={disabled ? 'true' : null}
role="switch"
>
<input
type="checkbox"
role="switch"
aria-checked={`${checked}`}
disabled={disabled}
/>
...
</Host>
);
}
```
#### Labels
A helper function has been created to get the proper `aria-label` for the switch. This can be imported as `getAriaLabel` like the following:
```tsx
const { label, labelId, labelText } = getAriaLabel(el, inputId);
```
where `el` and `inputId` are the following:
```tsx
export class Toggle implements ComponentInterface {
private inputId = `ion-tg-${toggleIds++}`;
@Element() el!: HTMLElement;
...
}
```
This can then be added to the `Host` like the following:
```tsx
<Host
aria-labelledby={label ? labelId : null}
aria-checked={`${checked}`}
aria-hidden={disabled ? 'true' : null}
role="switch"
>
```
In addition to that, the checkbox input should have a label added:
```tsx
<Host
aria-labelledby={label ? labelId : null}
aria-checked={`${checked}`}
aria-hidden={disabled ? 'true' : null}
role="switch"
>
<label htmlFor={inputId}>
{labelText}
</label>
<input
type="checkbox"
role="switch"
aria-checked={`${checked}`}
disabled={disabled}
id={inputId}
/>
```
#### Hidden Input
A helper function to render a hidden input has been added, it can be added in the `render`:
```tsx
renderHiddenInput(true, el, name, (checked ? value : ''), disabled);
```
> This is required for the switch to work with forms.
#### Known Issues
When using VoiceOver on macOS or iOS, Chrome will announce the switch as a checked or unchecked `checkbox`:
```
You are currently on a switch. To select or deselect this checkbox, press Control-Option-Space.
```
There is a WebKit bug open for this: https://bugs.webkit.org/show_bug.cgi?id=196354
## Rendering Anchor or Button
Certain components can render an `<a>` or a `<button>` depending on the presence of an `href` attribute.

View File

@@ -27,7 +27,10 @@ comment:
is added to issues that need a code reproduction.
Please provide a reproduction with the minimum amount of code required to reproduce the issue. Without a reliable code reproduction, it is unlikely we will be able to resolve the issue, leading to it being closed.
Please reproduce this issue in an Ionic starter application and provide a way for us to access it (GitHub repo, StackBlitz, etc). Without a reliable code reproduction, it is unlikely we will be able to resolve the issue, leading to it being closed.
If you have already provided a code snippet and are seeing this message, it is likely that the code snippet was not enough for our team to reproduce the issue.
For a guide on how to create a good reproduction, see our [Contributing Guide](https://ionicframework.com/docs/contributing/how-to-contribute#creating-a-good-code-reproduction).

2
.gitignore vendored
View File

@@ -59,10 +59,10 @@ prerender-static.html
angular/css/
packages/react/css/
packages/vue/css/
core/components/
core/css/
core/hydrate/
core/loader/
core/www/
.stencil/
angular/build/
core/components/

View File

@@ -63,8 +63,9 @@ async function main() {
function checkProductionRelease() {
const corePath = common.projectPath('core');
const hasEsm = fs.existsSync(path.join(corePath, 'dist', 'esm'));
const hasEsmEs5 = fs.existsSync(path.join(corePath, 'dist', 'esm-es5'));
const hasCjs = fs.existsSync(path.join(corePath, 'dist', 'cjs'));
if (!hasEsm || !hasCjs) {
if (!hasEsm || !hasEsmEs5 || !hasCjs) {
throw new Error('core build is not a production build');
}
}

View File

@@ -7,7 +7,25 @@ const fs = require('fs');
[
// core
{
files: ['../core/dist/index.js', '../core/dist/ionic/index.esm.js']
files: [
'../core/css/core.css',
'../core/css/core.css.map',
'../core/css/normalize.css',
'../core/css/normalize.css.map',
'../core/components/index.js',
'../core/components/index.d.ts',
'../core/components/package.json',
'../core/dist/index.js',
'../core/dist/ionic/index.esm.js',
]
},
// hydrate
{
files: [
'../core/hydrate/index.js',
'../core/hydrate/index.d.ts',
'../core/hydrate/package.json'
]
},
// angular
{

View File

@@ -1,3 +1,193 @@
## [5.6.1](https://github.com/ionic-team/ionic/compare/v5.6.0...v5.6.1) (2021-03-17)
### Bug Fixes
* **custom-elements:** overlays now present correctly when using custom elements build ([#23039](https://github.com/ionic-team/ionic/issues/23039)) ([e4bf052](https://github.com/ionic-team/ionic/commit/e4bf052794af9aac07f887013b9250d2a045eba3)), closes [#23029](https://github.com/ionic-team/ionic/issues/23029)
* **item:** detail icon is no longer announced by screen readers ([#23055](https://github.com/ionic-team/ionic/issues/23055)) ([c877061](https://github.com/ionic-team/ionic/commit/c877061a328c6ab6fa7248b9880d0205c6c4f6c1)), closes [#23054](https://github.com/ionic-team/ionic/issues/23054)
* **label:** properly float labels for non-input items ([#23060](https://github.com/ionic-team/ionic/issues/23060)) ([c8a3999](https://github.com/ionic-team/ionic/commit/c8a3999da109b1719777f2acb791ab5388d371ea))
* **react:** only pass tab event props from IonTabs to IonTabBar if defined ([#23024](https://github.com/ionic-team/ionic/issues/23024)) ([f94e618](https://github.com/ionic-team/ionic/commit/f94e618a7b307b143eb39c061dc9e6b80e11f862)), closes [#23023](https://github.com/ionic-team/ionic/issues/23023)
* **refresher:** progressEnd no longer errors when pulling quickly in MD native refresher ([#23056](https://github.com/ionic-team/ionic/issues/23056)) ([67617fb](https://github.com/ionic-team/ionic/commit/67617fbc0f7ec825f1fa4c6e7e2da70e3fcd2d66))
* **virtual-scroll:** allow null in items property ([#23047](https://github.com/ionic-team/ionic/issues/23047)) ([2a253a1](https://github.com/ionic-team/ionic/commit/2a253a1d334ca2c6a478a5bc426e3115268a29af))
* **vue:** passing params as props are correctly updated when switching pages ([#23049](https://github.com/ionic-team/ionic/issues/23049)) ([2f54bc1](https://github.com/ionic-team/ionic/commit/2f54bc14699656e6905452a4233d982f83d0001f)), closes [#23043](https://github.com/ionic-team/ionic/issues/23043)
# [5.6.0 Argon](https://github.com/ionic-team/ionic/compare/v5.5.4...v5.6.0) (2021-03-04)
### Bug Fixes
* **all:** improve support for ids with special characters when getting label element ([#22680](https://github.com/ionic-team/ionic/issues/22680)) ([19d63f6](https://github.com/ionic-team/ionic/commit/19d63f62431ef9d8279f1726dd63fac2f0d4b46b)), closes [#22678](https://github.com/ionic-team/ionic/issues/22678)
* **header:** collapsed toolbar is no longer incorrectly shown when using ion-refresher ([#22937](https://github.com/ionic-team/ionic/issues/22937)) ([5300dcc](https://github.com/ionic-team/ionic/commit/5300dcc693caf51a726f8c346cfc9a44474fd3d1)), closes [#22829](https://github.com/ionic-team/ionic/issues/22829)
* **label:** only show placeholder with floating label when focused ([#22958](https://github.com/ionic-team/ionic/issues/22958)) ([9282aa6](https://github.com/ionic-team/ionic/commit/9282aa68715c088e9c8fcd915e78fb7ae91f551f)), closes [#17571](https://github.com/ionic-team/ionic/issues/17571)
* **progress-bar:** use correct theme colors in dark mode ([#22965](https://github.com/ionic-team/ionic/issues/22965)) ([b6b2714](https://github.com/ionic-team/ionic/commit/b6b2714d70f71255315510c5e49708944875db72)), closes [#20098](https://github.com/ionic-team/ionic/issues/20098)
* **radio-group:** pressing space no longer jumps screen to bottom of page ([#22892](https://github.com/ionic-team/ionic/issues/22892)) ([3a0465e](https://github.com/ionic-team/ionic/commit/3a0465e7d6f9e3cb01336a8bdbd7001e4ec34559)), closes [#22716](https://github.com/ionic-team/ionic/issues/22716)
* **react:** IonRouterOutlet now respects animated={false} prop ([#22905](https://github.com/ionic-team/ionic/issues/22905)) ([da1b7a0](https://github.com/ionic-team/ionic/commit/da1b7a0e7a9a5e6a9120dc4d5459c97d8bca5390)), closes [#22903](https://github.com/ionic-team/ionic/issues/22903)
* **react:** onIonTabsWillChange and onIonTabsDidChange event handlers are now properly bound to IonTabs ([#22233](https://github.com/ionic-team/ionic/issues/22233)) ([b064fde](https://github.com/ionic-team/ionic/commit/b064fdebef14018b77242b791914d5bb10863d39))
* **react, vue:** navigating using ion-back-button now selects correct page ([#22974](https://github.com/ionic-team/ionic/issues/22974)) ([cd8ffd8](https://github.com/ionic-team/ionic/commit/cd8ffd82a03ee69ef4cbd7922544bfc39680def9)), closes [#22830](https://github.com/ionic-team/ionic/issues/22830)
* **react, vue:** tab buttons no longer throw an error if href is undefined ([#22998](https://github.com/ionic-team/ionic/issues/22998)) ([943e3f6](https://github.com/ionic-team/ionic/commit/943e3f6ae37ecc56f21168f057dde77a05e4e144)), closes [#22997](https://github.com/ionic-team/ionic/issues/22997)
* **refresher:** add correct dark mode styles ([#22639](https://github.com/ionic-team/ionic/issues/22639)) ([c05476b](https://github.com/ionic-team/ionic/commit/c05476b88e3e6884b4c490461c9c67dee3dca83d)), closes [#22637](https://github.com/ionic-team/ionic/issues/22637)
* **vue:** correctly remove active state from tab button when navigating away from tab ([#23000](https://github.com/ionic-team/ionic/issues/23000)) ([a2763af](https://github.com/ionic-team/ionic/commit/a2763afe8e1fe1dc0decdbcb757a03bc5038045e)), closes [#22597](https://github.com/ionic-team/ionic/issues/22597)
* **vue:** prevent race conditions when opening overlays ([#22883](https://github.com/ionic-team/ionic/issues/22883)) ([68a9b80](https://github.com/ionic-team/ionic/commit/68a9b800532f9c0b308a3b74ed18a7068a942301)), closes [#22880](https://github.com/ionic-team/ionic/issues/22880)
### Features
* **custom-elements:** add experimental custom elements build ([#22863](https://github.com/ionic-team/ionic/issues/22863)) ([0de75af](https://github.com/ionic-team/ionic/commit/0de75afbefc521c1d76adcd587f77ba19c285a95))
* **progress-bar:** add parts for more design customization ([#22938](https://github.com/ionic-team/ionic/issues/22938)) ([e256d3f](https://github.com/ionic-team/ionic/commit/e256d3f09fd6f231c4d9e1d0f0927612a591466b)), closes [#20062](https://github.com/ionic-team/ionic/issues/20062) [#21820](https://github.com/ionic-team/ionic/issues/21820)
* **react:** add react hooks to control overlay components ([#22484](https://github.com/ionic-team/ionic/issues/22484)) ([b83e009](https://github.com/ionic-team/ionic/commit/b83e00934e794a936c9d3d23d7f94bbe89cedcd5))
* **searchbar:** add showClearIcon property ([#22759](https://github.com/ionic-team/ionic/issues/22759)) ([215eb5d](https://github.com/ionic-team/ionic/commit/215eb5d4efbb9ade942dba1687469caf61da21e7)), closes [#22738](https://github.com/ionic-team/ionic/issues/22738)
* **vue:** add composition API ionic lifecycle hooks ([#22970](https://github.com/ionic-team/ionic/issues/22970)) ([dd1c8db](https://github.com/ionic-team/ionic/commit/dd1c8dbf3b20fbd423f70c96846d9c366d90e7c5)), closes [#22769](https://github.com/ionic-team/ionic/issues/22769)
## [5.5.5](https://github.com/ionic-team/ionic/compare/v5.5.4...v5.5.5) (2021-02-26)
### Bug Fixes
* **vue:** account for event name changes in vue 3.0.6+ ([#22980](https://github.com/ionic-team/ionic/issues/22980)) ([7dd2e6d](https://github.com/ionic-team/ionic/commit/7dd2e6d287b47cca758e1d4a71928dd3dc9ac24d)), closes [#22977](https://github.com/ionic-team/ionic/issues/22977)
## [5.5.4](https://github.com/ionic-team/ionic/compare/v5.5.3...v5.5.4) (2021-02-04)
### Bug Fixes
* **angular:** update ngAdd schematic ([#22858](https://github.com/ionic-team/ionic/issues/22858)) ([487349f](https://github.com/ionic-team/ionic/commit/487349f02a41344db2478735d27bf79f2a1c99b3))
* **app:** keyboard no longer hides when using contenteditable ([#22857](https://github.com/ionic-team/ionic/issues/22857)) ([b6b2d34](https://github.com/ionic-team/ionic/commit/b6b2d34fd446feb06cf0143946a014d19231a78e)), closes [#22856](https://github.com/ionic-team/ionic/issues/22856)
* **ios**: scroll assist no longer prevents first click event from firing ([#22845](https://github.com/ionic-team/ionic/issues/22845)) ([f7d4c21](https://github.com/ionic-team/ionic/commit/f7d4c21b64e27f9b655bc1ab2522d6357dc6010f)), closes [#21871](https://github.com/ionic-team/ionic/issues/21871)
* **select:** class on component now indicates when select is open ([#22846](https://github.com/ionic-team/ionic/issues/22846)) ([1a5accc](https://github.com/ionic-team/ionic/commit/1a5accc5f707f84063469c0bd3e5e153489f1e5d)), closes [#22801](https://github.com/ionic-team/ionic/issues/22801)
* **vue:** ionChange events now propagate correctly ([#22872](https://github.com/ionic-team/ionic/issues/22872)) ([ff0f1da](https://github.com/ionic-team/ionic/commit/ff0f1da9f11915b48c4258af7c48c4513785f3fc)), closes [#22870](https://github.com/ionic-team/ionic/issues/22870)
## [5.5.3](https://github.com/ionic-team/ionic/compare/v5.5.2...v5.5.3) (2021-01-28)
### Bug Fixes
* **react:** do not unmount overlay inner component until overlay is dismissed ([#22813](https://github.com/ionic-team/ionic/issues/22813)) ([ab1fc8f](https://github.com/ionic-team/ionic/commit/ab1fc8f2311fd252146942c7a947ebc96efd054f)), closes [#22761](https://github.com/ionic-team/ionic/issues/22761)
* **react:** adding dynamic class to ion-page no longer hides component ([#22666](https://github.com/ionic-team/ionic/issues/22666)) ([a01bdb8](https://github.com/ionic-team/ionic/commit/a01bdb8c8dfee760721eeb35a8b556954f3b5b13)), closes [#22631](https://github.com/ionic-team/ionic/issues/22631)
* **react:** improve view matching logic ([#22569](https://github.com/ionic-team/ionic/issues/22569)) ([f891f66](https://github.com/ionic-team/ionic/commit/f891f667082d2deb5f1b5f0f27af46e46ed1ca0f))
* **react, vue:** do not show back button when replacing to root page ([#22750](https://github.com/ionic-team/ionic/issues/22750)) ([9e9a372](https://github.com/ionic-team/ionic/commit/9e9a3724979e95f3df1a340be21d16d8664a013c)), closes [#22528](https://github.com/ionic-team/ionic/issues/22528)
* **refresher:** correctly detect spinner when using native refresher ([#22800](https://github.com/ionic-team/ionic/issues/22800)) ([e2d8e5c](https://github.com/ionic-team/ionic/commit/e2d8e5c4dcf893ddd8aaa556c1dd8fcaf52411c9)), closes [#22706](https://github.com/ionic-team/ionic/issues/22706)
* **title:** only add large title transition when using collapsible header ([#22762](https://github.com/ionic-team/ionic/issues/22762)) ([348c50b](https://github.com/ionic-team/ionic/commit/348c50b7ea5d4c74498c5d26e40c1c4fe923ee55)), closes [#22760](https://github.com/ionic-team/ionic/issues/22760)
* **vue:** all ionic vue components can now use router link ([#22743](https://github.com/ionic-team/ionic/issues/22743)) ([3d6ac13](https://github.com/ionic-team/ionic/commit/3d6ac1382e23663a3d010fd253d3c6017d3923e4))
* **vue:** correctly determine leaving view when transitioning to a new instance of a previous page ([#22655](https://github.com/ionic-team/ionic/issues/22655)) ([e3a05bf](https://github.com/ionic-team/ionic/commit/e3a05bfeb55d8eaa38aa08a37859aa4df6ffa2d4)), closes [#22654](https://github.com/ionic-team/ionic/issues/22654) [#22658](https://github.com/ionic-team/ionic/issues/22658)
* **vue:** ensure v-model value is properly synced before ionChange event ([#22749](https://github.com/ionic-team/ionic/issues/22749)) ([e1d6627](https://github.com/ionic-team/ionic/commit/e1d6627bf0ef1f47f980db1573c6b2a3d16d7677)), closes [#22610](https://github.com/ionic-team/ionic/issues/22610)
* **vue:** improve path matching with tabs, deprecated adding additional pages as children of tabs without a router outlet ([#22807](https://github.com/ionic-team/ionic/issues/22807)) ([2a3ce9a](https://github.com/ionic-team/ionic/commit/2a3ce9a74e85111a2f1f470b9d8bfe2cda793ca5)), closes [#22519](https://github.com/ionic-team/ionic/issues/22519)
* **vue:** improve v-model binding sync between vue wrappers and web components ([#22745](https://github.com/ionic-team/ionic/issues/22745)) ([64719f4](https://github.com/ionic-team/ionic/commit/64719f49f979c0296a01827d3c02599a48ba93a6)), closes [#22731](https://github.com/ionic-team/ionic/issues/22731)
* **vue:** output commonjs format for node environments ([#22766](https://github.com/ionic-team/ionic/issues/22766)) ([7ecae2e](https://github.com/ionic-team/ionic/commit/7ecae2e4cb5d0eebc6041a8a7a5acc156132c2e1))
* **vue:** tab bar is now correctly hidden when keyboard is open ([#22687](https://github.com/ionic-team/ionic/issues/22687)) ([5c27dd8](https://github.com/ionic-team/ionic/commit/5c27dd8032d32ebb57c31e1f6c112dc513344b93))
## [5.5.2](https://github.com/ionic-team/ionic/compare/v5.5.1...v5.5.2) (2020-12-09)
### Bug Fixes
* **android:** setting hardwareBackButton: false in config now disables default webview behavior ([#22555](https://github.com/ionic-team/ionic/issues/22555)) ([dc9faa6](https://github.com/ionic-team/ionic/commit/dc9faa6a0fbebb64c83c107c79cfd486cc0c096a)), closes [#18237](https://github.com/ionic-team/ionic/issues/18237)
* **button:** allow aria-label to be inherited on inner button ([#22632](https://github.com/ionic-team/ionic/issues/22632)) ([818e387](https://github.com/ionic-team/ionic/commit/818e387fe81ac7026fb374d8865116dadd433c87)), closes [#22629](https://github.com/ionic-team/ionic/issues/22629)
* **react:** hardware back button now navigates correctly ([36939e1](https://github.com/ionic-team/ionic/commit/36939e10ae0b8ac9a9275ee06d8e0d345de7c64f))
* **react:** setting a ref now allows other props to be passed in ([31f45cd](https://github.com/ionic-team/ionic/commit/31f45cdcc953b08749d9db08321fa5ec6cbe2532)), closes [#22609](https://github.com/ionic-team/ionic/issues/22609)
* **refresher:** clean up old css if calling refresh method before native refresher is setup ([#22640](https://github.com/ionic-team/ionic/issues/22640)) ([8d5ed47](https://github.com/ionic-team/ionic/commit/8d5ed47a282f92a60a2c4126a673cc2a5733067e)), closes [#22636](https://github.com/ionic-team/ionic/issues/22636)
* **refresher:** refresher correctly detects native refresher when shown asynchronously ([#22623](https://github.com/ionic-team/ionic/issues/22623)) ([5ed73cd](https://github.com/ionic-team/ionic/commit/5ed73cdf4d63eeee25ef28d9676fcaa4f8e07b47)), closes [#22616](https://github.com/ionic-team/ionic/issues/22616)
* **vue:** adding non tab button elements inside ion-tab-bar no longer causes errors ([#22643](https://github.com/ionic-team/ionic/issues/22643)) ([61cf0c5](https://github.com/ionic-team/ionic/commit/61cf0c534e45ce09410be6bfb50bdc27b657d1bc)), closes [#22642](https://github.com/ionic-team/ionic/issues/22642)
* **vue:** correctly handle navigation failures ([#22621](https://github.com/ionic-team/ionic/issues/22621)) ([216f51b](https://github.com/ionic-team/ionic/commit/216f51b12a8c4ae7b410f47ce3d350ea513b68a1)), closes [#22591](https://github.com/ionic-team/ionic/issues/22591)
* **vue:** correctly remove old view when replacing route ([#22566](https://github.com/ionic-team/ionic/issues/22566)) ([4f4f31b](https://github.com/ionic-team/ionic/commit/4f4f31b65e48294c3130ff24ae00b1a2aa1f9d31)), closes [#22492](https://github.com/ionic-team/ionic/issues/22492)
* **vue:** pass in correct route to props function ([#22605](https://github.com/ionic-team/ionic/issues/22605)) ([01afdc4](https://github.com/ionic-team/ionic/commit/01afdc42e5b1598d4d15cb51761bbb3eb5d13893)), closes [#22602](https://github.com/ionic-team/ionic/issues/22602)
* **vue:** query strings are now correctly handled when navigating back ([#22615](https://github.com/ionic-team/ionic/issues/22615)) ([a94e2a8](https://github.com/ionic-team/ionic/commit/a94e2a87fb759e7b7daed2d0304c1199dbc7afd1)), closes [#22517](https://github.com/ionic-team/ionic/issues/22517)
* **vue:** swipe back gesture is properly disabled when swipeBackEnabled config is false ([#22568](https://github.com/ionic-team/ionic/issues/22568)) ([9d04c12](https://github.com/ionic-team/ionic/commit/9d04c127e817676983940b034a4c7efc92fdfbc6)), closes [#22567](https://github.com/ionic-team/ionic/issues/22567)
### For Ionic Vue Developers
Vue Router 4 has been released! Be sure to update from the release candidate to the latest stable version of Vue Router.
For more information on the changes in Vue Router 4, see https://github.com/vuejs/vue-router-next/releases/tag/v4.0.0.
```
npm install vue-router@4
```
## [5.5.1](https://github.com/ionic-team/ionic/compare/v5.5.0...v5.5.1) (2020-11-25)
### Bug Fixes
* **checkbox:** click handler now fires properly ([#22573](https://github.com/ionic-team/ionic/issues/22573)) ([0786835](https://github.com/ionic-team/ionic/commit/07868354aaf88deebf7472a5bf0f34d7c823de17)), closes [#22557](https://github.com/ionic-team/ionic/issues/22557)
* **radio:** properly announce radios on screen readers and resolve axe errors ([#22507](https://github.com/ionic-team/ionic/issues/22507)) ([afcc46e](https://github.com/ionic-team/ionic/commit/afcc46e1cc4d7f6e9d1a50f8b367da4b1d0c3143))
* **react:** eliminate use of deprecated `findDOMNode`, resolves [#20972](https://github.com/ionic-team/ionic/issues/20972) ([5275332](https://github.com/ionic-team/ionic/commit/5275332e43694f3ee8738a1726c0d202b16c3052))
* **router:** navigation guards now fire when navigating to a page with params ([#22521](https://github.com/ionic-team/ionic/issues/22521)) ([1956f98](https://github.com/ionic-team/ionic/commit/1956f9896883dc4687488e5418e50ce0f6cbe6c9)), closes [#22516](https://github.com/ionic-team/ionic/issues/22516)
* **select:** fix a11y issues with axe and screen readers ([#22494](https://github.com/ionic-team/ionic/issues/22494)) ([04b874e](https://github.com/ionic-team/ionic/commit/04b874e32a65588ca79eda9399ab7e9d86a3cb77)), closes [#21552](https://github.com/ionic-team/ionic/issues/21552) [#21548](https://github.com/ionic-team/ionic/issues/21548)
* **select:** improvements for announcing placeholder and value on screenreaders ([#22556](https://github.com/ionic-team/ionic/issues/22556)) ([ea52db6](https://github.com/ionic-team/ionic/commit/ea52db66f05a185fed6b2e849734a7ffa1c6c6ea))
* **vue:** onBeforeRouteLeave and onBeforeRouteUpdate hooks now fire properly ([#22542](https://github.com/ionic-team/ionic/issues/22542)) ([8002114](https://github.com/ionic-team/ionic/commit/8002114e720361e60d7a7fe2d15ab88b49a72e1b)), closes [#22540](https://github.com/ionic-team/ionic/issues/22540)
* **vue:** tabs now correctly fire lifecycle events ([#22479](https://github.com/ionic-team/ionic/issues/22479)) ([cdc2fb6](https://github.com/ionic-team/ionic/commit/cdc2fb652fe5aa149eaa751a77fb506ac1f64195)), closes [#22466](https://github.com/ionic-team/ionic/issues/22466)
* **vue:** unit testing a routerLink-capable component no longer warns of missing router dependency ([#22532](https://github.com/ionic-team/ionic/issues/22532)) ([4e23aad](https://github.com/ionic-team/ionic/commit/4e23aad3d911188e4a2706545463a81332c00ce9)), closes [#22506](https://github.com/ionic-team/ionic/issues/22506)
### For Ionic Vue Developers
When updating to Ionic Vue v5.5.1 make sure you are on the latest version of `vue-router@next` to take advantage of the bug fixes in this release:
```
npm install vue-router@next
```
# [5.5.0 Chlorine](https://github.com/ionic-team/ionic/compare/v5.4.4...v5.5.0) (2020-11-18)
### Bug Fixes
* **backdrop:** nvda no longer incorrectly announces backdrop ([#22481](https://github.com/ionic-team/ionic/issues/22481)) ([2d878fc](https://github.com/ionic-team/ionic/commit/2d878fc4f6c7a710dbfb722e188e3e402e1672f9)), closes [#22102](https://github.com/ionic-team/ionic/issues/22102)
* **checkbox:** use a native input to fix a11y issues with axe and screen readers ([#22402](https://github.com/ionic-team/ionic/issues/22402)) ([7214a84](https://github.com/ionic-team/ionic/commit/7214a8401b709e1353155304cf6e9f97b2b4d294)), closes [#21644](https://github.com/ionic-team/ionic/issues/21644) [#20517](https://github.com/ionic-team/ionic/issues/20517) [#17796](https://github.com/ionic-team/ionic/issues/17796)
* **input:** title attribute is now automatically inherited ([#22493](https://github.com/ionic-team/ionic/issues/22493)) ([abad12f](https://github.com/ionic-team/ionic/commit/abad12fbdb1378066282fe8e9b7761747951b685)), closes [#22055](https://github.com/ionic-team/ionic/issues/22055)
* **refresher:** ios native refresher now works in side menu ([#22449](https://github.com/ionic-team/ionic/issues/22449)) ([a4a6453](https://github.com/ionic-team/ionic/commit/a4a64530ff083b83187b293dfdacb0fa45ad9f51))
* **refresher:** md native refresher now works in side menu ([#22446](https://github.com/ionic-team/ionic/issues/22446)) ([6b817f2](https://github.com/ionic-team/ionic/commit/6b817f26b08d01d8367d16308db775b6192e7628)), closes [#20832](https://github.com/ionic-team/ionic/issues/20832)
* **toggle:** use a native input to fix a11y issues with axe and screen readers ([#22477](https://github.com/ionic-team/ionic/issues/22477)) ([813611a](https://github.com/ionic-team/ionic/commit/813611a61b664c9827760ccaa889d0e2fcae7d94)), closes [#22011](https://github.com/ionic-team/ionic/issues/22011) [#21552](https://github.com/ionic-team/ionic/issues/21552)
* **vue:** correctly pass route props to components ([#22476](https://github.com/ionic-team/ionic/issues/22476)) ([0956f8b](https://github.com/ionic-team/ionic/commit/0956f8bc5588836996c8c74f98166c347414a312)), closes [#22472](https://github.com/ionic-team/ionic/issues/22472)
* **vue:** tab bar now works with slot="top" ([#22461](https://github.com/ionic-team/ionic/issues/22461)) ([e17c822](https://github.com/ionic-team/ionic/commit/e17c822bfbc2a876226738b77a4c95c02e0b5953)), closes [#22456](https://github.com/ionic-team/ionic/issues/22456)
### Features
* **chip:** add disabled property ([#20658](https://github.com/ionic-team/ionic/issues/20658)) ([0a0cbd8](https://github.com/ionic-team/ionic/commit/0a0cbd8f2a505ad2b3d8afb60cb1e940ced52e0d)), closes [#19510](https://github.com/ionic-team/ionic/issues/19510)
* **segment:** add swipeGesture property to allow for disabling of the swipe gesture ([#22087](https://github.com/ionic-team/ionic/issues/22087)) ([65bc995](https://github.com/ionic-team/ionic/commit/65bc99577c44cce653dafd9937c4d8f9c45fff61)), closes [#22048](https://github.com/ionic-team/ionic/issues/22048)
* **vue:** composition api lifecycle methods ([#22241](https://github.com/ionic-team/ionic/issues/22241)) ([f5b0299](https://github.com/ionic-team/ionic/commit/f5b0299729c2c639e432612e62fb7eaa189ca969))
* **vue:** vetur support ([#22403](https://github.com/ionic-team/ionic/issues/22403)) ([e76f79d](https://github.com/ionic-team/ionic/commit/e76f79d0548c97edd193808f5e0a19889cffae5b))
* **vue:** web-types support ([#22428](https://github.com/ionic-team/ionic/issues/22428)) ([639314a](https://github.com/ionic-team/ionic/commit/639314ab218b65a9a2de6040417b0e1b363e47ef)), closes [#19522](https://github.com/ionic-team/ionic/issues/19522)
### Performance Improvements
* **ios:** move content to stacking context while preserving position: fixed behavior ([#22489](https://github.com/ionic-team/ionic/issues/22489)) ([d77a9d5](https://github.com/ionic-team/ionic/commit/d77a9d57ec02c69df43ec2a286eea674a85cae36)), closes [#22473](https://github.com/ionic-team/ionic/issues/22473)
## [5.4.4](https://github.com/ionic-team/ionic/compare/v5.4.3...v5.4.4) (2020-11-12)
### Bug Fixes
* **build:** add missing es5 output ([228d349](https://github.com/ionic-team/ionic/commit/228d349c6e29b62cbfee5d5502883682cfa5032f))
## [5.4.3](https://github.com/ionic-team/ionic/compare/v5.4.2...v5.4.3) (2020-11-06)
### Bug Fixes
* **all** add missing vendor prefixes to css ([0989ea5](https://github.com/ionic-team/ionic/commit/0989ea5ac897f528e8fce5434861ca080b9b4a56))
## [5.4.2](https://github.com/ionic-team/ionic/compare/v5.4.1...v5.4.2) (2020-11-05)

View File

@@ -16,14 +16,14 @@ diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
community include the ability to:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
* Demonstrate empathy and kindness towards people
* Be respectful of differing opinions, viewpoints, and experiences
* Give and gracefully accept constructive feedback
* Accept responsibility and apologize to those affected by our mistakes,
and learn from the experience
* Focus on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:

View File

@@ -1,15 +1,15 @@
{
"name": "@ionic/angular",
"version": "5.4.2",
"version": "5.6.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@ionic/angular",
"version": "5.4.2",
"version": "5.6.1",
"license": "MIT",
"dependencies": {
"@ionic/core": "file:../core",
"@ionic/core": "5.6.0",
"tslib": "^1.9.3"
},
"devDependencies": {
@@ -42,43 +42,6 @@
"zone.js": ">=0.8.26"
}
},
"../core": {
"version": "5.4.1",
"license": "MIT",
"dependencies": {
"ionicons": "^5.1.2",
"tslib": "^1.10.0"
},
"devDependencies": {
"@rollup/plugin-node-resolve": "^8.4.0",
"@rollup/plugin-virtual": "^2.0.3",
"@stencil/core": "2.1.2",
"@stencil/sass": "1.3.2",
"@stencil/vue-output-target": "0.1.8",
"@types/jest": "^26.0.10",
"@types/node": "^14.6.0",
"@types/puppeteer": "3.0.1",
"@types/swiper": "5.4.0",
"aws-sdk": "^2.738.0",
"clean-css-cli": "^4.1.11",
"domino": "^2.1.6",
"fs-extra": "^9.0.1",
"jest": "^26.4.1",
"jest-cli": "^26.4.1",
"np": "^6.4.0",
"pixelmatch": "4.0.2",
"puppeteer": "^5.2.1",
"rollup": "^2.26.4",
"sass": "^1.26.10",
"stylelint": "^13.6.1",
"stylelint-order": "^4.1.0",
"swiper": "5.4.1",
"tslint": "^6.1.3",
"tslint-ionic-rules": "0.0.21",
"tslint-react": "^5.0.0",
"typescript": "^4.0.5"
}
},
"node_modules/@angular-devkit/core": {
"version": "8.3.17",
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-8.3.17.tgz",
@@ -241,8 +204,14 @@
}
},
"node_modules/@ionic/core": {
"resolved": "../core",
"link": true
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.6.0.tgz",
"integrity": "sha512-dRwNRjHBiE2nB0JKY7tNHbcYMGXmDp7QCCVmtQbU511rZkwDlPUtg5qMJFHuLQVVUE21bjmWIJliE0aB9kcwYQ==",
"dependencies": {
"@stencil/core": "^2.4.0",
"ionicons": "^5.5.0",
"tslib": "^1.10.0"
}
},
"node_modules/@rollup/plugin-commonjs": {
"version": "11.1.0",
@@ -322,6 +291,18 @@
"node": ">=6"
}
},
"node_modules/@stencil/core": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.4.0.tgz",
"integrity": "sha512-gU6+Yyd6O0KrCSS/O6j8KKqmRo+/Dcs2fI0+APCpbAWK+nqhwDISpdnSEfGDCLMoAC08XOZCycBRk2K1VGnEcg==",
"bin": {
"stencil": "bin/stencil"
},
"engines": {
"node": ">=12.10.0",
"npm": ">=6.0.0"
}
},
"node_modules/@szmarczak/http-timer": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz",
@@ -945,7 +926,6 @@
"anymatch": "^2.0.0",
"async-each": "^1.0.1",
"braces": "^2.3.2",
"fsevents": "^1.2.7",
"glob-parent": "^3.1.0",
"inherits": "^2.0.3",
"is-binary-path": "^1.0.0",
@@ -2047,6 +2027,14 @@
"node": ">=4"
}
},
"node_modules/ionicons": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/ionicons/-/ionicons-5.5.0.tgz",
"integrity": "sha512-0DUHTeoIrGSY+KNyNDaQW7v5+mDstjSkjx8dzT925kXKYBDrN3sGs8kUcSSQbTK132U4CbgDEZkn7FDUa9x8Qw==",
"dependencies": {
"@stencil/core": "^2.4.0"
}
},
"node_modules/is-accessor-descriptor": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
@@ -2429,13 +2417,7 @@
"integrity": "sha512-+1V2PCMFkL+OIj2/HrtrvZw0BC0sYLMICJfbQjuj/K8CEnlrFX6R5cKKgzzttsZDHyxQNL1jqMREjKN3ja/E3Q==",
"dev": true,
"dependencies": {
"errno": "^0.1.1",
"graceful-fs": "^4.1.2",
"image-size": "~0.5.0",
"make-dir": "^2.1.0",
"mime": "^1.4.1",
"native-request": "^1.0.5",
"source-map": "~0.6.0",
"tslib": "^1.10.0"
},
"bin": {
@@ -2818,7 +2800,6 @@
"dependencies": {
"anymatch": "~3.1.1",
"braces": "~3.0.2",
"fsevents": "~2.1.2",
"glob-parent": "~5.1.0",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
@@ -2959,9 +2940,6 @@
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.7.5.tgz",
"integrity": "sha512-xQSM8uzhgtF6tTnTVEvOQThrcG3LPUP3T/4l4EukzDp0kbTY1QRDuXjiwtYzs9odKj9Bj/PccRG6viFfS7DmCQ==",
"dev": true,
"dependencies": {
"fsevents": "~2.1.2"
},
"bin": {
"rollup": "dist/bin/rollup"
},
@@ -5173,37 +5151,13 @@
}
},
"@ionic/core": {
"version": "file:../core",
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/@ionic/core/-/core-5.6.0.tgz",
"integrity": "sha512-dRwNRjHBiE2nB0JKY7tNHbcYMGXmDp7QCCVmtQbU511rZkwDlPUtg5qMJFHuLQVVUE21bjmWIJliE0aB9kcwYQ==",
"requires": {
"@rollup/plugin-node-resolve": "^8.4.0",
"@rollup/plugin-virtual": "^2.0.3",
"@stencil/core": "2.1.2",
"@stencil/sass": "1.3.2",
"@stencil/vue-output-target": "0.1.8",
"@types/jest": "^26.0.10",
"@types/node": "^14.6.0",
"@types/puppeteer": "3.0.1",
"@types/swiper": "5.4.0",
"aws-sdk": "^2.738.0",
"clean-css-cli": "^4.1.11",
"domino": "^2.1.6",
"fs-extra": "^9.0.1",
"ionicons": "^5.1.2",
"jest": "^26.4.1",
"jest-cli": "^26.4.1",
"np": "^6.4.0",
"pixelmatch": "4.0.2",
"puppeteer": "^5.2.1",
"rollup": "^2.26.4",
"sass": "^1.26.10",
"stylelint": "^13.6.1",
"stylelint-order": "^4.1.0",
"swiper": "5.4.1",
"tslib": "^1.10.0",
"tslint": "^6.1.3",
"tslint-ionic-rules": "0.0.21",
"tslint-react": "^5.0.0",
"typescript": "^4.0.5"
"@stencil/core": "^2.4.0",
"ionicons": "^5.5.0",
"tslib": "^1.10.0"
}
},
"@rollup/plugin-commonjs": {
@@ -5276,6 +5230,11 @@
"integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==",
"dev": true
},
"@stencil/core": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@stencil/core/-/core-2.4.0.tgz",
"integrity": "sha512-gU6+Yyd6O0KrCSS/O6j8KKqmRo+/Dcs2fI0+APCpbAWK+nqhwDISpdnSEfGDCLMoAC08XOZCycBRk2K1VGnEcg=="
},
"@szmarczak/http-timer": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz",
@@ -6690,6 +6649,14 @@
"integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==",
"dev": true
},
"ionicons": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/ionicons/-/ionicons-5.5.0.tgz",
"integrity": "sha512-0DUHTeoIrGSY+KNyNDaQW7v5+mDstjSkjx8dzT925kXKYBDrN3sGs8kUcSSQbTK132U4CbgDEZkn7FDUa9x8Qw==",
"requires": {
"@stencil/core": "^2.4.0"
}
},
"is-accessor-descriptor": {
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/angular",
"version": "5.4.2",
"version": "5.6.1",
"description": "Angular specific wrappers for @ionic/core",
"keywords": [
"ionic",
@@ -42,7 +42,7 @@
"validate": "npm i && npm run lint && npm run test && npm run build"
},
"dependencies": {
"@ionic/core": "5.4.2",
"@ionic/core": "5.6.1",
"tslib": "^1.9.3"
},
"peerDependencies": {

View File

@@ -1,6 +1,7 @@
import { Location } from '@angular/common';
import { Attribute, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, EventEmitter, Injector, NgZone, OnDestroy, OnInit, Optional, Output, SkipSelf, ViewContainerRef } from '@angular/core';
import { ActivatedRoute, ChildrenOutletContexts, OutletContext, PRIMARY_OUTLET, Router } from '@angular/router';
import { componentOnReady } from '@ionic/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { distinctUntilChanged, filter, switchMap } from 'rxjs/operators';
@@ -96,13 +97,12 @@ export class IonRouterOutlet implements OnDestroy, OnInit {
this.activateWith(context.route, context.resolver || null);
}
}
if ((this.nativeEl as any).componentOnReady) {
this.nativeEl.componentOnReady().then(() => {
if (this._swipeGesture === undefined) {
this.swipeGesture = this.config.getBoolean('swipeBackEnabled', (this.nativeEl as any).mode === 'ios');
}
});
}
new Promise(resolve => componentOnReady(this.nativeEl, resolve)).then(() => {
if (this._swipeGesture === undefined) {
this.swipeGesture = this.config.getBoolean('swipeBackEnabled', (this.nativeEl as any).mode === 'ios');
}
});
}
get isActivated(): boolean {

View File

@@ -156,8 +156,8 @@ export class IonCheckbox {
}
export declare interface IonChip extends Components.IonChip {
}
@ProxyCmp({ inputs: ["color", "mode", "outline"] })
@Component({ selector: "ion-chip", changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content></ng-content>", inputs: ["color", "mode", "outline"] })
@ProxyCmp({ inputs: ["color", "disabled", "mode", "outline"] })
@Component({ selector: "ion-chip", changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content></ng-content>", inputs: ["color", "disabled", "mode", "outline"] })
export class IonChip {
protected el: HTMLElement;
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
@@ -278,8 +278,8 @@ export class IonHeader {
}
export declare interface IonIcon extends Components.IonIcon {
}
@ProxyCmp({ inputs: ["ariaLabel", "color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "size", "src"] })
@Component({ selector: "ion-icon", changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content></ng-content>", inputs: ["ariaLabel", "color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "size", "src"] })
@ProxyCmp({ inputs: ["ariaHidden", "ariaLabel", "color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] })
@Component({ selector: "ion-icon", changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content></ng-content>", inputs: ["ariaHidden", "ariaLabel", "color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] })
export class IonIcon {
protected el: HTMLElement;
constructor(c: ChangeDetectorRef, r: ElementRef, protected z: NgZone) {
@@ -643,8 +643,8 @@ export class IonRow {
}
export declare interface IonSearchbar extends Components.IonSearchbar {
}
@ProxyCmp({ inputs: ["animated", "autocomplete", "autocorrect", "cancelButtonIcon", "cancelButtonText", "clearIcon", "color", "debounce", "disabled", "enterkeyhint", "inputmode", "mode", "placeholder", "searchIcon", "showCancelButton", "spellcheck", "type", "value"], "methods": ["setFocus", "getInputElement"] })
@Component({ selector: "ion-searchbar", changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content></ng-content>", inputs: ["animated", "autocomplete", "autocorrect", "cancelButtonIcon", "cancelButtonText", "clearIcon", "color", "debounce", "disabled", "enterkeyhint", "inputmode", "mode", "placeholder", "searchIcon", "showCancelButton", "spellcheck", "type", "value"] })
@ProxyCmp({ inputs: ["animated", "autocomplete", "autocorrect", "cancelButtonIcon", "cancelButtonText", "clearIcon", "color", "debounce", "disabled", "enterkeyhint", "inputmode", "mode", "placeholder", "searchIcon", "showCancelButton", "showClearButton", "spellcheck", "type", "value"], "methods": ["setFocus", "getInputElement"] })
@Component({ selector: "ion-searchbar", changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content></ng-content>", inputs: ["animated", "autocomplete", "autocorrect", "cancelButtonIcon", "cancelButtonText", "clearIcon", "color", "debounce", "disabled", "enterkeyhint", "inputmode", "mode", "placeholder", "searchIcon", "showCancelButton", "showClearButton", "spellcheck", "type", "value"] })
export class IonSearchbar {
ionInput!: EventEmitter<CustomEvent>;
ionChange!: EventEmitter<CustomEvent>;
@@ -661,8 +661,8 @@ export class IonSearchbar {
}
export declare interface IonSegment extends Components.IonSegment {
}
@ProxyCmp({ inputs: ["color", "disabled", "mode", "scrollable", "value"] })
@Component({ selector: "ion-segment", changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content></ng-content>", inputs: ["color", "disabled", "mode", "scrollable", "value"] })
@ProxyCmp({ inputs: ["color", "disabled", "mode", "scrollable", "swipeGesture", "value"] })
@Component({ selector: "ion-segment", changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-content></ng-content>", inputs: ["color", "disabled", "mode", "scrollable", "swipeGesture", "value"] })
export class IonSegment {
ionChange!: EventEmitter<CustomEvent>;
protected el: HTMLElement;

View File

@@ -71,7 +71,7 @@ export declare interface IonVirtualScroll {
* entire virtual scroll is reset, which is an expensive operation and
* should be avoided if possible.
*/
items?: any[];
items?: any[] | null;
/**
* An optional function that maps each item within their height.

View File

@@ -40,20 +40,23 @@ function addIonicAngularModuleToAppModule(projectSourceRoot: Path): Rule {
function addIonicStyles(projectName: string, projectSourceRoot: Path): Rule {
return (host: Tree) => {
const ionicStyles = [
'node_modules/@ionic/angular/css/core.css',
'node_modules/@ionic/angular/css/normalize.css',
'node_modules/@ionic/angular/css/structure.css',
'node_modules/@ionic/angular/css/typography.css',
'node_modules/@ionic/angular/css/core.css',
'node_modules/@ionic/angular/css/display.css',
'node_modules/@ionic/angular/css/padding.css',
'node_modules/@ionic/angular/css/float-elements.css',
'node_modules/@ionic/angular/css/text-alignment.css',
'node_modules/@ionic/angular/css/text-transformation.css',
'node_modules/@ionic/angular/css/flex-utils.css',
`${projectSourceRoot}/theme/variables.css`
].forEach(entry => {
]
ionicStyles.forEach(entry => {
addStyle(host, projectName, entry);
});
return host;
return host;
};
}
@@ -64,7 +67,8 @@ function addIonicons(projectName: string): Rule {
input: 'node_modules/ionicons/dist/ionicons/svg',
output: './svg'
};
addAsset(host, projectName, ioniconsGlob);
addAsset(host, projectName, 'build', ioniconsGlob);
addAsset(host, projectName, 'test', ioniconsGlob);
return host;
};
}

View File

@@ -62,10 +62,10 @@ export function addStyle(host: Tree, projectName: string, stylePath: string) {
writeConfig(host, config);
}
export function addAsset(host: Tree, projectName: string, asset: string | {glob: string; input: string; output: string}) {
export function addAsset(host: Tree, projectName: string, architect: string, asset: string | {glob: string; input: string; output: string}) {
const config = readConfig(host);
const appConfig = getAngularAppConfig(config, projectName);
appConfig.architect.build.options.assets.push(asset);
appConfig.architect[architect].options.assets.push(asset);
writeConfig(host, config);
}

View File

@@ -10,6 +10,6 @@ export interface IonicWindow extends Window {
}
export interface HTMLStencilElement extends HTMLElement {
componentOnReady(): Promise<this>;
forceUpdate(): void;
componentOnReady?(): Promise<this>;
forceUpdate?(): void;
}

View File

@@ -8,7 +8,7 @@
"sync:build": "sh scripts/build-ionic.sh",
"sync": "sh scripts/sync.sh",
"build": "npm run sync && ng build --prod --no-progress",
"pretest": "webdriver-manager update --versions.chrome 85.0.4183.87",
"pretest": "webdriver-manager update --versions.chrome 89.0.4389.23",
"test": "ng e2e --prod --webdriver-update=false",
"test.dev": "npm run sync && ng e2e",
"lint": "ng lint",

View File

@@ -40,6 +40,24 @@ The `@ionic/core` package can by used in simple HTML, or by vanilla JavaScript w
* [@ionic/angular](https://www.npmjs.com/package/@ionic/angular)
## Custom Elements Build (Experimental)
In addition to the default, self lazy-loading components built by Stencil, this package also comes with each component exported as a stand-alone custom element within `@ionic/core/components`. Each component extends `HTMLElement`, and does not lazy-load itself. Instead, this package is useful for projects already using a bundler such as Webpack or Rollup. While all components are available to be imported, the custom elements build also ensures bundlers only import what's used, and tree-shakes any unused components.
Below is an example of importing `ion-toggle`, and initializing Ionic so it's able to correctly load the "mode", such as Material Design or iOS. Additionally, the `initialize({...})` function can receive the Ionic config.
```typescript
import { IonBadge } from "@ionic/core/components/ion-badge";
import { initialize } from "@ionic/core/components";
initialize();
customElements.define("ion-badge", IonBadge);
```
Notice how `IonBadge` is imported from `@ionic/core/components/ion-badge` rather than just `@ionic/core/components`. Additionally, the `initialize` function is imported from `@ionic/core/components` rather than `@ionic/core`. All of this helps to ensure bundlers do not pull in more code than is needed.
## How to contribute
[Check out the CONTRIBUTE guide](CONTRIBUTING.md)

View File

@@ -248,6 +248,7 @@ ion-checkbox,part,mark
ion-chip,shadow
ion-chip,prop,color,string | undefined,undefined,false,false
ion-chip,prop,disabled,boolean,false,false,false
ion-chip,prop,mode,"ios" | "md",undefined,false,false
ion-chip,prop,outline,boolean,false,false,false
ion-chip,css-prop,--background
@@ -843,6 +844,9 @@ ion-progress-bar,prop,value,number,0,false,false
ion-progress-bar,css-prop,--background
ion-progress-bar,css-prop,--buffer-background
ion-progress-bar,css-prop,--progress-background
ion-progress-bar,part,progress
ion-progress-bar,part,stream
ion-progress-bar,part,track
ion-radio,shadow
ion-radio,prop,color,string | undefined,undefined,false,false
@@ -986,6 +990,7 @@ ion-searchbar,prop,mode,"ios" | "md",undefined,false,false
ion-searchbar,prop,placeholder,string,'Search',false,false
ion-searchbar,prop,searchIcon,string | undefined,undefined,false,false
ion-searchbar,prop,showCancelButton,"always" | "focus" | "never",'never',false,false
ion-searchbar,prop,showClearButton,"always" | "focus" | "never",'focus',false,false
ion-searchbar,prop,spellcheck,boolean,false,false,false
ion-searchbar,prop,type,"email" | "number" | "password" | "search" | "tel" | "text" | "url",'search',false,false
ion-searchbar,prop,value,null | string | undefined,'',false,false
@@ -1014,6 +1019,7 @@ ion-segment,prop,color,string | undefined,undefined,false,false
ion-segment,prop,disabled,boolean,false,false,false
ion-segment,prop,mode,"ios" | "md",undefined,false,false
ion-segment,prop,scrollable,boolean,false,false,false
ion-segment,prop,swipeGesture,boolean,true,false,false
ion-segment,prop,value,null | string | undefined,undefined,false,false
ion-segment,event,ionChange,SegmentChangeEventDetail,true
ion-segment,css-prop,--background

12848
core/package-lock.json generated
View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "@ionic/core",
"version": "5.4.2",
"version": "5.6.1",
"description": "Base components for Ionic",
"keywords": [
"ionic",
@@ -24,24 +24,26 @@
"collection": "dist/collection/collection-manifest.json",
"types": "dist/types/interface.d.ts",
"files": [
"dist/",
"components/",
"css/",
"dist/",
"hydrate/",
"loader/"
],
"dependencies": {
"ionicons": "^5.1.2",
"@stencil/core": "^2.4.0",
"ionicons": "^5.5.0",
"tslib": "^1.10.0"
},
"devDependencies": {
"@jest/core": "^26.6.3",
"@rollup/plugin-node-resolve": "^8.4.0",
"@rollup/plugin-virtual": "^2.0.3",
"@stencil/core": "2.1.2",
"@stencil/sass": "1.3.2",
"@stencil/vue-output-target": "0.1.8",
"@types/jest": "^26.0.10",
"@stencil/vue-output-target": "^0.4.1",
"@types/jest": "^26.0.20",
"@types/node": "^14.6.0",
"@types/puppeteer": "3.0.1",
"@types/puppeteer": "5.4.3",
"@types/swiper": "5.4.0",
"aws-sdk": "^2.738.0",
"clean-css-cli": "^4.1.11",
@@ -51,7 +53,7 @@
"jest-cli": "^26.4.1",
"np": "^6.4.0",
"pixelmatch": "4.0.2",
"puppeteer": "^5.2.1",
"puppeteer": "^7.0.1",
"rollup": "^2.26.4",
"sass": "^1.26.10",
"stylelint": "^13.6.1",
@@ -75,7 +77,7 @@
"css.sass": "sass src/css:./css",
"lint": "npm run lint.ts && npm run lint.sass",
"lint.fix": "npm run lint.ts.fix && npm run lint.sass.fix",
"lint.sass": "stylelint 'src/**/*.scss'",
"lint.sass": "stylelint \"src/**/*.scss\"",
"lint.sass.fix": "npm run lint.sass -- --fix",
"lint.ts": "tslint --project .",
"lint.ts.fix": "tslint --project . --fix",

View File

@@ -0,0 +1,2 @@
export * from './index';
export * from '../dist/types/interface';

View File

@@ -0,0 +1,9 @@
{
"name": "@ionic/core/components",
"version": "0.0.0",
"description": "Ionic Components exported as custom elements, extending HTMLElement.",
"main": "./index.js",
"types": "./custom-elements.d.ts",
"private": true,
"sideEffects": false
}

View File

@@ -4,15 +4,15 @@ const virtual = require('@rollup/plugin-virtual');
const fs = require('fs');
async function main() {
const input = process.argv[2] || getInput();
const result = await check(input);
const relative = path.relative(process.cwd(), input);
const input = process.argv[2] || getMainEntry();
const result = await check(input);
const relative = path.relative(process.cwd(), input);
if (result.shaken) {
console.error(`Success! ${relative} is fully tree-shakeable`);
} else {
error(`Failed to tree-shake ${relative}`);
}
if (result.shaken) {
console.error(`Success! ${relative} is fully tree-shakeable`);
} else {
error(`Failed to tree-shake ${relative}`);
};
}
function error(msg) {
@@ -20,10 +20,10 @@ function error(msg) {
process.exit(1);
}
function getInput() {
if (!fs.existsSync('package.json')) {
error(`Could not find package.json`);
}
function getMainEntry() {
if (!fs.existsSync('package.json')) {
error(`Could not find package.json`);
}
const pkg = JSON.parse(fs.readFileSync('package.json'), 'utf-8');
@@ -38,20 +38,20 @@ function getInput() {
}
function resolve(file) {
if (isDirectory(file)) {
return ifExists(`${file}/index.js`) || ifExists(`${file}/index.cjs.js`);
}
if (isDirectory(file)) {
return ifExists(`${file}/index.cjs.js`) || ifExists(`${file}/index.js`);
}
return ifExists(file) || ifExists(`${file}.js`) || ifExists(`${file}.cjs.js`);
return ifExists(file) || ifExists(`${file}.cjs.js`) || ifExists(`${file}.js`);
}
function isDirectory(file) {
try {
const stats = fs.statSync(file);
return stats.isDirectory();
} catch (err) {
return false;
}
try {
const stats = fs.statSync(file);
return stats.isDirectory();
} catch (err) {
return false;
}
}
function ifExists(file) {
@@ -67,7 +67,7 @@ async function check(input) {
virtual({
__agadoo__: `import ${JSON.stringify(resolved)}`,
tslib: `
const noop = () => {};
const noop = () => {};
export const __awaiter = noop;
export const __extends = noop;
export const __generator = noop;

View File

@@ -394,7 +394,7 @@ export namespace Components {
*/
"name": string;
/**
* The value of the toggle does not mean if it's checked or not, use the `checked` property for that. The value of a toggle is analogous to the value of a `<input type="checkbox">`, it's only used when the toggle participates in a native `<form>`.
* The value of the checkbox does not mean if it's checked or not, use the `checked` property for that. The value of a checkbox is analogous to the value of an `<input type="checkbox">`, it's only used when the checkbox participates in a native `<form>`.
*/
"value": string;
}
@@ -403,6 +403,10 @@ export namespace Components {
* The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics).
*/
"color"?: Color;
/**
* If `true`, the user cannot interact with the chip.
*/
"disabled": boolean;
/**
* The mode determines which platform styles to use.
*/
@@ -830,7 +834,7 @@ export namespace Components {
*/
"accept"?: string;
/**
* Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user.
* Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user. Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`.
*/
"autocapitalize": string;
/**
@@ -1221,7 +1225,7 @@ export namespace Components {
*/
"close": (animated?: boolean) => Promise<boolean>;
/**
* The content's id the menu should use.
* The `id` of the main content. When using a router this is typically `ion-router-outlet`. When not using a router, this is typically your main view's `ion-content`. This is not the id of the `ion-content` inside of your `ion-menu`.
*/
"contentId"?: string;
/**
@@ -1704,7 +1708,7 @@ export namespace Components {
*/
"name": string;
"setButtonTabindex": (value: number) => Promise<void>;
"setFocus": () => Promise<void>;
"setFocus": (ev: any) => Promise<void>;
/**
* the value of the radio.
*/
@@ -2030,6 +2034,10 @@ export namespace Components {
* Sets the behavior for the cancel button. Defaults to `"never"`. Setting to `"focus"` shows the cancel button on focus. Setting to `"never"` hides the cancel button. Setting to `"always"` shows the cancel button regardless of focus state.
*/
"showCancelButton": 'never' | 'focus' | 'always';
/**
* Sets the behavior for the clear button. Defaults to `"focus"`. Setting to `"focus"` shows the clear button on focus if the input is not empty. Setting to `"never"` hides the clear button. Setting to `"always"` shows the clear button regardless of focus state, but only if the input is not empty.
*/
"showClearButton": 'never' | 'focus' | 'always';
/**
* If `true`, enable spellcheck on the input.
*/
@@ -2060,6 +2068,10 @@ export namespace Components {
* If `true`, the segment buttons will overflow and the user can swipe to see them. In addition, this will disable the gesture to drag the indicator between the buttons in order to swipe to see hidden buttons.
*/
"scrollable": boolean;
/**
* If `true`, users will be able to swipe between segment buttons to activate them.
*/
"swipeGesture": boolean;
/**
* the value of the segment.
*/
@@ -2291,7 +2303,7 @@ export namespace Components {
}
interface IonSplitPane {
/**
* The content `id` of the split-pane's main content.
* The `id` of the main content. When using a router this is typically `ion-router-outlet`. When not using a router, this is typically your main view's `ion-content`. This is not the id of the `ion-content` inside of your `ion-menu`.
*/
"contentId"?: string;
/**
@@ -3701,7 +3713,7 @@ declare namespace LocalJSX {
*/
"name"?: string;
/**
* Emitted when the toggle loses focus.
* Emitted when the checkbox loses focus.
*/
"onIonBlur"?: (event: CustomEvent<void>) => void;
/**
@@ -3709,7 +3721,7 @@ declare namespace LocalJSX {
*/
"onIonChange"?: (event: CustomEvent<CheckboxChangeEventDetail>) => void;
/**
* Emitted when the toggle has focus.
* Emitted when the checkbox has focus.
*/
"onIonFocus"?: (event: CustomEvent<void>) => void;
/**
@@ -3717,7 +3729,7 @@ declare namespace LocalJSX {
*/
"onIonStyle"?: (event: CustomEvent<StyleEventDetail>) => void;
/**
* The value of the toggle does not mean if it's checked or not, use the `checked` property for that. The value of a toggle is analogous to the value of a `<input type="checkbox">`, it's only used when the toggle participates in a native `<form>`.
* The value of the checkbox does not mean if it's checked or not, use the `checked` property for that. The value of a checkbox is analogous to the value of an `<input type="checkbox">`, it's only used when the checkbox participates in a native `<form>`.
*/
"value"?: string;
}
@@ -3726,6 +3738,10 @@ declare namespace LocalJSX {
* The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics).
*/
"color"?: Color;
/**
* If `true`, the user cannot interact with the chip.
*/
"disabled"?: boolean;
/**
* The mode determines which platform styles to use.
*/
@@ -4169,7 +4185,7 @@ declare namespace LocalJSX {
*/
"accept"?: string;
/**
* Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user.
* Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user. Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`.
*/
"autocapitalize"?: string;
/**
@@ -4552,7 +4568,7 @@ declare namespace LocalJSX {
}
interface IonMenu {
/**
* The content's id the menu should use.
* The `id` of the main content. When using a router this is typically `ion-router-outlet`. When not using a router, this is typically your main view's `ion-content`. This is not the id of the `ion-content` inside of your `ion-menu`.
*/
"contentId"?: string;
/**
@@ -5324,6 +5340,10 @@ declare namespace LocalJSX {
* Sets the behavior for the cancel button. Defaults to `"never"`. Setting to `"focus"` shows the cancel button on focus. Setting to `"never"` hides the cancel button. Setting to `"always"` shows the cancel button regardless of focus state.
*/
"showCancelButton"?: 'never' | 'focus' | 'always';
/**
* Sets the behavior for the clear button. Defaults to `"focus"`. Setting to `"focus"` shows the clear button on focus if the input is not empty. Setting to `"never"` hides the clear button. Setting to `"always"` shows the clear button regardless of focus state, but only if the input is not empty.
*/
"showClearButton"?: 'never' | 'focus' | 'always';
/**
* If `true`, enable spellcheck on the input.
*/
@@ -5366,6 +5386,10 @@ declare namespace LocalJSX {
* If `true`, the segment buttons will overflow and the user can swipe to see them. In addition, this will disable the gesture to drag the indicator between the buttons in order to swipe to see hidden buttons.
*/
"scrollable"?: boolean;
/**
* If `true`, users will be able to swipe between segment buttons to activate them.
*/
"swipeGesture"?: boolean;
/**
* the value of the segment.
*/
@@ -5601,7 +5625,7 @@ declare namespace LocalJSX {
}
interface IonSplitPane {
/**
* The content `id` of the split-pane's main content.
* The `id` of the main content. When using a router this is typically `ion-router-outlet`. When not using a router, this is typically your main view's `ion-content`. This is not the id of the `ion-content` inside of your `ion-menu`.
*/
"contentId"?: string;
/**

View File

@@ -155,6 +155,59 @@ async function presentActionSheet() {
### React
```tsx
/* Using with useIonActionSheet Hook */
import React from 'react';
import {
IonButton,
IonContent,
IonPage,
useIonActionSheet,
} from '@ionic/react';
const ActionSheetExample: React.FC = () => {
const [present, dismiss] = useIonActionSheet();
return (
<IonPage>
<IonContent>
<IonButton
expand="block"
onClick={() =>
present({
buttons: [{ text: 'Ok' }, { text: 'Cancel' }],
header: 'Action Sheet'
})
}
>
Show ActionSheet
</IonButton>
<IonButton
expand="block"
onClick={() =>
present([{ text: 'Ok' }, { text: 'Cancel' }], 'Action Sheet')
}
>
Show ActionSheet using params
</IonButton>
<IonButton
expand="block"
onClick={() => {
present([{ text: 'Ok' }, { text: 'Cancel' }], 'Action Sheet');
setTimeout(dismiss, 3000);
}}
>
Show ActionSheet, hide after 3 seconds
</IonButton>
</IonContent>
</IonPage>
);
};
```
```tsx
/* Using with IonActionSheet Component */
import React, { useState } from 'react';
import { IonActionSheet, IonContent, IonButton } from '@ionic/react';
import { trash, share, caretForwardCircle, heart, close } from 'ionicons/icons';

View File

@@ -71,7 +71,7 @@ export const testActionSheetAlert = async (
const alert = await page.find('ion-alert');
await alert.waitForVisible();
await page.waitFor(250);
await page.waitForTimeout(250);
screenshotCompares.push(await page.compareScreenshot(`alert open`));

View File

@@ -1,4 +1,57 @@
```tsx
/* Using with useIonActionSheet Hook */
import React from 'react';
import {
IonButton,
IonContent,
IonPage,
useIonActionSheet,
} from '@ionic/react';
const ActionSheetExample: React.FC = () => {
const [present, dismiss] = useIonActionSheet();
return (
<IonPage>
<IonContent>
<IonButton
expand="block"
onClick={() =>
present({
buttons: [{ text: 'Ok' }, { text: 'Cancel' }],
header: 'Action Sheet'
})
}
>
Show ActionSheet
</IonButton>
<IonButton
expand="block"
onClick={() =>
present([{ text: 'Ok' }, { text: 'Cancel' }], 'Action Sheet')
}
>
Show ActionSheet using params
</IonButton>
<IonButton
expand="block"
onClick={() => {
present([{ text: 'Ok' }, { text: 'Cancel' }], 'Action Sheet');
setTimeout(dismiss, 3000);
}}
>
Show ActionSheet, hide after 3 seconds
</IonButton>
</IonContent>
</IonPage>
);
};
```
```tsx
/* Using with IonActionSheet Component */
import React, { useState } from 'react';
import { IonActionSheet, IonContent, IonButton } from '@ionic/react';
import { trash, share, caretForwardCircle, heart, close } from 'ionicons/icons';

View File

@@ -158,7 +158,7 @@ export class Alert implements ComponentInterface, OverlayInterface {
// If hitting arrow down or arrow right, move to the next radio
// If we're on the last radio, move to the first radio
if (['ArrowDown', 'ArrowRight'].includes(ev.key)) {
if (['ArrowDown', 'ArrowRight'].includes(ev.code)) {
nextEl = (index === radios.length - 1)
? radios[0]
: radios[index + 1];
@@ -166,7 +166,7 @@ export class Alert implements ComponentInterface, OverlayInterface {
// If hitting arrow up or arrow left, move to the previous radio
// If we're on the first radio, move to the last radio
if (['ArrowUp', 'ArrowLeft'].includes(ev.key)) {
if (['ArrowUp', 'ArrowLeft'].includes(ev.code)) {
nextEl = (index === 0)
? radios[radios.length - 1]
: radios[index - 1];
@@ -373,22 +373,24 @@ export class Alert implements ComponentInterface, OverlayInterface {
return values;
}
private renderAlertInputs(labelledBy: string | undefined) {
private renderAlertInputs() {
switch (this.inputType) {
case 'checkbox': return this.renderCheckbox(labelledBy);
case 'radio': return this.renderRadio(labelledBy);
default: return this.renderInput(labelledBy);
case 'checkbox': return this.renderCheckbox();
case 'radio': return this.renderRadio();
default: return this.renderInput();
}
}
private renderCheckbox(labelledby: string | undefined) {
private renderCheckbox() {
const inputs = this.processedInputs;
const mode = getIonMode(this);
if (inputs.length === 0) {
return null;
}
return (
<div class="alert-checkbox-group" aria-labelledby={labelledby}>
<div class="alert-checkbox-group">
{ inputs.map(i => (
<button
type="button"
@@ -422,13 +424,15 @@ export class Alert implements ComponentInterface, OverlayInterface {
);
}
private renderRadio(labelledby: string | undefined) {
private renderRadio() {
const inputs = this.processedInputs;
if (inputs.length === 0) {
return null;
}
return (
<div class="alert-radio-group" role="radiogroup" aria-labelledby={labelledby} aria-activedescendant={this.activeId}>
<div class="alert-radio-group" role="radiogroup" aria-activedescendant={this.activeId}>
{ inputs.map(i => (
<button
type="button"
@@ -459,13 +463,13 @@ export class Alert implements ComponentInterface, OverlayInterface {
);
}
private renderInput(labelledby: string | undefined) {
private renderInput() {
const inputs = this.processedInputs;
if (inputs.length === 0) {
return null;
}
return (
<div class="alert-input-group" aria-labelledby={labelledby}>
<div class="alert-input-group">
{ inputs.map(i => {
if (i.type === 'textarea') {
return (
@@ -552,13 +556,6 @@ export class Alert implements ComponentInterface, OverlayInterface {
const subHdrId = `alert-${overlayIndex}-sub-hdr`;
const msgId = `alert-${overlayIndex}-msg`;
let labelledById: string | undefined;
if (header !== undefined) {
labelledById = hdrId;
} else if (subHeader !== undefined) {
labelledById = subHdrId;
}
return (
<Host
role="dialog"
@@ -589,7 +586,7 @@ export class Alert implements ComponentInterface, OverlayInterface {
<div id={msgId} class="alert-message" innerHTML={sanitizeDOMString(this.message)}></div>
{this.renderAlertInputs(labelledById)}
{this.renderAlertInputs()}
{this.renderAlertButtons()}
</div>

View File

@@ -588,6 +588,48 @@ function presentAlertCheckbox() {
### React
```tsx
/* Using with useIonAlert Hook */
import React from 'react';
import { IonButton, IonContent, IonPage, useIonAlert } from '@ionic/react';
const AlertExample: React.FC = () => {
const [present] = useIonAlert();
return (
<IonPage>
<IonContent fullscreen>
<IonButton
expand="block"
onClick={() =>
present({
cssClass: 'my-css',
header: 'Alert',
message: 'alert from hook',
buttons: [
'Cancel',
{ text: 'Ok', handler: (d) => console.log('ok pressed') },
],
onDidDismiss: (e) => console.log('did dismiss'),
})
}
>
Show Alert
</IonButton>
<IonButton
expand="block"
onClick={() => present('hello with params', [{ text: 'Ok' }])}
>
Show Alert using params
</IonButton>
</IonContent>
</IonPage>
);
};
```
```tsx
/* Using with IonAlert Component */
import React, { useState } from 'react';
import { IonAlert, IonButton, IonContent } from '@ionic/react';

View File

@@ -1,4 +1,46 @@
```tsx
/* Using with useIonAlert Hook */
import React from 'react';
import { IonButton, IonContent, IonPage, useIonAlert } from '@ionic/react';
const AlertExample: React.FC = () => {
const [present] = useIonAlert();
return (
<IonPage>
<IonContent fullscreen>
<IonButton
expand="block"
onClick={() =>
present({
cssClass: 'my-css',
header: 'Alert',
message: 'alert from hook',
buttons: [
'Cancel',
{ text: 'Ok', handler: (d) => console.log('ok pressed') },
],
onDidDismiss: (e) => console.log('did dismiss'),
})
}
>
Show Alert
</IonButton>
<IonButton
expand="block"
onClick={() => present('hello with params', [{ text: 'Ok' }])}
>
Show Alert using params
</IonButton>
</IonContent>
</IonPage>
);
};
```
```tsx
/* Using with IonAlert Component */
import React, { useState } from 'react';
import { IonAlert, IonButton, IonContent } from '@ionic/react';

View File

@@ -13,7 +13,7 @@ export class App implements ComponentInterface {
componentDidLoad() {
if (Build.isBrowser) {
rIC(() => {
rIC(async () => {
const isHybrid = isPlatform(window, 'hybrid');
if (!config.getBoolean('_testing')) {
import('../../utils/tap-click').then(module => module.startTapClick(config));
@@ -24,8 +24,11 @@ export class App implements ComponentInterface {
if (config.getBoolean('inputShims', needInputShims())) {
import('../../utils/input-shims/input-shims').then(module => module.startInputShims(config));
}
const hardwareBackButtonModule = await import('../../utils/hardware-back-button');
if (config.getBoolean('hardwareBackButton', isHybrid)) {
import('../../utils/hardware-back-button').then(module => module.startHardwareBackButton());
hardwareBackButtonModule.startHardwareBackButton();
} else {
hardwareBackButtonModule.blockHardwareBackButton();
}
if (typeof (window as any) !== 'undefined') {
import('../../utils/keyboard/keyboard').then(module => module.startKeyboardAssist(window));

View File

@@ -67,6 +67,7 @@ export class Backdrop implements ComponentInterface {
return (
<Host
tabindex="-1"
aria-hidden="true"
class={{
[mode]: true,
'backdrop-hide': !this.visible,

View File

@@ -3,7 +3,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Host, Prop
import { getIonMode } from '../../global/ionic-global';
import { AnimationBuilder, Color, RouterDirection } from '../../interface';
import { AnchorInterface, ButtonInterface } from '../../utils/element-interface';
import { hasShadowDom } from '../../utils/helpers';
import { hasShadowDom, inheritAttributes } from '../../utils/helpers';
import { createColorClasses, hostContext, openURL } from '../../utils/theme';
/**
@@ -28,6 +28,7 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
private inItem = false;
private inListHeader = false;
private inToolbar = false;
private inheritedAttributes: { [k: string]: any } = {};
@Element() el!: HTMLElement;
@@ -134,6 +135,7 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
this.inToolbar = !!this.el.closest('ion-buttons');
this.inListHeader = !!this.el.closest('ion-list-header');
this.inItem = !!this.el.closest('ion-item') || !!this.el.closest('ion-item-divider');
this.inheritedAttributes = inheritAttributes(this.el, ['aria-label']);
}
private get hasIconOnly() {
@@ -184,7 +186,7 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
render() {
const mode = getIonMode(this);
const { buttonType, type, disabled, rel, target, size, href, color, expand, hasIconOnly, shape, strong } = this;
const { buttonType, type, disabled, rel, target, size, href, color, expand, hasIconOnly, shape, strong, inheritedAttributes } = this;
const finalSize = size === undefined && this.inItem ? 'small' : size;
const TagType = href === undefined ? 'button' : 'a' as any;
const attrs = (TagType === 'button')
@@ -227,6 +229,7 @@ export class Button implements ComponentInterface, AnchorInterface, ButtonInterf
disabled={disabled}
onFocus={this.onFocus}
onBlur={this.onBlur}
{...inheritedAttributes}
>
<span class="button-inner">
<slot name="icon-only"></slot>

View File

@@ -50,6 +50,7 @@
<ion-button class="wide">wide</ion-button>
<ion-button class="large">large</ion-button>
<ion-button class="round">rounded</ion-button>
<ion-button aria-label="this is my custom label">custom aria-label</ion-button>
<!-- Custom Colors -->
<ion-button class="custom">custom</ion-button>

View File

@@ -40,8 +40,18 @@
--checkmark-color: #{current-color(contrast)};
}
button {
label {
@include input-cover();
display: flex;
align-items: center;
opacity: 0;
}
input {
@include visually-hidden();
}
.checkbox-icon {

View File

@@ -2,7 +2,7 @@ import { Component, ComponentInterface, Element, Event, EventEmitter, Host, Prop
import { getIonMode } from '../../global/ionic-global';
import { CheckboxChangeEventDetail, Color, StyleEventDetail } from '../../interface';
import { findItemLabel, renderHiddenInput } from '../../utils/helpers';
import { getAriaLabel, renderHiddenInput } from '../../utils/helpers';
import { createColorClasses, hostContext } from '../../utils/theme';
/**
@@ -22,7 +22,7 @@ import { createColorClasses, hostContext } from '../../utils/theme';
export class Checkbox implements ComponentInterface {
private inputId = `ion-cb-${checkboxIds++}`;
private buttonEl?: HTMLElement;
private focusEl?: HTMLElement;
@Element() el!: HTMLElement;
@@ -54,11 +54,11 @@ export class Checkbox implements ComponentInterface {
@Prop() disabled = false;
/**
* The value of the toggle does not mean if it's checked or not, use the `checked`
* The value of the checkbox does not mean if it's checked or not, use the `checked`
* property for that.
*
* The value of a toggle is analogous to the value of a `<input type="checkbox">`,
* it's only used when the toggle participates in a native `<form>`.
* The value of a checkbox is analogous to the value of an `<input type="checkbox">`,
* it's only used when the checkbox participates in a native `<form>`.
*/
@Prop() value = 'on';
@@ -68,12 +68,12 @@ export class Checkbox implements ComponentInterface {
@Event() ionChange!: EventEmitter<CheckboxChangeEventDetail>;
/**
* Emitted when the toggle has focus.
* Emitted when the checkbox has focus.
*/
@Event() ionFocus!: EventEmitter<void>;
/**
* Emitted when the toggle loses focus.
* Emitted when the checkbox loses focus.
*/
@Event() ionBlur!: EventEmitter<void>;
@@ -109,12 +109,14 @@ export class Checkbox implements ComponentInterface {
}
private setFocus() {
if (this.buttonEl) {
this.buttonEl.focus();
if (this.focusEl) {
this.focusEl.focus();
}
}
private onClick = () => {
private onClick = (ev: any) => {
ev.preventDefault();
this.setFocus();
this.checked = !this.checked;
this.indeterminate = false;
@@ -129,14 +131,11 @@ export class Checkbox implements ComponentInterface {
}
render() {
const { inputId, indeterminate, disabled, checked, value, color, el } = this;
const labelId = inputId + '-lbl';
const { color, checked, disabled, el, indeterminate, inputId, name, value } = this;
const mode = getIonMode(this);
const label = findItemLabel(el);
if (label) {
label.id = labelId;
}
renderHiddenInput(true, el, this.name, (checked ? value : ''), disabled);
const { label, labelId, labelText } = getAriaLabel(el, inputId);
renderHiddenInput(true, el, name, (checked ? value : ''), disabled);
let path = indeterminate
? <path d="M6 12L18 12" part="mark" />
@@ -151,10 +150,10 @@ export class Checkbox implements ComponentInterface {
return (
<Host
onClick={this.onClick}
role="checkbox"
aria-disabled={disabled ? 'true' : null}
aria-labelledby={label ? labelId : null}
aria-checked={`${checked}`}
aria-labelledby={labelId}
aria-hidden={disabled ? 'true' : null}
role="checkbox"
class={createColorClasses(color, {
[mode]: true,
'in-item': hostContext('ion-item', el),
@@ -167,14 +166,18 @@ export class Checkbox implements ComponentInterface {
<svg class="checkbox-icon" viewBox="0 0 24 24" part="container">
{path}
</svg>
<button
type="button"
onFocus={this.onFocus}
onBlur={this.onBlur}
disabled={this.disabled}
ref={btnEl => this.buttonEl = btnEl}
>
</button>
<label htmlFor={inputId}>
{labelText}
</label>
<input
type="checkbox"
aria-checked={`${checked}`}
disabled={disabled}
id={inputId}
onFocus={() => this.onFocus()}
onBlur={() => this.onBlur()}
ref={focusEl => this.focusEl = focusEl}
/>
</Host>
);
}

View File

@@ -228,8 +228,8 @@ export class CheckboxExample {
<ion-label>{{entry.val}}</ion-label>
<ion-checkbox
slot="end"
@input="entry.checked = $event.target.value"
:value="entry.isChecked">
@update:modelValue="entry.isChecked = $event"
:modelValue="entry.isChecked">
</ion-checkbox>
</ion-item>
</ion-list>
@@ -266,16 +266,16 @@ export default defineComponent({
| `indeterminate` | `indeterminate` | If `true`, the checkbox will visually appear as indeterminate. | `boolean` | `false` |
| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `undefined` |
| `name` | `name` | The name of the control, which is submitted with the form data. | `string` | `this.inputId` |
| `value` | `value` | The value of the toggle does not mean if it's checked or not, use the `checked` property for that. The value of a toggle is analogous to the value of a `<input type="checkbox">`, it's only used when the toggle participates in a native `<form>`. | `string` | `'on'` |
| `value` | `value` | The value of the checkbox does not mean if it's checked or not, use the `checked` property for that. The value of a checkbox is analogous to the value of an `<input type="checkbox">`, it's only used when the checkbox participates in a native `<form>`. | `string` | `'on'` |
## Events
| Event | Description | Type |
| ----------- | ---------------------------------------------- | ---------------------------------------- |
| `ionBlur` | Emitted when the toggle loses focus. | `CustomEvent<void>` |
| `ionBlur` | Emitted when the checkbox loses focus. | `CustomEvent<void>` |
| `ionChange` | Emitted when the checked property has changed. | `CustomEvent<CheckboxChangeEventDetail>` |
| `ionFocus` | Emitted when the toggle has focus. | `CustomEvent<void>` |
| `ionFocus` | Emitted when the checkbox has focus. | `CustomEvent<void>` |
## Shadow Parts

View File

@@ -26,6 +26,11 @@
</ion-header>
<ion-content id="content">
<ion-item onClick="clickItem()">
<ion-label>Clickable Item</ion-label>
<ion-checkbox></ion-checkbox>
</ion-item>
<ion-item>
<ion-label>Default</ion-label>
<ion-checkbox checked></ion-checkbox>
@@ -38,7 +43,7 @@
<ion-item>
<ion-label>Secondary</ion-label>
<ion-checkbox checked color="secondary"></ion-checkbox>
<ion-checkbox disabled checked color="secondary"></ion-checkbox>
</ion-item>
<ion-item>
@@ -103,6 +108,35 @@
</ion-content>
</ion-app>
<script>
const inputs = document.querySelectorAll('ion-checkbox');
for (var i = 0; i < inputs.length; i++) {
const input = inputs[i];
input.addEventListener('ionBlur', function() {
console.log('Listen ionBlur: fired');
});
input.addEventListener('ionFocus', function() {
console.log('Listen ionFocus: fired');
});
input.addEventListener('ionChange', function(ev) {
console.log('Listen ionChange: fired', ev.detail);
});
input.addEventListener('click', function() {
console.log('Listen click: fired');
});
}
const clickItem = () => {
console.log('Item click: fired');
}
</script>
</body>
</html>

View File

@@ -31,22 +31,22 @@
<div class="ion-padding-start">
<!-- Default to unchecked -->
<label for="unchecked">Unchecked</label>
<input name="unchecked" type="checkbox">
<input name="unchecked" id="unchecked" type="checkbox">
<br>
<!-- Default to checked -->
<label for="checked">Checked</label>
<input name="checked" type="checkbox" checked />
<input name="checked" id="checked" type="checkbox" checked />
<br>
<!-- Default to indeterminate -->
<label for="indeterminate">Indeterminate</label>
<input name="indeterminate" type="checkbox" class="indeterminate">
<input name="indeterminate" id="indeterminate" type="checkbox" class="indeterminate">
<br>
<!-- Default to checked / indeterminate -->
<label for="both">Checked / Indeterminate</label>
<input name="both" type="checkbox" checked class="indeterminate">
<input name="both" id="both" type="checkbox" checked class="indeterminate">
<br>
</div>
@@ -81,15 +81,15 @@
</ion-label>
</ion-list-header>
<div class="ion-padding-start">
<ion-checkbox indeterminate></ion-checkbox>
<ion-checkbox indeterminate color="secondary"></ion-checkbox>
<ion-checkbox indeterminate color="tertiary"></ion-checkbox>
<ion-checkbox indeterminate color="success"></ion-checkbox>
<ion-checkbox indeterminate color="warning"></ion-checkbox>
<ion-checkbox indeterminate color="danger"></ion-checkbox>
<ion-checkbox indeterminate color="dark"></ion-checkbox>
<ion-checkbox indeterminate color="medium"></ion-checkbox>
<ion-checkbox indeterminate color="light"></ion-checkbox>
<ion-checkbox aria-label="Default Indeterminate" indeterminate></ion-checkbox>
<ion-checkbox aria-label="Secondary Indeterminate" indeterminate color="secondary"></ion-checkbox>
<ion-checkbox aria-label="Tertiary Indeterminate" indeterminate color="tertiary"></ion-checkbox>
<ion-checkbox aria-label="Success Indeterminate" indeterminate color="success"></ion-checkbox>
<ion-checkbox aria-label="Warning Indeterminate" indeterminate color="warning"></ion-checkbox>
<ion-checkbox aria-label="Danger Indeterminate" indeterminate color="danger"></ion-checkbox>
<ion-checkbox aria-label="Dark Indeterminate" indeterminate color="dark"></ion-checkbox>
<ion-checkbox aria-label="Medium Indeterminate" indeterminate color="medium"></ion-checkbox>
<ion-checkbox aria-label="Light Indeterminate" indeterminate color="light"></ion-checkbox>
</div>
<ion-list-header>
@@ -100,20 +100,20 @@
<ul>
<li>
<ion-checkbox name="tall" id="tall" indeterminate></ion-checkbox>
<label for="tall">Tall Things</label>
<ion-checkbox aria-labelledby="tall-label-0" indeterminate></ion-checkbox>
<label id="tall-label-0">Tall Things</label>
<ul>
<li>
<ion-checkbox name="tall-1" id="tall-1" checked></ion-checkbox>
<label for="tall-1">Skyscrapers</label>
<ion-checkbox aria-labelledby="tall-label-1" checked></ion-checkbox>
<label id="tall-label-1">Skyscrapers</label>
</li>
<li>
<ion-checkbox name="tall-2" id="tall-2"></ion-checkbox>
<label for="tall-2">Trees</label>
<ion-checkbox aria-labelledby="tall-label-2"></ion-checkbox>
<label id="tall-label-2">Trees</label>
</li>
<li>
<ion-checkbox name="tall-2" id="tall-2"></ion-checkbox>
<label for="tall-2">Giants</label>
<ion-checkbox aria-labelledby="tall-label-3"></ion-checkbox>
<label id="tall-label-3">Giants</label>
</li>
</ul>
</li>

View File

@@ -13,67 +13,67 @@
<body class="ion-padding">
<h1>Default</h1>
<ion-checkbox></ion-checkbox>
<ion-checkbox checked></ion-checkbox>
<ion-checkbox disabled></ion-checkbox>
<ion-checkbox disabled checked></ion-checkbox>
<ion-checkbox aria-label="Default Checkbox"></ion-checkbox>
<ion-checkbox aria-label="Default Checkbox" checked></ion-checkbox>
<ion-checkbox aria-label="Default Checkbox" disabled></ion-checkbox>
<ion-checkbox aria-label="Default Checkbox" disabled checked></ion-checkbox>
<h1>Colors</h1>
<ion-checkbox color="primary"></ion-checkbox>
<ion-checkbox color="secondary"></ion-checkbox>
<ion-checkbox color="tertiary"></ion-checkbox>
<ion-checkbox color="success"></ion-checkbox>
<ion-checkbox color="warning"></ion-checkbox>
<ion-checkbox color="danger"></ion-checkbox>
<ion-checkbox color="light"></ion-checkbox>
<ion-checkbox color="medium"></ion-checkbox>
<ion-checkbox color="dark"></ion-checkbox>
<ion-checkbox aria-label="Checkbox Primary" color="primary"></ion-checkbox>
<ion-checkbox aria-label="Checkbox Secondary" color="secondary"></ion-checkbox>
<ion-checkbox aria-label="Checkbox Tertiary" color="tertiary"></ion-checkbox>
<ion-checkbox aria-label="Checkbox Success" color="success"></ion-checkbox>
<ion-checkbox aria-label="Checkbox Warning" color="warning"></ion-checkbox>
<ion-checkbox aria-label="Checkbox Danger" color="danger"></ion-checkbox>
<ion-checkbox aria-label="Checkbox Light" color="light"></ion-checkbox>
<ion-checkbox aria-label="Checkbox Medium" color="medium"></ion-checkbox>
<ion-checkbox aria-label="Checkbox Dark" color="dark"></ion-checkbox>
<hr>
<ion-checkbox checked color="primary"></ion-checkbox>
<ion-checkbox checked color="secondary"></ion-checkbox>
<ion-checkbox checked color="tertiary"></ion-checkbox>
<ion-checkbox checked color="success"></ion-checkbox>
<ion-checkbox checked color="warning"></ion-checkbox>
<ion-checkbox checked color="danger"></ion-checkbox>
<ion-checkbox checked color="light"></ion-checkbox>
<ion-checkbox checked color="medium"></ion-checkbox>
<ion-checkbox checked color="dark"></ion-checkbox>
<ion-checkbox aria-label="Checkbox Primary" checked color="primary"></ion-checkbox>
<ion-checkbox aria-label="Checkbox Secondary" checked color="secondary"></ion-checkbox>
<ion-checkbox aria-label="Checkbox Tertiary" checked color="tertiary"></ion-checkbox>
<ion-checkbox aria-label="Checkbox Success" checked color="success"></ion-checkbox>
<ion-checkbox aria-label="Checkbox Warning" checked color="warning"></ion-checkbox>
<ion-checkbox aria-label="Checkbox Danger" checked color="danger"></ion-checkbox>
<ion-checkbox aria-label="Checkbox Light" checked color="light"></ion-checkbox>
<ion-checkbox aria-label="Checkbox Medium" checked color="medium"></ion-checkbox>
<ion-checkbox aria-label="Checkbox Dark" checked color="dark"></ion-checkbox>
<hr>
<ion-checkbox checked disabled color="primary"></ion-checkbox>
<ion-checkbox checked disabled color="secondary"></ion-checkbox>
<ion-checkbox checked disabled color="tertiary"></ion-checkbox>
<ion-checkbox checked disabled color="success"></ion-checkbox>
<ion-checkbox checked disabled color="warning"></ion-checkbox>
<ion-checkbox checked disabled color="danger"></ion-checkbox>
<ion-checkbox checked disabled color="light"></ion-checkbox>
<ion-checkbox checked disabled color="medium"></ion-checkbox>
<ion-checkbox checked disabled color="dark"></ion-checkbox>
<ion-checkbox aria-label="Checkbox Primary" checked disabled color="primary"></ion-checkbox>
<ion-checkbox aria-label="Checkbox Secondary" checked disabled color="secondary"></ion-checkbox>
<ion-checkbox aria-label="Checkbox Tertiary" checked disabled color="tertiary"></ion-checkbox>
<ion-checkbox aria-label="Checkbox Success" checked disabled color="success"></ion-checkbox>
<ion-checkbox aria-label="Checkbox Warning" checked disabled color="warning"></ion-checkbox>
<ion-checkbox aria-label="Checkbox Danger" checked disabled color="danger"></ion-checkbox>
<ion-checkbox aria-label="Checkbox Light" checked disabled color="light"></ion-checkbox>
<ion-checkbox aria-label="Checkbox Medium" checked disabled color="medium"></ion-checkbox>
<ion-checkbox aria-label="Checkbox Dark" checked disabled color="dark"></ion-checkbox>
<h1>Custom</h1>
<ion-checkbox class="custom"></ion-checkbox>
<ion-checkbox class="custom" checked></ion-checkbox>
<ion-checkbox class="custom" disabled></ion-checkbox>
<ion-checkbox class="custom" disabled checked></ion-checkbox>
<ion-checkbox aria-label="Checkbox Custom" class="custom"></ion-checkbox>
<ion-checkbox aria-label="Checkbox Custom" class="custom" checked></ion-checkbox>
<ion-checkbox aria-label="Checkbox Custom" class="custom" disabled></ion-checkbox>
<ion-checkbox aria-label="Checkbox Custom" class="custom" disabled checked></ion-checkbox>
<h1>Custom: checked</h1>
<ion-checkbox class="custom-checked"></ion-checkbox>
<ion-checkbox class="custom-checked" checked></ion-checkbox>
<ion-checkbox class="custom-checked" disabled></ion-checkbox>
<ion-checkbox class="custom-checked" disabled checked></ion-checkbox>
<ion-checkbox aria-label="Checkbox Custom" class="custom-checked"></ion-checkbox>
<ion-checkbox aria-label="Checkbox Custom" class="custom-checked" checked></ion-checkbox>
<ion-checkbox aria-label="Checkbox Custom" class="custom-checked" disabled></ion-checkbox>
<ion-checkbox aria-label="Checkbox Custom" class="custom-checked" disabled checked></ion-checkbox>
<h1>Custom: light</h1>
<ion-checkbox class="custom-light"></ion-checkbox>
<ion-checkbox class="custom-light" checked></ion-checkbox>
<ion-checkbox class="custom-light" disabled></ion-checkbox>
<ion-checkbox class="custom-light" disabled checked></ion-checkbox>
<ion-checkbox aria-label="Checkbox Custom Light" class="custom-light"></ion-checkbox>
<ion-checkbox aria-label="Checkbox Custom Light" class="custom-light" checked></ion-checkbox>
<ion-checkbox aria-label="Checkbox Custom Light" class="custom-light" disabled></ion-checkbox>
<ion-checkbox aria-label="Checkbox Custom Light" class="custom-light" disabled checked></ion-checkbox>
<h1>Custom: transition</h1>
<ion-checkbox class="custom-transition"></ion-checkbox>
<ion-checkbox class="custom-transition" checked></ion-checkbox>
<ion-checkbox aria-label="Checkbox Custom Transition" class="custom-transition"></ion-checkbox>
<ion-checkbox aria-label="Checkbox Custom Transition" class="custom-transition" checked></ion-checkbox>
<style>
.custom {

View File

@@ -22,8 +22,8 @@
<ion-label>{{entry.val}}</ion-label>
<ion-checkbox
slot="end"
@input="entry.checked = $event.target.value"
:value="entry.isChecked">
@update:modelValue="entry.isChecked = $event"
:modelValue="entry.isChecked">
</ion-checkbox>
</ion-item>
</ion-list>

View File

@@ -37,6 +37,11 @@
box-sizing: border-box;
}
:host(.chip-disabled) {
cursor: default;
opacity: .4;
pointer-events: none;
}
// Chip Colors
// ---------------------------------------------

View File

@@ -28,14 +28,21 @@ export class Chip implements ComponentInterface {
*/
@Prop() outline = false;
/**
* If `true`, the user cannot interact with the chip.
*/
@Prop() disabled = false;
render() {
const mode = getIonMode(this);
return (
<Host
aria-disabled={this.disabled ? 'true' : null}
class={createColorClasses(this.color, {
[mode]: true,
'chip-outline': this.outline,
'chip-disabled': this.disabled,
'ion-activatable': true,
})}
>

View File

@@ -7,7 +7,7 @@ Chips represent complex entities in small blocks, such as a contact. A chip can
## Usage
### Angular / javascript
### Angular
```html
<ion-chip>
@@ -22,6 +22,60 @@ Chips represent complex entities in small blocks, such as a contact. A chip can
<ion-label color="dark">Secondary w/ Dark label</ion-label>
</ion-chip>
<ion-chip [disabled]="true">
<ion-label>Disabled Chip</ion-label>
</ion-chip>
<ion-chip>
<ion-icon name="pin"></ion-icon>
<ion-label>Default</ion-label>
</ion-chip>
<ion-chip>
<ion-icon name="heart" color="dark"></ion-icon>
<ion-label>Default</ion-label>
</ion-chip>
<ion-chip>
<ion-label>Button Chip</ion-label>
<ion-icon name="close-circle"></ion-icon>
</ion-chip>
<ion-chip>
<ion-icon name="pin" color="primary"></ion-icon>
<ion-label>Icon Chip</ion-label>
<ion-icon name="close"></ion-icon>
</ion-chip>
<ion-chip>
<ion-avatar>
<img src="https://gravatar.com/avatar/dba6bae8c566f9d4041fb9cd9ada7741?d=identicon&f=y">
</ion-avatar>
<ion-label>Avatar Chip</ion-label>
<ion-icon name="close-circle"></ion-icon>
</ion-chip>
```
### Javascript
```html
<ion-chip>
<ion-label>Default</ion-label>
</ion-chip>
<ion-chip>
<ion-label color="secondary">Secondary Label</ion-label>
</ion-chip>
<ion-chip color="secondary">
<ion-label color="dark">Secondary w/ Dark label</ion-label>
</ion-chip>
<ion-chip disabled="true">
<ion-label>Disabled Chip</ion-label>
</ion-chip>
<ion-chip>
<ion-icon name="pin"></ion-icon>
<ion-label>Default</ion-label>
@@ -81,6 +135,10 @@ export const ChipExamples: React.FC = () => {
<IonLabel color="dark">Secondary w/ Dark label</IonLabel>
</IonChip>
<IonChip disabled={true}>
<IonLabel>Disabled Chip</IonLabel>
</IonChip>
<IonChip>
<IonIcon icon={pin} />
<IonLabel>Default</IonLabel>
@@ -191,6 +249,10 @@ export class ChipExample {
<ion-label color="dark">Secondary w/ Dark label</ion-label>
</ion-chip>
<ion-chip :disabled="true">
<ion-label>Disabled Chip</ion-label>
</ion-chip>
<ion-chip>
<ion-icon :icon="pin"></ion-icon>
<ion-label>Default</ion-label>
@@ -240,11 +302,12 @@ export default defineComponent({
## Properties
| Property | Attribute | Description | Type | Default |
| --------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | ----------- |
| `color` | `color` | The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). | `string \| undefined` | `undefined` |
| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `undefined` |
| `outline` | `outline` | Display an outline style button. | `boolean` | `false` |
| Property | Attribute | Description | Type | Default |
| ---------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | ----------- |
| `color` | `color` | The color to use from your application's color palette. Default options are: `"primary"`, `"secondary"`, `"tertiary"`, `"success"`, `"warning"`, `"danger"`, `"light"`, `"medium"`, and `"dark"`. For more information on colors, see [theming](/docs/theming/basics). | `string \| undefined` | `undefined` |
| `disabled` | `disabled` | If `true`, the user cannot interact with the chip. | `boolean` | `false` |
| `mode` | `mode` | The mode determines which platform styles to use. | `"ios" \| "md"` | `undefined` |
| `outline` | `outline` | Display an outline style button. | `boolean` | `false` |
## CSS Custom Properties

View File

@@ -17,6 +17,7 @@
<ion-app>
<ion-content>
<h3>Default</h3>
<p>
<ion-chip>
<ion-label>Default</ion-label>
@@ -195,6 +196,26 @@
</ion-chip>
</p>
<h3>Disabled</h3>
<p>
<ion-chip disabled>
<ion-label>Disabled</ion-label>
</ion-chip>
<ion-chip outline color="danger" class="ion-focused" disabled>
<ion-label>Disabled Outline</ion-label>
</ion-chip>
<ion-chip color="secondary" class="ion-focused" disabled>
<ion-icon name="checkmark-circle"></ion-icon>
<ion-label>Disabled Secondary with Icon</ion-label>
</ion-chip>
<ion-chip outline class="ion-focused" disabled>
<ion-icon name="git-pull-request"></ion-icon>
<ion-label>Disabled Outline with Icon and Avatar</ion-label>
<ion-icon name="close-circle"></ion-icon>
</ion-chip>
</p>
<h3>Custom</h3>
<!-- Custom Font -->
@@ -246,11 +267,6 @@
padding-left: 8px;
}
ion-chip {
display: inline-block !important;
vertical-align: middle;
}
.wide {
--background: #d1f3ff;
--background-hover: #add8e6;

View File

@@ -11,6 +11,10 @@
<ion-label color="dark">Secondary w/ Dark label</ion-label>
</ion-chip>
<ion-chip [disabled]="true">
<ion-label>Disabled Chip</ion-label>
</ion-chip>
<ion-chip>
<ion-icon name="pin"></ion-icon>
<ion-label>Default</ion-label>

View File

@@ -11,6 +11,10 @@
<ion-label color="dark">Secondary w/ Dark label</ion-label>
</ion-chip>
<ion-chip disabled="true">
<ion-label>Disabled Chip</ion-label>
</ion-chip>
<ion-chip>
<ion-icon name="pin"></ion-icon>
<ion-label>Default</ion-label>

View File

@@ -24,6 +24,10 @@ export const ChipExamples: React.FC = () => {
<IonLabel color="dark">Secondary w/ Dark label</IonLabel>
</IonChip>
<IonChip disabled={true}>
<IonLabel>Disabled Chip</IonLabel>
</IonChip>
<IonChip>
<IonIcon icon={pin} />
<IonLabel>Default</IonLabel>
@@ -57,4 +61,4 @@ export const ChipExamples: React.FC = () => {
);
};
```
```

View File

@@ -12,6 +12,10 @@
<ion-label color="dark">Secondary w/ Dark label</ion-label>
</ion-chip>
<ion-chip :disabled="true">
<ion-label>Disabled Chip</ion-label>
</ion-chip>
<ion-chip>
<ion-icon :icon="pin"></ion-icon>
<ion-label>Default</ion-label>

View File

@@ -100,7 +100,9 @@
*
* See: https://bugs.webkit.org/show_bug.cgi?id=216701
*/
will-change: scroll-position, transform;
z-index: 0;
will-change: scroll-position;
}
.scroll-y {

View File

@@ -5,6 +5,11 @@ const getActiveElementText = async (page) => {
return await page.evaluate(el => el && el.textContent, activeElement);
}
const getActiveElementClass = async (page) => {
const activeElement = await page.evaluateHandle(() => document.activeElement);
return await page.evaluate(el => el && el.className, activeElement);
}
test('datetime/picker: focus trap', async () => {
const page = await newE2EPage({ url: '/src/components/datetime/test/basic?ionic:_testing=true' });
await page.click('#datetime-part');
@@ -15,7 +20,7 @@ test('datetime/picker: focus trap', async () => {
expect(datetime).not.toBe(null);
// TODO fix
await page.waitFor(100);
await page.waitForTimeout(250);
await page.keyboard.press('Tab');
@@ -26,8 +31,8 @@ test('datetime/picker: focus trap', async () => {
await page.keyboard.press('Tab');
await page.keyboard.up('Shift');
const activeElementTextTwo = await getActiveElementText(page);
expect(activeElementTextTwo).toEqual('1920');
const activeElementClass = await getActiveElementClass(page);
expect(activeElementClass).toEqual('picker-opt');
await page.keyboard.press('Tab');
@@ -49,7 +54,7 @@ test('datetime: basic', async () => {
const picker = await page.find('ion-picker');
await picker.waitForVisible();
await page.waitFor(250);
await page.waitForTimeout(250);
compare = await page.compareScreenshot('should open custom picker');
expect(compare).toMatchScreenshot();
@@ -65,7 +70,7 @@ test('datetime: basic-rtl', async () => {
const picker = await page.find('ion-picker');
await picker.waitForVisible();
await page.waitFor(250);
await page.waitForTimeout(250);
const compare = await page.compareScreenshot('should open custom picker');
expect(compare).toMatchScreenshot();

View File

@@ -13,14 +13,14 @@ test('datetime: standalone', async () => {
const picker = await page.find('ion-picker');
await picker.waitForVisible();
await page.waitFor(250);
await page.waitForTimeout(250);
compare = await page.compareScreenshot('should open basic picker');
expect(compare).toMatchScreenshot();
const octoberOpt = await page.find({ text: 'October' });
await octoberOpt.click();
await page.waitFor(500);
await page.waitForTimeout(500);
compare = await page.compareScreenshot('should click "October" option');
expect(compare).toMatchScreenshot();

View File

@@ -20,7 +20,7 @@ export const testFab = async (
const fab = await getFabComponent(page, selector);
await fab.click();
await page.waitFor(250);
await page.waitForTimeout(250);
await ensureFabState(fab, 'active');
@@ -29,7 +29,7 @@ export const testFab = async (
const fabButton = await getFabButton(fab);
await fabButton.click();
await page.waitFor(250);
await page.waitForTimeout(250);
await ensureFabState(fab, 'inactive');

View File

@@ -120,7 +120,7 @@ export class Header implements ComponentInterface {
* as well as progressively showing/hiding the main header
* border as the top-most toolbar collapses or expands.
*/
const toolbarIntersection = (ev: any) => { handleToolbarIntersection(ev, mainHeaderIndex, scrollHeaderIndex); };
const toolbarIntersection = (ev: any) => { handleToolbarIntersection(ev, mainHeaderIndex, scrollHeaderIndex, this.scrollEl!); };
this.intersectionObserver = new IntersectionObserver(toolbarIntersection, { root: contentEl, threshold: [0.25, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1] });
this.intersectionObserver.observe(scrollHeaderIndex.toolbars[scrollHeaderIndex.toolbars.length - 1].el);

View File

@@ -72,7 +72,7 @@ export const setToolbarBackgroundOpacity = (toolbar: ToolbarIndex, opacity?: num
}
};
const handleToolbarBorderIntersection = (ev: any, mainHeaderIndex: HeaderIndex) => {
const handleToolbarBorderIntersection = (ev: any, mainHeaderIndex: HeaderIndex, scrollTop: number) => {
if (!ev[0].isIntersecting) { return; }
/**
@@ -80,8 +80,13 @@ const handleToolbarBorderIntersection = (ev: any, mainHeaderIndex: HeaderIndex)
* does not always reset the scrollTop position to 0 when letting go. It will
* set to 1 once the rubber band effect has ended. This causes the background to
* appear slightly on certain app setups.
*
* Additionally, we check if user is rubber banding (scrolling is negative)
* as this can mean they are using pull to refresh. Once the refresher starts,
* the content is transformed which can cause the intersection observer to erroneously
* fire here as well.
*/
const scale = (ev[0].intersectionRatio > 0.9) ? 0 : ((1 - ev[0].intersectionRatio) * 100) / 75;
const scale = (ev[0].intersectionRatio > 0.9 || scrollTop <= 0) ? 0 : ((1 - ev[0].intersectionRatio) * 100) / 75;
mainHeaderIndex.toolbars.forEach(toolbar => {
setToolbarBackgroundOpacity(toolbar, (scale === 1) ? undefined : scale);
@@ -93,9 +98,10 @@ const handleToolbarBorderIntersection = (ev: any, mainHeaderIndex: HeaderIndex)
* and show the primary toolbar content. If the toolbars are not intersecting,
* hide the primary toolbar content and show the scrollable toolbar content
*/
export const handleToolbarIntersection = (ev: any, mainHeaderIndex: HeaderIndex, scrollHeaderIndex: HeaderIndex) => {
export const handleToolbarIntersection = (ev: any, mainHeaderIndex: HeaderIndex, scrollHeaderIndex: HeaderIndex, scrollEl: HTMLElement) => {
writeTask(() => {
handleToolbarBorderIntersection(ev, mainHeaderIndex);
const scrollTop = scrollEl.scrollTop;
handleToolbarBorderIntersection(ev, mainHeaderIndex, scrollTop);
const event = ev[0];
@@ -127,7 +133,7 @@ export const handleToolbarIntersection = (ev: any, mainHeaderIndex: HeaderIndex,
const hasValidIntersection = (intersection.x === 0 && intersection.y === 0) || (intersection.width !== 0 && intersection.height !== 0);
if (hasValidIntersection) {
if (hasValidIntersection && scrollTop > 0) {
setHeaderActive(mainHeaderIndex);
setHeaderActive(scrollHeaderIndex, false);
setToolbarBackgroundOpacity(mainHeaderIndex.toolbars[0]);

View File

@@ -169,3 +169,18 @@
:host(.has-focus) button {
pointer-events: auto;
}
// Item Floating: Placeholder
// ----------------------------------------------------------------
// When used with a floating item the placeholder should hide
:host-context(.item-label-floating.item-has-placeholder:not(.item-has-value)) {
opacity: 0;
}
:host-context(.item-label-floating.item-has-placeholder:not(.item-has-value).item-has-focus) {
transition: opacity 0.15s cubic-bezier(0.4, 0, 0.2, 1);
opacity: 1;
}

View File

@@ -2,7 +2,7 @@ import { Build, Component, ComponentInterface, Element, Event, EventEmitter, Hos
import { getIonMode } from '../../global/ionic-global';
import { AutocompleteTypes, Color, InputChangeEventDetail, StyleEventDetail, TextFieldTypes } from '../../interface';
import { debounceEvent, findItemLabel } from '../../utils/helpers';
import { debounceEvent, findItemLabel, inheritAttributes } from '../../utils/helpers';
import { createColorClasses } from '../../utils/theme';
/**
@@ -21,7 +21,7 @@ export class Input implements ComponentInterface {
private nativeInput?: HTMLInputElement;
private inputId = `ion-input-${inputIds++}`;
private didBlurAfterEdit = false;
private tabindex?: string | number;
private inheritedAttributes: { [k: string]: any } = {};
/**
* This is required for a WebKit bug which requires us to
@@ -51,6 +51,7 @@ export class Input implements ComponentInterface {
/**
* Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user.
* Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`.
*/
@Prop() autocapitalize = 'off';
@@ -189,15 +190,6 @@ export class Input implements ComponentInterface {
*/
@Prop({ mutable: true }) value?: string | number | null = '';
/**
* Update the native input element when the value changes
*/
@Watch('value')
protected valueChanged() {
this.emitStyle();
this.ionChange.emit({ value: this.value == null ? this.value : this.value.toString() });
}
/**
* Emitted when a keyboard input occurred.
*/
@@ -224,15 +216,25 @@ export class Input implements ComponentInterface {
*/
@Event() ionStyle!: EventEmitter<StyleEventDetail>;
/**
* Update the item classes when the placeholder changes
*/
@Watch('placeholder')
protected placeholderChanged() {
this.emitStyle();
}
/**
* Update the native input element when the value changes
*/
@Watch('value')
protected valueChanged() {
this.emitStyle();
this.ionChange.emit({ value: this.value == null ? this.value : this.value.toString() });
}
componentWillLoad() {
// If the ion-input has a tabindex attribute we get the value
// and pass it down to the native input, then remove it from the
// ion-input to avoid causing tabbing twice on the same element
if (this.el.hasAttribute('tabindex')) {
const tabindex = this.el.getAttribute('tabindex');
this.tabindex = tabindex !== null ? tabindex : undefined;
this.el.removeAttribute('tabindex');
}
this.inheritedAttributes = inheritAttributes(this.el, ['tabindex', 'title']);
}
connectedCallback() {
@@ -428,13 +430,13 @@ export class Input implements ComponentInterface {
spellcheck={this.spellcheck}
step={this.step}
size={this.size}
tabindex={this.tabindex}
type={this.type}
value={value}
onInput={this.onInput}
onBlur={this.onBlur}
onFocus={this.onFocus}
onKeyDown={this.onKeydown}
{...this.inheritedAttributes}
/>
{(this.clearInput && !this.readonly && !this.disabled) && <button
aria-label="reset"

View File

@@ -301,7 +301,7 @@ export default defineComponent({
| Property | Attribute | Description | Type | Default |
| ---------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- |
| `accept` | `accept` | If the value of the type attribute is `"file"`, then this attribute will indicate the types of files that the server accepts, otherwise it will be ignored. The value must be a comma-separated list of unique content type specifiers. | `string \| undefined` | `undefined` |
| `autocapitalize` | `autocapitalize` | Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user. | `string` | `'off'` |
| `autocapitalize` | `autocapitalize` | Indicates whether and how the text value should be automatically capitalized as it is entered/edited by the user. Available options: `"off"`, `"none"`, `"on"`, `"sentences"`, `"words"`, `"characters"`. | `string` | `'off'` |
| `autocomplete` | `autocomplete` | Indicates whether the value of the control can be automatically completed by the browser. | `"on" \| "off" \| "name" \| "honorific-prefix" \| "given-name" \| "additional-name" \| "family-name" \| "honorific-suffix" \| "nickname" \| "email" \| "username" \| "new-password" \| "current-password" \| "one-time-code" \| "organization-title" \| "organization" \| "street-address" \| "address-line1" \| "address-line2" \| "address-line3" \| "address-level4" \| "address-level3" \| "address-level2" \| "address-level1" \| "country" \| "country-name" \| "postal-code" \| "cc-name" \| "cc-given-name" \| "cc-additional-name" \| "cc-family-name" \| "cc-number" \| "cc-exp" \| "cc-exp-month" \| "cc-exp-year" \| "cc-csc" \| "cc-type" \| "transaction-currency" \| "transaction-amount" \| "language" \| "bday" \| "bday-day" \| "bday-month" \| "bday-year" \| "sex" \| "tel" \| "tel-country-code" \| "tel-national" \| "tel-area-code" \| "tel-local" \| "tel-extension" \| "impp" \| "url" \| "photo"` | `'off'` |
| `autocorrect` | `autocorrect` | Whether auto correction should be enabled when the user is entering/editing the text value. | `"off" \| "on"` | `'off'` |
| `autofocus` | `autofocus` | This Boolean attribute lets you specify that a form control should have input focus when the page loads. | `boolean` | `false` |

View File

@@ -132,11 +132,11 @@
<ion-label>Right</ion-label>
<ion-input class="ion-text-right" value="Narrow input"></ion-input>
</ion-item>
</ion-content>
<script>
document.querySelector('ion-input').addEventListener('ionBlur', (ev) => { console.log(ev)})
document.querySelector('ion-input').addEventListener('ionBlur', (ev) => { console.log(ev)});
function toggleBoolean(id, prop) {
var el = document.getElementById(id);

View File

@@ -13,27 +13,130 @@
<body>
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Input - Spec</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<h1>Floating Inputs</h1>
<div class="grid">
<div class="column">
<h2>Inactive</h2>
<ion-item>
<ion-label position="floating">Label</ion-label>
<ion-input placeholder="Placeholder Text"></ion-input>
</ion-item>
</div>
<div class="column">
<h2>Focused</h2>
<ion-item class="item-has-focus">
<ion-label position="floating">Label</ion-label>
<ion-input placeholder="Placeholder Text"></ion-input>
</ion-item>
</div>
<div class="column">
<h2>Activated</h2>
<ion-item>
<ion-label position="floating">Label</ion-label>
<ion-input placeholder="Placeholder Text" value="Input Text"></ion-input>
</ion-item>
</div>
<div class="column">
<h2>Hover</h2>
<ion-item class="item-hovered">
<ion-label position="floating">Label</ion-label>
<ion-input></ion-input>
</ion-item>
</div>
<div class="column">
<h2>Disabled</h2>
<ion-item>
<ion-label position="floating">Label</ion-label>
<ion-input disabled></ion-input>
</ion-item>
</div>
<div class="column">
<h2>Toggle Placeholder</h2>
<ion-item>
<ion-label position="floating">Label</ion-label>
<ion-input id="floatingToggle" type="password"></ion-input>
<ion-button fill="clear" slot="end" onClick="togglePlaceholder('#floatingToggle')" class="ion-align-self-center">
Toggle
</ion-button>
</ion-item>
</div>
</div>
<h1>Stacked Inputs</h1>
<div class="grid">
<div class="column">
<h2>Inactive</h2>
<ion-item>
<ion-label position="stacked">Label</ion-label>
<ion-input></ion-input>
</ion-item>
</div>
<div class="column">
<h2>Focused</h2>
<ion-item class="item-has-focus">
<ion-label position="stacked">Label</ion-label>
<ion-input placeholder="Placeholder Text"></ion-input>
</ion-item>
</div>
<div class="column">
<h2>Activated</h2>
<ion-item>
<ion-label position="stacked">Label</ion-label>
<ion-input placeholder="Placeholder Text" value="Input Text"></ion-input>
</ion-item>
</div>
<div class="column">
<h2>Hover</h2>
<ion-item class="item-hovered">
<ion-label position="stacked">Label</ion-label>
<ion-input></ion-input>
</ion-item>
</div>
<div class="column">
<h2>Disabled</h2>
<ion-item>
<ion-label position="stacked">Label</ion-label>
<ion-input disabled></ion-input>
</ion-item>
</div>
<div class="column">
<h2>Toggle Placeholder</h2>
<ion-item>
<ion-label position="stacked">Label</ion-label>
<ion-input id="stackedToggle" type="password"></ion-input>
<ion-button fill="clear" slot="end" onClick="togglePlaceholder('#stackedToggle')" class="ion-align-self-center">
Toggle
</ion-button>
</ion-item>
</div>
</div>
<hr>
<h2>Stacked Div</h2>
<ion-item>
<ion-label position="floating">Floating: input</ion-label>
<ion-input></ion-input>
</ion-item>
<ion-item class="item-has-focus">
<ion-label position="floating">Floating: input focused value</ion-label>
<ion-input value="value"></ion-input>
</ion-item>
<ion-item>
<ion-label position="stacked">Stacked: input</ion-label>
<ion-input></ion-input>
</ion-item>
<ion-item>
<ion-label position="stacked">Stacked: input value</ion-label>
<ion-input value="value"></ion-input>
</ion-item>
<ion-item>
<ion-label position="stacked">Stacked: div</ion-label>
<ion-label position="stacked">Label</ion-label>
<div>A div</div>
</ion-item>
<ion-item class="ion-align-items-center">
<ion-icon slot="start" name="planet"></ion-icon>
<ion-label position="stacked">Align items: center</ion-label>
@@ -68,8 +171,45 @@
</ion-content>
<style>
h1 {
font-size: 14px;
color: #54575e;
margin: 25px 0 5px 25px;
}
h2 {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 12px;
font-weight: normal;
color: #a1a7b0;
margin-top: 10px;
margin-left: 5px;
}
hr {
background: #eff1f3;
margin-top: 18px;
margin-bottom: 25px;
}
.grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
row-gap: 20px;
column-gap: 20px;
padding: 0 20px 20px;
}
ion-item {
--background: #f5f5f5;
--background: #e0e0e0;
--background-hover: #d3d3d3;
}
.custom {
@@ -84,6 +224,13 @@
color: purple !important;
}
</style>
<script>
function togglePlaceholder(id) {
const input = document.querySelector(id);
input.placeholder = input.placeholder ? undefined : 'Placeholder Text';
}
</script>
</ion-app>
</body>

View File

@@ -209,7 +209,14 @@ export class ItemSliding implements ComponentInterface {
this.leftOptions = this.rightOptions = undefined;
for (let i = 0; i < options.length; i++) {
const option = await options.item(i).componentOnReady();
const item = options.item(i);
/**
* We cannot use the componentOnReady helper
* util here since we need to wait for all of these items
* to be ready before we set `this.sides` and `this.optsDirty`.
*/
const option = ((item as any).componentOnReady !== undefined) ? await item.componentOnReady() : item;
const side = isEndSide(option.side) ? 'end' : 'start';

View File

@@ -47,5 +47,5 @@ const deleteItemSliding = async (item: any, page: any, id: string) => {
// Wait for element to be removed from DOM
await page.waitForSelector(id, { hidden: true });
await page.waitFor(1000);
await page.waitForTimeout(1000);
};

View File

@@ -42,7 +42,7 @@ export const openItemSliding = async (id: string, page: any, rtl = false) => {
await page.mouse.up();
// Add a timeout to make sure the item is open
await page.waitFor(2000);
await page.waitForTimeout(2000);
} catch (err) {
throw err;
}
@@ -54,5 +54,5 @@ export const closeItemSliding = async (page: any) => {
await page.mouse.move(0, 0);
await page.mouse.down();
await page.mouse.up();
await page.waitFor(1000);
await page.waitForTimeout(1000);
};

View File

@@ -18,7 +18,6 @@
--color: #{$item-md-color};
--transition: opacity 15ms linear, background-color 15ms linear;
--padding-start: #{$item-md-padding-start};
--color: #{$item-md-color};
--border-color: #{$item-md-border-bottom-color};
--inner-padding-end: #{$item-md-padding-end};
--inner-border-width: #{0 0 $item-md-border-bottom-width 0};

View File

@@ -305,7 +305,7 @@ export class Item implements ComponentInterface, AnchorInterface, ButtonInterfac
<slot></slot>
</div>
<slot name="end"></slot>
{showDetail && <ion-icon icon={detailIcon} lazy={false} class="item-detail-icon" part="detail-icon"></ion-icon>}
{showDetail && <ion-icon icon={detailIcon} lazy={false} class="item-detail-icon" part="detail-icon" aria-hidden="true"></ion-icon>}
<div class="item-inner-highlight"></div>
</div>
{canActivate && mode === 'md' && <ion-ripple-effect></ion-ripple-effect>}

View File

@@ -9,7 +9,7 @@
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet">
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet">
<script src="../../../../../scripts/testing/scripts.js"></script>
<script src="../../../../../dist/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
</head>
<body>

View File

@@ -11,7 +11,7 @@ test('item: inputs', async () => {
page,
'{"date":"","select":"n64","toggle":"","input":"","input2":"","checkbox":"","range":"10"}'
);
await page.waitFor(100);
await page.waitForTimeout(100);
// Default case, enabled and no value
let compare = await page.compareScreenshot();
@@ -21,13 +21,13 @@ test('item: inputs', async () => {
const disableToggle = await page.find('#btnDisabled');
await disableToggle.waitForVisible();
await disableToggle.click();
await page.waitFor(300);
await page.waitForTimeout(300);
// check form
await page.click('#submit');
await page.waitFor(100);
await page.waitForTimeout(100);
await checkFormResult(page, '{}');
await page.waitFor(100);
await page.waitForTimeout(100);
// screenshot
compare = await page.compareScreenshot('should disable all');
@@ -36,7 +36,7 @@ test('item: inputs', async () => {
// Reenable and set some value
await disableToggle.click();
await page.click('#btnSomeValue');
await page.waitFor(100);
await page.waitForTimeout(100);
// check form
await page.click('#submit');
@@ -44,28 +44,28 @@ test('item: inputs', async () => {
page,
'{"date":"2016-12-09","select":"nes","toggle":"on","input":"Some text","input2":"Some text","checkbox":"on","range":"20"}'
);
await page.waitFor(100);
await page.waitForTimeout(100);
compare = await page.compareScreenshot('should reenable and set value');
expect(compare).toMatchScreenshot();
// Set "null"
await page.click('#btnNullValue');
await page.waitFor(100);
await page.waitForTimeout(100);
compare = await page.compareScreenshot('should set null');
expect(compare).toMatchScreenshot();
// Set "empty"
await page.click('#btnEmptyValue');
await page.waitFor(100);
await page.waitForTimeout(100);
compare = await page.compareScreenshot('should set empty');
expect(compare).toMatchScreenshot();
// Set "empty"
await page.click('#btnEmptyValue');
await page.waitFor(100);
await page.waitForTimeout(100);
compare = await page.compareScreenshot('should set empty');
expect(compare).toMatchScreenshot();
@@ -73,7 +73,7 @@ test('item: inputs', async () => {
// Test multiple
await page.click('#checkbox-start');
await page.click('#datetime-end');
await page.waitFor(300);
await page.waitForTimeout(300);
compare = await page.compareScreenshot(
'should check checkbox and open datepicker'
@@ -81,7 +81,7 @@ test('item: inputs', async () => {
expect(compare).toMatchScreenshot();
await page.click('#button-end');
await page.waitFor(100);
await page.waitForTimeout(100);
compare = await page.compareScreenshot('should change button color to red');
expect(compare).toMatchScreenshot();

View File

@@ -23,24 +23,27 @@
:host(.label-floating) {
@include margin(null, null, 0, null);
@include transform(translate3d(0, 27px, 0));
@include transform(translate3d(0, 29px, 0));
@include transform-origin(start, top);
transition: transform 150ms ease-in-out;
}
:host-context(.item-textarea).label-floating {
@include transform(translate3d(0, 28px, 0));
}
:host-context(.item-has-focus).label-stacked,
:host-context(.item-has-focus).label-floating {
color: $label-ios-text-color-focused;
}
:host-context(.item-has-focus).label-floating,
:host-context(.item-has-placeholder).label-floating,
:host-context(.item-has-placeholder:not(.item-input)).label-floating,
:host-context(.item-has-value).label-floating {
@include transform(translate3d(0, 0, 0), scale(.82));
}
// iOS Typography
// --------------------------------------------------

View File

@@ -33,13 +33,17 @@
transform 150ms $label-md-transition-timing-function;
}
:host-context(.item-textarea).label-floating {
@include transform(translateY(185%));
}
:host(.label-stacked),
:host(.label-floating) {
@include margin(0, 0, 0, 0);
}
:host-context(.item-has-focus).label-floating,
:host-context(.item-has-placeholder).label-floating,
:host-context(.item-has-placeholder:not(.item-input)).label-floating,
:host-context(.item-has-value).label-floating {
@include transform(translateY(50%), scale(.75));
}

View File

@@ -0,0 +1,121 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="UTF-8">
<title>Label - Floating</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<link href="../../../../../css/ionic.bundle.css" rel="stylesheet">
<link href="../../../../../scripts/testing/styles.css" rel="stylesheet">
<script src="../../../../../scripts/testing/scripts.js"></script>
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script></head>
<body>
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Label - Floating</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<ion-list-header>
<ion-label>Default</ion-label>
</ion-list-header>
<ion-item>
<ion-label position="floating">Input</ion-label>
<ion-input></ion-input>
</ion-item>
<ion-item>
<ion-label position="floating">Textarea</ion-label>
<ion-textarea></ion-textarea>
</ion-item>
<ion-item>
<ion-label position="floating">Datetime</ion-label>
<ion-datetime></ion-datetime>
</ion-item>
<ion-item>
<ion-label position="floating">Select</ion-label>
<ion-select>
<ion-select-option>Option</ion-select-option>
</ion-select>
</ion-item>
</ion-list>
<ion-list>
<ion-list-header>
<ion-label>Placeholders</ion-label>
</ion-list-header>
<ion-item>
<ion-label position="floating">Input</ion-label>
<ion-input placeholder="Placeholder Text"></ion-input>
</ion-item>
<ion-item>
<ion-label position="floating">Textarea</ion-label>
<ion-textarea placeholder="Placeholder Text"></ion-textarea>
</ion-item>
<ion-item>
<ion-label position="floating">Datetime</ion-label>
<ion-datetime placeholder="Placeholder Text"></ion-datetime>
</ion-item>
<ion-item>
<ion-label position="floating">Select</ion-label>
<ion-select placeholder="Placeholder Text">
<ion-select-option>Option</ion-select-option>
</ion-select>
</ion-item>
</ion-list>
<ion-list>
<ion-list-header>
<ion-label>Values</ion-label>
</ion-list-header>
<ion-item>
<ion-label position="floating">Input</ion-label>
<ion-input value="Input Value"></ion-input>
</ion-item>
<ion-item>
<ion-label position="floating">Textarea</ion-label>
<ion-textarea value="Textarea Value"></ion-textarea>
</ion-item>
<ion-item>
<ion-label position="floating">Datetime</ion-label>
<ion-datetime value="2020-03-04"></ion-datetime>
</ion-item>
<ion-item>
<ion-label position="floating">Select</ion-label>
<ion-select value="option">
<ion-select-option value="option">Option</ion-select-option>
</ion-select>
</ion-item>
</ion-list>
</ion-content>
<style>
ion-content {
--background: #f6f6f6;
}
ion-list {
background: transparent !important;
}
</style>
</ion-app>
</body>
</html>

View File

@@ -131,6 +131,43 @@ async function presentLoadingWithOptions() {
### React
```tsx
/* Using with useIonLoading Hook */
import React from 'react';
import { IonButton, IonContent, IonPage, useIonLoading } from '@ionic/react';
interface LoadingProps {}
const LoadingExample: React.FC<LoadingProps> = () => {
const [present] = useIonLoading();
return (
<IonPage>
<IonContent>
<IonButton
expand="block"
onClick={() =>
present({
duration: 3000,
})
}
>
Show Loading
</IonButton>
<IonButton
expand="block"
onClick={() => present('Loading', 2000, 'dots')}
>
Show Loading using params
</IonButton>
</IonContent>
</IonPage>
);
};
```
```tsx
/* Using with IonLoading Component */
import React, { useState } from 'react';
import { IonLoading, IonButton, IonContent } from '@ionic/react';

View File

@@ -11,7 +11,7 @@
<script nomodule src="../../../../../dist/ionic/ionic.js"></script>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script></head>
<script type="module">
import { loadingController } from '../../../../dist/ionic/index.esm.js';
import { loadingController } from '../../../../../dist/ionic/index.esm.js';
window.loadingController = loadingController;
</script>
<body>

View File

@@ -1,4 +1,41 @@
```tsx
/* Using with useIonLoading Hook */
import React from 'react';
import { IonButton, IonContent, IonPage, useIonLoading } from '@ionic/react';
interface LoadingProps {}
const LoadingExample: React.FC<LoadingProps> = () => {
const [present] = useIonLoading();
return (
<IonPage>
<IonContent>
<IonButton
expand="block"
onClick={() =>
present({
duration: 3000,
})
}
>
Show Loading
</IonButton>
<IonButton
expand="block"
onClick={() => present('Loading', 2000, 'dots')}
>
Show Loading using params
</IonButton>
</IonContent>
</IonPage>
);
};
```
```tsx
/* Using with IonLoading Component */
import React, { useState } from 'react';
import { IonLoading, IonButton, IonContent } from '@ionic/react';

View File

@@ -46,7 +46,11 @@ export class Menu implements ComponentInterface, MenuI {
@State() isEndSide = false;
/**
* The content's id the menu should use.
* The `id` of the main content. When using
* a router this is typically `ion-router-outlet`.
* When not using a router, this is typically
* your main view's `ion-content`. This is not the
* id of the `ion-content` inside of your `ion-menu`.
*/
@Prop({ reflect: true }) contentId?: string;

View File

@@ -423,7 +423,7 @@ export class MenuExample {
</ion-content>
</ion-menu>
<ion-router-outlet main></ion-router-outlet>
<ion-router-outlet id="main"></ion-router-outlet>
</template>
<style>
.my-custom-menu {
@@ -477,15 +477,15 @@ export default defineComponent({
## Properties
| Property | Attribute | Description | Type | Default |
| -------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------ | --------------------- | ----------- |
| `contentId` | `content-id` | The content's id the menu should use. | `string \| undefined` | `undefined` |
| `disabled` | `disabled` | If `true`, the menu is disabled. | `boolean` | `false` |
| `maxEdgeStart` | `max-edge-start` | The edge threshold for dragging the menu open. If a drag/swipe happens over this value, the menu is not triggered. | `number` | `50` |
| `menuId` | `menu-id` | An id for the menu. | `string \| undefined` | `undefined` |
| `side` | `side` | Which side of the view the menu should be placed. | `"end" \| "start"` | `'start'` |
| `swipeGesture` | `swipe-gesture` | If `true`, swiping the menu is enabled. | `boolean` | `true` |
| `type` | `type` | The display type of the menu. Available options: `"overlay"`, `"reveal"`, `"push"`. | `string \| undefined` | `undefined` |
| Property | Attribute | Description | Type | Default |
| -------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | ----------- |
| `contentId` | `content-id` | The `id` of the main content. When using a router this is typically `ion-router-outlet`. When not using a router, this is typically your main view's `ion-content`. This is not the id of the `ion-content` inside of your `ion-menu`. | `string \| undefined` | `undefined` |
| `disabled` | `disabled` | If `true`, the menu is disabled. | `boolean` | `false` |
| `maxEdgeStart` | `max-edge-start` | The edge threshold for dragging the menu open. If a drag/swipe happens over this value, the menu is not triggered. | `number` | `50` |
| `menuId` | `menu-id` | An id for the menu. | `string \| undefined` | `undefined` |
| `side` | `side` | Which side of the view the menu should be placed. | `"end" \| "start"` | `'start'` |
| `swipeGesture` | `swipe-gesture` | If `true`, swiping the menu is enabled. | `boolean` | `true` |
| `type` | `type` | The display type of the menu. Available options: `"overlay"`, `"reveal"`, `"push"`. | `string \| undefined` | `undefined` |
## Events

View File

@@ -25,12 +25,12 @@ export const testMenu = async (
const menu = await page.find(selector);
await menu.callMethod('open');
await page.waitFor(1000);
await page.waitForTimeout(1000);
screenshotCompares.push(await page.compareScreenshot());
await menu.callMethod('close');
await page.waitFor(250);
await page.waitForTimeout(250);
screenshotCompares.push(await page.compareScreenshot('dismiss'));

View File

@@ -51,7 +51,7 @@
</ion-content>
</ion-menu>
<ion-router-outlet main></ion-router-outlet>
<ion-router-outlet id="main"></ion-router-outlet>
</template>
<style>
.my-custom-menu {

View File

@@ -79,6 +79,8 @@ export class ModalPage {
}
```
> If you need a wrapper element inside of your modal component, we recommend using a `<div class="ion-page">` so that the component dimensions are still computed properly.
### Passing Data
During creation of a modal, data can be passed in through the `componentProps`.
@@ -251,6 +253,8 @@ function presentModal() {
}
```
> If you need a wrapper element inside of your modal component, we recommend using a `<div class="ion-page">` so that the component dimensions are still computed properly.
### Passing Data
During creation of a modal, data can be passed in through the `componentProps`. The previous example can be written to include data:
@@ -328,6 +332,70 @@ modalElement.presentingElement = await modalController.getTop(); // Get the top-
### React
```tsx
/* Using with useIonModal Hook */
import React, { useState } from 'react';
import { IonButton, IonContent, IonPage, useIonModal } from '@ionic/react';
const Body: React.FC<{
count: number;
onDismiss: () => void;
onIncrement: () => void;
}> = ({ count, onDismiss, onIncrement }) => (
<div>
count: {count}
<IonButton expand="block" onClick={() => onIncrement()}>
Increment Count
</IonButton>
<IonButton expand="block" onClick={() => onDismiss()}>
Close
</IonButton>
</div>
);
const ModalExample: React.FC = () => {
const [count, setCount] = useState(0);
const handleIncrement = () => {
setCount(count + 1);
};
const handleDismiss = () => {
dismiss();
};
/**
* First parameter is the component to show, second is the props to pass
*/
const [present, dismiss] = useIonModal(Body, {
count,
onDismiss: handleDismiss,
onIncrement: handleIncrement,
});
return (
<IonPage>
<IonContent fullscreen>
<IonButton
expand="block"
onClick={() => {
present({
cssClass: 'my-class',
});
}}
>
Show Modal
</IonButton>
<div>Count: {count}</div>
</IonContent>
</IonPage>
);
};
```
```tsx
/* Using with IonModal Component */
import React, { useState } from 'react';
import { IonModal, IonButton, IonContent } from '@ionic/react';
@@ -467,6 +535,8 @@ export class PageModal {
}
```
> If you need a wrapper element inside of your modal component, we recommend using a `<div class="ion-page">` so that the component dimensions are still computed properly.
### Passing Data
During creation of a modal, data can be passed in through the `componentProps`.
@@ -578,16 +648,14 @@ async presentModal() {
```html
<template>
<div>
<ion-header>
<ion-toolbar>
<ion-title>{{ title }}</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
{{ content }}
</ion-content>
</div>
<ion-header>
<ion-toolbar>
<ion-title>{{ title }}</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
{{ content }}
</ion-content>
</template>
<script>
@@ -672,6 +740,8 @@ export default defineComponent({
</script>
```
> If you need a wrapper element inside of your modal component, we recommend using an `<ion-page>` so that the component dimensions are still computed properly.
## Properties

View File

@@ -28,7 +28,7 @@ export const testModal = async (
let modal = await page.find('ion-modal');
await modal.waitForVisible();
await page.waitFor(100);
await page.waitForTimeout(100);
screenshotCompares.push(await page.compareScreenshot());

View File

@@ -36,6 +36,8 @@ export class ModalPage {
}
```
> If you need a wrapper element inside of your modal component, we recommend using a `<div class="ion-page">` so that the component dimensions are still computed properly.
### Passing Data
During creation of a modal, data can be passed in through the `componentProps`.

View File

@@ -31,6 +31,8 @@ function presentModal() {
}
```
> If you need a wrapper element inside of your modal component, we recommend using a `<div class="ion-page">` so that the component dimensions are still computed properly.
### Passing Data
During creation of a modal, data can be passed in through the `componentProps`. The previous example can be written to include data:

View File

@@ -1,4 +1,68 @@
```tsx
/* Using with useIonModal Hook */
import React, { useState } from 'react';
import { IonButton, IonContent, IonPage, useIonModal } from '@ionic/react';
const Body: React.FC<{
count: number;
onDismiss: () => void;
onIncrement: () => void;
}> = ({ count, onDismiss, onIncrement }) => (
<div>
count: {count}
<IonButton expand="block" onClick={() => onIncrement()}>
Increment Count
</IonButton>
<IonButton expand="block" onClick={() => onDismiss()}>
Close
</IonButton>
</div>
);
const ModalExample: React.FC = () => {
const [count, setCount] = useState(0);
const handleIncrement = () => {
setCount(count + 1);
};
const handleDismiss = () => {
dismiss();
};
/**
* First parameter is the component to show, second is the props to pass
*/
const [present, dismiss] = useIonModal(Body, {
count,
onDismiss: handleDismiss,
onIncrement: handleIncrement,
});
return (
<IonPage>
<IonContent fullscreen>
<IonButton
expand="block"
onClick={() => {
present({
cssClass: 'my-class',
});
}}
>
Show Modal
</IonButton>
<div>Count: {count}</div>
</IonContent>
</IonPage>
);
};
```
```tsx
/* Using with IonModal Component */
import React, { useState } from 'react';
import { IonModal, IonButton, IonContent } from '@ionic/react';

View File

@@ -44,6 +44,8 @@ export class PageModal {
}
```
> If you need a wrapper element inside of your modal component, we recommend using a `<div class="ion-page">` so that the component dimensions are still computed properly.
### Passing Data
During creation of a modal, data can be passed in through the `componentProps`.

View File

@@ -1,15 +1,13 @@
```html
<template>
<div>
<ion-header>
<ion-toolbar>
<ion-title>{{ title }}</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
{{ content }}
</ion-content>
</div>
<ion-header>
<ion-toolbar>
<ion-title>{{ title }}</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
{{ content }}
</ion-content>
</template>
<script>
@@ -93,3 +91,5 @@ export default defineComponent({
});
</script>
```
> If you need a wrapper element inside of your modal component, we recommend using an `<ion-page>` so that the component dimensions are still computed properly.

View File

@@ -11,13 +11,13 @@ test.skip('nav: basic', async () => {
expect(await page.compareScreenshot()).toMatchScreenshot();
page.click('page-one ion-button.next');
await page.waitFor(navChanged);
await page.waitForTimeout(navChanged);
page.click('page-two ion-button.next');
await page.waitFor(navChanged);
await page.waitForTimeout(navChanged);
page.click('page-three ion-back-button');
await page.waitFor(navChanged);
await page.waitForTimeout(navChanged);
page.click('page-two ion-back-button');
await page.waitFor(navChanged);
await page.waitForTimeout(navChanged);
expect(await page.compareScreenshot('stack traversal')).toMatchScreenshot();
});

View File

@@ -12,17 +12,17 @@ test.skip('nav: nested', async () => {
expect(await page.compareScreenshot()).toMatchScreenshot();
await page.click('page-one ion-button.next');
await page.waitFor(navChanged);
await page.waitForTimeout(navChanged);
await page.click('page-two-one ion-button.next');
await page.waitFor(navChanged);
await page.waitForTimeout(navChanged);
await page.click('page-two-two ion-button.next');
await page.waitFor(navChanged);
await page.waitForTimeout(navChanged);
await page.click('page-three ion-back-button');
await page.waitFor(navChanged);
await page.waitForTimeout(navChanged);
await page.click('page-two-two ion-back-button');
await page.waitFor(navChanged);
await page.waitForTimeout(navChanged);
await page.click('page-two-one ion-back-button');
await page.waitFor(navChanged);
await page.waitForTimeout(navChanged);
expect(await page.compareScreenshot('stack traversal')).toMatchScreenshot();
});

View File

@@ -11,17 +11,17 @@ test.skip('nav: routing', async () => {
expect(await page.compareScreenshot()).toMatchScreenshot();
await page.click('page-root ion-button.next');
await page.waitFor(navChanged);
await page.waitForTimeout(navChanged);
await page.click('page-one ion-button.next');
await page.waitFor(navChanged);
await page.waitForTimeout(navChanged);
await page.click('page-two ion-button.next');
await page.waitFor(navChanged);
await page.waitForTimeout(navChanged);
await page.click('page-three ion-back-button');
await page.waitFor(navChanged);
await page.waitForTimeout(navChanged);
await page.click('page-two ion-back-button');
await page.waitFor(navChanged);
await page.waitForTimeout(navChanged);
await page.click('page-one ion-back-button');
await page.waitFor(navChanged);
await page.waitForTimeout(navChanged);
expect(await page.compareScreenshot('stack traversal')).toMatchScreenshot();
});

View File

@@ -18,7 +18,7 @@ export const testPickerColumn = async (
const openButton = await page.find(selector);
await openButton.click();
await page.waitFor(250);
await page.waitForTimeout(250);
screenshotCompares.push(await page.compareScreenshot());

View File

@@ -7,6 +7,95 @@ A Picker is a dialog that displays a row of buttons and columns underneath. It a
<!-- Auto Generated Below -->
## Usage
### React
```tsx
/* Using with useIonPicker Hook */
import React, { useState } from 'react';
import { IonButton, IonContent, IonPage, useIonPicker } from '@ionic/react';
const PickerExample: React.FC = () => {
const [present] = useIonPicker();
const [value, setValue] = useState('');
return (
<IonPage>
<IonContent>
<IonButton
expand="block"
onClick={() =>
present({
buttons: [
{
text: 'Confirm',
handler: (selected) => {
setValue(selected.animal.value)
},
},
],
columns: [
{
name: 'animal',
options: [
{ text: 'Dog', value: 'dog' },
{ text: 'Cat', value: 'cat' },
{ text: 'Bird', value: 'bird' },
],
},
],
})
}
>
Show Picker
</IonButton>
<IonButton
expand="block"
onClick={() =>
present(
[
{
name: 'animal',
options: [
{ text: 'Dog', value: 'dog' },
{ text: 'Cat', value: 'cat' },
{ text: 'Bird', value: 'bird' },
],
},
{
name: 'vehicle',
options: [
{ text: 'Car', value: 'car' },
{ text: 'Truck', value: 'truck' },
{ text: 'Bike', value: 'bike' },
],
},
],
[
{
text: 'Confirm',
handler: (selected) => {
setValue(`${selected.animal.value}, ${selected.vehicle.value}`)
},
},
]
)
}
>
Show Picker using params
</IonButton>
{value && (
<div>Selected Value: {value}</div>
)}
</IonContent>
</IonPage>
);
};
```
## Properties
| Property | Attribute | Description | Type | Default |

View File

@@ -0,0 +1,82 @@
```tsx
/* Using with useIonPicker Hook */
import React, { useState } from 'react';
import { IonButton, IonContent, IonPage, useIonPicker } from '@ionic/react';
const PickerExample: React.FC = () => {
const [present] = useIonPicker();
const [value, setValue] = useState('');
return (
<IonPage>
<IonContent>
<IonButton
expand="block"
onClick={() =>
present({
buttons: [
{
text: 'Confirm',
handler: (selected) => {
setValue(selected.animal.value)
},
},
],
columns: [
{
name: 'animal',
options: [
{ text: 'Dog', value: 'dog' },
{ text: 'Cat', value: 'cat' },
{ text: 'Bird', value: 'bird' },
],
},
],
})
}
>
Show Picker
</IonButton>
<IonButton
expand="block"
onClick={() =>
present(
[
{
name: 'animal',
options: [
{ text: 'Dog', value: 'dog' },
{ text: 'Cat', value: 'cat' },
{ text: 'Bird', value: 'bird' },
],
},
{
name: 'vehicle',
options: [
{ text: 'Car', value: 'car' },
{ text: 'Truck', value: 'truck' },
{ text: 'Bike', value: 'bike' },
],
},
],
[
{
text: 'Confirm',
handler: (selected) => {
setValue(`${selected.animal.value}, ${selected.vehicle.value}`)
},
},
]
)
}
>
Show Picker using params
</IonButton>
{value && (
<div>Selected Value: {value}</div>
)}
</IonContent>
</IonPage>
);
};
```

View File

@@ -114,6 +114,59 @@ function presentPopover(ev) {
### React
```tsx
/* Using with useIonPopover Hook */
import React from 'react';
import {
IonButton,
IonContent,
IonItem,
IonList,
IonListHeader,
IonPage,
useIonPopover,
} from '@ionic/react';
const PopoverList: React.FC<{
onHide: () => void;
}> = ({ onHide }) => (
<IonList>
<IonListHeader>Ionic</IonListHeader>
<IonItem button>Learn Ionic</IonItem>
<IonItem button>Documentation</IonItem>
<IonItem button>Showcase</IonItem>
<IonItem button>GitHub Repo</IonItem>
<IonItem lines="none" detail={false} button onClick={onHide}>
Close
</IonItem>
</IonList>
);
const PopoverExample: React.FC = () => {
const [present, dismiss] = useIonPopover(PopoverList, { onHide: () => dismiss() });
return (
<IonPage>
<IonContent>
<IonButton
expand="block"
onClick={(e) =>
present({
event: e.nativeEvent,
})
}
>
Show Popover
</IonButton>
</IonContent>
</IonPage>
);
};
```
```tsx
/* Using with IonPopover Component */
import React, { useState } from 'react';
import { IonPopover, IonButton } from '@ionic/react';

View File

@@ -1,4 +1,57 @@
```tsx
/* Using with useIonPopover Hook */
import React from 'react';
import {
IonButton,
IonContent,
IonItem,
IonList,
IonListHeader,
IonPage,
useIonPopover,
} from '@ionic/react';
const PopoverList: React.FC<{
onHide: () => void;
}> = ({ onHide }) => (
<IonList>
<IonListHeader>Ionic</IonListHeader>
<IonItem button>Learn Ionic</IonItem>
<IonItem button>Documentation</IonItem>
<IonItem button>Showcase</IonItem>
<IonItem button>GitHub Repo</IonItem>
<IonItem lines="none" detail={false} button onClick={onHide}>
Close
</IonItem>
</IonList>
);
const PopoverExample: React.FC = () => {
const [present, dismiss] = useIonPopover(PopoverList, { onHide: () => dismiss() });
return (
<IonPage>
<IonContent>
<IonButton
expand="block"
onClick={(e) =>
present({
event: e.nativeEvent,
})
}
>
Show Popover
</IonButton>
</IonContent>
</IonPage>
);
};
```
```tsx
/* Using with IonPopover Component */
import React, { useState } from 'react';
import { IonPopover, IonButton } from '@ionic/react';

View File

@@ -1,18 +1,19 @@
@import "../../themes/ionic.globals";
// Progress bar
// --------------------------------------------------
// Host has no background by default - this will be added to the progress-buffer-bar
// ------------------------------------------------------------------------
// Host has no background by default - this will be added to the progress-buffer-bar
:host {
/**
* @prop --background: Same as --buffer-background when using a determinate progress bar, otherwise it styles the background of the ion-progress-bar itself.
* @prop --progress-background: Color of the progress bar
* @prop --buffer-background: Color of the buffer bar
*/
--background: #{ion-color(primary, base, 0.2)};
* @prop --background: Background of the progress track, or the buffer bar if `buffer` is set
* @prop --progress-background: Background of the progress bar representing the current value
* @prop --buffer-background: DEPRECATED, use `--background` instead
*/
--background: #{ion-color(primary, base, 0.3)};
--progress-background: #{ion-color(primary, base)};
--buffer-background: var(--background);
display: block;
position: relative;
@@ -25,36 +26,38 @@
overflow: hidden;
}
:host(.ion-color) {
--progress-background: #{current-color(base)};
--buffer-background: #{current-color(base, 0.2)};
}
// indeterminate has no progress-buffer-bar, so it will be added to the host
:host(.progress-bar-indeterminate) {
background: var(--buffer-background);
}
.progress,
.progress-indeterminate,
.indeterminate-bar-primary,
.indeterminate-bar-secondary,
.progress-buffer-bar,
.progress-buffer-bar:before,
.buffer-circles {
.progress-buffer-bar {
@include position(0, 0, 0, 0);
position: absolute;
width: 100%;
height: 100%;
}
.buffer-circles-container,
.buffer-circles {
@include position(0, 0, 0, 0);
position: absolute;
}
// Extend a bit to overflow. The size of animated distance.
.buffer-circles {
/* stylelint-disable property-blacklist */
right: -10px;
left: -10px;
/* stylelint-enable property-blacklist */
}
// Determinate progress bar
// --------------------------------------------------
.progress,
.progress-buffer-bar {
.progress-buffer-bar,
.buffer-circles-container {
/* stylelint-disable-next-line property-blacklist */
transform-origin: left top;
@@ -72,17 +75,13 @@
}
.progress-buffer-bar {
// It's currently here because --buffer-background has an alpha
// Otherwise the buffer circles would be seen through
background: #fff;
background: var(--buffer-background);
z-index: 1; // Make it behind the progress
z-index: 1;
}
&:before {
background: var(--buffer-background);
content: "";
}
.buffer-circles-container {
overflow: hidden;
}
// MD based animation on indeterminate type
@@ -109,7 +108,7 @@
top: 0;
right: 0;
bottom: 0;
left: -54.888891%;
left: -54.888891%;
/* stylelint-enable property-blacklist */
animation: secondary-indeterminate-translate 2s infinite linear;
@@ -120,38 +119,33 @@
}
}
// Buffer style
// --------------------------------------------------
// Progress Bar: Buffer Circles
// ------------------------------------------------------------------------
.buffer-circles {
background: radial-gradient(ellipse at center, var(--buffer-background) 0%, var(--buffer-background) 30%, transparent 30%) repeat-x 5px center;
background-image: radial-gradient(ellipse at center, var(--buffer-background) 0%, var(--buffer-background) 30%, transparent 30%);
/* stylelint-disable property-blacklist */
background-repeat: repeat-x;
background-position: 5px center;
background-size: 10px 10px;
/* stylelint-enable property-blacklist */
z-index: 0;
animation: buffering 450ms infinite linear;
}
// Progress Bar: Reversed
// ------------------------------------------------------------------------
// If reversed is set to true, the animation will be reversed
// and the bars starting at the top right
// --------------------------------------------------
// and the bar will start at the top right
:host(.progress-bar-reversed) {
.progress,
.progress-buffer-bar {
/* stylelint-disable-next-line property-blacklist */
transform-origin: right top;
}
.buffer-circles,
.indeterminate-bar-primary,
.indeterminate-bar-secondary,
.progress-indeterminate {
animation-direction: reverse;
}
transform: scaleX(-1);
}
// Progress paused
// --------------------------------------------------
// Progress Bar: Paused
// ------------------------------------------------------------------------
:host(.progress-paused) {
.indeterminate-bar-secondary,
@@ -161,8 +155,27 @@
}
}
// Animation Keyframes
// --------------------------------------------------
// Progress Bar: Color
// ------------------------------------------------------------------------
:host(.ion-color) .progress-buffer-bar {
background: #{current-color(base, 0.3)};
}
:host(.ion-color) .buffer-circles {
background-image: radial-gradient(ellipse at center, #{current-color(base, 0.3)} 0%, #{current-color(base, 0.3)} 30%, transparent 30%);
}
:host(.ion-color) {
.progress,
.progress-indeterminate {
background: #{current-color(base)};
}
}
// Progress Bar: Animation Keyframes
// ------------------------------------------------------------------------
// Source: https://github.com/material-components/material-components-web/blob/master/packages/mdc-linear-progress/_keyframes.scss
@keyframes primary-indeterminate-translate {
@@ -262,3 +275,4 @@
transform: translateX(-10px);
}
}

View File

@@ -8,6 +8,11 @@ import { createColorClasses } from '../../utils/theme';
/**
* @virtualProp {"ios" | "md"} mode - The mode determines which platform styles to use.
*
* @part progress - The progress bar that shows the current value when `type` is `"determinate"` and slides back and forth when `type` is `"indeterminate"`.
* @part stream - The animated circles that appear while buffering. This only shows when `buffer` is set and `type` is `"determinate"`.
* @part track - The track bar behind the progress bar. If the `buffer` property is set and `type` is `"determinate"` the track will be the
* width of the `buffer` value.
*/
@Component({
tag: 'ion-progress-bar',
@@ -77,10 +82,12 @@ export class ProgressBar implements ComponentInterface {
}
const renderIndeterminate = () => {
return [
<div class="indeterminate-bar-primary"><span class="progress-indeterminate"></span></div>,
<div class="indeterminate-bar-secondary"><span class="progress-indeterminate"></span></div>
];
return (
<div part="track" class="progress-buffer-bar">
<div class="indeterminate-bar-primary"><span part="progress" class="progress-indeterminate"></span></div>
<div class="indeterminate-bar-secondary"><span part="progress" class="progress-indeterminate"></span></div>
</div>
);
};
const renderProgress = (value: number, buffer: number) => {
@@ -88,8 +95,19 @@ const renderProgress = (value: number, buffer: number) => {
const finalBuffer = clamp(0, buffer, 1);
return [
<div class="progress" style={{ transform: `scaleX(${finalValue})` }}></div>,
finalBuffer !== 1 && <div class="buffer-circles"></div>,
<div class="progress-buffer-bar" style={{ transform: `scaleX(${finalBuffer})` }}></div>,
<div part="progress" class="progress" style={{ transform: `scaleX(${finalValue})` }}></div>,
/**
* Buffer circles with two container to move
* the circles behind the buffer progress
* with respecting the animation.
* When finalBuffer === 1, we use display: none
* instead of removing the element to avoid flickering.
*/
<div class={{ 'buffer-circles-container': true, 'ion-hide': finalBuffer === 1 }} style={{ transform: `translateX(${finalBuffer * 100}%)` }}>
<div class="buffer-circles-container" style={{ transform: `translateX(-${finalBuffer * 100}%)` }}>
<div part="stream" class="buffer-circles"></div>
</div>
</div>,
<div part="track" class="progress-buffer-bar" style={{ transform: `scaleX(${finalBuffer})` }}></div>,
];
};

View File

@@ -1,20 +1,18 @@
# ion-progress-bar
ion-progress-bar is a horizontal progress bar to visualize the progression of an operation and activity. You can choose between two types: `determinate` and `indeterminate`.
Progress bars inform users about the status of ongoing processes, such as loading an app, submitting a form, or saving updates. There are two types of progress bars: `determinate` and `indeterminate`.
## Progress Type
### Determinate
If the percentage of an operation is known, you should use the determinate type. This is the default type and the progress is represented by the `value` property.
Determinate is the default type. It should be used when the percentage of an operation is known. The progress is represented by setting the `value` property. This can be used to show the progress increasing from 0 to 100% of the track.
A buffer shows circles as animation to indicate some activity. If the `buffer` property is smaller than 1 you can show the additional buffering progress.
If the `buffer` property is set, a buffer stream will show with animated circles to indicate activity. The value of the `buffer` property will also be represented by how much visible track there is. If the value of `buffer` is less than the `value` property, there will be no visible track. If `buffer` is equal to `1` then the buffer stream will be hidden.
### Indeterminate
If an operation is in progress and it's not necessary to indicate how long it will take.
If you add `reversed="true"`, you receive a query which is used to indicate pre-loading.
The indeterminate type should be used when it is unknown how long the process will take. The progress bar is not tied to the `value`, instead it continually slides along the track until the process is complete.
<!-- Auto Generated Below -->
@@ -144,13 +142,22 @@ export default defineComponent({
| `value` | `value` | The value determines how much of the active bar should display when the `type` is `"determinate"`. The value should be between [0, 1]. | `number` | `0` |
## Shadow Parts
| Part | Description |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `"progress"` | The progress bar that shows the current value when `type` is `"determinate"` and slides back and forth when `type` is `"indeterminate"`. |
| `"stream"` | The animated circles that appear while buffering. This only shows when `buffer` is set and `type` is `"determinate"`. |
| `"track"` | The track bar behind the progress bar. If the `buffer` property is set and `type` is `"determinate"` the track will be the width of the `buffer` value. |
## CSS Custom Properties
| Name | Description |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `--background` | Same as --buffer-background when using a determinate progress bar, otherwise it styles the background of the ion-progress-bar itself. |
| `--buffer-background` | Color of the buffer bar |
| `--progress-background` | Color of the progress bar |
| Name | Description |
| ----------------------- | ---------------------------------------------------------------------- |
| `--background` | Background of the progress track, or the buffer bar if `buffer` is set |
| `--buffer-background` | DEPRECATED, use `--background` instead |
| `--progress-background` | Background of the progress bar representing the current value |
----------------------------------------------

View File

@@ -9,7 +9,9 @@
<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>
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script>
<style>
.custom-bar-background {
--buffer-background: red;
}
@@ -126,13 +128,26 @@
</ion-list-header>
<ion-progress-bar color="tertiary" buffer="0"></ion-progress-bar>
<ion-list-header>
<ion-label>
Buffer (change buffer with slider)
</ion-label>
</ion-list-header>
<ion-progress-bar class="progressBarBuffer" value="0.20" buffer="0.4"></ion-progress-bar>
<ion-progress-bar class="progressBarBuffer" value="0.20" buffer="0.4" reversed="true"></ion-progress-bar>
<ion-item>
<ion-range pin="true" value="0" id="progressValueBuffer">
<ion-label slot="start">0</ion-label>
<ion-label slot="end">100</ion-label>
</ion-range>
</ion-item>
</ion-list>
</ion-content>
</ion-app>
<script>
// Progress Bar Value
const progressValue = document.getElementById('progressValue');
const progressBar = document.getElementById('progressBar');
@@ -140,6 +155,13 @@
progressBar.value = ev.detail.value / 100;
});
// Progress Bar Buffer
const progressValueBuffer = document.getElementById('progressValueBuffer');
const progressBarBuffer = document.querySelectorAll('.progressBarBuffer');
progressValueBuffer.addEventListener('ionChange', function (ev) {
progressBarBuffer.forEach(ele => ele.buffer = ev.detail.value / 100);
});
</script>
</body>

View File

@@ -0,0 +1,10 @@
import { newE2EPage } from '@stencil/core/testing';
test('progress-bar: standalone', async () => {
const page = await newE2EPage({
url: '/src/components/progress-bar/test/standalone?ionic:_testing=true'
});
const compare = await page.compareScreenshot();
expect(compare).toMatchScreenshot();
});

View File

@@ -12,19 +12,171 @@
<script type="module" src="../../../../../dist/ionic/ionic.esm.js"></script></head>
<body>
<h1>Default Progress Bar</h1>
<h1>Default Progress Bars</h1>
<ion-progress-bar></ion-progress-bar>
<h1>Default Progress Bar with 50% width</h1>
<ion-progress-bar value="0.5"></ion-progress-bar>
<h1>Colorize Progress Bar</h1>
<ion-progress-bar color="primary" value="0.5"></ion-progress-bar>
<ion-progress-bar color="secondary" value="0.5"></ion-progress-bar>
<h1>Other types</h1>
<ion-progress-bar type="indeterminate"></ion-progress-bar>
<hr>
<h1>Progress Bar: Colors</h1>
<ion-progress-bar color="primary" value="0.5"></ion-progress-bar>
<ion-progress-bar color="secondary" value="0.5"></ion-progress-bar>
<ion-progress-bar color="tertiary" value="0.5"></ion-progress-bar>
<ion-progress-bar color="success" value="0.5"></ion-progress-bar>
<ion-progress-bar color="warning" value="0.5"></ion-progress-bar>
<ion-progress-bar color="danger" value="0.5"></ion-progress-bar>
<ion-progress-bar color="dark" value="0.5"></ion-progress-bar>
<ion-progress-bar color="light" value="0.5"></ion-progress-bar>
<hr>
<h1>Default Types</h1>
<ion-progress-bar></ion-progress-bar>
<ion-progress-bar value="0.5"></ion-progress-bar>
<ion-progress-bar value="0.20" buffer="0.4"></ion-progress-bar>
<ion-progress-bar color="warning" reversed="true" value="0.20" buffer="0.4"></ion-progress-bar>
<ion-progress-bar buffer="0"></ion-progress-bar>
<ion-progress-bar color="danger" type="indeterminate"></ion-progress-bar>
<ion-progress-bar type="indeterminate" reversed="true"></ion-progress-bar>
<hr>
<h1>Custom: colors by part</h1>
<ion-progress-bar class="custom-color-parts"></ion-progress-bar>
<ion-progress-bar class="custom-color-parts" buffer="0.9"></ion-progress-bar>
<ion-progress-bar class="custom-color-parts" value="0.5"></ion-progress-bar>
<ion-progress-bar class="custom-color-parts" value="0.20" buffer="0.4"></ion-progress-bar>
<ion-progress-bar class="custom-color-parts" color="warning" reversed="true" value="0.20" buffer="0.4"></ion-progress-bar>
<ion-progress-bar class="custom-color-parts" buffer="0"></ion-progress-bar>
<ion-progress-bar class="custom-color-parts" color="danger" type="indeterminate"></ion-progress-bar>
<ion-progress-bar class="custom-color-parts" type="indeterminate" reversed="true"></ion-progress-bar>
<hr>
<h1>Custom: colors by css variable</h1>
<ion-progress-bar class="custom-color-variables"></ion-progress-bar>
<ion-progress-bar class="custom-color-variables" value="0.5"></ion-progress-bar>
<ion-progress-bar class="custom-color-variables" value="0.20" buffer="0.4"></ion-progress-bar>
<ion-progress-bar class="custom-color-variables" color="warning" reversed="true" value="0.20" buffer="0.4"></ion-progress-bar>
<ion-progress-bar class="custom-color-variables" buffer="0"></ion-progress-bar>
<ion-progress-bar class="custom-color-variables" color="danger" type="indeterminate"></ion-progress-bar>
<ion-progress-bar class="custom-color-variables" type="indeterminate" reversed="true"></ion-progress-bar>
<hr>
<h1>Custom border radius</h1>
<ion-progress-bar class="custom-border-radius"></ion-progress-bar>
<ion-progress-bar class="custom-border-radius" value="0.5"></ion-progress-bar>
<ion-progress-bar class="custom-border-radius" type="indeterminate"></ion-progress-bar>
<h1>Custom transition</h1>
<ion-progress-bar class="random-value" max="100"></ion-progress-bar>
<ion-progress-bar class="random-value custom-transition" max="100"></ion-progress-bar>
</body>
<script>
let randomValues = document.querySelectorAll('.random-value');
setInterval(() => {
let value = Math.random();
for (let i = 0; i < randomValues.length; i++) {
randomValues[i].value = value;
}
}, 100);
</script>
<style>
h1 {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 12px;
font-weight: normal;
color: #a1a7b0;
margin-top: 10px;
margin-left: 5px;
}
hr {
background: #eff1f3;
border: none;
height: 1px;
margin-top: 18px;
margin-bottom: 25px;
}
ion-progress-bar {
margin-bottom: 10px;
}
/*
* Custom progress bar color using parts
* ------------------------------------------------------
* Note: in these examples setting the background on
* each element should override the color prop
*/
/* determinate buffer / track and indeterminate track */
.custom-color-parts::part(track) {
background:rgb(158, 157, 36, 0.2);
}
/* determinate and indeterminate progress background */
.custom-color-parts::part(progress) {
background: #9e9d24;
}
/* buffer stream (animated circles) */
.custom-color-parts::part(stream) {
background-image: radial-gradient(ellipse at center, rgb(158, 157, 36, 0.2) 0%, rgb(158, 157, 36, 0.2) 30%, transparent 30%);
}
/*
* Custom progress bar color using css variables
* ------------------------------------------------------
* Note: in this example setting the background via
* CSS variables should NOT override the color prop
*/
.custom-color-variables {
--background: rgb(158, 157, 36, 0.2);
--progress-background: #9e9d24;
}
/*
* Custom progress bar border radius using parts
* ------------------------------------------------------
*/
.custom-border-radius {
border-radius: 10px;
height: 20px;
}
.custom-border-radius::part(progress) {
border-radius: 0 50% 50% 0;
}
.custom-border-radius[type="indeterminate"]::part(progress) {
border-radius: 8px;
}
/*
* Custom transition for fast value changes
* ------------------------------------------------------
* The first progress bar in the example has the default
* transition, while the second has none. This is
* apparent because they use the same values.
*/
.custom-transition::part(progress) {
transition: none;
}
</style>
</html>

View File

@@ -10,6 +10,7 @@ export class RadioGroup implements ComponentInterface {
private inputId = `ion-rg-${radioGroupIds++}`;
private labelId = `${this.inputId}-lbl`;
private label?: HTMLIonLabelElement | null;
@Element() el!: HTMLElement;
@@ -68,10 +69,9 @@ export class RadioGroup implements ComponentInterface {
async connectedCallback() {
// Get the list header if it exists and set the id
// this is used to set aria-labelledby
const el = this.el;
const header = el.querySelector('ion-list-header') || el.querySelector('ion-item-divider');
const header = this.el.querySelector('ion-list-header') || this.el.querySelector('ion-item-divider');
if (header) {
const label = header.querySelector('ion-label');
const label = this.label = header.querySelector('ion-label');
if (label) {
this.labelId = label.id = this.name + '-lbl';
}
@@ -83,6 +83,8 @@ export class RadioGroup implements ComponentInterface {
}
private onClick = (ev: Event) => {
ev.preventDefault();
const selectedRadio = ev.target && (ev.target as HTMLElement).closest('ion-radio');
if (selectedRadio) {
const currentValue = this.value;
@@ -110,12 +112,13 @@ export class RadioGroup implements ComponentInterface {
// Only move the radio if the current focus is in the radio group
if (ev.target && radios.includes(ev.target)) {
const index = radios.findIndex(radio => radio === ev.target);
const current = radios[index];
let next;
// If hitting arrow down or arrow right, move to the next radio
// If we're on the last radio, move to the first radio
if (['ArrowDown', 'ArrowRight'].includes(ev.key)) {
if (['ArrowDown', 'ArrowRight'].includes(ev.code)) {
next = (index === radios.length - 1)
? radios[0]
: radios[index + 1];
@@ -123,29 +126,43 @@ export class RadioGroup implements ComponentInterface {
// If hitting arrow up or arrow left, move to the previous radio
// If we're on the first radio, move to the last radio
if (['ArrowUp', 'ArrowLeft'].includes(ev.key)) {
if (['ArrowUp', 'ArrowLeft'].includes(ev.code)) {
next = (index === 0)
? radios[radios.length - 1]
: radios[index - 1];
}
if (next && radios.includes(next)) {
next.setFocus();
next.setFocus(ev);
if (!inSelectPopover) {
this.value = next.value;
}
}
// Update the radio group value when a user presses the
// space bar on top of a selected radio (only applies
// to radios in a select popover)
if (['Space'].includes(ev.code)) {
this.value = current.value;
// Prevent browsers from jumping
// to the bottom of the screen
ev.preventDefault();
}
}
}
render() {
const { label, labelId } = this;
const mode = getIonMode(this);
return (
<Host
role="radiogroup"
aria-labelledby={this.labelId}
aria-labelledby={label ? labelId : null}
onClick={this.onClick}
class={getIonMode(this)}
class={mode}
>
</Host>
);

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