mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
refactor(content): use css instead of js
This commit is contained in:
40
packages/core/src/components.d.ts
vendored
40
packages/core/src/components.d.ts
vendored
@@ -596,6 +596,12 @@ declare global {
|
||||
mode?: string,
|
||||
color?: string,
|
||||
|
||||
enableJsScroll?: any,
|
||||
scrollToTop?: any,
|
||||
scrollToBottom?: any,
|
||||
ionScrollStart?: any,
|
||||
ionScroll?: any,
|
||||
ionScrollEnd?: any,
|
||||
fullscreen?: boolean
|
||||
}
|
||||
}
|
||||
@@ -744,35 +750,6 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
import { Fixed as IonFixed } from './components/fixed/fixed';
|
||||
|
||||
interface HTMLIonFixedElement extends IonFixed, HTMLElement {
|
||||
}
|
||||
declare var HTMLIonFixedElement: {
|
||||
prototype: HTMLIonFixedElement;
|
||||
new (): HTMLIonFixedElement;
|
||||
};
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ion-fixed": HTMLIonFixedElement;
|
||||
}
|
||||
interface ElementTagNameMap {
|
||||
"ion-fixed": HTMLIonFixedElement;
|
||||
}
|
||||
namespace JSX {
|
||||
interface IntrinsicElements {
|
||||
"ion-fixed": JSXElements.IonFixedAttributes;
|
||||
}
|
||||
}
|
||||
namespace JSXElements {
|
||||
export interface IonFixedAttributes extends HTMLAttributes {
|
||||
mode?: string,
|
||||
color?: string,
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
import { Footer as IonFooter } from './components/footer/footer';
|
||||
|
||||
interface HTMLIonFooterElement extends IonFooter, HTMLElement {
|
||||
@@ -1053,7 +1030,7 @@ declare global {
|
||||
complete?: any,
|
||||
threshold?: string,
|
||||
enabled?: boolean,
|
||||
position?: any
|
||||
position?: string
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2416,6 +2393,9 @@ declare global {
|
||||
mode?: string,
|
||||
color?: string,
|
||||
|
||||
scrollToTop?: any,
|
||||
scrollToBottom?: any,
|
||||
scrollToPoint?: any,
|
||||
enabled?: boolean,
|
||||
jsScroll?: boolean,
|
||||
onionScrollStart?: any,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
// Globals
|
||||
// --------------------------------------------------
|
||||
@import "../../themes/ionic.globals";
|
||||
@@ -226,7 +225,9 @@ sub {
|
||||
ion-app,
|
||||
ion-nav,
|
||||
ion-tab,
|
||||
ion-tabs {
|
||||
ion-tabs,
|
||||
ion-page,
|
||||
.ion-page {
|
||||
@include position(0, null, null, 0);
|
||||
|
||||
position: absolute;
|
||||
@@ -235,6 +236,8 @@ ion-tabs {
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
contain: strict;
|
||||
}
|
||||
|
||||
ion-nav,
|
||||
@@ -251,58 +254,39 @@ ion-tab.show-tab {
|
||||
display: block;
|
||||
}
|
||||
|
||||
ion-app,
|
||||
ion-nav,
|
||||
ion-tab,
|
||||
ion-tabs,
|
||||
ion-page {
|
||||
contain: strict;
|
||||
}
|
||||
|
||||
|
||||
// Page Container Structure
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-page {
|
||||
@include position(0, null, null, 0);
|
||||
ion-page,
|
||||
.ion-page,
|
||||
.page-inner {
|
||||
display: flex;
|
||||
|
||||
position: absolute;
|
||||
display: block;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
// do not show, but still render so we can get dimensions
|
||||
// opacity: 0;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
ion-page.show-page {
|
||||
// show the page now that it's ready
|
||||
// opacity: 1;
|
||||
.hide-page {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
|
||||
// Toolbar Container Structure
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-header {
|
||||
@include position(0, null, null, 0);
|
||||
|
||||
position: absolute;
|
||||
ion-header,
|
||||
ion-footer {
|
||||
position: relative;
|
||||
z-index: $z-index-toolbar;
|
||||
display: block;
|
||||
|
||||
order: 1;
|
||||
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
ion-footer {
|
||||
@include position(null, null, 0, 0);
|
||||
|
||||
position: absolute;
|
||||
z-index: $z-index-toolbar;
|
||||
display: block;
|
||||
|
||||
width: 100%;
|
||||
ion-header {
|
||||
order: -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -312,8 +296,7 @@ ion-footer {
|
||||
[app-viewport],
|
||||
[overlay-portal],
|
||||
[nav-viewport],
|
||||
[tab-portal],
|
||||
.nav-decor {
|
||||
[tab-portal] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -69,9 +69,9 @@ export class IonApp implements App {
|
||||
}
|
||||
|
||||
protected render() {
|
||||
return ([
|
||||
return (
|
||||
<slot></slot>
|
||||
]);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,9 +122,3 @@ export function handleBackButtonClick(): Promise<any> {
|
||||
console.log('todo');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -27,23 +27,6 @@ $content-ios-transition-background: #000 !default;
|
||||
background-color: rgba(0, 0, 0, .12);
|
||||
}
|
||||
|
||||
.ios .ion-page.show-page ~ .nav-decor {
|
||||
// when ios pages transition, the leaving page grays out
|
||||
// this is the black square behind all pages so they gray out
|
||||
@include position(0, null, null, 0);
|
||||
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
display: block;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
background: $content-ios-transition-background;
|
||||
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
// iOS Content Padding
|
||||
// --------------------------------------------------
|
||||
|
||||
@@ -4,21 +4,16 @@
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-content {
|
||||
@include position(0, null, null, 0);
|
||||
|
||||
position: relative;
|
||||
display: block;
|
||||
|
||||
flex: 1;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
contain: layout size style;
|
||||
}
|
||||
|
||||
.ion-page > ion-content {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $link-color;
|
||||
}
|
||||
@@ -53,11 +48,6 @@ ion-content.js-scroll ion-scroll {
|
||||
will-change: initial;
|
||||
}
|
||||
|
||||
.disable-scroll .ion-page {
|
||||
pointer-events: none;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
ion-content.has-refresher ion-scroll {
|
||||
background-color: inherit;
|
||||
}
|
||||
@@ -72,38 +62,31 @@ ion-app [no-padding] ion-scroll {
|
||||
}
|
||||
|
||||
@mixin content-padding($mode, $content-padding) {
|
||||
.app-#{$mode} [padding],
|
||||
.app-#{$mode} [padding] ion-scroll {
|
||||
.app-#{$mode} [padding] .scroll-inner {
|
||||
@include padding($content-padding);
|
||||
}
|
||||
|
||||
.app-#{$mode} [padding-top],
|
||||
.app-#{$mode} [padding-top] ion-scroll {
|
||||
.app-#{$mode} [padding-top] .scroll-inner {
|
||||
@include padding($content-padding, null, null, null);
|
||||
}
|
||||
|
||||
.app-#{$mode} [padding-left],
|
||||
.app-#{$mode} [padding-left] ion-scroll {
|
||||
.app-#{$mode} [padding-left] .scroll-inner {
|
||||
@include padding-horizontal($content-padding, null);
|
||||
}
|
||||
|
||||
.app-#{$mode} [padding-right],
|
||||
.app-#{$mode} [padding-right] ion-scroll {
|
||||
.app-#{$mode} [padding-right] .scroll-inner {
|
||||
@include padding-horizontal(null, $content-padding);
|
||||
}
|
||||
|
||||
.app-#{$mode} [padding-bottom],
|
||||
.app-#{$mode} [padding-bottom] ion-scroll {
|
||||
.app-#{$mode} [padding-bottom] .scroll-inner {
|
||||
@include padding(null, null, $content-padding, null);
|
||||
}
|
||||
|
||||
.app-#{$mode} [padding-vertical],
|
||||
.app-#{$mode} [padding-vertical] ion-scroll {
|
||||
.app-#{$mode} [padding-vertical] .scroll-inner {
|
||||
@include padding($content-padding, null, $content-padding, null);
|
||||
}
|
||||
|
||||
.app-#{$mode} [padding-horizontal],
|
||||
.app-#{$mode} [padding-horizontal] ion-scroll {
|
||||
.app-#{$mode} [padding-horizontal] .scroll-inner {
|
||||
@include padding-horizontal($content-padding);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Component, Element, Prop } from '@stencil/core';
|
||||
import { Config, Scroll, ScrollDetail } from '../../index';
|
||||
import { Component, Element, Method, Prop } from '@stencil/core';
|
||||
import { Config, HTMLIonScrollElement } from '../../index';
|
||||
import { createThemedClasses, getElementClassObject } from '../../utils/theme';
|
||||
import { getParentElement, getToolbarHeight } from '../../utils/helpers';
|
||||
|
||||
import { getPageElement } from '../../utils/helpers';
|
||||
|
||||
@Component({
|
||||
tag: 'ion-content',
|
||||
@@ -13,47 +12,33 @@ import { getParentElement, getToolbarHeight } from '../../utils/helpers';
|
||||
}
|
||||
})
|
||||
export class Content {
|
||||
private mode: string;
|
||||
private color: string;
|
||||
|
||||
private cTop = 0;
|
||||
private cBottom = 0;
|
||||
private dirty = false;
|
||||
private scrollEl: HTMLIonScrollElement;
|
||||
|
||||
mode: string;
|
||||
color: string;
|
||||
|
||||
@Element() private el: HTMLElement;
|
||||
|
||||
@Prop({ context: 'config' }) config: Config;
|
||||
|
||||
$scroll: Scroll;
|
||||
$scrollDetail: ScrollDetail = {};
|
||||
$fixed: HTMLElement;
|
||||
$siblingHeader: HTMLElement;
|
||||
$siblingFooter: HTMLElement;
|
||||
|
||||
headerHeight: string;
|
||||
|
||||
|
||||
protected ionViewDidUnload() {
|
||||
this.$fixed = this.$scroll = this.$siblingFooter = this.$siblingHeader = this.$scrollDetail = null;
|
||||
}
|
||||
|
||||
enableJsScroll() {
|
||||
this.$scroll.jsScroll = true;
|
||||
}
|
||||
/**
|
||||
* @output {ScrollEvent} Emitted when the scrolling first starts.
|
||||
*/
|
||||
@Prop() ionScrollStart: Function;
|
||||
|
||||
/**
|
||||
* Scroll to the top of the content component.
|
||||
*
|
||||
* @param {number} [duration] Duration of the scroll animation in milliseconds. Defaults to `300`.
|
||||
* @returns {Promise} Returns a promise which is resolved when the scroll has completed.
|
||||
* @output {ScrollEvent} Emitted on every scroll event.
|
||||
*/
|
||||
scrollToTop(duration: number = 300) {
|
||||
return this.$scroll.scrollToTop(duration);
|
||||
}
|
||||
@Prop() ionScroll: Function;
|
||||
|
||||
/**
|
||||
* Scroll to the bottom of the content component.
|
||||
*
|
||||
* @param {number} [duration] Duration of the scroll animation in milliseconds. Defaults to `300`.
|
||||
* @returns {Promise} Returns a promise which is resolved when the scroll has completed.
|
||||
* @output {ScrollEvent} Emitted when scrolling ends.
|
||||
*/
|
||||
scrollToBottom(duration: number = 300) {
|
||||
return this.$scroll.scrollToBottom(duration);
|
||||
}
|
||||
@Prop() ionScrollEnd: Function;
|
||||
|
||||
/**
|
||||
* @input {boolean} If true, the content will scroll behind the headers
|
||||
@@ -62,35 +47,91 @@ export class Content {
|
||||
*/
|
||||
@Prop() fullscreen: boolean = false;
|
||||
|
||||
protected ionViewDidLoad() {
|
||||
this.scrollEl = this.el.querySelector('ion-scroll') as HTMLIonScrollElement;
|
||||
this.resize();
|
||||
}
|
||||
|
||||
protected ionViewDidUnload() {
|
||||
this.scrollEl = null;
|
||||
}
|
||||
|
||||
@Method()
|
||||
enableJsScroll() {
|
||||
this.scrollEl.jsScroll = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll to the top of the content component.
|
||||
*
|
||||
* @param {number} [duration] Duration of the scroll animation in milliseconds. Defaults to `300`.
|
||||
* @returns {Promise} Returns a promise which is resolved when the scroll has completed.
|
||||
*/
|
||||
@Method()
|
||||
scrollToTop(duration: number = 300) {
|
||||
return this.scrollEl.scrollToTop(duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll to the bottom of the content component.
|
||||
*
|
||||
* @param {number} [duration] Duration of the scroll animation in milliseconds. Defaults to `300`.
|
||||
* @returns {Promise} Returns a promise which is resolved when the scroll has completed.
|
||||
*/
|
||||
@Method()
|
||||
scrollToBottom(duration: number = 300) {
|
||||
return this.scrollEl.scrollToBottom(duration);
|
||||
}
|
||||
|
||||
resize() {
|
||||
if (!this.scrollEl) {
|
||||
return;
|
||||
}
|
||||
if (this.fullscreen) {
|
||||
Context.dom.read(this.readDimensions.bind(this));
|
||||
Context.dom.write(this.writeDimensions.bind(this));
|
||||
} else {
|
||||
this.cTop = this.cBottom = null;
|
||||
Context.dom.write(() => this.scrollEl.removeAttribute('style'));
|
||||
}
|
||||
}
|
||||
|
||||
private readDimensions() {
|
||||
const page = getPageElement(this.el);
|
||||
const top = Math.max(this.el.offsetTop, 0);
|
||||
const bottom = Math.max(page.offsetHeight - top - this.el.offsetHeight, 0);
|
||||
this.dirty = top !== this.cTop || bottom !== this.cBottom;
|
||||
this.cTop = top;
|
||||
this.cBottom = bottom;
|
||||
}
|
||||
|
||||
private writeDimensions() {
|
||||
if (this.dirty && this.scrollEl) {
|
||||
const style = this.scrollEl.style;
|
||||
style.paddingTop = this.cTop + 'px';
|
||||
style.paddingBottom = this.cBottom + 'px';
|
||||
style.top = -this.cTop + 'px';
|
||||
style.bottom = -this.cBottom + 'px';
|
||||
this.dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected render() {
|
||||
const scrollStyle: any = {};
|
||||
|
||||
const pageChildren: HTMLElement[] = getParentElement(this.el).children;
|
||||
const headerHeight = getToolbarHeight('ION-HEADER', pageChildren, this.mode, '44px', '56px');
|
||||
const footerHeight = getToolbarHeight('ION-FOOTER', pageChildren, this.mode, '50px', '48px');
|
||||
|
||||
if (this.fullscreen) {
|
||||
scrollStyle.paddingTop = headerHeight;
|
||||
scrollStyle.paddingBottom = footerHeight;
|
||||
} else {
|
||||
scrollStyle.marginTop = headerHeight;
|
||||
scrollStyle.marginBottom = footerHeight;
|
||||
}
|
||||
|
||||
const themedClasses = createThemedClasses(this.mode, this.color, 'content');
|
||||
const hostClasses = getElementClassObject(this.el.classList);
|
||||
|
||||
const scrollClasses = {
|
||||
...themedClasses,
|
||||
...hostClasses,
|
||||
'statusbar-padding': this.config.getBoolean('statusbarPadding')
|
||||
};
|
||||
|
||||
return (
|
||||
<ion-scroll style={scrollStyle} class={scrollClasses}>
|
||||
this.resize();
|
||||
|
||||
return [
|
||||
<ion-scroll class={scrollClasses}>
|
||||
<slot></slot>
|
||||
</ion-scroll>
|
||||
);
|
||||
</ion-scroll>,
|
||||
<slot name='fixed'></slot>
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
142
packages/core/src/components/content/test/basic.html
Normal file
142
packages/core/src/components/content/test/basic.html
Normal file
@@ -0,0 +1,142 @@
|
||||
<!DOCTYPE html>
|
||||
<html dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Ionic Chips</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<script src="/dist/ionic.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<ion-app>
|
||||
<ion-page>
|
||||
<ion-header id="header">
|
||||
<ion-toolbar style="display: none" id="toolbar2">
|
||||
<ion-title>hidden toolbar</ion-title>
|
||||
</ion-toolbar>
|
||||
<ion-toolbar>
|
||||
<ion-title>Header</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<div style="display: flex; flex: 1">
|
||||
<ion-content padding style="text-align: center; flex: 2" id="content">
|
||||
<f></f>
|
||||
<ion-button onclick="toggleFullscreen()">Toggle content.fullscreen</ion-button>
|
||||
<p>
|
||||
<ion-button onclick="toggleDisplay('header')" color="secondary">Toggle header</ion-button>
|
||||
<ion-button onclick="toggleDisplay('footer')" color="secondary">Toggle footer</ion-button>
|
||||
<ion-button onclick="toggleDisplay('toolbar2')" color="secondary">Toggle 2nd toolbar</ion-button>
|
||||
</p>
|
||||
<p>
|
||||
<ion-button onclick="toggleDisplay('content2')" color="danger">Toggle bottom content</ion-button>
|
||||
<ion-button onclick="toggleDisplay('content3')" color="danger">Toggle right content</ion-button>
|
||||
</p>
|
||||
<p>
|
||||
<ion-button onclick="myanimation()" color="dark">Animate</ion-button>
|
||||
</p>
|
||||
<f></f>
|
||||
<f></f>
|
||||
<f></f>
|
||||
<f></f>
|
||||
<f></f>
|
||||
<f></f>
|
||||
<f></f>
|
||||
<f></f>
|
||||
</ion-content>
|
||||
<ion-content padding style="display: none" id="content3">
|
||||
<f></f>
|
||||
<f></f>
|
||||
<f></f>
|
||||
<f></f>
|
||||
<f></f>
|
||||
<f></f>
|
||||
</ion-content>
|
||||
</div>
|
||||
|
||||
<ion-content padding style="display: none" id="content2">
|
||||
<f></f>
|
||||
<f></f>
|
||||
<f></f>
|
||||
<f></f>
|
||||
<f></f>
|
||||
<f></f>
|
||||
</ion-content>
|
||||
|
||||
<ion-footer id="footer">
|
||||
<ion-toolbar>
|
||||
<ion-title>Footer</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-footer>
|
||||
</ion-page>
|
||||
|
||||
<script>
|
||||
function toggleFullscreen() {
|
||||
const content = document.getElementById('content');
|
||||
content.fullscreen = !content.fullscreen;
|
||||
console.log('content.fullscren =', content.fullscreen);
|
||||
}
|
||||
function toggleDisplay(id) {
|
||||
const el = document.getElementById(id);
|
||||
el.style.display = (!el.style.display) ? 'none' : null;
|
||||
}
|
||||
function myanimation() {
|
||||
const content = document.getElementById('content');
|
||||
const header = document.getElementById('header');
|
||||
const footer = document.getElementById('footer');
|
||||
content.classList.add('animation');
|
||||
header.classList.add('animation');
|
||||
footer.classList.add('animation');
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
ion-header.animation,
|
||||
ion-footer.animation,
|
||||
ion-content.animation {
|
||||
animation: ani1 5s infinite;
|
||||
}
|
||||
|
||||
#content2 ion-scroll {
|
||||
background: black;
|
||||
}
|
||||
@keyframes ani1 {
|
||||
0% {
|
||||
transform: translateX(0)
|
||||
}
|
||||
25% {
|
||||
transform: translateX(-100%)
|
||||
}
|
||||
50% {
|
||||
transform: translateX(0)
|
||||
}
|
||||
75% {
|
||||
transform: translateX(100%)
|
||||
}
|
||||
100% {
|
||||
transform: translateX(0%);
|
||||
}
|
||||
}
|
||||
ion-content.animation {
|
||||
animation-delay: 500ms;
|
||||
}
|
||||
ion-footer.animation {
|
||||
animation-delay: 1000ms;
|
||||
}
|
||||
|
||||
.toolbar-background {
|
||||
background: #f8f8f8b8;
|
||||
}
|
||||
f {
|
||||
display: block;
|
||||
margin: 15px auto;
|
||||
max-width: 150px;
|
||||
height: 150px;
|
||||
background: blue;
|
||||
}
|
||||
|
||||
f:last-of-type {
|
||||
background: yellow;
|
||||
}
|
||||
</style>
|
||||
</ion-app>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,19 +0,0 @@
|
||||
@import "../../themes/ionic.globals";
|
||||
|
||||
// Fixed Content (ion-fixed)
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-fixed {
|
||||
@include position(0, 0, 0, 0);
|
||||
|
||||
position: absolute;
|
||||
display: block;
|
||||
}
|
||||
|
||||
[ion-fixed] {
|
||||
position: absolute;
|
||||
|
||||
z-index: $z-index-fixed-content;
|
||||
|
||||
transform: translateZ(0);
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
import { Component, Element, Prop } from '@stencil/core';
|
||||
import { getParentElement, getToolbarHeight } from '../../utils/helpers';
|
||||
import { Config } from '../../index';
|
||||
|
||||
|
||||
@Component({
|
||||
tag: 'ion-fixed',
|
||||
styleUrl: 'fixed.scss'
|
||||
})
|
||||
export class Fixed {
|
||||
@Element() private el: HTMLElement;
|
||||
@Prop({ context: 'config' }) config: Config;
|
||||
mode: string;
|
||||
|
||||
hostData() {
|
||||
const pageChildren: HTMLElement[] = getParentElement(this.el).children;
|
||||
const headerHeight = getToolbarHeight('ION-HEADER', pageChildren, this.mode, '44px', '56px');
|
||||
const footerHeight = getToolbarHeight('ION-FOOTER', pageChildren, this.mode, '50px', '48px');
|
||||
|
||||
return {
|
||||
class: {
|
||||
'statusbar-padding': this.config.getBoolean('statusbarPadding')
|
||||
},
|
||||
style: {
|
||||
'margin-top': headerHeight,
|
||||
'margin-bottom': footerHeight
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected render() {
|
||||
return (
|
||||
<slot></slot>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component, Element, Event, EventEmitter, HostElement, Listen, Method, Prop, PropDidChange, State } from '@stencil/core';
|
||||
import { ScrollDetail } from '../../index';
|
||||
import { Component, Element, Event, EventEmitter, Listen, Method, Prop, PropDidChange, State } from '@stencil/core';
|
||||
import { HTMLIonScrollElement, ScrollDetail, StencilElement } from '../../index';
|
||||
|
||||
const enum Position {
|
||||
Top = 'top',
|
||||
@@ -148,10 +148,10 @@ export class InfiniteScroll {
|
||||
|
||||
private thrPx: number = 0;
|
||||
private thrPc: number = 0.15;
|
||||
private init: boolean = false;
|
||||
private scrollEl: HTMLElement;
|
||||
private scrollEl: HTMLIonScrollElement;
|
||||
private didFire = false;
|
||||
private isBusy = false;
|
||||
private init = false;
|
||||
|
||||
@Element() private el: HTMLElement;
|
||||
@State() isLoading: boolean = false;
|
||||
@@ -204,7 +204,7 @@ export class InfiniteScroll {
|
||||
* The value can be either `top` or `bottom`.
|
||||
* Default is `bottom`.
|
||||
*/
|
||||
@Prop() position: Position = Position.Bottom;
|
||||
@Prop() position: string = Position.Bottom;
|
||||
|
||||
/**
|
||||
* @output {event} Emitted when the scroll reaches
|
||||
@@ -214,16 +214,23 @@ export class InfiniteScroll {
|
||||
*/
|
||||
@Event() private ionInfinite: EventEmitter;
|
||||
|
||||
ionViewWillLoad() {
|
||||
const scrollEl = this.el.closest('ion-scroll') as StencilElement;
|
||||
return scrollEl.componentOnReady().then((el) => {
|
||||
this.scrollEl = el as HTMLIonScrollElement;
|
||||
});
|
||||
}
|
||||
|
||||
ionViewDidLoad() {
|
||||
const scrollEl = this.scrollEl = this.el.closest('ion-scroll') as HostElement;
|
||||
if (!scrollEl) {
|
||||
console.error('ion-infinite-scroll must be used ion-content');
|
||||
if (this.init) {
|
||||
console.warn('instance was already initialized');
|
||||
return;
|
||||
}
|
||||
this.init = true;
|
||||
this.thresholdChanged(this.threshold);
|
||||
this.enableScrollEvents(this.enabled);
|
||||
if (this.position === Position.Top) {
|
||||
// scrollEl.scrollDownOnLoad = true;
|
||||
Context.dom.write(() => this.scrollEl.scrollToBottom(0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,9 +358,6 @@ export class InfiniteScroll {
|
||||
* @hidden
|
||||
*/
|
||||
private enableScrollEvents(shouldListen: boolean) {
|
||||
if (!this.init) {
|
||||
return;
|
||||
}
|
||||
Context.enableListener(this, 'ionScroll', shouldListen, this.scrollEl);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
<body>
|
||||
<ion-app>
|
||||
<ion-page main class="show-page">
|
||||
<ion-page main>
|
||||
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
|
||||
@@ -40,12 +40,6 @@ ion-menu.show-menu {
|
||||
contain: strict;
|
||||
}
|
||||
|
||||
.menu-inner > ion-header,
|
||||
.menu-inner > ion-content,
|
||||
.menu-inner > ion-footer {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.menu-side-left > .menu-inner {
|
||||
@include multi-dir() {
|
||||
// scss-lint:disable PropertySpelling
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import { Component, Element, Event, EventEmitter, Listen, Method, Prop, PropDidChange, PropWillChange } from '@stencil/core';
|
||||
import { Animation, Config, GestureDetail, HTMLIonMenuElement, SplitPaneAlert } from '../../index';
|
||||
import { MenuController } from './menu-controller';
|
||||
import { Animation, Config, GestureDetail, HTMLIonMenuControllerElement, HTMLIonMenuElement, SplitPaneAlert, StencilElement } from '../../index';
|
||||
import { Side, assert, checkEdgeSide, isRightSide } from '../../utils/helpers';
|
||||
|
||||
export type Lazy<T> = T &
|
||||
{ componentOnReady(): Promise<T> } &
|
||||
{ componentOnReady(done: (cmp: T) => void): void };
|
||||
|
||||
@Component({
|
||||
tag: 'ion-menu',
|
||||
styleUrls: {
|
||||
@@ -35,12 +30,12 @@ export class Menu {
|
||||
backdropEl: HTMLElement;
|
||||
menuInnerEl: HTMLElement;
|
||||
contentEl: HTMLElement;
|
||||
menuCtrl: MenuController;
|
||||
menuCtrl: HTMLIonMenuControllerElement;
|
||||
|
||||
@Element() el: HTMLIonMenuElement;
|
||||
|
||||
@Prop({ context: 'config' }) config: Config;
|
||||
@Prop({ connect: 'ion-menu-controller' }) lazyMenuCtrl: Lazy<MenuController>;
|
||||
@Prop({ connect: 'ion-menu-controller' }) lazyMenuCtrl: StencilElement;
|
||||
|
||||
/**
|
||||
* @input {string} The content's id the menu should use.
|
||||
@@ -116,8 +111,9 @@ export class Menu {
|
||||
@Event() ionClose: EventEmitter;
|
||||
|
||||
protected ionViewWillLoad() {
|
||||
return this.lazyMenuCtrl.componentOnReady()
|
||||
.then(menu => this.menuCtrl = menu);
|
||||
return this.lazyMenuCtrl.componentOnReady().then(menu => {
|
||||
this.menuCtrl = menu as HTMLIonMenuControllerElement;
|
||||
});
|
||||
}
|
||||
|
||||
protected ionViewDidLoad() {
|
||||
|
||||
@@ -11,14 +11,11 @@ export class StencilNavDelegate implements FrameworkDelegate {
|
||||
@Method()
|
||||
attachViewToDom(nav: Nav, enteringView: ViewController): Promise<any> {
|
||||
return new Promise((resolve) => {
|
||||
const usersElement = document.createElement(enteringView.component);
|
||||
const ionPage = document.createElement('ion-page');
|
||||
enteringView.element = ionPage;
|
||||
ionPage.appendChild(usersElement);
|
||||
nav.element.appendChild(ionPage);
|
||||
(ionPage as StencilElement).componentOnReady(() => {
|
||||
resolve();
|
||||
});
|
||||
const usersElement = document.createElement(enteringView.component) as HTMLElement;
|
||||
usersElement.classList.add('ion-page');
|
||||
enteringView.element = usersElement;
|
||||
nav.element.appendChild(usersElement);
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@ export class PageOne {
|
||||
|
||||
protected render() {
|
||||
return [<ion-header>
|
||||
<ion-navbar>
|
||||
<ion-toolbar>
|
||||
<ion-title>Page One</ion-title>
|
||||
</ion-navbar>
|
||||
</ion-toolbar>
|
||||
</ion-header>,
|
||||
<ion-content>
|
||||
Page One Content
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
@import "../../themes/ionic.globals.ios";
|
||||
@import "./page";
|
||||
|
||||
|
||||
// iOS Page
|
||||
// --------------------------------------------------
|
||||
@@ -1,6 +0,0 @@
|
||||
@import "../../themes/ionic.globals.md";
|
||||
@import "./page";
|
||||
|
||||
|
||||
// Material Design Page
|
||||
// --------------------------------------------------
|
||||
@@ -1,25 +0,0 @@
|
||||
@import "../../themes/ionic.globals";
|
||||
|
||||
|
||||
// Page
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-page {
|
||||
@include position(0, null, null, 0);
|
||||
|
||||
position: absolute;
|
||||
display: block;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
// do not show, but still render so we can get dimensions
|
||||
opacity: 0;
|
||||
|
||||
contain: strict;
|
||||
}
|
||||
|
||||
ion-page.show-page {
|
||||
// show the page now that it's ready
|
||||
opacity: 1;
|
||||
}
|
||||
@@ -3,14 +3,6 @@ import { Component } from '@stencil/core';
|
||||
|
||||
@Component({
|
||||
tag: 'ion-page',
|
||||
styleUrls: {
|
||||
ios: 'page.ios.scss',
|
||||
md: 'page.md.scss',
|
||||
wp: 'page.wp.scss'
|
||||
},
|
||||
host: {
|
||||
theme: 'page'
|
||||
}
|
||||
})
|
||||
export class Page {
|
||||
protected render() {
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
@import "../../themes/ionic.globals.wp";
|
||||
@import "./page";
|
||||
|
||||
|
||||
// Windows Page
|
||||
// --------------------------------------------------
|
||||
@@ -16,7 +16,7 @@ ion-reorder-group > ion-gesture {
|
||||
|
||||
.reorder-selected {
|
||||
position: relative;
|
||||
z-index: 100;
|
||||
z-index: $z-index-reorder-selected;
|
||||
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, .4);
|
||||
opacity: .8;
|
||||
@@ -29,6 +29,7 @@ ion-reorder-group > ion-gesture {
|
||||
|
||||
ion-reorder.no-hide {
|
||||
display: block;
|
||||
|
||||
visibility: normal;
|
||||
}
|
||||
|
||||
@@ -72,7 +73,7 @@ ion-reorder[slot="start"] {
|
||||
|
||||
.item-ios .reorder-icon {
|
||||
font-size: 2em;
|
||||
opacity: 0.3;
|
||||
opacity: .3;
|
||||
}
|
||||
|
||||
ion-reorder[slot="start"] .reorder-icon {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, Element, Event, EventEmitter, Listen, Prop } from '@stencil/core';
|
||||
import { Component, Element, Event, EventEmitter, Listen, Method, Prop, PropDidChange } from '@stencil/core';
|
||||
import { Config, GestureDetail } from '../../index';
|
||||
import { GestureController, GestureDelegate } from '../gesture-controller/gesture-controller';
|
||||
|
||||
@@ -7,7 +7,6 @@ import { GestureController, GestureDelegate } from '../gesture-controller/gestur
|
||||
tag: 'ion-scroll'
|
||||
})
|
||||
export class Scroll {
|
||||
@Element() private el: HTMLElement;
|
||||
|
||||
private gesture: GestureDelegate;
|
||||
private positions: number[] = [];
|
||||
@@ -19,9 +18,17 @@ export class Scroll {
|
||||
isScrolling: boolean = false;
|
||||
detail: ScrollDetail = {};
|
||||
|
||||
@Element() private el: HTMLElement;
|
||||
|
||||
@Prop({ context: 'config'}) config: Config;
|
||||
@Prop() enabled: boolean = true;
|
||||
@Prop() jsScroll: boolean = false;
|
||||
@PropDidChange('jsScroll')
|
||||
jsScrollChanged(js: boolean) {
|
||||
if (js) {
|
||||
throw 'jsScroll: TODO!';
|
||||
}
|
||||
}
|
||||
|
||||
@Prop() onionScrollStart: ScrollCallback;
|
||||
@Prop() onionScroll: ScrollCallback;
|
||||
@@ -32,218 +39,34 @@ export class Scroll {
|
||||
@Event() ionScrollEnd: EventEmitter;
|
||||
|
||||
protected ionViewDidLoad() {
|
||||
if (Context.isServer) return;
|
||||
if (Context.isServer) {
|
||||
return;
|
||||
}
|
||||
|
||||
const gestureCtrl = Context.gesture = Context.gesture || new GestureController;
|
||||
this.gesture = gestureCtrl.createGesture('scroll', 100, false);
|
||||
}
|
||||
|
||||
|
||||
// Native Scroll *************************
|
||||
|
||||
@Listen('scroll', { passive: true })
|
||||
onNativeScroll() {
|
||||
const self = this;
|
||||
|
||||
if (!self.queued && self.enabled) {
|
||||
self.queued = true;
|
||||
|
||||
Context.dom.read(function(timeStamp) {
|
||||
self.queued = false;
|
||||
self.onScroll(timeStamp || Date.now());
|
||||
});
|
||||
}
|
||||
protected ionViewDidUnload() {
|
||||
this.gesture && this.gesture.destroy();
|
||||
this.gesture = this.detail = this.detail.event = null;
|
||||
}
|
||||
|
||||
onScroll(timeStamp: number) {
|
||||
const self = this;
|
||||
const detail = self.detail;
|
||||
const positions = self.positions;
|
||||
|
||||
detail.timeStamp = timeStamp;
|
||||
|
||||
// get the current scrollTop
|
||||
// ******** DOM READ ****************
|
||||
detail.scrollTop = self.getTop();
|
||||
|
||||
// get the current scrollLeft
|
||||
// ******** DOM READ ****************
|
||||
detail.scrollLeft = self.getLeft();
|
||||
|
||||
if (!self.isScrolling) {
|
||||
// currently not scrolling, so this is a scroll start
|
||||
self.isScrolling = true;
|
||||
|
||||
// remember the start positions
|
||||
detail.startY = detail.scrollTop;
|
||||
detail.startX = detail.scrollLeft;
|
||||
|
||||
// new scroll, so do some resets
|
||||
detail.velocityY = detail.velocityX = detail.deltaY = detail.deltaX = positions.length = 0;
|
||||
|
||||
// emit only on the first scroll event
|
||||
if (self.onionScrollStart) {
|
||||
self.onionScrollStart(detail);
|
||||
} else {
|
||||
self.ionScrollStart.emit(detail);
|
||||
}
|
||||
}
|
||||
|
||||
// actively scrolling
|
||||
positions.push(detail.scrollTop, detail.scrollLeft, detail.timeStamp);
|
||||
|
||||
if (positions.length > 3) {
|
||||
// we've gotten at least 2 scroll events so far
|
||||
detail.deltaY = (detail.scrollTop - detail.startY);
|
||||
detail.deltaX = (detail.scrollLeft - detail.startX);
|
||||
|
||||
var endPos = (positions.length - 1);
|
||||
var startPos = endPos;
|
||||
var timeRange = (detail.timeStamp - 100);
|
||||
|
||||
// move pointer to position measured 100ms ago
|
||||
for (var i = endPos; i > 0 && positions[i] > timeRange; i -= 3) {
|
||||
startPos = i;
|
||||
}
|
||||
|
||||
if (startPos !== endPos) {
|
||||
// compute relative movement between these two points
|
||||
var movedTop = (positions[startPos - 2] - positions[endPos - 2]);
|
||||
var movedLeft = (positions[startPos - 1] - positions[endPos - 1]);
|
||||
var factor = 16.67 / (positions[startPos] - positions[endPos]);
|
||||
|
||||
// based on XXms compute the movement to apply for each render step
|
||||
detail.velocityY = movedTop * factor;
|
||||
detail.velocityX = movedLeft * factor;
|
||||
}
|
||||
}
|
||||
|
||||
clearTimeout(self.tmr);
|
||||
self.tmr = setTimeout(function() {
|
||||
|
||||
// haven't scrolled in a while, so it's a scrollend
|
||||
self.isScrolling = false;
|
||||
|
||||
Context.dom.read(function(timeStamp) {
|
||||
if (!self.isScrolling) {
|
||||
self.onEnd(timeStamp);
|
||||
}
|
||||
});
|
||||
}, 80);
|
||||
|
||||
// emit on each scroll event
|
||||
if (self.onionScroll) {
|
||||
self.onionScroll(detail);
|
||||
} else {
|
||||
self.ionScroll.emit(detail);
|
||||
}
|
||||
@Method()
|
||||
scrollToTop(duration: number): Promise<void> {
|
||||
return this.scrollToPoint(0, 0, duration);
|
||||
}
|
||||
|
||||
@Method()
|
||||
scrollToBottom(duration: number): Promise<void> {
|
||||
const y = (this.el)
|
||||
? this.el.scrollHeight - this.el.clientHeight
|
||||
: 0;
|
||||
|
||||
onEnd(timeStamp: number) {
|
||||
const detail = this.detail;
|
||||
|
||||
detail.timeStamp = timeStamp || Date.now();
|
||||
|
||||
// emit that the scroll has ended
|
||||
if (this.onionScrollEnd) {
|
||||
this.onionScrollEnd(detail);
|
||||
} else {
|
||||
this.ionScrollEnd.emit(detail);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enableJsScroll(contentTop: number, contentBottom: number) {
|
||||
this.jsScroll = true;
|
||||
|
||||
Context.enableListener(this, 'scroll', false);
|
||||
Context.enableListener(this, 'touchstart', true);
|
||||
|
||||
contentTop; contentBottom;
|
||||
}
|
||||
|
||||
|
||||
// Touch Scroll *************************
|
||||
|
||||
@Listen('touchstart', { passive: true, enabled: false })
|
||||
onTouchStart() {
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
Context.enableListener(this, 'touchmove', true);
|
||||
Context.enableListener(this, 'touchend', true);
|
||||
|
||||
throw 'jsScroll: TODO!';
|
||||
}
|
||||
|
||||
@Listen('touchmove', { passive: true, enabled: false })
|
||||
onTouchMove() {
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@Listen('touchend', { passive: true, enabled: false })
|
||||
onTouchEnd() {
|
||||
Context.enableListener(this, 'touchmove', false);
|
||||
Context.enableListener(this, 'touchend', false);
|
||||
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* DOM READ
|
||||
*/
|
||||
getTop() {
|
||||
if (this.jsScroll) {
|
||||
return this._t;
|
||||
}
|
||||
return this._t = this.el.scrollTop;
|
||||
}
|
||||
|
||||
/**
|
||||
* DOM READ
|
||||
*/
|
||||
getLeft() {
|
||||
if (this.jsScroll) {
|
||||
return 0;
|
||||
}
|
||||
return this._l = this.el.scrollLeft;
|
||||
}
|
||||
|
||||
/**
|
||||
* DOM WRITE
|
||||
*/
|
||||
setTop(top: number) {
|
||||
this._t = top;
|
||||
|
||||
if (this.jsScroll) {
|
||||
this.el.style.transform = this.el.style.webkitTransform = `translate3d(${this._l * -1}px,${top * -1}px,0px)`;
|
||||
|
||||
} else {
|
||||
this.el.scrollTop = top;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DOM WRITE
|
||||
*/
|
||||
setLeft(left: number) {
|
||||
this._l = left;
|
||||
|
||||
if (this.jsScroll) {
|
||||
this.el.style.transform = this.el.style.webkitTransform = `translate3d(${left * -1}px,${this._t * -1}px,0px)`;
|
||||
|
||||
} else {
|
||||
this.el.scrollLeft = left;
|
||||
}
|
||||
return this.scrollToPoint(0, y, duration);
|
||||
}
|
||||
|
||||
@Method()
|
||||
scrollToPoint(x: number, y: number, duration: number, done?: Function): Promise<any> {
|
||||
// scroll animation loop w/ easing
|
||||
// credit https://gist.github.com/dezinezync/5487119
|
||||
@@ -333,26 +156,164 @@ export class Scroll {
|
||||
return promise;
|
||||
}
|
||||
|
||||
scrollToTop(duration: number): Promise<void> {
|
||||
return this.scrollToPoint(0, 0, duration);
|
||||
}
|
||||
// Native Scroll *************************
|
||||
|
||||
scrollToBottom(duration: number): Promise<void> {
|
||||
let y = 0;
|
||||
if (this.el) {
|
||||
y = this.el.scrollHeight - this.el.clientHeight;
|
||||
@Listen('scroll', { passive: true })
|
||||
protected onNativeScroll() {
|
||||
if (!this.queued) {
|
||||
this.queued = true;
|
||||
|
||||
Context.dom.read((timeStamp) => {
|
||||
this.queued = false;
|
||||
this.onScroll(timeStamp);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private onScroll(timeStamp: number) {
|
||||
const detail = this.detail;
|
||||
const positions = this.positions;
|
||||
|
||||
detail.timeStamp = timeStamp;
|
||||
|
||||
// get the current scrollTop
|
||||
// ******** DOM READ ****************
|
||||
detail.scrollTop = this.getTop();
|
||||
|
||||
// get the current scrollLeft
|
||||
// ******** DOM READ ****************
|
||||
detail.scrollLeft = this.getLeft();
|
||||
|
||||
if (!this.isScrolling) {
|
||||
// currently not scrolling, so this is a scroll start
|
||||
this.isScrolling = true;
|
||||
|
||||
// remember the start positions
|
||||
detail.startY = detail.scrollTop;
|
||||
detail.startX = detail.scrollLeft;
|
||||
|
||||
// new scroll, so do some resets
|
||||
detail.velocityY = detail.velocityX = detail.deltaY = detail.deltaX = positions.length = 0;
|
||||
|
||||
// emit only on the first scroll event
|
||||
if (this.onionScrollStart) {
|
||||
this.onionScrollStart(detail);
|
||||
} else {
|
||||
this.ionScrollStart.emit(detail);
|
||||
}
|
||||
}
|
||||
|
||||
// actively scrolling
|
||||
positions.push(detail.scrollTop, detail.scrollLeft, detail.timeStamp);
|
||||
|
||||
if (positions.length > 3) {
|
||||
// we've gotten at least 2 scroll events so far
|
||||
detail.deltaY = (detail.scrollTop - detail.startY);
|
||||
detail.deltaX = (detail.scrollLeft - detail.startX);
|
||||
|
||||
var endPos = (positions.length - 1);
|
||||
var startPos = endPos;
|
||||
var timeRange = (detail.timeStamp - 100);
|
||||
|
||||
// move pointer to position measured 100ms ago
|
||||
for (var i = endPos; i > 0 && positions[i] > timeRange; i -= 3) {
|
||||
startPos = i;
|
||||
}
|
||||
|
||||
if (startPos !== endPos) {
|
||||
// compute relative movement between these two points
|
||||
var deltaY = (positions[startPos - 2] - positions[endPos - 2]);
|
||||
var deltaX = (positions[startPos - 1] - positions[endPos - 1]);
|
||||
var factor = 1 / (positions[startPos] - positions[endPos]);
|
||||
|
||||
// based on XXms compute the movement to apply for each render step
|
||||
detail.velocityY = deltaY * factor;
|
||||
detail.velocityX = deltaX * factor;
|
||||
}
|
||||
}
|
||||
|
||||
clearTimeout(this.tmr);
|
||||
this.tmr = setTimeout(() => {
|
||||
|
||||
// haven't scrolled in a while, so it's a scrollend
|
||||
this.isScrolling = false;
|
||||
|
||||
Context.dom.read((timeStamp) => {
|
||||
if (!this.isScrolling) {
|
||||
this.onEnd(timeStamp);
|
||||
}
|
||||
});
|
||||
}, 80);
|
||||
|
||||
// emit on each scroll event
|
||||
if (this.onionScroll) {
|
||||
this.onionScroll(detail);
|
||||
} else {
|
||||
this.ionScroll.emit(detail);
|
||||
}
|
||||
return this.scrollToPoint(0, y, duration);
|
||||
}
|
||||
|
||||
|
||||
protected ionViewDidUnload() {
|
||||
this.gesture && this.gesture.destroy();
|
||||
this.gesture = this.detail = this.detail.event = null;
|
||||
private onEnd(timeStamp: number) {
|
||||
const detail = this.detail;
|
||||
|
||||
detail.timeStamp = timeStamp;
|
||||
|
||||
// emit that the scroll has ended
|
||||
if (this.onionScrollEnd) {
|
||||
this.onionScrollEnd(detail);
|
||||
} else {
|
||||
this.ionScrollEnd.emit(detail);
|
||||
}
|
||||
}
|
||||
|
||||
/** DOM READ */
|
||||
private getTop() {
|
||||
if (this.jsScroll) {
|
||||
return this._t;
|
||||
}
|
||||
return this._t = this.el.scrollTop;
|
||||
}
|
||||
|
||||
/** DOM READ */
|
||||
private getLeft() {
|
||||
if (this.jsScroll) {
|
||||
return 0;
|
||||
}
|
||||
return this._l = this.el.scrollLeft;
|
||||
}
|
||||
|
||||
/** DOM WRITE */
|
||||
private setTop(top: number) {
|
||||
this._t = top;
|
||||
|
||||
if (this.jsScroll) {
|
||||
this.el.style.transform = this.el.style.webkitTransform = `translate3d(${this._l * -1}px,${top * -1}px,0px)`;
|
||||
|
||||
} else {
|
||||
this.el.scrollTop = top;
|
||||
}
|
||||
}
|
||||
|
||||
/** DOM WRITE */
|
||||
private setLeft(left: number) {
|
||||
this._l = left;
|
||||
|
||||
if (this.jsScroll) {
|
||||
this.el.style.transform = this.el.style.webkitTransform = `translate3d(${left * -1}px,${this._t * -1}px,0px)`;
|
||||
|
||||
} else {
|
||||
this.el.scrollLeft = left;
|
||||
}
|
||||
}
|
||||
|
||||
protected render() {
|
||||
return <slot></slot>;
|
||||
return (
|
||||
// scroll-inner is used to keep custom user padding
|
||||
<div class='scroll-inner'>
|
||||
<slot></slot>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -366,14 +327,8 @@ export interface ScrollDetail extends GestureDetail {
|
||||
contentWidth?: number;
|
||||
contentTop?: number;
|
||||
contentBottom?: number;
|
||||
contentElement?: HTMLElement;
|
||||
fixedElement?: HTMLElement;
|
||||
scrollElement?: HTMLElement;
|
||||
headerElement?: HTMLElement;
|
||||
footerElement?: HTMLElement;
|
||||
}
|
||||
|
||||
|
||||
export interface ScrollCallback {
|
||||
(detail?: ScrollDetail): boolean|void;
|
||||
}
|
||||
|
||||
@@ -273,7 +273,7 @@ $navbar-ios-height: $toolbar-ios-height !default;
|
||||
.back-button-ios {
|
||||
@include margin(0);
|
||||
|
||||
z-index: 99;
|
||||
z-index: $z-index-toolbar-buttons;
|
||||
overflow: visible;
|
||||
|
||||
order: map-get($toolbar-order-ios, back-button);
|
||||
|
||||
@@ -4,12 +4,10 @@
|
||||
// Toolbar
|
||||
// --------------------------------------------------
|
||||
|
||||
ion-toolbar {
|
||||
.toolbar {
|
||||
position: relative;
|
||||
z-index: $z-index-toolbar;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -70,7 +68,7 @@ ion-buttons div {
|
||||
// TODO this is a temp hack to fix segment overlapping ion-nav-item
|
||||
ion-buttons,
|
||||
.bar-button-menutoggle {
|
||||
z-index: 99;
|
||||
z-index: $z-index-toolbar-buttons;
|
||||
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
4
packages/core/src/index.d.ts
vendored
4
packages/core/src/index.d.ts
vendored
@@ -112,6 +112,8 @@ export {
|
||||
ToastController
|
||||
}
|
||||
|
||||
|
||||
export interface StencilElement extends HTMLElement {
|
||||
componentOnReady?: (cb: (elm?: StencilElement) => void) => void;
|
||||
componentOnReady(): Promise<HTMLElement>;
|
||||
componentOnReady(done: (cmp?: HTMLElement) => void): void;
|
||||
}
|
||||
@@ -27,7 +27,7 @@ export function buildIOSTransition(rootTransition: Transition, enteringView: Vie
|
||||
|
||||
|
||||
rootTransition.addElement(enteringView.element);
|
||||
rootTransition.beforeAddClass('show-page');
|
||||
rootTransition.beforeRemoveClass('hide-page');
|
||||
|
||||
const backDirection = (opts.direction === 'back');
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ export function buildMdTransition(rootTransition: Transition, enteringView: View
|
||||
rootTransition.leavingView = leavingView;
|
||||
|
||||
rootTransition.addElement(enteringView.element);
|
||||
rootTransition.beforeAddClass('show-page');
|
||||
rootTransition.beforeRemoveClass('hide-page');
|
||||
|
||||
const backDirection = (opts.direction === 'back');
|
||||
if (enteringView) {
|
||||
|
||||
@@ -38,9 +38,12 @@ $z-index-refresher: 0; // scss-lint:disable DefaultRule
|
||||
$z-index-page-container: 0; // scss-lint:disable DefaultRule
|
||||
$z-index-toolbar: 10; // scss-lint:disable DefaultRule
|
||||
$z-index-toolbar-background: -1; // scss-lint:disable DefaultRule
|
||||
$z-index-toolbar-buttons: 99; // scss-lint:disable DefaultRule
|
||||
|
||||
$z-index-backdrop: 2; // scss-lint:disable DefaultRule
|
||||
$z-index-overlay-wrapper: 10; // scss-lint:disable DefaultRule
|
||||
|
||||
$z-index-item-options: 1; // scss-lint:disable DefaultRule
|
||||
$z-index-item-divider: 100; // scss-lint:disable DefaultRule
|
||||
$z-index-item-divider: 100; // scss-lint:disable DefaultRule
|
||||
|
||||
$z-index-reorder-selected: 100; // scss-lint:disable DefaultRule
|
||||
|
||||
@@ -138,6 +138,14 @@ export function getParentElement(elm: any) {
|
||||
return null;
|
||||
}
|
||||
|
||||
export function getPageElement(el: HTMLElement) {
|
||||
const page = el.closest('ion-page,.ion-page,page-inner');
|
||||
if (page) {
|
||||
return page;
|
||||
}
|
||||
return getParentElement(el);
|
||||
}
|
||||
|
||||
export function applyStyles(elm: HTMLElement, styles: {[styleProp: string]: string|number}) {
|
||||
const styleProps = Object.keys(styles);
|
||||
|
||||
@@ -148,25 +156,6 @@ export function applyStyles(elm: HTMLElement, styles: {[styleProp: string]: stri
|
||||
}
|
||||
}
|
||||
|
||||
export function getToolbarHeight(toolbarTagName: string, pageChildren: HTMLElement[], mode: string, iosHeight: string, defaultHeight: string) {
|
||||
for (var i = 0; i < pageChildren.length; i++) {
|
||||
if (pageChildren[i].tagName === toolbarTagName) {
|
||||
var headerHeight = pageChildren[i].getAttribute(`${mode}-height`);
|
||||
if (headerHeight) {
|
||||
return headerHeight;
|
||||
}
|
||||
|
||||
if (mode === 'ios') {
|
||||
return iosHeight;
|
||||
}
|
||||
|
||||
return defaultHeight;
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/** @hidden */
|
||||
export type Side = 'left' | 'right' | 'start' | 'end';
|
||||
|
||||
@@ -216,11 +205,7 @@ export function swipeShouldReset(isResetDirection: boolean, isMovingFast: boolea
|
||||
}
|
||||
|
||||
export function isReady(element: Element): Promise<any> {
|
||||
return new Promise((resolve) => {
|
||||
(element as StencilElement).componentOnReady((elm: HTMLElement) => {
|
||||
resolve(elm);
|
||||
});
|
||||
});
|
||||
return (element as StencilElement).componentOnReady();
|
||||
}
|
||||
|
||||
export function getOrAppendElement(tagName: string): Element {
|
||||
|
||||
@@ -4,7 +4,7 @@ exports.config = {
|
||||
generateWWW: false,
|
||||
bundles: [
|
||||
{ components: ['ion-animation-controller'] },
|
||||
{ components: ['ion-app', 'ion-content', 'ion-fixed', 'ion-footer', 'ion-header', 'ion-navbar', 'ion-page', 'ion-title', 'ion-toolbar'] },
|
||||
{ components: ['ion-app', 'ion-content', 'ion-footer', 'ion-header', 'ion-navbar', 'ion-page', 'ion-title', 'ion-toolbar'] },
|
||||
{ components: ['ion-action-sheet', 'ion-action-sheet-controller'] },
|
||||
{ components: ['ion-alert', 'ion-alert-controller'] },
|
||||
{ components: ['ion-avatar', 'ion-badge', 'ion-thumbnail'] },
|
||||
|
||||
Reference in New Issue
Block a user