diff --git a/demo/e2e/screenshot.spec.ts b/demo/e2e/screenshot.spec.ts index cc62e971..a6296a3c 100644 --- a/demo/e2e/screenshot.spec.ts +++ b/demo/e2e/screenshot.spec.ts @@ -51,6 +51,16 @@ const routes = [ const prepareScreenShot = async (page: Page, routeName: string) => { await page.waitForTimeout(1000); await page.waitForSelector('ion-content[role="main"]', { timeout: 10000 }); + await expect + .poll(() => + page + .locator('ion-content[role="main"] img') + .evaluateAll((images) => + images.every((image) => (image as HTMLImageElement).complete && (image as HTMLImageElement).naturalWidth > 0), + ), + ) + .toBe(true); + if (!routeName.includes(':')) { const scrollHeight = await page.locator('ion-content[role="main"]').evaluate(async (el: any) => { const scrollEl = await el.getScrollElement(); diff --git a/demo/e2e/screenshot.spec.ts-snapshots/modal-card-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/modal-card-dark.png index 2824de16..db818195 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/modal-card-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/modal-card-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/modal-card.png b/demo/e2e/screenshot.spec.ts-snapshots/modal-card.png index 3246df04..d4fe7773 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/modal-card.png and b/demo/e2e/screenshot.spec.ts-snapshots/modal-card.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/modal-normal-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/modal-normal-dark.png index 8019b72f..b8a9e731 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/modal-normal-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/modal-normal-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/modal-normal.png b/demo/e2e/screenshot.spec.ts-snapshots/modal-normal.png index 227e7737..88d82398 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/modal-normal.png and b/demo/e2e/screenshot.spec.ts-snapshots/modal-normal.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/modal-sheet-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/modal-sheet-dark.png index 174cdacd..9506e7a8 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/modal-sheet-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/modal-sheet-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/modal-sheet.png b/demo/e2e/screenshot.spec.ts-snapshots/modal-sheet.png index d80f94b2..85ed0afc 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/modal-sheet.png and b/demo/e2e/screenshot.spec.ts-snapshots/modal-sheet.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/select-modal-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/select-modal-dark.png index e6ee4247..5f598ae6 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/select-modal-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/select-modal-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/select-modal.png b/demo/e2e/screenshot.spec.ts-snapshots/select-modal.png index 8455293f..6ac8e1e9 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/select-modal.png and b/demo/e2e/screenshot.spec.ts-snapshots/select-modal.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/toolbar-dark.png b/demo/e2e/screenshot.spec.ts-snapshots/toolbar-dark.png index 962eb044..0ce4b1d2 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/toolbar-dark.png and b/demo/e2e/screenshot.spec.ts-snapshots/toolbar-dark.png differ diff --git a/demo/e2e/screenshot.spec.ts-snapshots/toolbar.png b/demo/e2e/screenshot.spec.ts-snapshots/toolbar.png index 51ef6d2b..53fa343d 100644 Binary files a/demo/e2e/screenshot.spec.ts-snapshots/toolbar.png and b/demo/e2e/screenshot.spec.ts-snapshots/toolbar.png differ diff --git a/demo/src/app/index/pages/card/card.page.html b/demo/src/app/index/pages/card/card.page.html index 040ebf65..82b1e8d5 100644 --- a/demo/src/app/index/pages/card/card.page.html +++ b/demo/src/app/index/pages/card/card.page.html @@ -33,7 +33,7 @@

card

Here's a small text description for the card content. Nothing more, nothing less. - Silhouette of mountains + Silhouette of mountains Card Title Card Subtitle @@ -61,28 +61,28 @@

card

- Silhouette of mountains + Silhouette of mountains Item - Silhouette of mountains + Silhouette of mountains Item - Silhouette of mountains + Silhouette of mountains Item - Silhouette of mountains + Silhouette of mountains Item diff --git a/demo/src/app/index/pages/chip/chip.page.html b/demo/src/app/index/pages/chip/chip.page.html index 01fc479d..db4efba7 100644 --- a/demo/src/app/index/pages/chip/chip.page.html +++ b/demo/src/app/index/pages/chip/chip.page.html @@ -29,7 +29,7 @@

chip

Outline - Silhouette of a person's head + Silhouette of a person's head Avatar Chip @@ -47,7 +47,7 @@

chip

Outline - Silhouette of a person's head + Silhouette of a person's head Avatar Chip diff --git a/demo/src/app/index/pages/toolbar/toolbar.page.html b/demo/src/app/index/pages/toolbar/toolbar.page.html index fa9c6126..b0ead585 100644 --- a/demo/src/app/index/pages/toolbar/toolbar.page.html +++ b/demo/src/app/index/pages/toolbar/toolbar.page.html @@ -106,6 +106,85 @@

toolbar

Menu Button + + + + +

Toolbar spacing

+

Compare the outer inset and spacing between visible actions.

+
+
+
+
+ + @for (slot of ['start', 'end']; track slot) { + {{ slot }}: single group + + Save + + + {{ slot }}: two groups + + Save + Edit + + + {{ slot }}: standalone button and group + + Save + Edit + + + {{ slot }}: hidden menu group and visible group + + + Save + + + {{ slot }}: hidden child and visible child + + + + Save + + Edit + + + {{ slot }}: empty group between visible groups + + Save + + Edit + + } + @for ( + slots of [ + ['start', 'secondary'], + ['primary', 'end'], + ]; + track slots[0] + ) { + {{ slots[0] }} / {{ slots[1] }} + + Save + Edit + + } + diff --git a/demo/src/assets/ionic-demos/LICENSE b/demo/src/assets/ionic-demos/LICENSE new file mode 100644 index 00000000..022e9472 --- /dev/null +++ b/demo/src/assets/ionic-demos/LICENSE @@ -0,0 +1,190 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2016 Drifty Co. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/demo/src/assets/ionic-demos/README.md b/demo/src/assets/ionic-demos/README.md new file mode 100644 index 00000000..a3a00631 --- /dev/null +++ b/demo/src/assets/ionic-demos/README.md @@ -0,0 +1 @@ +These unmodified demo images are vendored from [ionic-team/ionic-docs](https://github.com/ionic-team/ionic-docs/tree/226ce07e4b2e73ab556a61dd55c9dc4a87a4a48d/static/img/demos) under the Apache License 2.0 (see LICENSE). Local copies keep the demo and screenshot tests independent of the documentation website. diff --git a/demo/src/assets/ionic-demos/avatar.svg b/demo/src/assets/ionic-demos/avatar.svg new file mode 100644 index 00000000..bd0cec9a --- /dev/null +++ b/demo/src/assets/ionic-demos/avatar.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/demo/src/assets/ionic-demos/card-media.png b/demo/src/assets/ionic-demos/card-media.png new file mode 100644 index 00000000..1a5bd321 Binary files /dev/null and b/demo/src/assets/ionic-demos/card-media.png differ diff --git a/demo/src/assets/ionic-demos/thumbnail.svg b/demo/src/assets/ionic-demos/thumbnail.svg new file mode 100644 index 00000000..b12a43f9 --- /dev/null +++ b/demo/src/assets/ionic-demos/thumbnail.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/styles/components/ion-modal.scss b/src/styles/components/ion-modal.scss index 91d0ea58..adc39a69 100644 --- a/src/styles/components/ion-modal.scss +++ b/src/styles/components/ion-modal.scss @@ -12,13 +12,6 @@ ion-modal.ios:not(.ios-theme-disabled, .ios26-disabled) { padding-left: var(--ion-safe-area-left); } - &.modal-sheet { - ion-toolbar { - padding-right: calc(var(--ion-safe-area-right) + 4px); - padding-left: calc(var(--ion-safe-area-left) + 4px); - } - } - &.modal-card { /** * IonicでCardModalのborder-radiusはハードコーディングされています。 diff --git a/src/styles/components/ion-toolbar.scss b/src/styles/components/ion-toolbar.scss index 9d7937e3..f5cd8e5b 100644 --- a/src/styles/components/ion-toolbar.scss +++ b/src/styles/components/ion-toolbar.scss @@ -13,7 +13,37 @@ ion-header.ios:not(.ios-theme-disabled, .ios26-disabled) ion-toolbar:not(.ios-th ion-modal.ios:not(.ios-theme-disabled, .ios26-disabled).modal-card, ion-modal.ios:not(.ios-theme-disabled, .ios26-disabled).modal-sheet { ion-header.ios:not(.ios-theme-disabled, .ios26-disabled) ion-toolbar.ios:not(.ios-theme-disabled, .ios26-disabled):first-of-type { + // A 44px control is centered 16px below the content edge. padding-top: 6px; + padding-bottom: 0; + --min-height: 64px; + } +} + +@media (min-width: 768px) and (min-height: 600px) { + ion-modal.ios:not(.ios-theme-disabled, .ios26-disabled) { + ion-header.ios:not(.ios-theme-disabled, .ios26-disabled) ion-toolbar.ios:not(.ios-theme-disabled, .ios26-disabled):first-of-type { + // Center a 44px control at 10px, retaining the safe area in fullscreen modals. + padding-top: max(calc(var(--ion-safe-area-top, 0px) - 10px), 0px); + padding-bottom: 0; + --min-height: 64px; + } + } + // iPad card and sheet toolbars use the centered 10px system inset. + ion-modal.ios:not(.ios-theme-disabled, .ios26-disabled):is(.modal-card, .modal-sheet) { + &.modal-card { + container-name: ios-theme-modal; + } + ion-header.ios:not(.ios-theme-disabled, .ios26-disabled) ion-toolbar.ios:not(.ios-theme-disabled, .ios26-disabled):first-of-type { + padding-top: 0; + } + } + @container ios-theme-modal style(--height: 100%) or style(--height: 100vh) or style(--height: 100dvh) { + ion-modal.ios:not(.ios-theme-disabled, .ios26-disabled).modal-card + ion-header.ios:not(.ios-theme-disabled, .ios26-disabled) + ion-toolbar.ios:not(.ios-theme-disabled, .ios26-disabled):first-of-type { + padding-top: max(calc(var(--ion-safe-area-top, 0px) - 10px), 0px); + } } } @@ -101,4 +131,52 @@ ion-toolbar.ios:not(.ios-theme-disabled, .ios26-disabled) { ion-buttons > ion-back-button { margin-inline: 0; } + + > ion-button:not(.ios-theme-disabled, .ios26-disabled) { + margin-inline: 12px; + } + // Only visible actions participate in toolbar spacing. + $visible: ':not([hidden], ion-menu-button.menu-button-hidden, ion-back-button:not(.show-back-button))'; + $button: ':is(ion-button, ion-back-button)#{$visible}'; + $group: 'ion-buttons#{$visible}'; + $action: ':is(#{$button}, #{$group}:has(> #{$visible}))'; + $optOut: ':is(.ios-theme-disabled, .ios26-disabled)'; + + > ion-buttons:not(:has(> #{$visible})) { + display: none; + } + + @each $slot in start, end, primary, secondary { + // Keep standalone margins when the slot includes a visible opted-out action. + &:not(.toolbar-searchbar):not(:has(> #{$button}#{$optOut}[slot='#{$slot}'], > #{$group}#{$optOut}[slot='#{$slot}'] > #{$visible})) { + > #{$action}[slot='#{$slot}'] { + margin-inline: 6px; + } + > :nth-child(1 of #{$action}[slot='#{$slot}']) { + margin-inline-start: 12px; + } + > :nth-last-child(1 of #{$action}[slot='#{$slot}']) { + margin-inline-end: 12px; + } + } + } + // Ionic orders these slots together, independently of their DOM order. + @each $first, $second in (start, secondary), (primary, end) { + &:not(.toolbar-searchbar):has(> #{$button}[slot='#{$first}'], > #{$group}[slot='#{$first}'] > #{$visible}):has( + > #{$button}[slot='#{$second}'], + > #{$group}[slot='#{$second}'] > #{$visible} + ):not( + :has( + > #{$button}#{$optOut}:is([slot='#{$first}'], [slot='#{$second}']), + > #{$group}#{$optOut}:is([slot='#{$first}'], [slot='#{$second}']) > #{$visible} + ) + ) { + > :nth-last-child(1 of #{$action}[slot='#{$first}']) { + margin-inline-end: 6px; + } + > :nth-child(1 of #{$action}[slot='#{$second}']) { + margin-inline-start: 6px; + } + } + } }