mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
fix(content): better device suport (ios)
This commit is contained in:
3
packages/core/src/components.d.ts
vendored
3
packages/core/src/components.d.ts
vendored
@@ -761,6 +761,7 @@ declare global {
|
||||
}
|
||||
namespace JSXElements {
|
||||
export interface IonContentAttributes extends HTMLAttributes {
|
||||
forceOverscroll?: boolean;
|
||||
fullscreen?: boolean;
|
||||
}
|
||||
}
|
||||
@@ -2689,6 +2690,8 @@ declare global {
|
||||
}
|
||||
namespace JSXElements {
|
||||
export interface IonScrollAttributes extends HTMLAttributes {
|
||||
forceOverscroll?: boolean;
|
||||
mode?: string;
|
||||
onionScroll?: ScrollCallback;
|
||||
onionScrollEnd?: ScrollCallback;
|
||||
onionScrollStart?: ScrollCallback;
|
||||
|
||||
@@ -29,3 +29,15 @@
|
||||
a {
|
||||
color: $app-ios-link-color;
|
||||
}
|
||||
|
||||
|
||||
// iOS Content Padding
|
||||
// --------------------------------------------------
|
||||
|
||||
@include app-padding("ios", $content-ios-padding);
|
||||
|
||||
|
||||
// iOS Content Margin
|
||||
// --------------------------------------------------
|
||||
|
||||
@include app-margin("ios", $content-ios-margin);
|
||||
|
||||
@@ -28,3 +28,14 @@
|
||||
a {
|
||||
color: $app-md-link-color;
|
||||
}
|
||||
|
||||
// iOS Content Padding
|
||||
// --------------------------------------------------
|
||||
|
||||
@include app-padding("md", $content-md-padding);
|
||||
|
||||
|
||||
// iOS Content Margin
|
||||
// --------------------------------------------------
|
||||
|
||||
@include app-margin("md", $content-md-margin);
|
||||
|
||||
@@ -308,3 +308,105 @@ ion-toast-controller,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
ion-app [no-padding],
|
||||
ion-app [no-padding] .scroll-inner {
|
||||
@include padding(0);
|
||||
}
|
||||
|
||||
@mixin app-padding($mode, $content-padding) {
|
||||
.app-#{$mode} [padding],
|
||||
.app-#{$mode} [padding] .scroll-inner {
|
||||
@include padding($content-padding);
|
||||
}
|
||||
|
||||
.app-#{$mode} [padding-top],
|
||||
.app-#{$mode} [padding-top] .scroll-inner {
|
||||
@include padding($content-padding, null, null, null);
|
||||
}
|
||||
|
||||
.app-#{$mode} [padding-left],
|
||||
.app-#{$mode} [padding-left] .scroll-inner {
|
||||
@include padding-horizontal($content-padding, null);
|
||||
}
|
||||
|
||||
.app-#{$mode} [padding-right],
|
||||
.app-#{$mode} [padding-right] .scroll-inner {
|
||||
@include padding-horizontal(null, $content-padding);
|
||||
}
|
||||
|
||||
.app-#{$mode} [padding-bottom],
|
||||
.app-#{$mode} [padding-bottom] .scroll-inner {
|
||||
@include padding(null, null, $content-padding, null);
|
||||
}
|
||||
|
||||
.app-#{$mode} [padding-vertical],
|
||||
.app-#{$mode} [padding-vertical] .scroll-inner {
|
||||
@include padding($content-padding, null, $content-padding, null);
|
||||
}
|
||||
|
||||
.app-#{$mode} [padding-horizontal],
|
||||
.app-#{$mode} [padding-horizontal] .scroll-inner {
|
||||
@include padding-horizontal($content-padding);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Content Margin
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-app [no-margin],
|
||||
ion-app [no-margin] ion-scroll {
|
||||
@include margin(0);
|
||||
}
|
||||
|
||||
@mixin app-margin($mode, $content-margin) {
|
||||
.app-#{$mode} [margin],
|
||||
.app-#{$mode} [margin] .scroll-inner {
|
||||
@include margin($content-margin);
|
||||
}
|
||||
|
||||
.app-#{$mode} [margin-top],
|
||||
.app-#{$mode} [margin-top] .scroll-inner {
|
||||
@include margin($content-margin, null, null, null);
|
||||
}
|
||||
|
||||
.app-#{$mode} [margin-left],
|
||||
.app-#{$mode} [margin-left] .scroll-inner {
|
||||
// scss-lint:disable PropertySpelling
|
||||
margin-left: $content-margin;
|
||||
}
|
||||
|
||||
.app-#{$mode} [margin-start],
|
||||
.app-#{$mode} [margin-start] .scroll-inner {
|
||||
@include margin-horizontal($content-margin, null);
|
||||
}
|
||||
|
||||
.app-#{$mode} [margin-right],
|
||||
.app-#{$mode} [margin-right] .scroll-inner {
|
||||
// scss-lint:disable PropertySpelling
|
||||
margin-right: $content-margin;
|
||||
}
|
||||
|
||||
.app-#{$mode} [margin-end],
|
||||
.app-#{$mode} [margin-end] .scroll-inner {
|
||||
@include margin-horizontal(null, $content-margin);
|
||||
}
|
||||
|
||||
.app-#{$mode} [margin-bottom],
|
||||
.app-#{$mode} [margin-bottom] .scroll-inner {
|
||||
@include margin(null, null, $content-margin, null);
|
||||
}
|
||||
|
||||
.app-#{$mode} [margin-vertical],
|
||||
.app-#{$mode} [margin-vertical] .scroll-inner {
|
||||
@include margin($content-margin, null, $content-margin, null);
|
||||
}
|
||||
|
||||
.app-#{$mode} [margin-horizontal],
|
||||
.app-#{$mode} [margin-horizontal] .scroll-inner {
|
||||
@include margin-horizontal($content-margin);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
@import "./content";
|
||||
@import "./content.ios.vars";
|
||||
|
||||
// iOS Content
|
||||
// --------------------------------------------------
|
||||
|
||||
.content-ios {
|
||||
font-family: $content-ios-font-family;
|
||||
|
||||
color: $text-ios-color;
|
||||
background-color: $background-ios-color;
|
||||
}
|
||||
|
||||
.content-ios.outer-content {
|
||||
background: $content-ios-outer-background;
|
||||
}
|
||||
|
||||
.content-ios hr {
|
||||
height: $hairlines-width;
|
||||
|
||||
background-color: ion-color-alpha($background-ios-color-value, background-ios-color, $alpha-ios-low);
|
||||
}
|
||||
|
||||
|
||||
// iOS Content Padding
|
||||
// --------------------------------------------------
|
||||
|
||||
@include content-padding("ios", $content-ios-padding);
|
||||
|
||||
|
||||
// iOS Content Margin
|
||||
// --------------------------------------------------
|
||||
|
||||
@include content-margin("ios", $content-ios-margin);
|
||||
|
||||
|
||||
// iOS Content Scroll
|
||||
// --------------------------------------------------
|
||||
|
||||
.content-ios:not([no-bounce]) > .scroll-content::before,
|
||||
.content-ios:not([no-bounce]) > .scroll-content::after {
|
||||
position: absolute;
|
||||
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
|
||||
content: "";
|
||||
}
|
||||
|
||||
.content-ios:not([no-bounce]) > .scroll-content::before {
|
||||
bottom: -1px;
|
||||
}
|
||||
|
||||
.content-ios:not([no-bounce]) > .scroll-content::after {
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
.platform-core .content-ios .scroll-content::after,
|
||||
.platform-core .content-ios .scroll-content::before {
|
||||
position: initial;
|
||||
|
||||
top: initial;
|
||||
bottom: initial;
|
||||
|
||||
width: initial;
|
||||
height: initial;
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
@import "./content";
|
||||
@import "./content.md.vars";
|
||||
|
||||
// Material Design Content
|
||||
// --------------------------------------------------
|
||||
|
||||
.content-md {
|
||||
font-family: $content-md-font-family;
|
||||
|
||||
color: $text-md-color;
|
||||
background-color: $background-md-color;
|
||||
}
|
||||
|
||||
.content-md hr {
|
||||
background-color: $background-md-color-step-50;
|
||||
}
|
||||
|
||||
|
||||
// Material Design Content Padding
|
||||
// --------------------------------------------------
|
||||
|
||||
@include content-padding("md", $content-md-padding);
|
||||
|
||||
|
||||
// Material Design Content Margin
|
||||
// --------------------------------------------------
|
||||
|
||||
@include content-margin("md", $content-md-margin);
|
||||
@@ -13,137 +13,6 @@ ion-content {
|
||||
|
||||
contain: layout size style;
|
||||
|
||||
margin: 0 !important; // scss-lint:disable all
|
||||
padding: 0 !important; // scss-lint:disable all
|
||||
}
|
||||
|
||||
// Scrollable Content
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-scroll {
|
||||
@include position(0, 0, 0, 0);
|
||||
|
||||
position: absolute;
|
||||
z-index: $z-index-scroll-content;
|
||||
display: block;
|
||||
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
will-change: scroll-position;
|
||||
|
||||
contain: size style layout;
|
||||
}
|
||||
|
||||
ion-content.js-scroll ion-scroll {
|
||||
position: relative;
|
||||
|
||||
min-height: 100%;
|
||||
|
||||
overflow-x: initial;
|
||||
overflow-y: initial;
|
||||
-webkit-overflow-scrolling: auto;
|
||||
will-change: initial;
|
||||
}
|
||||
|
||||
// Content Padding
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-app [no-padding],
|
||||
ion-app [no-padding] ion-scroll {
|
||||
@include padding(0);
|
||||
}
|
||||
|
||||
@mixin content-padding($mode, $content-padding) {
|
||||
.app-#{$mode} [padding],
|
||||
.app-#{$mode} [padding] .scroll-inner {
|
||||
@include padding($content-padding);
|
||||
}
|
||||
|
||||
.app-#{$mode} [padding-top],
|
||||
.app-#{$mode} [padding-top] .scroll-inner {
|
||||
@include padding($content-padding, null, null, null);
|
||||
}
|
||||
|
||||
.app-#{$mode} [padding-left],
|
||||
.app-#{$mode} [padding-left] .scroll-inner {
|
||||
@include padding-horizontal($content-padding, null);
|
||||
}
|
||||
|
||||
.app-#{$mode} [padding-right],
|
||||
.app-#{$mode} [padding-right] .scroll-inner {
|
||||
@include padding-horizontal(null, $content-padding);
|
||||
}
|
||||
|
||||
.app-#{$mode} [padding-bottom],
|
||||
.app-#{$mode} [padding-bottom] .scroll-inner {
|
||||
@include padding(null, null, $content-padding, null);
|
||||
}
|
||||
|
||||
.app-#{$mode} [padding-vertical],
|
||||
.app-#{$mode} [padding-vertical] .scroll-inner {
|
||||
@include padding($content-padding, null, $content-padding, null);
|
||||
}
|
||||
|
||||
.app-#{$mode} [padding-horizontal],
|
||||
.app-#{$mode} [padding-horizontal] .scroll-inner {
|
||||
@include padding-horizontal($content-padding);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Content Margin
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-app [no-margin],
|
||||
ion-app [no-margin] ion-scroll {
|
||||
@include margin(0);
|
||||
}
|
||||
|
||||
@mixin content-margin($mode, $content-margin) {
|
||||
.app-#{$mode} [margin],
|
||||
.app-#{$mode} [margin] ion-scroll {
|
||||
@include margin($content-margin);
|
||||
}
|
||||
|
||||
.app-#{$mode} [margin-top],
|
||||
.app-#{$mode} [margin-top] ion-scroll {
|
||||
@include margin($content-margin, null, null, null);
|
||||
}
|
||||
|
||||
.app-#{$mode} [margin-left],
|
||||
.app-#{$mode} [margin-left] ion-scroll {
|
||||
// scss-lint:disable PropertySpelling
|
||||
margin-left: $content-margin;
|
||||
}
|
||||
|
||||
.app-#{$mode} [margin-start],
|
||||
.app-#{$mode} [margin-start] ion-scroll {
|
||||
@include margin-horizontal($content-margin, null);
|
||||
}
|
||||
|
||||
.app-#{$mode} [margin-right],
|
||||
.app-#{$mode} [margin-right] ion-scroll {
|
||||
// scss-lint:disable PropertySpelling
|
||||
margin-right: $content-margin;
|
||||
}
|
||||
|
||||
.app-#{$mode} [margin-end],
|
||||
.app-#{$mode} [margin-end] ion-scroll {
|
||||
@include margin-horizontal(null, $content-margin);
|
||||
}
|
||||
|
||||
.app-#{$mode} [margin-bottom],
|
||||
.app-#{$mode} [margin-bottom] ion-scroll {
|
||||
@include margin(null, null, $content-margin, null);
|
||||
}
|
||||
|
||||
.app-#{$mode} [margin-vertical],
|
||||
.app-#{$mode} [margin-vertical] ion-scroll {
|
||||
@include margin($content-margin, null, $content-margin, null);
|
||||
}
|
||||
|
||||
.app-#{$mode} [margin-horizontal],
|
||||
.app-#{$mode} [margin-horizontal] ion-scroll {
|
||||
@include margin-horizontal($content-margin);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
import { Component, Element, Listen, Method, Prop } from '@stencil/core';
|
||||
import { Config, DomController } from '../../index';
|
||||
import { createThemedClasses, getElementClassMap } from '../../utils/theme';
|
||||
import { getPageElement } from '../../utils/helpers';
|
||||
|
||||
@Component({
|
||||
tag: 'ion-content',
|
||||
styleUrls: {
|
||||
ios: 'content.ios.scss',
|
||||
md: 'content.md.scss'
|
||||
}
|
||||
styleUrl: 'content.scss'
|
||||
})
|
||||
export class Content {
|
||||
|
||||
@@ -32,6 +28,8 @@ export class Content {
|
||||
*/
|
||||
@Prop() fullscreen = false;
|
||||
|
||||
@Prop() forceOverscroll: boolean;
|
||||
|
||||
@Listen('body:ionNavChanged')
|
||||
onNavChanged() {
|
||||
this.resize();
|
||||
@@ -112,18 +110,10 @@ export class Content {
|
||||
}
|
||||
|
||||
render() {
|
||||
const themedClasses = createThemedClasses(this.mode, this.color, 'content');
|
||||
const hostClasses = getElementClassMap(this.el.classList);
|
||||
|
||||
const scrollClasses = {
|
||||
...themedClasses,
|
||||
...hostClasses,
|
||||
};
|
||||
|
||||
this.resize();
|
||||
|
||||
return [
|
||||
<ion-scroll class={scrollClasses}>
|
||||
<ion-scroll mode={this.mode} forceOverscroll={this.forceOverscroll}>
|
||||
<slot></slot>
|
||||
</ion-scroll>,
|
||||
<slot name='fixed'></slot>
|
||||
|
||||
@@ -16,6 +16,11 @@ view component.
|
||||
|
||||
## Properties
|
||||
|
||||
#### forceOverscroll
|
||||
|
||||
boolean
|
||||
|
||||
|
||||
#### fullscreen
|
||||
|
||||
boolean
|
||||
@@ -27,6 +32,11 @@ to transparent.
|
||||
|
||||
## Attributes
|
||||
|
||||
#### force-overscroll
|
||||
|
||||
boolean
|
||||
|
||||
|
||||
#### fullscreen
|
||||
|
||||
boolean
|
||||
|
||||
@@ -7,6 +7,16 @@
|
||||
|
||||
## Properties
|
||||
|
||||
#### forceOverscroll
|
||||
|
||||
boolean
|
||||
|
||||
|
||||
#### mode
|
||||
|
||||
string
|
||||
|
||||
|
||||
#### onionScroll
|
||||
|
||||
|
||||
@@ -24,6 +34,16 @@
|
||||
|
||||
## Attributes
|
||||
|
||||
#### force-overscroll
|
||||
|
||||
boolean
|
||||
|
||||
|
||||
#### mode
|
||||
|
||||
string
|
||||
|
||||
|
||||
#### onion-scroll
|
||||
|
||||
|
||||
|
||||
22
packages/core/src/components/scroll/scroll.ios.scss
Normal file
22
packages/core/src/components/scroll/scroll.ios.scss
Normal file
@@ -0,0 +1,22 @@
|
||||
@import "./scroll";
|
||||
@import "./scroll.ios.vars";
|
||||
|
||||
// iOS Content
|
||||
// --------------------------------------------------
|
||||
|
||||
.outer-content .scroll-ios {
|
||||
background: $content-ios-outer-background;
|
||||
}
|
||||
|
||||
.scroll-ios {
|
||||
font-family: $content-ios-font-family;
|
||||
|
||||
color: $text-ios-color;
|
||||
background-color: $background-ios-color;
|
||||
}
|
||||
|
||||
.scroll-ios hr {
|
||||
height: $hairlines-width;
|
||||
|
||||
background-color: ion-color-alpha($background-ios-color-value, background-ios-color, $alpha-ios-low);
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
// --------------------------------------------------
|
||||
|
||||
/// @prop - Font family of the content
|
||||
$content-ios-font-family: $font-family-ios-base !default;
|
||||
$content-ios-font-family: $font-family-ios-base !default;
|
||||
|
||||
/// @prop - Background color of the outer content
|
||||
$content-ios-outer-background: $background-ios-color-step-50 !default;
|
||||
$content-ios-outer-background: $background-ios-color-step-50 !default;
|
||||
16
packages/core/src/components/scroll/scroll.md.scss
Normal file
16
packages/core/src/components/scroll/scroll.md.scss
Normal file
@@ -0,0 +1,16 @@
|
||||
@import "./scroll";
|
||||
@import "./scroll.md.vars";
|
||||
|
||||
// Material Design Content
|
||||
// --------------------------------------------------
|
||||
|
||||
.scroll-md {
|
||||
font-family: $content-md-font-family;
|
||||
|
||||
color: $text-md-color;
|
||||
background-color: $background-md-color;
|
||||
|
||||
hr {
|
||||
background-color: $background-md-color-step-50;
|
||||
}
|
||||
}
|
||||
@@ -4,4 +4,4 @@
|
||||
// --------------------------------------------------
|
||||
|
||||
/// @prop - Font family of the content
|
||||
$content-md-font-family: $font-family-md-base !default;
|
||||
$content-md-font-family: $font-family-md-base !default;
|
||||
54
packages/core/src/components/scroll/scroll.scss
Normal file
54
packages/core/src/components/scroll/scroll.scss
Normal file
@@ -0,0 +1,54 @@
|
||||
@import "../../themes/ionic.globals";
|
||||
|
||||
ion-scroll {
|
||||
@include position(0, 0, 0, 0);
|
||||
|
||||
position: absolute;
|
||||
z-index: $z-index-scroll-content;
|
||||
display: block;
|
||||
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
will-change: scroll-position;
|
||||
|
||||
contain: size style layout;
|
||||
|
||||
margin: 0 !important; // scss-lint:disable all
|
||||
padding: 0 !important; // scss-lint:disable all
|
||||
|
||||
.focus-input {
|
||||
@include margin(0);
|
||||
@include padding(0);
|
||||
|
||||
position: absolute;
|
||||
|
||||
width: 0;
|
||||
height: 0;
|
||||
|
||||
border: 0;
|
||||
background: none;
|
||||
opacity: 0;
|
||||
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
|
||||
&.overscroll::before,
|
||||
&.overscroll::after {
|
||||
position: absolute;
|
||||
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
|
||||
content: "";
|
||||
}
|
||||
|
||||
&.overscroll::before {
|
||||
bottom: -1px;
|
||||
}
|
||||
|
||||
&.overscroll::after {
|
||||
top: -1px;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,14 @@ import { GestureDelegate } from '../gesture-controller/gesture-controller';
|
||||
|
||||
|
||||
@Component({
|
||||
tag: 'ion-scroll'
|
||||
tag: 'ion-scroll',
|
||||
styleUrls: {
|
||||
ios: 'scroll.ios.scss',
|
||||
md: 'scroll.md.scss'
|
||||
},
|
||||
host: {
|
||||
theme: 'scroll'
|
||||
}
|
||||
})
|
||||
export class Scroll {
|
||||
|
||||
@@ -24,6 +31,9 @@ export class Scroll {
|
||||
@Prop({ context: 'dom' }) dom: DomController;
|
||||
@Prop({ context: 'isServer' }) isServer: boolean;
|
||||
|
||||
@Prop() mode: string;
|
||||
@Prop({mutable: true}) forceOverscroll: boolean;
|
||||
|
||||
@Prop() onionScrollStart: ScrollCallback;
|
||||
@Prop() onionScroll: ScrollCallback;
|
||||
@Prop() onionScrollEnd: ScrollCallback;
|
||||
@@ -55,6 +65,9 @@ export class Scroll {
|
||||
if (this.isServer) {
|
||||
return;
|
||||
}
|
||||
if (this.forceOverscroll === undefined) {
|
||||
this.forceOverscroll = this.mode === 'ios' && ('ontouchstart' in window);
|
||||
}
|
||||
this.app = this.el.closest('ion-app') as HTMLIonAppElement;
|
||||
}
|
||||
|
||||
@@ -77,6 +90,11 @@ export class Scroll {
|
||||
}
|
||||
}
|
||||
|
||||
@Listen('touchend', {passive: true, capture: true })
|
||||
onTouchEnd(_ev: TouchEvent) {
|
||||
this.refocus();
|
||||
}
|
||||
|
||||
@Method()
|
||||
scrollToTop(duration: number): Promise<void> {
|
||||
return this.scrollToPoint(0, 0, duration);
|
||||
@@ -280,15 +298,33 @@ export class Scroll {
|
||||
this.onionScrollEnd(detail);
|
||||
}
|
||||
this.ionScrollEnd.emit(detail);
|
||||
this.refocus();
|
||||
}
|
||||
|
||||
private refocus() {
|
||||
setTimeout(() => {
|
||||
if (document.activeElement === document.body) {
|
||||
(this.el.querySelector('.focus-input') as HTMLElement).focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
hostData() {
|
||||
return {
|
||||
class: {
|
||||
overscroll: this.forceOverscroll
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
return [
|
||||
// scroll-inner is used to keep custom user padding
|
||||
<div class='scroll-inner'>
|
||||
<slot></slot>
|
||||
</div>
|
||||
);
|
||||
</div>,
|
||||
<input type='button' class='focus-input'/>
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user