1 |
|
2 |
|
3 |
|
4 | import { writeTask, proxyCustomElement, HTMLElement, createEvent, readTask, h, Host } from '@stencil/core/internal/client';
|
5 | import { a as isPlatform, b as getIonMode } from './ionic-global.js';
|
6 | import { g as getTimeGivenProgression } from './cubic-bezier.js';
|
7 | import { t as transitionEndAsync, c as componentOnReady, j as clamp, g as getElementRoot, r as raf } from './helpers.js';
|
8 | import { c as hapticImpact } from './haptic.js';
|
9 | import { c as createAnimation } from './animation.js';
|
10 |
|
11 | const getRefresherAnimationType = (contentEl) => {
|
12 | const previousSibling = contentEl.previousElementSibling;
|
13 | const hasHeader = previousSibling !== null && previousSibling.tagName === 'ION-HEADER';
|
14 | return hasHeader ? 'translate' : 'scale';
|
15 | };
|
16 | const createPullingAnimation = (type, pullingSpinner, refresherEl) => {
|
17 | return type === 'scale' ? createScaleAnimation(pullingSpinner, refresherEl) : createTranslateAnimation(pullingSpinner, refresherEl);
|
18 | };
|
19 | const createBaseAnimation = (pullingRefresherIcon) => {
|
20 | const spinner = pullingRefresherIcon.querySelector('ion-spinner');
|
21 | const circle = spinner.shadowRoot.querySelector('circle');
|
22 | const spinnerArrowContainer = pullingRefresherIcon.querySelector('.spinner-arrow-container');
|
23 | const arrowContainer = pullingRefresherIcon.querySelector('.arrow-container');
|
24 | const arrow = (arrowContainer) ? arrowContainer.querySelector('ion-icon') : null;
|
25 | const baseAnimation = createAnimation()
|
26 | .duration(1000)
|
27 | .easing('ease-out');
|
28 | const spinnerArrowContainerAnimation = createAnimation()
|
29 | .addElement(spinnerArrowContainer)
|
30 | .keyframes([
|
31 | { offset: 0, opacity: '0.3' },
|
32 | { offset: 0.45, opacity: '0.3' },
|
33 | { offset: 0.55, opacity: '1' },
|
34 | { offset: 1, opacity: '1' }
|
35 | ]);
|
36 | const circleInnerAnimation = createAnimation()
|
37 | .addElement(circle)
|
38 | .keyframes([
|
39 | { offset: 0, strokeDasharray: '1px, 200px' },
|
40 | { offset: 0.20, strokeDasharray: '1px, 200px' },
|
41 | { offset: 0.55, strokeDasharray: '100px, 200px' },
|
42 | { offset: 1, strokeDasharray: '100px, 200px' }
|
43 | ]);
|
44 | const circleOuterAnimation = createAnimation()
|
45 | .addElement(spinner)
|
46 | .keyframes([
|
47 | { offset: 0, transform: 'rotate(-90deg)' },
|
48 | { offset: 1, transform: 'rotate(210deg)' }
|
49 | ]);
|
50 | |
51 |
|
52 |
|
53 |
|
54 |
|
55 | if (arrowContainer && arrow) {
|
56 | const arrowContainerAnimation = createAnimation()
|
57 | .addElement(arrowContainer)
|
58 | .keyframes([
|
59 | { offset: 0, transform: 'rotate(0deg)' },
|
60 | { offset: 0.30, transform: 'rotate(0deg)' },
|
61 | { offset: 0.55, transform: 'rotate(280deg)' },
|
62 | { offset: 1, transform: 'rotate(400deg)' }
|
63 | ]);
|
64 | const arrowAnimation = createAnimation()
|
65 | .addElement(arrow)
|
66 | .keyframes([
|
67 | { offset: 0, transform: 'translateX(2px) scale(0)' },
|
68 | { offset: 0.30, transform: 'translateX(2px) scale(0)' },
|
69 | { offset: 0.55, transform: 'translateX(-1.5px) scale(1)' },
|
70 | { offset: 1, transform: 'translateX(-1.5px) scale(1)' }
|
71 | ]);
|
72 | baseAnimation.addAnimation([arrowContainerAnimation, arrowAnimation]);
|
73 | }
|
74 | return baseAnimation.addAnimation([spinnerArrowContainerAnimation, circleInnerAnimation, circleOuterAnimation]);
|
75 | };
|
76 | const createScaleAnimation = (pullingRefresherIcon, refresherEl) => {
|
77 | |
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 | const height = refresherEl.clientHeight;
|
87 | const spinnerAnimation = createAnimation()
|
88 | .addElement(pullingRefresherIcon)
|
89 | .keyframes([
|
90 | { offset: 0, transform: `scale(0) translateY(-${height}px)` },
|
91 | { offset: 1, transform: 'scale(1) translateY(100px)' }
|
92 | ]);
|
93 | return createBaseAnimation(pullingRefresherIcon).addAnimation([spinnerAnimation]);
|
94 | };
|
95 | const createTranslateAnimation = (pullingRefresherIcon, refresherEl) => {
|
96 | |
97 |
|
98 |
|
99 |
|
100 |
|
101 |
|
102 |
|
103 |
|
104 |
|
105 | const height = refresherEl.clientHeight;
|
106 | const spinnerAnimation = createAnimation()
|
107 | .addElement(pullingRefresherIcon)
|
108 | .keyframes([
|
109 | { offset: 0, transform: `translateY(-${height}px)` },
|
110 | { offset: 1, transform: 'translateY(100px)' }
|
111 | ]);
|
112 | return createBaseAnimation(pullingRefresherIcon).addAnimation([spinnerAnimation]);
|
113 | };
|
114 | const createSnapBackAnimation = (pullingRefresherIcon) => {
|
115 | return createAnimation()
|
116 | .duration(125)
|
117 | .addElement(pullingRefresherIcon)
|
118 | .fromTo('transform', 'translateY(var(--ion-pulling-refresher-translate, 100px))', 'translateY(0px)');
|
119 | };
|
120 |
|
121 |
|
122 | const setSpinnerOpacity = (spinner, opacity) => {
|
123 | spinner.style.setProperty('opacity', opacity.toString());
|
124 | };
|
125 | const handleScrollWhilePulling = (ticks, numTicks, pullAmount) => {
|
126 | const max = 1;
|
127 | writeTask(() => {
|
128 | ticks.forEach((el, i) => {
|
129 | |
130 |
|
131 |
|
132 |
|
133 |
|
134 |
|
135 | const min = i * (max / numTicks);
|
136 | const range = max - min;
|
137 | const start = pullAmount - min;
|
138 | const progression = clamp(0, start / range, 1);
|
139 | el.style.setProperty('opacity', progression.toString());
|
140 | });
|
141 | });
|
142 | };
|
143 | const handleScrollWhileRefreshing = (spinner, lastVelocityY) => {
|
144 | writeTask(() => {
|
145 |
|
146 | spinner.style.setProperty('--refreshing-rotation-duration', (lastVelocityY >= 1.0) ? '0.5s' : '2s');
|
147 | spinner.style.setProperty('opacity', '1');
|
148 | });
|
149 | };
|
150 | const translateElement = (el, value, duration = 200) => {
|
151 | if (!el) {
|
152 | return Promise.resolve();
|
153 | }
|
154 | const trans = transitionEndAsync(el, duration);
|
155 | writeTask(() => {
|
156 | el.style.setProperty('transition', `${duration}ms all ease-out`);
|
157 | if (value === undefined) {
|
158 | el.style.removeProperty('transform');
|
159 | }
|
160 | else {
|
161 | el.style.setProperty('transform', `translate3d(0px, ${value}, 0px)`);
|
162 | }
|
163 | });
|
164 | return trans;
|
165 | };
|
166 |
|
167 |
|
168 | const shouldUseNativeRefresher = async (referenceEl, mode) => {
|
169 | const refresherContent = referenceEl.querySelector('ion-refresher-content');
|
170 | if (!refresherContent) {
|
171 | return Promise.resolve(false);
|
172 | }
|
173 | await new Promise(resolve => componentOnReady(refresherContent, resolve));
|
174 | const pullingSpinner = referenceEl.querySelector('ion-refresher-content .refresher-pulling ion-spinner');
|
175 | const refreshingSpinner = referenceEl.querySelector('ion-refresher-content .refresher-refreshing ion-spinner');
|
176 | return (pullingSpinner !== null &&
|
177 | refreshingSpinner !== null &&
|
178 | ((mode === 'ios' && isPlatform('mobile') && referenceEl.style.webkitOverflowScrolling !== undefined) ||
|
179 | mode === 'md'));
|
180 | };
|
181 |
|
182 | const refresherIosCss = "ion-refresher{left:0;top:0;display:none;position:absolute;width:100%;height:60px;pointer-events:none;z-index:-1}[dir=rtl] ion-refresher,:host-context([dir=rtl]) ion-refresher{left:unset;right:unset;right:0}ion-refresher.refresher-active{display:block}ion-refresher-content{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;height:100%}.refresher-pulling,.refresher-refreshing{display:none;width:100%}.refresher-pulling-icon,.refresher-refreshing-icon{-webkit-transform-origin:center;transform-origin:center;-webkit-transition:200ms;transition:200ms;font-size:30px;text-align:center}[dir=rtl] .refresher-pulling-icon,:host-context([dir=rtl]) .refresher-pulling-icon,[dir=rtl] .refresher-refreshing-icon,:host-context([dir=rtl]) .refresher-refreshing-icon{-webkit-transform-origin:calc(100% - center);transform-origin:calc(100% - center)}.refresher-pulling-text,.refresher-refreshing-text{font-size:16px;text-align:center}ion-refresher-content .arrow-container{display:none}.refresher-pulling ion-refresher-content .refresher-pulling{display:block}.refresher-ready ion-refresher-content .refresher-pulling{display:block}.refresher-ready ion-refresher-content .refresher-pulling-icon{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.refresher-refreshing ion-refresher-content .refresher-refreshing{display:block}.refresher-cancelling ion-refresher-content .refresher-pulling{display:block}.refresher-cancelling ion-refresher-content .refresher-pulling-icon{-webkit-transform:scale(0);transform:scale(0)}.refresher-completing ion-refresher-content .refresher-refreshing{display:block}.refresher-completing ion-refresher-content .refresher-refreshing-icon{-webkit-transform:scale(0);transform:scale(0)}.refresher-native .refresher-pulling-text,.refresher-native .refresher-refreshing-text{display:none}.refresher-ios .refresher-pulling-icon,.refresher-ios .refresher-refreshing-icon{color:var(--ion-text-color, #000)}.refresher-ios .refresher-pulling-text,.refresher-ios .refresher-refreshing-text{color:var(--ion-text-color, #000)}.refresher-ios .refresher-refreshing .spinner-lines-ios line,.refresher-ios .refresher-refreshing .spinner-lines-small-ios line,.refresher-ios .refresher-refreshing .spinner-crescent circle{stroke:var(--ion-text-color, #000)}.refresher-ios .refresher-refreshing .spinner-bubbles circle,.refresher-ios .refresher-refreshing .spinner-circles circle,.refresher-ios .refresher-refreshing .spinner-dots circle{fill:var(--ion-text-color, #000)}ion-refresher.refresher-native{display:block;z-index:1}ion-refresher.refresher-native ion-spinner{margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0}@supports ((-webkit-margin-start: 0) or (margin-inline-start: 0)) or (-webkit-margin-start: 0){ion-refresher.refresher-native ion-spinner{margin-left:unset;margin-right:unset;-webkit-margin-start:auto;margin-inline-start:auto;-webkit-margin-end:auto;margin-inline-end:auto}}.refresher-native .refresher-refreshing ion-spinner{--refreshing-rotation-duration:2s;display:none;-webkit-animation:var(--refreshing-rotation-duration) ease-out refresher-rotate forwards;animation:var(--refreshing-rotation-duration) ease-out refresher-rotate forwards}.refresher-native .refresher-refreshing{display:none;-webkit-animation:250ms linear refresher-pop forwards;animation:250ms linear refresher-pop forwards}.refresher-native ion-spinner{width:32px;height:32px;color:var(--ion-color-step-450, #747577)}.refresher-native.refresher-refreshing .refresher-pulling ion-spinner,.refresher-native.refresher-completing .refresher-pulling ion-spinner{display:none}.refresher-native.refresher-refreshing .refresher-refreshing ion-spinner,.refresher-native.refresher-completing .refresher-refreshing ion-spinner{display:block}.refresher-native.refresher-pulling .refresher-pulling ion-spinner{display:block}.refresher-native.refresher-pulling .refresher-refreshing ion-spinner{display:none}.refresher-native.refresher-completing ion-refresher-content .refresher-refreshing-icon{-webkit-transform:scale(0) rotate(180deg);transform:scale(0) rotate(180deg);-webkit-transition:300ms;transition:300ms}@-webkit-keyframes refresher-pop{0%{-webkit-transform:scale(1);transform:scale(1);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}50%{-webkit-transform:scale(1.2);transform:scale(1.2);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}100%{-webkit-transform:scale(1);transform:scale(1)}}@keyframes refresher-pop{0%{-webkit-transform:scale(1);transform:scale(1);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}50%{-webkit-transform:scale(1.2);transform:scale(1.2);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}100%{-webkit-transform:scale(1);transform:scale(1)}}@-webkit-keyframes refresher-rotate{from{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(180deg);transform:rotate(180deg)}}@keyframes refresher-rotate{from{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(180deg);transform:rotate(180deg)}}";
|
183 |
|
184 | const refresherMdCss = "ion-refresher{left:0;top:0;display:none;position:absolute;width:100%;height:60px;pointer-events:none;z-index:-1}[dir=rtl] ion-refresher,:host-context([dir=rtl]) ion-refresher{left:unset;right:unset;right:0}ion-refresher.refresher-active{display:block}ion-refresher-content{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;height:100%}.refresher-pulling,.refresher-refreshing{display:none;width:100%}.refresher-pulling-icon,.refresher-refreshing-icon{-webkit-transform-origin:center;transform-origin:center;-webkit-transition:200ms;transition:200ms;font-size:30px;text-align:center}[dir=rtl] .refresher-pulling-icon,:host-context([dir=rtl]) .refresher-pulling-icon,[dir=rtl] .refresher-refreshing-icon,:host-context([dir=rtl]) .refresher-refreshing-icon{-webkit-transform-origin:calc(100% - center);transform-origin:calc(100% - center)}.refresher-pulling-text,.refresher-refreshing-text{font-size:16px;text-align:center}ion-refresher-content .arrow-container{display:none}.refresher-pulling ion-refresher-content .refresher-pulling{display:block}.refresher-ready ion-refresher-content .refresher-pulling{display:block}.refresher-ready ion-refresher-content .refresher-pulling-icon{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.refresher-refreshing ion-refresher-content .refresher-refreshing{display:block}.refresher-cancelling ion-refresher-content .refresher-pulling{display:block}.refresher-cancelling ion-refresher-content .refresher-pulling-icon{-webkit-transform:scale(0);transform:scale(0)}.refresher-completing ion-refresher-content .refresher-refreshing{display:block}.refresher-completing ion-refresher-content .refresher-refreshing-icon{-webkit-transform:scale(0);transform:scale(0)}.refresher-native .refresher-pulling-text,.refresher-native .refresher-refreshing-text{display:none}.refresher-md .refresher-pulling-icon,.refresher-md .refresher-refreshing-icon{color:var(--ion-text-color, #000)}.refresher-md .refresher-pulling-text,.refresher-md .refresher-refreshing-text{color:var(--ion-text-color, #000)}.refresher-md .refresher-refreshing .spinner-lines-md line,.refresher-md .refresher-refreshing .spinner-lines-small-md line,.refresher-md .refresher-refreshing .spinner-crescent circle{stroke:var(--ion-text-color, #000)}.refresher-md .refresher-refreshing .spinner-bubbles circle,.refresher-md .refresher-refreshing .spinner-circles circle,.refresher-md .refresher-refreshing .spinner-dots circle{fill:var(--ion-text-color, #000)}ion-refresher.refresher-native{display:block;z-index:1}ion-refresher.refresher-native ion-spinner{margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;width:24px;height:24px;color:var(--ion-color-primary, #3880ff)}@supports ((-webkit-margin-start: 0) or (margin-inline-start: 0)) or (-webkit-margin-start: 0){ion-refresher.refresher-native ion-spinner{margin-left:unset;margin-right:unset;-webkit-margin-start:auto;margin-inline-start:auto;-webkit-margin-end:auto;margin-inline-end:auto}}ion-refresher.refresher-native .spinner-arrow-container{display:inherit}ion-refresher.refresher-native .arrow-container{display:block;position:absolute;width:24px;height:24px}ion-refresher.refresher-native .arrow-container ion-icon{margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;left:0;right:0;bottom:-4px;position:absolute;color:var(--ion-color-primary, #3880ff);font-size:12px}@supports ((-webkit-margin-start: 0) or (margin-inline-start: 0)) or (-webkit-margin-start: 0){ion-refresher.refresher-native .arrow-container ion-icon{margin-left:unset;margin-right:unset;-webkit-margin-start:auto;margin-inline-start:auto;-webkit-margin-end:auto;margin-inline-end:auto}}ion-refresher.refresher-native.refresher-pulling ion-refresher-content .refresher-pulling,ion-refresher.refresher-native.refresher-ready ion-refresher-content .refresher-pulling{display:-ms-flexbox;display:flex}ion-refresher.refresher-native.refresher-refreshing ion-refresher-content .refresher-refreshing,ion-refresher.refresher-native.refresher-completing ion-refresher-content .refresher-refreshing,ion-refresher.refresher-native.refresher-cancelling ion-refresher-content .refresher-refreshing{display:-ms-flexbox;display:flex}ion-refresher.refresher-native .refresher-pulling-icon{-webkit-transform:translateY(calc(-100% - 10px));transform:translateY(calc(-100% - 10px))}ion-refresher.refresher-native .refresher-pulling-icon,ion-refresher.refresher-native .refresher-refreshing-icon{margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;border-radius:100%;padding-left:8px;padding-right:8px;padding-top:8px;padding-bottom:8px;display:-ms-flexbox;display:flex;border:1px solid var(--ion-color-step-200, #ececec);background:var(--ion-color-step-250, #ffffff);-webkit-box-shadow:0px 1px 6px rgba(0, 0, 0, 0.1);box-shadow:0px 1px 6px rgba(0, 0, 0, 0.1)}@supports ((-webkit-margin-start: 0) or (margin-inline-start: 0)) or (-webkit-margin-start: 0){ion-refresher.refresher-native .refresher-pulling-icon,ion-refresher.refresher-native .refresher-refreshing-icon{margin-left:unset;margin-right:unset;-webkit-margin-start:auto;margin-inline-start:auto;-webkit-margin-end:auto;margin-inline-end:auto}}@supports ((-webkit-margin-start: 0) or (margin-inline-start: 0)) or (-webkit-margin-start: 0){ion-refresher.refresher-native .refresher-pulling-icon,ion-refresher.refresher-native .refresher-refreshing-icon{padding-left:unset;padding-right:unset;-webkit-padding-start:8px;padding-inline-start:8px;-webkit-padding-end:8px;padding-inline-end:8px}}";
|
185 |
|
186 | const Refresher = proxyCustomElement(class extends HTMLElement {
|
187 | constructor() {
|
188 | super();
|
189 | this.__registerHost();
|
190 | this.ionRefresh = createEvent(this, "ionRefresh", 7);
|
191 | this.ionPull = createEvent(this, "ionPull", 7);
|
192 | this.ionStart = createEvent(this, "ionStart", 7);
|
193 | this.appliedStyles = false;
|
194 | this.didStart = false;
|
195 | this.progress = 0;
|
196 | this.pointerDown = false;
|
197 | this.needsCompletion = false;
|
198 | this.didRefresh = false;
|
199 | this.lastVelocityY = 0;
|
200 | this.animations = [];
|
201 | this.nativeRefresher = false;
|
202 | |
203 |
|
204 |
|
205 |
|
206 |
|
207 |
|
208 |
|
209 |
|
210 |
|
211 |
|
212 | this.state = 1 ;
|
213 | |
214 |
|
215 |
|
216 |
|
217 |
|
218 |
|
219 | this.pullMin = 60;
|
220 | |
221 |
|
222 |
|
223 |
|
224 |
|
225 |
|
226 |
|
227 | this.pullMax = this.pullMin + 60;
|
228 | |
229 |
|
230 |
|
231 |
|
232 |
|
233 | this.closeDuration = '280ms';
|
234 | |
235 |
|
236 |
|
237 |
|
238 |
|
239 | this.snapbackDuration = '280ms';
|
240 | |
241 |
|
242 |
|
243 |
|
244 |
|
245 |
|
246 |
|
247 |
|
248 |
|
249 |
|
250 |
|
251 |
|
252 |
|
253 |
|
254 | this.pullFactor = 1;
|
255 | |
256 |
|
257 |
|
258 | this.disabled = false;
|
259 | }
|
260 | disabledChanged() {
|
261 | if (this.gesture) {
|
262 | this.gesture.enable(!this.disabled);
|
263 | }
|
264 | }
|
265 | async checkNativeRefresher() {
|
266 | const useNativeRefresher = await shouldUseNativeRefresher(this.el, getIonMode(this));
|
267 | if (useNativeRefresher && !this.nativeRefresher) {
|
268 | const contentEl = this.el.closest('ion-content');
|
269 | this.setupNativeRefresher(contentEl);
|
270 | }
|
271 | else if (!useNativeRefresher) {
|
272 | this.destroyNativeRefresher();
|
273 | }
|
274 | }
|
275 | destroyNativeRefresher() {
|
276 | if (this.scrollEl && this.scrollListenerCallback) {
|
277 | this.scrollEl.removeEventListener('scroll', this.scrollListenerCallback);
|
278 | this.scrollListenerCallback = undefined;
|
279 | }
|
280 | this.nativeRefresher = false;
|
281 | }
|
282 | async resetNativeRefresher(el, state) {
|
283 | this.state = state;
|
284 | if (getIonMode(this) === 'ios') {
|
285 | await translateElement(el, undefined, 300);
|
286 | }
|
287 | else {
|
288 | await transitionEndAsync(this.el.querySelector('.refresher-refreshing-icon'), 200);
|
289 | }
|
290 | this.didRefresh = false;
|
291 | this.needsCompletion = false;
|
292 | this.pointerDown = false;
|
293 | this.animations.forEach(ani => ani.destroy());
|
294 | this.animations = [];
|
295 | this.progress = 0;
|
296 | this.state = 1 ;
|
297 | }
|
298 | async setupiOSNativeRefresher(pullingSpinner, refreshingSpinner) {
|
299 | this.elementToTransform = this.scrollEl;
|
300 | const ticks = pullingSpinner.shadowRoot.querySelectorAll('svg');
|
301 | let MAX_PULL = this.scrollEl.clientHeight * 0.16;
|
302 | const NUM_TICKS = ticks.length;
|
303 | writeTask(() => ticks.forEach(el => el.style.setProperty('animation', 'none')));
|
304 | this.scrollListenerCallback = () => {
|
305 |
|
306 | if (!this.pointerDown && this.state === 1 ) {
|
307 | return;
|
308 | }
|
309 | readTask(() => {
|
310 |
|
311 | const scrollTop = this.scrollEl.scrollTop;
|
312 | const refresherHeight = this.el.clientHeight;
|
313 | if (scrollTop > 0) {
|
314 | |
315 |
|
316 |
|
317 |
|
318 | if (this.state === 8 ) {
|
319 | const ratio = clamp(0, scrollTop / (refresherHeight * 0.5), 1);
|
320 | writeTask(() => setSpinnerOpacity(refreshingSpinner, 1 - ratio));
|
321 | return;
|
322 | }
|
323 | return;
|
324 | }
|
325 | if (this.pointerDown) {
|
326 | if (!this.didStart) {
|
327 | this.didStart = true;
|
328 | this.ionStart.emit();
|
329 | }
|
330 |
|
331 | if (this.pointerDown) {
|
332 | this.ionPull.emit();
|
333 | }
|
334 | }
|
335 | |
336 |
|
337 |
|
338 |
|
339 |
|
340 |
|
341 |
|
342 | const offset = (this.didStart) ? 30 : 0;
|
343 | const pullAmount = this.progress = clamp(0, (Math.abs(scrollTop) - offset) / MAX_PULL, 1);
|
344 | const shouldShowRefreshingSpinner = this.state === 8 || pullAmount === 1;
|
345 | if (shouldShowRefreshingSpinner) {
|
346 | if (this.pointerDown) {
|
347 | handleScrollWhileRefreshing(refreshingSpinner, this.lastVelocityY);
|
348 | }
|
349 | if (!this.didRefresh) {
|
350 | this.beginRefresh();
|
351 | this.didRefresh = true;
|
352 | hapticImpact({ style: 'light' });
|
353 | |
354 |
|
355 |
|
356 |
|
357 | if (!this.pointerDown) {
|
358 | translateElement(this.elementToTransform, `${refresherHeight}px`);
|
359 | }
|
360 | }
|
361 | }
|
362 | else {
|
363 | this.state = 2 ;
|
364 | handleScrollWhilePulling(ticks, NUM_TICKS, pullAmount);
|
365 | }
|
366 | });
|
367 | };
|
368 | this.scrollEl.addEventListener('scroll', this.scrollListenerCallback);
|
369 | this.gesture = (await import('./index2.js')).createGesture({
|
370 | el: this.scrollEl,
|
371 | gestureName: 'refresher',
|
372 | gesturePriority: 31,
|
373 | direction: 'y',
|
374 | threshold: 5,
|
375 | onStart: () => {
|
376 | this.pointerDown = true;
|
377 | if (!this.didRefresh) {
|
378 | translateElement(this.elementToTransform, '0px');
|
379 | }
|
380 | |
381 |
|
382 |
|
383 |
|
384 |
|
385 |
|
386 |
|
387 |
|
388 | if (MAX_PULL === 0) {
|
389 | MAX_PULL = this.scrollEl.clientHeight * 0.16;
|
390 | }
|
391 | },
|
392 | onMove: ev => {
|
393 | this.lastVelocityY = ev.velocityY;
|
394 | },
|
395 | onEnd: () => {
|
396 | this.pointerDown = false;
|
397 | this.didStart = false;
|
398 | if (this.needsCompletion) {
|
399 | this.resetNativeRefresher(this.elementToTransform, 32 );
|
400 | this.needsCompletion = false;
|
401 | }
|
402 | else if (this.didRefresh) {
|
403 | readTask(() => translateElement(this.elementToTransform, `${this.el.clientHeight}px`));
|
404 | }
|
405 | },
|
406 | });
|
407 | this.disabledChanged();
|
408 | }
|
409 | async setupMDNativeRefresher(contentEl, pullingSpinner, refreshingSpinner) {
|
410 | const circle = getElementRoot(pullingSpinner).querySelector('circle');
|
411 | const pullingRefresherIcon = this.el.querySelector('ion-refresher-content .refresher-pulling-icon');
|
412 | const refreshingCircle = getElementRoot(refreshingSpinner).querySelector('circle');
|
413 | if (circle !== null && refreshingCircle !== null) {
|
414 | writeTask(() => {
|
415 | circle.style.setProperty('animation', 'none');
|
416 |
|
417 | refreshingSpinner.style.setProperty('animation-delay', '-655ms');
|
418 | refreshingCircle.style.setProperty('animation-delay', '-655ms');
|
419 | });
|
420 | }
|
421 | this.gesture = (await import('./index2.js')).createGesture({
|
422 | el: this.scrollEl,
|
423 | gestureName: 'refresher',
|
424 | gesturePriority: 31,
|
425 | direction: 'y',
|
426 | threshold: 5,
|
427 | canStart: () => this.state !== 8 && this.state !== 32 && this.scrollEl.scrollTop === 0,
|
428 | onStart: (ev) => {
|
429 | ev.data = { animation: undefined, didStart: false, cancelled: false };
|
430 | },
|
431 | onMove: (ev) => {
|
432 | if ((ev.velocityY < 0 && this.progress === 0 && !ev.data.didStart) || ev.data.cancelled) {
|
433 | ev.data.cancelled = true;
|
434 | return;
|
435 | }
|
436 | if (!ev.data.didStart) {
|
437 | ev.data.didStart = true;
|
438 | this.state = 2 ;
|
439 | writeTask(() => this.scrollEl.style.setProperty('--overflow', 'hidden'));
|
440 | const animationType = getRefresherAnimationType(contentEl);
|
441 | const animation = createPullingAnimation(animationType, pullingRefresherIcon, this.el);
|
442 | ev.data.animation = animation;
|
443 | animation.progressStart(false, 0);
|
444 | this.ionStart.emit();
|
445 | this.animations.push(animation);
|
446 | return;
|
447 | }
|
448 |
|
449 | this.progress = clamp(0, (ev.deltaY / 180) * 0.5, 1);
|
450 | ev.data.animation.progressStep(this.progress);
|
451 | this.ionPull.emit();
|
452 | },
|
453 | onEnd: (ev) => {
|
454 | if (!ev.data.didStart) {
|
455 | return;
|
456 | }
|
457 | writeTask(() => this.scrollEl.style.removeProperty('--overflow'));
|
458 | if (this.progress <= 0.4) {
|
459 | this.gesture.enable(false);
|
460 | ev.data.animation
|
461 | .progressEnd(0, this.progress, 500)
|
462 | .onFinish(() => {
|
463 | this.animations.forEach(ani => ani.destroy());
|
464 | this.animations = [];
|
465 | this.gesture.enable(true);
|
466 | this.state = 1 ;
|
467 | });
|
468 | return;
|
469 | }
|
470 | const progress = getTimeGivenProgression([0, 0], [0, 0], [1, 1], [1, 1], this.progress)[0];
|
471 | const snapBackAnimation = createSnapBackAnimation(pullingRefresherIcon);
|
472 | this.animations.push(snapBackAnimation);
|
473 | writeTask(async () => {
|
474 | pullingRefresherIcon.style.setProperty('--ion-pulling-refresher-translate', `${(progress * 100)}px`);
|
475 | ev.data.animation.progressEnd();
|
476 | await snapBackAnimation.play();
|
477 | this.beginRefresh();
|
478 | ev.data.animation.destroy();
|
479 | });
|
480 | }
|
481 | });
|
482 | this.disabledChanged();
|
483 | }
|
484 | async setupNativeRefresher(contentEl) {
|
485 | if (this.scrollListenerCallback || !contentEl || this.nativeRefresher || !this.scrollEl) {
|
486 | return;
|
487 | }
|
488 | |
489 |
|
490 |
|
491 |
|
492 |
|
493 |
|
494 |
|
495 | this.setCss(0, '', false, '');
|
496 | this.nativeRefresher = true;
|
497 | const pullingSpinner = this.el.querySelector('ion-refresher-content .refresher-pulling ion-spinner');
|
498 | const refreshingSpinner = this.el.querySelector('ion-refresher-content .refresher-refreshing ion-spinner');
|
499 | if (getIonMode(this) === 'ios') {
|
500 | this.setupiOSNativeRefresher(pullingSpinner, refreshingSpinner);
|
501 | }
|
502 | else {
|
503 | this.setupMDNativeRefresher(contentEl, pullingSpinner, refreshingSpinner);
|
504 | }
|
505 | }
|
506 | componentDidUpdate() {
|
507 | this.checkNativeRefresher();
|
508 | }
|
509 | async connectedCallback() {
|
510 | if (this.el.getAttribute('slot') !== 'fixed') {
|
511 | console.error('Make sure you use: <ion-refresher slot="fixed">');
|
512 | return;
|
513 | }
|
514 | const contentEl = this.el.closest('ion-content');
|
515 | if (!contentEl) {
|
516 | console.error('<ion-refresher> must be used inside an <ion-content>');
|
517 | return;
|
518 | }
|
519 | await new Promise(resolve => componentOnReady(contentEl, resolve));
|
520 | this.scrollEl = await contentEl.getScrollElement();
|
521 | this.backgroundContentEl = getElementRoot(contentEl).querySelector('#background-content');
|
522 | if (await shouldUseNativeRefresher(this.el, getIonMode(this))) {
|
523 | this.setupNativeRefresher(contentEl);
|
524 | }
|
525 | else {
|
526 | this.gesture = (await import('./index2.js')).createGesture({
|
527 | el: contentEl,
|
528 | gestureName: 'refresher',
|
529 | gesturePriority: 31,
|
530 | direction: 'y',
|
531 | threshold: 20,
|
532 | passive: false,
|
533 | canStart: () => this.canStart(),
|
534 | onStart: () => this.onStart(),
|
535 | onMove: ev => this.onMove(ev),
|
536 | onEnd: () => this.onEnd(),
|
537 | });
|
538 | this.disabledChanged();
|
539 | }
|
540 | }
|
541 | disconnectedCallback() {
|
542 | this.destroyNativeRefresher();
|
543 | this.scrollEl = undefined;
|
544 | if (this.gesture) {
|
545 | this.gesture.destroy();
|
546 | this.gesture = undefined;
|
547 | }
|
548 | }
|
549 | |
550 |
|
551 |
|
552 |
|
553 |
|
554 |
|
555 |
|
556 |
|
557 |
|
558 | async complete() {
|
559 | if (this.nativeRefresher) {
|
560 | this.needsCompletion = true;
|
561 |
|
562 | if (!this.pointerDown) {
|
563 | raf(() => raf(() => this.resetNativeRefresher(this.elementToTransform, 32 )));
|
564 | }
|
565 | }
|
566 | else {
|
567 | this.close(32 , '120ms');
|
568 | }
|
569 | }
|
570 | |
571 |
|
572 |
|
573 | async cancel() {
|
574 | if (this.nativeRefresher) {
|
575 |
|
576 | if (!this.pointerDown) {
|
577 | raf(() => raf(() => this.resetNativeRefresher(this.elementToTransform, 16 )));
|
578 | }
|
579 | }
|
580 | else {
|
581 | this.close(16 , '');
|
582 | }
|
583 | }
|
584 | |
585 |
|
586 |
|
587 |
|
588 |
|
589 |
|
590 |
|
591 |
|
592 |
|
593 | getProgress() {
|
594 | return Promise.resolve(this.progress);
|
595 | }
|
596 | canStart() {
|
597 | if (!this.scrollEl) {
|
598 | return false;
|
599 | }
|
600 | if (this.state !== 1 ) {
|
601 | return false;
|
602 | }
|
603 |
|
604 |
|
605 | if (this.scrollEl.scrollTop > 0) {
|
606 | return false;
|
607 | }
|
608 | return true;
|
609 | }
|
610 | onStart() {
|
611 | this.progress = 0;
|
612 | this.state = 1 ;
|
613 | }
|
614 | onMove(detail) {
|
615 | if (!this.scrollEl) {
|
616 | return;
|
617 | }
|
618 |
|
619 |
|
620 |
|
621 |
|
622 | const ev = detail.event;
|
623 | if (ev.touches && ev.touches.length > 1) {
|
624 | return;
|
625 | }
|
626 |
|
627 |
|
628 |
|
629 | if ((this.state & 56 ) !== 0) {
|
630 | return;
|
631 | }
|
632 | const pullFactor = (Number.isNaN(this.pullFactor) || this.pullFactor < 0) ? 1 : this.pullFactor;
|
633 | const deltaY = detail.deltaY * pullFactor;
|
634 |
|
635 |
|
636 | if (deltaY <= 0) {
|
637 |
|
638 |
|
639 | this.progress = 0;
|
640 | this.state = 1 ;
|
641 | if (this.appliedStyles) {
|
642 |
|
643 | this.setCss(0, '', false, '');
|
644 | return;
|
645 | }
|
646 | return;
|
647 | }
|
648 | if (this.state === 1 ) {
|
649 |
|
650 |
|
651 | const scrollHostScrollTop = this.scrollEl.scrollTop;
|
652 |
|
653 |
|
654 | if (scrollHostScrollTop > 0) {
|
655 | this.progress = 0;
|
656 | return;
|
657 | }
|
658 |
|
659 | this.state = 2 ;
|
660 | }
|
661 |
|
662 | if (ev.cancelable) {
|
663 | ev.preventDefault();
|
664 | }
|
665 |
|
666 |
|
667 | this.setCss(deltaY, '0ms', true, '');
|
668 | if (deltaY === 0) {
|
669 |
|
670 | this.progress = 0;
|
671 | return;
|
672 | }
|
673 | const pullMin = this.pullMin;
|
674 |
|
675 | this.progress = deltaY / pullMin;
|
676 |
|
677 | if (!this.didStart) {
|
678 | this.didStart = true;
|
679 | this.ionStart.emit();
|
680 | }
|
681 |
|
682 | this.ionPull.emit();
|
683 |
|
684 | if (deltaY < pullMin) {
|
685 |
|
686 | this.state = 2 ;
|
687 | return;
|
688 | }
|
689 | if (deltaY > this.pullMax) {
|
690 |
|
691 | this.beginRefresh();
|
692 | return;
|
693 | }
|
694 |
|
695 |
|
696 |
|
697 | this.state = 4 ;
|
698 | return;
|
699 | }
|
700 | onEnd() {
|
701 |
|
702 | if (this.state === 4 ) {
|
703 |
|
704 | this.beginRefresh();
|
705 | }
|
706 | else if (this.state === 2 ) {
|
707 |
|
708 |
|
709 |
|
710 |
|
711 | this.cancel();
|
712 | }
|
713 | }
|
714 | beginRefresh() {
|
715 |
|
716 |
|
717 | this.state = 8 ;
|
718 |
|
719 | this.setCss(this.pullMin, this.snapbackDuration, true, '');
|
720 |
|
721 |
|
722 | this.ionRefresh.emit({
|
723 | complete: this.complete.bind(this)
|
724 | });
|
725 | }
|
726 | close(state, delay) {
|
727 |
|
728 | setTimeout(() => {
|
729 | this.state = 1 ;
|
730 | this.progress = 0;
|
731 | this.didStart = false;
|
732 | this.setCss(0, '0ms', false, '');
|
733 | }, 600);
|
734 |
|
735 |
|
736 | this.state = state;
|
737 | this.setCss(0, this.closeDuration, true, delay);
|
738 |
|
739 | }
|
740 | setCss(y, duration, overflowVisible, delay) {
|
741 | if (this.nativeRefresher) {
|
742 | return;
|
743 | }
|
744 | this.appliedStyles = (y > 0);
|
745 | writeTask(() => {
|
746 | if (this.scrollEl && this.backgroundContentEl) {
|
747 | const scrollStyle = this.scrollEl.style;
|
748 | const backgroundStyle = this.backgroundContentEl.style;
|
749 | scrollStyle.transform = backgroundStyle.transform = ((y > 0) ? `translateY(${y}px) translateZ(0px)` : '');
|
750 | scrollStyle.transitionDuration = backgroundStyle.transitionDuration = duration;
|
751 | scrollStyle.transitionDelay = backgroundStyle.transitionDelay = delay;
|
752 | scrollStyle.overflow = (overflowVisible ? 'hidden' : '');
|
753 | }
|
754 | });
|
755 | }
|
756 | render() {
|
757 | const mode = getIonMode(this);
|
758 | return (h(Host, { slot: "fixed", class: {
|
759 | [mode]: true,
|
760 |
|
761 | [`refresher-${mode}`]: true,
|
762 | 'refresher-native': this.nativeRefresher,
|
763 | 'refresher-active': this.state !== 1 ,
|
764 | 'refresher-pulling': this.state === 2 ,
|
765 | 'refresher-ready': this.state === 4 ,
|
766 | 'refresher-refreshing': this.state === 8 ,
|
767 | 'refresher-cancelling': this.state === 16 ,
|
768 | 'refresher-completing': this.state === 32 ,
|
769 | } }));
|
770 | }
|
771 | get el() { return this; }
|
772 | static get watchers() { return {
|
773 | "disabled": ["disabledChanged"]
|
774 | }; }
|
775 | static get style() { return {
|
776 | ios: refresherIosCss,
|
777 | md: refresherMdCss
|
778 | }; }
|
779 | }, [32, "ion-refresher", {
|
780 | "pullMin": [2, "pull-min"],
|
781 | "pullMax": [2, "pull-max"],
|
782 | "closeDuration": [1, "close-duration"],
|
783 | "snapbackDuration": [1, "snapback-duration"],
|
784 | "pullFactor": [2, "pull-factor"],
|
785 | "disabled": [4],
|
786 | "nativeRefresher": [32],
|
787 | "state": [32],
|
788 | "complete": [64],
|
789 | "cancel": [64],
|
790 | "getProgress": [64]
|
791 | }]);
|
792 | function defineCustomElement$1() {
|
793 | if (typeof customElements === "undefined") {
|
794 | return;
|
795 | }
|
796 | const components = ["ion-refresher"];
|
797 | components.forEach(tagName => { switch (tagName) {
|
798 | case "ion-refresher":
|
799 | if (!customElements.get(tagName)) {
|
800 | customElements.define(tagName, Refresher);
|
801 | }
|
802 | break;
|
803 | } });
|
804 | }
|
805 |
|
806 | const IonRefresher = Refresher;
|
807 | const defineCustomElement = defineCustomElement$1;
|
808 |
|
809 | export { IonRefresher, defineCustomElement };
|