@ionic/core 8.8.4-dev.11775576543.172b7b99 → 8.8.4-dev.11775666666.132201b7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
1
  /*!
2
2
  * (C) Ionic http://ionicframework.com - MIT License
3
3
  */
4
- import{p as t,H as i,e as s,f as e,h as n,t as o,d as r}from"./p-3Ni1Z654.js";import{a as h,d as a,r as d}from"./p-wCDzv5Q8.js";import{m as l}from"./p-BqDiJgC_.js";import{w as m}from"./p-Dtdm8lKC.js";import{b as c}from"./p-CVBkx7m1.js";let p;const u=t(class extends i{constructor(t){super(),!1!==t&&this.__registerHost(),this.ionDrag=s(this,"ionDrag",7),this.item=null,this.openAmount=0,this.initialOpenAmount=0,this.optsWidthRightSide=0,this.optsWidthLeftSide=0,this.sides=0,this.optsDirty=!0,this.contentEl=null,this.initialContentScrollY=!0,this.state=2,this.disabled=!1}disabledChanged(){this.gesture&&this.gesture.enable(!this.disabled)}async connectedCallback(){const{el:t}=this;this.item=t.querySelector("ion-item"),this.contentEl=h(t),this.mutationObserver=m(t,"ion-item-option",(async()=>{await this.updateOptions()})),await this.updateOptions(),this.gesture=(await import("./p-Cl0B-RWe.js")).createGesture({el:t,gestureName:"item-swipe",gesturePriority:100,threshold:5,canStart:t=>this.canStart(t),onStart:()=>this.onStart(),onMove:t=>this.onMove(t),onEnd:t=>this.onEnd(t)}),this.disabledChanged()}disconnectedCallback(){this.gesture&&(this.gesture.destroy(),this.gesture=void 0),this.item=null,this.leftOptions=this.rightOptions=void 0,p===this.el&&(p=void 0),this.mutationObserver&&(this.mutationObserver.disconnect(),this.mutationObserver=void 0)}getOpenAmount(){return Promise.resolve(this.openAmount)}getSlidingRatio(){return Promise.resolve(this.getSlidingRatioSync())}async open(t){var i;if(null===(this.item=null!==(i=this.item)&&void 0!==i?i:this.el.querySelector("ion-item")))return;const s=this.getOptions(t);s&&(void 0===t&&(t=s===this.leftOptions?"start":"end"),t=l(t)?"end":"start",this.openAmount<0&&s===this.leftOptions||this.openAmount>0&&s===this.rightOptions||(this.closeOpened(),this.state=4,requestAnimationFrame((()=>{this.calculateOptsWidth(),p=this.el,this.setOpenAmount("end"===t?this.optsWidthRightSide:-this.optsWidthLeftSide,!1),this.state="end"===t?8:16}))))}async close(){this.setOpenAmount(0,!0)}async closeOpened(){return void 0!==p&&(p.close(),p=void 0,!0)}getOptions(t){return void 0===t?this.leftOptions||this.rightOptions:"start"===t?this.leftOptions:this.rightOptions}async updateOptions(){var t;const i=this.el.querySelectorAll("ion-item-options");let s=0;this.leftOptions=this.rightOptions=void 0;for(let e=0;e<i.length;e++){const n=i.item(e),o=void 0!==n.componentOnReady?await n.componentOnReady():n;"start"==(l(null!==(t=o.side)&&void 0!==t?t:o.getAttribute("side"))?"end":"start")?(this.leftOptions=o,s|=1):(this.rightOptions=o,s|=2)}this.optsDirty=!0,this.sides=s}canStart(t){return!("rtl"===document.dir?window.innerWidth-t.startX<15:t.startX<15)&&(p&&p!==this.el&&this.closeOpened(),!(!this.rightOptions&&!this.leftOptions))}onStart(){this.item=this.el.querySelector("ion-item");const{contentEl:t}=this;t&&(this.initialContentScrollY=a(t)),p=this.el,void 0!==this.tmr&&(clearTimeout(this.tmr),this.tmr=void 0),0===this.openAmount&&(this.optsDirty=!0,this.state=4),this.initialOpenAmount=this.openAmount,this.item&&(this.item.style.transition="none")}onMove(t){this.optsDirty&&this.calculateOptsWidth();let i,s=this.initialOpenAmount-t.deltaX;switch(this.sides){case 2:s=Math.max(0,s);break;case 1:s=Math.min(0,s);break;case 3:break;case 0:return;default:e("[ion-item-sliding] - invalid ItemSideFlags value",this.sides)}s>this.optsWidthRightSide?(i=this.optsWidthRightSide,s=i+.55*(s-i)):s<-this.optsWidthLeftSide&&(i=-this.optsWidthLeftSide,s=i+.55*(s-i)),this.setOpenAmount(s,!1)}onEnd(t){const{contentEl:i,initialContentScrollY:s}=this;i&&d(i,s);const e=t.velocityX;let n=this.openAmount>0?this.optsWidthRightSide:-this.optsWidthLeftSide;g(this.openAmount>0==!(e<0),Math.abs(e)>.3,Math.abs(this.openAmount)<Math.abs(n/2))&&(n=0);const o=this.state;this.setOpenAmount(n,!0),32&o&&this.rightOptions?this.rightOptions.fireSwipeEvent():64&o&&this.leftOptions&&this.leftOptions.fireSwipeEvent()}calculateOptsWidth(){this.optsWidthRightSide=0,this.rightOptions&&(this.rightOptions.style.display="flex",this.optsWidthRightSide=this.rightOptions.offsetWidth,this.rightOptions.style.display=""),this.optsWidthLeftSide=0,this.leftOptions&&(this.leftOptions.style.display="flex",this.optsWidthLeftSide=this.leftOptions.offsetWidth,this.leftOptions.style.display=""),this.optsDirty=!1}setOpenAmount(t,i){if(void 0!==this.tmr&&(clearTimeout(this.tmr),this.tmr=void 0),!this.item)return;const{el:s}=this,e=this.item.style;if(this.openAmount=t,i&&(e.transition=""),t>0)this.state=t>=this.optsWidthRightSide+30?40:8;else{if(!(t<0))return s.classList.add("item-sliding-closing"),this.gesture&&this.gesture.enable(!1),this.tmr=setTimeout((()=>{this.state=2,this.tmr=void 0,this.gesture&&this.gesture.enable(!this.disabled),s.classList.remove("item-sliding-closing")}),600),p=void 0,void(e.transform="");this.state=t<=-this.optsWidthLeftSide-30?80:16}e.transform=`translate3d(${-t}px,0,0)`,this.ionDrag.emit({amount:t,ratio:this.getSlidingRatioSync()})}getSlidingRatioSync(){return this.openAmount>0?this.openAmount/this.optsWidthRightSide:this.openAmount<0?this.openAmount/this.optsWidthLeftSide:0}render(){const t=c(this);return n(r,{key:"79cd09dd43183008f470b31abb7b3606f653a98b",class:{[t]:!0,"item-sliding-active-slide":2!==this.state,"item-sliding-active-options-end":!!(8&this.state),"item-sliding-active-options-start":!!(16&this.state),"item-sliding-active-swipe-end":!!(32&this.state),"item-sliding-active-swipe-start":!!(64&this.state)}})}get el(){return this}static get watchers(){return{disabled:[{disabledChanged:0}]}}static get style(){return"ion-item-sliding{display:block;position:relative;width:100%;overflow:hidden;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}ion-item-sliding .item{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.item-sliding-active-slide .item{position:relative;-webkit-transition:-webkit-transform 500ms cubic-bezier(0.36, 0.66, 0.04, 1);transition:-webkit-transform 500ms cubic-bezier(0.36, 0.66, 0.04, 1);transition:transform 500ms cubic-bezier(0.36, 0.66, 0.04, 1);transition:transform 500ms cubic-bezier(0.36, 0.66, 0.04, 1), -webkit-transform 500ms cubic-bezier(0.36, 0.66, 0.04, 1);opacity:1;z-index:2;pointer-events:none;will-change:transform}.item-sliding-closing ion-item-options{pointer-events:none}.item-sliding-active-swipe-end .item-options-end .item-option-expandable{padding-left:100%;-ms-flex-order:1;order:1;-webkit-transition-duration:0.6s;transition-duration:0.6s;-webkit-transition-property:padding-left;transition-property:padding-left}:host-context([dir=rtl]) .item-sliding-active-swipe-end .item-options-end .item-option-expandable{-ms-flex-order:-1;order:-1}[dir=rtl] .item-sliding-active-swipe-end .item-options-end .item-option-expandable{-ms-flex-order:-1;order:-1}@supports selector(:dir(rtl)){.item-sliding-active-swipe-end .item-options-end .item-option-expandable:dir(rtl){-ms-flex-order:-1;order:-1}}.item-sliding-active-swipe-start .item-options-start .item-option-expandable{padding-right:100%;-ms-flex-order:-1;order:-1;-webkit-transition-duration:0.6s;transition-duration:0.6s;-webkit-transition-property:padding-right;transition-property:padding-right}:host-context([dir=rtl]) .item-sliding-active-swipe-start .item-options-start .item-option-expandable{-ms-flex-order:1;order:1}[dir=rtl] .item-sliding-active-swipe-start .item-options-start .item-option-expandable{-ms-flex-order:1;order:1}@supports selector(:dir(rtl)){.item-sliding-active-swipe-start .item-options-start .item-option-expandable:dir(rtl){-ms-flex-order:1;order:1}}"}},[0,"ion-item-sliding",{disabled:[4],state:[32],getOpenAmount:[64],getSlidingRatio:[64],open:[64],close:[64],closeOpened:[64]},void 0,{disabled:[{disabledChanged:0}]}]),g=(t,i,s)=>!i&&s||t&&i,b=u,v=function(){"undefined"!=typeof customElements&&["ion-item-sliding"].forEach((t=>{"ion-item-sliding"===t&&(customElements.get(o(t))||customElements.define(o(t),u))}))};export{b as IonItemSliding,v as defineCustomElement}
4
+ import{p as t,H as i,e as s,f as e,h as n,t as o,d as r}from"./p-3Ni1Z654.js";import{a as h,d as a,r as d}from"./p-wCDzv5Q8.js";import{m as l}from"./p-BqDiJgC_.js";import{w as c}from"./p-Dtdm8lKC.js";import{b as m}from"./p-CVBkx7m1.js";let p;const u=t(class extends i{constructor(t){super(),!1!==t&&this.__registerHost(),this.ionDrag=s(this,"ionDrag",7),this.item=null,this.openAmount=0,this.initialOpenAmount=0,this.optsWidthRightSide=0,this.optsWidthLeftSide=0,this.sides=0,this.optsDirty=!0,this.contentEl=null,this.initialContentScrollY=!0,this.state=2,this.disabled=!1}disabledChanged(){this.gesture&&this.gesture.enable(!this.disabled)}async connectedCallback(){const{el:t}=this;this.item=t.querySelector("ion-item"),this.contentEl=h(t),this.mutationObserver=c(t,"ion-item-option",(async()=>{await this.updateOptions()})),await this.updateOptions(),this.gesture=(await import("./p-Cl0B-RWe.js")).createGesture({el:t,gestureName:"item-swipe",gesturePriority:100,threshold:5,canStart:t=>this.canStart(t),onStart:()=>this.onStart(),onMove:t=>this.onMove(t),onEnd:t=>this.onEnd(t)}),this.disabledChanged()}disconnectedCallback(){var t;this.gesture&&(this.gesture.destroy(),this.gesture=void 0),void 0!==this.tmr&&(clearTimeout(this.tmr),this.tmr=void 0),null===(t=this.animationAbortController)||void 0===t||t.abort(),this.item=null,this.leftOptions=this.rightOptions=void 0,p===this.el&&(p=void 0),this.mutationObserver&&(this.mutationObserver.disconnect(),this.mutationObserver=void 0)}getOpenAmount(){return Promise.resolve(this.openAmount)}getSlidingRatio(){return Promise.resolve(this.getSlidingRatioSync())}async open(t){var i;if(128&this.state)return;if(null===(this.item=null!==(i=this.item)&&void 0!==i?i:this.el.querySelector("ion-item")))return;const s=this.getOptions(t);s&&(void 0===t&&(t=s===this.leftOptions?"start":"end"),t=l(t)?"end":"start",this.openAmount<0&&s===this.leftOptions||this.openAmount>0&&s===this.rightOptions||(this.closeOpened(),this.state=4,requestAnimationFrame((()=>{this.calculateOptsWidth(),p=this.el,this.setOpenAmount("end"===t?this.optsWidthRightSide:-this.optsWidthLeftSide,!1),this.state="end"===t?8:16}))))}async close(){128&this.state||this.setOpenAmount(0,!0)}async closeOpened(){return void 0!==p&&(p.close(),p=void 0,!0)}getOptions(t){return void 0===t?this.leftOptions||this.rightOptions:"start"===t?this.leftOptions:this.rightOptions}hasExpandableOptions(t){if(!t)return!1;const i=t.querySelectorAll("ion-item-option");return Array.from(i).some((t=>!0===t.expandable&&!t.disabled))}delay(t,i){return new Promise(((s,e)=>{const n=setTimeout(s,t);i.addEventListener("abort",(()=>{clearTimeout(n),e(new DOMException("Animation cancelled","AbortError"))}),{once:!0})}))}animateToPosition(t,i,s){return new Promise(((e,n)=>{if(!this.item)return e();this.item.style.transition=`transform ${i}ms ease-out`,this.item.style.transform=`translate3d(${-t}px, 0, 0)`;const o=setTimeout(e,i);s.addEventListener("abort",(()=>{clearTimeout(o),n(new DOMException("Animation cancelled","AbortError"))}),{once:!0})}))}getSwipeThreshold(t){return("end"===t?this.optsWidthRightSide:this.optsWidthLeftSide)+30}async animateFullSwipe(t){const i=new AbortController;this.animationAbortController=i;const{signal:s}=i;this.gesture&&this.gesture.enable(!1);try{const i="end"===t?this.rightOptions:this.leftOptions;this.state="end"===t?168:208,await this.delay(100,s);const e="end"===t?window.innerWidth:-window.innerWidth;await this.animateToPosition(e,250,s),i&&i.fireSwipeEvent(),await this.delay(300,s),await this.animateToPosition(0,250,s)}catch(t){}finally{this.animationAbortController=void 0,this.item&&(this.item.style.transition="",this.item.style.transform=""),this.openAmount=0,this.state=2,p===this.el&&(p=void 0),this.gesture&&this.gesture.enable(!this.disabled)}}async updateOptions(){var t;const i=this.el.querySelectorAll("ion-item-options");let s=0;this.leftOptions=this.rightOptions=void 0;for(let e=0;e<i.length;e++){const n=i.item(e),o=void 0!==n.componentOnReady?await n.componentOnReady():n;"start"==(l(null!==(t=o.side)&&void 0!==t?t:o.getAttribute("side"))?"end":"start")?(this.leftOptions=o,s|=1):(this.rightOptions=o,s|=2)}this.optsDirty=!0,this.sides=s}canStart(t){return!("rtl"===document.dir?window.innerWidth-t.startX<15:t.startX<15)&&(p&&p!==this.el&&this.closeOpened(),!(!this.rightOptions&&!this.leftOptions))}onStart(){this.item=this.el.querySelector("ion-item");const{contentEl:t}=this;t&&(this.initialContentScrollY=a(t)),p=this.el,void 0!==this.tmr&&(clearTimeout(this.tmr),this.tmr=void 0),0===this.openAmount&&(this.optsDirty=!0,this.state=4),this.initialOpenAmount=this.openAmount,this.item&&(this.item.style.transition="none")}onMove(t){this.optsDirty&&this.calculateOptsWidth();let i,s=this.initialOpenAmount-t.deltaX;switch(this.sides){case 2:s=Math.max(0,s);break;case 1:s=Math.min(0,s);break;case 3:break;case 0:return;default:e("[ion-item-sliding] - invalid ItemSideFlags value",this.sides)}s>this.optsWidthRightSide?(i=this.optsWidthRightSide,s=i+.55*(s-i)):s<-this.optsWidthLeftSide&&(i=-this.optsWidthLeftSide,s=i+.55*(s-i)),this.setOpenAmount(s,!1)}onEnd(t){const{contentEl:i,initialContentScrollY:s}=this;i&&d(i,s);const e=Math.abs(t.deltaX),n=t.deltaX<0?"end":"start";if(this.hasExpandableOptions("end"===n?this.rightOptions:this.leftOptions)&&(e>this.getSwipeThreshold(n)||Math.abs(t.velocityX)>.5&&e>.5*("end"===n?this.optsWidthRightSide:this.optsWidthLeftSide)))return void this.animateFullSwipe(n).catch((()=>{this.gesture&&this.gesture.enable(!this.disabled)}));const o=t.velocityX;let r=this.openAmount>0?this.optsWidthRightSide:-this.optsWidthLeftSide;g(this.openAmount>0==!(o<0),Math.abs(o)>.3,Math.abs(this.openAmount)<Math.abs(r/2))&&(r=0);const h=this.state;this.setOpenAmount(r,!0),32&h&&this.rightOptions?this.rightOptions.fireSwipeEvent():64&h&&this.leftOptions&&this.leftOptions.fireSwipeEvent()}calculateOptsWidth(){this.optsWidthRightSide=0,this.rightOptions&&(this.rightOptions.style.display="flex",this.optsWidthRightSide=this.rightOptions.offsetWidth,this.rightOptions.style.display=""),this.optsWidthLeftSide=0,this.leftOptions&&(this.leftOptions.style.display="flex",this.optsWidthLeftSide=this.leftOptions.offsetWidth,this.leftOptions.style.display=""),this.optsDirty=!1}setOpenAmount(t,i){if(void 0!==this.tmr&&(clearTimeout(this.tmr),this.tmr=void 0),!this.item)return;const{el:s}=this,e=this.item.style;if(this.openAmount=t,i&&(e.transition=""),t>0)this.state=t>=this.optsWidthRightSide+30?40:8;else{if(!(t<0))return s.classList.add("item-sliding-closing"),this.gesture&&this.gesture.enable(!1),this.tmr=setTimeout((()=>{this.state=2,this.tmr=void 0,this.gesture&&this.gesture.enable(!this.disabled),s.classList.remove("item-sliding-closing")}),600),p=void 0,void(e.transform="");this.state=t<=-this.optsWidthLeftSide-30?80:16}e.transform=`translate3d(${-t}px,0,0)`,this.ionDrag.emit({amount:t,ratio:this.getSlidingRatioSync()})}getSlidingRatioSync(){return this.openAmount>0?this.openAmount/this.optsWidthRightSide:this.openAmount<0?this.openAmount/this.optsWidthLeftSide:0}render(){const t=m(this);return n(r,{key:"c945f30d9f7deb90d22064d4059e2b08f35614be",class:{[t]:!0,"item-sliding-active-slide":2!==this.state,"item-sliding-active-options-end":!!(8&this.state),"item-sliding-active-options-start":!!(16&this.state),"item-sliding-active-swipe-end":!!(32&this.state),"item-sliding-active-swipe-start":!!(64&this.state)}})}get el(){return this}static get watchers(){return{disabled:[{disabledChanged:0}]}}static get style(){return"ion-item-sliding{display:block;position:relative;width:100%;overflow:hidden;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}ion-item-sliding .item{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.item-sliding-active-slide .item{position:relative;-webkit-transition:-webkit-transform 500ms cubic-bezier(0.36, 0.66, 0.04, 1);transition:-webkit-transform 500ms cubic-bezier(0.36, 0.66, 0.04, 1);transition:transform 500ms cubic-bezier(0.36, 0.66, 0.04, 1);transition:transform 500ms cubic-bezier(0.36, 0.66, 0.04, 1), -webkit-transform 500ms cubic-bezier(0.36, 0.66, 0.04, 1);opacity:1;z-index:2;pointer-events:none;will-change:transform}.item-sliding-closing ion-item-options{pointer-events:none}.item-sliding-active-swipe-end .item-options-end .item-option-expandable{padding-left:100%;-ms-flex-order:1;order:1;-webkit-transition-duration:0.6s;transition-duration:0.6s;-webkit-transition-property:padding-left;transition-property:padding-left}:host-context([dir=rtl]) .item-sliding-active-swipe-end .item-options-end .item-option-expandable{-ms-flex-order:-1;order:-1}[dir=rtl] .item-sliding-active-swipe-end .item-options-end .item-option-expandable{-ms-flex-order:-1;order:-1}@supports selector(:dir(rtl)){.item-sliding-active-swipe-end .item-options-end .item-option-expandable:dir(rtl){-ms-flex-order:-1;order:-1}}.item-sliding-active-swipe-start .item-options-start .item-option-expandable{padding-right:100%;-ms-flex-order:-1;order:-1;-webkit-transition-duration:0.6s;transition-duration:0.6s;-webkit-transition-property:padding-right;transition-property:padding-right}:host-context([dir=rtl]) .item-sliding-active-swipe-start .item-options-start .item-option-expandable{-ms-flex-order:1;order:1}[dir=rtl] .item-sliding-active-swipe-start .item-options-start .item-option-expandable{-ms-flex-order:1;order:1}@supports selector(:dir(rtl)){.item-sliding-active-swipe-start .item-options-start .item-option-expandable:dir(rtl){-ms-flex-order:1;order:1}}"}},[0,"ion-item-sliding",{disabled:[4],state:[32],getOpenAmount:[64],getSlidingRatio:[64],open:[64],close:[64],closeOpened:[64]},void 0,{disabled:[{disabledChanged:0}]}]),g=(t,i,s)=>!i&&s||t&&i,v=u,b=function(){"undefined"!=typeof customElements&&["ion-item-sliding"].forEach((t=>{"ion-item-sliding"===t&&(customElements.get(o(t))||customElements.define(o(t),u))}))};export{v as IonItemSliding,b as defineCustomElement}
@@ -193,10 +193,18 @@ const ItemSliding = class {
193
193
  this.disabledChanged();
194
194
  }
195
195
  disconnectedCallback() {
196
+ var _a;
196
197
  if (this.gesture) {
197
198
  this.gesture.destroy();
198
199
  this.gesture = undefined;
199
200
  }
201
+ if (this.tmr !== undefined) {
202
+ clearTimeout(this.tmr);
203
+ this.tmr = undefined;
204
+ }
205
+ // Abort any in-progress animation. The abort handler rejects the pending
206
+ // promise, causing animateFullSwipe's finally block to run cleanup.
207
+ (_a = this.animationAbortController) === null || _a === void 0 ? void 0 : _a.abort();
200
208
  this.item = null;
201
209
  this.leftOptions = this.rightOptions = undefined;
202
210
  if (openSlidingItem === this.el) {
@@ -230,6 +238,9 @@ const ItemSliding = class {
230
238
  */
231
239
  async open(side) {
232
240
  var _a;
241
+ if ((this.state & 128 /* SlidingState.AnimatingFullSwipe */) !== 0) {
242
+ return;
243
+ }
233
244
  /**
234
245
  * It is possible for the item to be added to the DOM
235
246
  * after the item-sliding component was created. As a result,
@@ -281,6 +292,9 @@ const ItemSliding = class {
281
292
  * Close the sliding item. Items can also be closed from the [List](./list).
282
293
  */
283
294
  async close() {
295
+ if ((this.state & 128 /* SlidingState.AnimatingFullSwipe */) !== 0) {
296
+ return;
297
+ }
284
298
  this.setOpenAmount(0, true);
285
299
  }
286
300
  /**
@@ -311,6 +325,111 @@ const ItemSliding = class {
311
325
  return this.rightOptions;
312
326
  }
313
327
  }
328
+ /**
329
+ * Check if the given item options element contains at least one expandable, non-disabled option.
330
+ */
331
+ hasExpandableOptions(options) {
332
+ if (!options)
333
+ return false;
334
+ const optionElements = options.querySelectorAll('ion-item-option');
335
+ return Array.from(optionElements).some((option) => {
336
+ return option.expandable === true && !option.disabled;
337
+ });
338
+ }
339
+ /**
340
+ * Returns a Promise that resolves after `ms` milliseconds, or rejects if the
341
+ * given AbortSignal is fired before the timer expires.
342
+ */
343
+ delay(ms, signal) {
344
+ return new Promise((resolve, reject) => {
345
+ const id = setTimeout(resolve, ms);
346
+ signal.addEventListener('abort', () => {
347
+ clearTimeout(id);
348
+ reject(new DOMException('Animation cancelled', 'AbortError'));
349
+ }, { once: true });
350
+ });
351
+ }
352
+ /**
353
+ * Animate the item to a specific position using CSS transitions.
354
+ * Returns a Promise that resolves when the animation completes, or rejects if
355
+ * the given AbortSignal is fired.
356
+ */
357
+ animateToPosition(position, duration, signal) {
358
+ return new Promise((resolve, reject) => {
359
+ if (!this.item) {
360
+ return resolve();
361
+ }
362
+ this.item.style.transition = `transform ${duration}ms ease-out`;
363
+ this.item.style.transform = `translate3d(${-position}px, 0, 0)`;
364
+ const id = setTimeout(resolve, duration);
365
+ signal.addEventListener('abort', () => {
366
+ clearTimeout(id);
367
+ reject(new DOMException('Animation cancelled', 'AbortError'));
368
+ }, { once: true });
369
+ });
370
+ }
371
+ /**
372
+ * Calculate the swipe threshold distance required to trigger a full swipe animation.
373
+ * Returns the maximum options width plus a margin to ensure it's achievable.
374
+ */
375
+ getSwipeThreshold(direction) {
376
+ const maxWidth = direction === 'end' ? this.optsWidthRightSide : this.optsWidthLeftSide;
377
+ return maxWidth + SWIPE_MARGIN;
378
+ }
379
+ /**
380
+ * Animate the item through a full swipe sequence: off-screen → trigger action → return.
381
+ * This is used when an expandable option is swiped beyond the threshold.
382
+ */
383
+ async animateFullSwipe(direction) {
384
+ const abortController = new AbortController();
385
+ this.animationAbortController = abortController;
386
+ const { signal } = abortController;
387
+ // Prevent interruption during animation
388
+ if (this.gesture) {
389
+ this.gesture.enable(false);
390
+ }
391
+ try {
392
+ const options = direction === 'end' ? this.rightOptions : this.leftOptions;
393
+ // Trigger expandable state without moving the item
394
+ // Set state directly so expandable option fills its container, starting from
395
+ // the exact position where the user released, without any visual snap.
396
+ this.state =
397
+ direction === 'end'
398
+ ? 8 /* SlidingState.End */ | 32 /* SlidingState.SwipeEnd */ | 128 /* SlidingState.AnimatingFullSwipe */
399
+ : 16 /* SlidingState.Start */ | 64 /* SlidingState.SwipeStart */ | 128 /* SlidingState.AnimatingFullSwipe */;
400
+ await this.delay(100, signal);
401
+ // Animate off-screen while maintaining the expanded state
402
+ const offScreenDistance = direction === 'end' ? window.innerWidth : -window.innerWidth;
403
+ await this.animateToPosition(offScreenDistance, 250, signal);
404
+ // Trigger action
405
+ if (options) {
406
+ options.fireSwipeEvent();
407
+ }
408
+ // Small delay before returning
409
+ await this.delay(300, signal);
410
+ // Return to closed state
411
+ await this.animateToPosition(0, 250, signal);
412
+ }
413
+ catch (_a) {
414
+ // Animation was aborted (e.g. component disconnected). finally handles cleanup.
415
+ }
416
+ finally {
417
+ this.animationAbortController = undefined;
418
+ // Reset state
419
+ if (this.item) {
420
+ this.item.style.transition = '';
421
+ this.item.style.transform = '';
422
+ }
423
+ this.openAmount = 0;
424
+ this.state = 2 /* SlidingState.Disabled */;
425
+ if (openSlidingItem === this.el) {
426
+ openSlidingItem = undefined;
427
+ }
428
+ if (this.gesture) {
429
+ this.gesture.enable(!this.disabled);
430
+ }
431
+ }
432
+ }
314
433
  async updateOptions() {
315
434
  var _a;
316
435
  const options = this.el.querySelectorAll('ion-item-options');
@@ -417,6 +536,23 @@ const ItemSliding = class {
417
536
  if (contentEl) {
418
537
  index$1.resetContentScrollY(contentEl, initialContentScrollY);
419
538
  }
539
+ // Check for full swipe conditions with expandable options
540
+ const rawSwipeDistance = Math.abs(gesture.deltaX);
541
+ const direction = gesture.deltaX < 0 ? 'end' : 'start';
542
+ const options = direction === 'end' ? this.rightOptions : this.leftOptions;
543
+ const hasExpandable = this.hasExpandableOptions(options);
544
+ const shouldTriggerFullSwipe = hasExpandable &&
545
+ (rawSwipeDistance > this.getSwipeThreshold(direction) ||
546
+ (Math.abs(gesture.velocityX) > 0.5 &&
547
+ rawSwipeDistance > (direction === 'end' ? this.optsWidthRightSide : this.optsWidthLeftSide) * 0.5));
548
+ if (shouldTriggerFullSwipe) {
549
+ this.animateFullSwipe(direction).catch(() => {
550
+ if (this.gesture) {
551
+ this.gesture.enable(!this.disabled);
552
+ }
553
+ });
554
+ return;
555
+ }
420
556
  const velocity = gesture.velocityX;
421
557
  let restingPoint = this.openAmount > 0 ? this.optsWidthRightSide : -this.optsWidthLeftSide;
422
558
  // Check if the drag didn't clear the buttons mid-point
@@ -524,7 +660,7 @@ const ItemSliding = class {
524
660
  }
525
661
  render() {
526
662
  const theme = ionicGlobal.getIonTheme(this);
527
- return (index.h(index.Host, { key: '79cd09dd43183008f470b31abb7b3606f653a98b', class: {
663
+ return (index.h(index.Host, { key: 'c945f30d9f7deb90d22064d4059e2b08f35614be', class: {
528
664
  [theme]: true,
529
665
  'item-sliding-active-slide': this.state !== 2 /* SlidingState.Disabled */,
530
666
  'item-sliding-active-options-end': (this.state & 8 /* SlidingState.End */) !== 0,
@@ -64,10 +64,18 @@ export class ItemSliding {
64
64
  this.disabledChanged();
65
65
  }
66
66
  disconnectedCallback() {
67
+ var _a;
67
68
  if (this.gesture) {
68
69
  this.gesture.destroy();
69
70
  this.gesture = undefined;
70
71
  }
72
+ if (this.tmr !== undefined) {
73
+ clearTimeout(this.tmr);
74
+ this.tmr = undefined;
75
+ }
76
+ // Abort any in-progress animation. The abort handler rejects the pending
77
+ // promise, causing animateFullSwipe's finally block to run cleanup.
78
+ (_a = this.animationAbortController) === null || _a === void 0 ? void 0 : _a.abort();
71
79
  this.item = null;
72
80
  this.leftOptions = this.rightOptions = undefined;
73
81
  if (openSlidingItem === this.el) {
@@ -101,6 +109,9 @@ export class ItemSliding {
101
109
  */
102
110
  async open(side) {
103
111
  var _a;
112
+ if ((this.state & 128 /* SlidingState.AnimatingFullSwipe */) !== 0) {
113
+ return;
114
+ }
104
115
  /**
105
116
  * It is possible for the item to be added to the DOM
106
117
  * after the item-sliding component was created. As a result,
@@ -152,6 +163,9 @@ export class ItemSliding {
152
163
  * Close the sliding item. Items can also be closed from the [List](./list).
153
164
  */
154
165
  async close() {
166
+ if ((this.state & 128 /* SlidingState.AnimatingFullSwipe */) !== 0) {
167
+ return;
168
+ }
155
169
  this.setOpenAmount(0, true);
156
170
  }
157
171
  /**
@@ -182,6 +196,111 @@ export class ItemSliding {
182
196
  return this.rightOptions;
183
197
  }
184
198
  }
199
+ /**
200
+ * Check if the given item options element contains at least one expandable, non-disabled option.
201
+ */
202
+ hasExpandableOptions(options) {
203
+ if (!options)
204
+ return false;
205
+ const optionElements = options.querySelectorAll('ion-item-option');
206
+ return Array.from(optionElements).some((option) => {
207
+ return option.expandable === true && !option.disabled;
208
+ });
209
+ }
210
+ /**
211
+ * Returns a Promise that resolves after `ms` milliseconds, or rejects if the
212
+ * given AbortSignal is fired before the timer expires.
213
+ */
214
+ delay(ms, signal) {
215
+ return new Promise((resolve, reject) => {
216
+ const id = setTimeout(resolve, ms);
217
+ signal.addEventListener('abort', () => {
218
+ clearTimeout(id);
219
+ reject(new DOMException('Animation cancelled', 'AbortError'));
220
+ }, { once: true });
221
+ });
222
+ }
223
+ /**
224
+ * Animate the item to a specific position using CSS transitions.
225
+ * Returns a Promise that resolves when the animation completes, or rejects if
226
+ * the given AbortSignal is fired.
227
+ */
228
+ animateToPosition(position, duration, signal) {
229
+ return new Promise((resolve, reject) => {
230
+ if (!this.item) {
231
+ return resolve();
232
+ }
233
+ this.item.style.transition = `transform ${duration}ms ease-out`;
234
+ this.item.style.transform = `translate3d(${-position}px, 0, 0)`;
235
+ const id = setTimeout(resolve, duration);
236
+ signal.addEventListener('abort', () => {
237
+ clearTimeout(id);
238
+ reject(new DOMException('Animation cancelled', 'AbortError'));
239
+ }, { once: true });
240
+ });
241
+ }
242
+ /**
243
+ * Calculate the swipe threshold distance required to trigger a full swipe animation.
244
+ * Returns the maximum options width plus a margin to ensure it's achievable.
245
+ */
246
+ getSwipeThreshold(direction) {
247
+ const maxWidth = direction === 'end' ? this.optsWidthRightSide : this.optsWidthLeftSide;
248
+ return maxWidth + SWIPE_MARGIN;
249
+ }
250
+ /**
251
+ * Animate the item through a full swipe sequence: off-screen → trigger action → return.
252
+ * This is used when an expandable option is swiped beyond the threshold.
253
+ */
254
+ async animateFullSwipe(direction) {
255
+ const abortController = new AbortController();
256
+ this.animationAbortController = abortController;
257
+ const { signal } = abortController;
258
+ // Prevent interruption during animation
259
+ if (this.gesture) {
260
+ this.gesture.enable(false);
261
+ }
262
+ try {
263
+ const options = direction === 'end' ? this.rightOptions : this.leftOptions;
264
+ // Trigger expandable state without moving the item
265
+ // Set state directly so expandable option fills its container, starting from
266
+ // the exact position where the user released, without any visual snap.
267
+ this.state =
268
+ direction === 'end'
269
+ ? 8 /* SlidingState.End */ | 32 /* SlidingState.SwipeEnd */ | 128 /* SlidingState.AnimatingFullSwipe */
270
+ : 16 /* SlidingState.Start */ | 64 /* SlidingState.SwipeStart */ | 128 /* SlidingState.AnimatingFullSwipe */;
271
+ await this.delay(100, signal);
272
+ // Animate off-screen while maintaining the expanded state
273
+ const offScreenDistance = direction === 'end' ? window.innerWidth : -window.innerWidth;
274
+ await this.animateToPosition(offScreenDistance, 250, signal);
275
+ // Trigger action
276
+ if (options) {
277
+ options.fireSwipeEvent();
278
+ }
279
+ // Small delay before returning
280
+ await this.delay(300, signal);
281
+ // Return to closed state
282
+ await this.animateToPosition(0, 250, signal);
283
+ }
284
+ catch (_a) {
285
+ // Animation was aborted (e.g. component disconnected). finally handles cleanup.
286
+ }
287
+ finally {
288
+ this.animationAbortController = undefined;
289
+ // Reset state
290
+ if (this.item) {
291
+ this.item.style.transition = '';
292
+ this.item.style.transform = '';
293
+ }
294
+ this.openAmount = 0;
295
+ this.state = 2 /* SlidingState.Disabled */;
296
+ if (openSlidingItem === this.el) {
297
+ openSlidingItem = undefined;
298
+ }
299
+ if (this.gesture) {
300
+ this.gesture.enable(!this.disabled);
301
+ }
302
+ }
303
+ }
185
304
  async updateOptions() {
186
305
  var _a;
187
306
  const options = this.el.querySelectorAll('ion-item-options');
@@ -288,6 +407,23 @@ export class ItemSliding {
288
407
  if (contentEl) {
289
408
  resetContentScrollY(contentEl, initialContentScrollY);
290
409
  }
410
+ // Check for full swipe conditions with expandable options
411
+ const rawSwipeDistance = Math.abs(gesture.deltaX);
412
+ const direction = gesture.deltaX < 0 ? 'end' : 'start';
413
+ const options = direction === 'end' ? this.rightOptions : this.leftOptions;
414
+ const hasExpandable = this.hasExpandableOptions(options);
415
+ const shouldTriggerFullSwipe = hasExpandable &&
416
+ (rawSwipeDistance > this.getSwipeThreshold(direction) ||
417
+ (Math.abs(gesture.velocityX) > 0.5 &&
418
+ rawSwipeDistance > (direction === 'end' ? this.optsWidthRightSide : this.optsWidthLeftSide) * 0.5));
419
+ if (shouldTriggerFullSwipe) {
420
+ this.animateFullSwipe(direction).catch(() => {
421
+ if (this.gesture) {
422
+ this.gesture.enable(!this.disabled);
423
+ }
424
+ });
425
+ return;
426
+ }
291
427
  const velocity = gesture.velocityX;
292
428
  let restingPoint = this.openAmount > 0 ? this.optsWidthRightSide : -this.optsWidthLeftSide;
293
429
  // Check if the drag didn't clear the buttons mid-point
@@ -395,7 +531,7 @@ export class ItemSliding {
395
531
  }
396
532
  render() {
397
533
  const theme = getIonTheme(this);
398
- return (h(Host, { key: '79cd09dd43183008f470b31abb7b3606f653a98b', class: {
534
+ return (h(Host, { key: 'c945f30d9f7deb90d22064d4059e2b08f35614be', class: {
399
535
  [theme]: true,
400
536
  'item-sliding-active-slide': this.state !== 2 /* SlidingState.Disabled */,
401
537
  'item-sliding-active-options-end': (this.state & 8 /* SlidingState.End */) !== 0,
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * See https://playwright.dev/docs/api/class-mouse#mouse-move for more information.
10
10
  */
11
- export const dragElementBy = async (el, page, dragByX = 0, dragByY = 0, startXCoord, startYCoord, releaseDrag = true) => {
11
+ export const dragElementBy = async (el, page, dragByX = 0, dragByY = 0, startXCoord, startYCoord, releaseDrag = true, steps) => {
12
12
  const boundingBox = await el.boundingBox();
13
13
  if (!boundingBox) {
14
14
  throw new Error('Cannot get a bounding box for an element that is not visible. See https://playwright.dev/docs/api/class-locator#locator-bounding-box for more information');
@@ -19,7 +19,7 @@ export const dragElementBy = async (el, page, dragByX = 0, dragByY = 0, startXCo
19
19
  await page.mouse.move(startX, startY);
20
20
  await page.mouse.down();
21
21
  // Drag the element.
22
- await moveElement(page, startX, startY, dragByX, dragByY);
22
+ await moveElement(page, startX, startY, dragByX, dragByY, steps);
23
23
  if (releaseDrag) {
24
24
  await page.mouse.up();
25
25
  }
@@ -71,8 +71,7 @@ const validateDragByY = (startY, dragByY, viewportHeight) => {
71
71
  throw new Error(`The element is being dragged past the top of the viewport. Update the dragByY value to prevent going out of bounds. A recommended value is ${recommendedDragByY}.`);
72
72
  }
73
73
  };
74
- const moveElement = async (page, startX, startY, dragByX = 0, dragByY = 0) => {
75
- const steps = 10;
74
+ const moveElement = async (page, startX, startY, dragByX = 0, dragByY = 0, steps = 10) => {
76
75
  const browser = page.context().browser().browserType().name();
77
76
  const viewport = page.viewportSize();
78
77
  if (viewport === null) {
package/dist/docs.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "timestamp": "2026-04-07T15:44:58",
2
+ "timestamp": "2026-04-08T16:46:46",
3
3
  "compiler": {
4
4
  "name": "@stencil/core",
5
5
  "version": "4.43.0",
@@ -191,10 +191,18 @@ const ItemSliding = class {
191
191
  this.disabledChanged();
192
192
  }
193
193
  disconnectedCallback() {
194
+ var _a;
194
195
  if (this.gesture) {
195
196
  this.gesture.destroy();
196
197
  this.gesture = undefined;
197
198
  }
199
+ if (this.tmr !== undefined) {
200
+ clearTimeout(this.tmr);
201
+ this.tmr = undefined;
202
+ }
203
+ // Abort any in-progress animation. The abort handler rejects the pending
204
+ // promise, causing animateFullSwipe's finally block to run cleanup.
205
+ (_a = this.animationAbortController) === null || _a === void 0 ? void 0 : _a.abort();
198
206
  this.item = null;
199
207
  this.leftOptions = this.rightOptions = undefined;
200
208
  if (openSlidingItem === this.el) {
@@ -228,6 +236,9 @@ const ItemSliding = class {
228
236
  */
229
237
  async open(side) {
230
238
  var _a;
239
+ if ((this.state & 128 /* SlidingState.AnimatingFullSwipe */) !== 0) {
240
+ return;
241
+ }
231
242
  /**
232
243
  * It is possible for the item to be added to the DOM
233
244
  * after the item-sliding component was created. As a result,
@@ -279,6 +290,9 @@ const ItemSliding = class {
279
290
  * Close the sliding item. Items can also be closed from the [List](./list).
280
291
  */
281
292
  async close() {
293
+ if ((this.state & 128 /* SlidingState.AnimatingFullSwipe */) !== 0) {
294
+ return;
295
+ }
282
296
  this.setOpenAmount(0, true);
283
297
  }
284
298
  /**
@@ -309,6 +323,111 @@ const ItemSliding = class {
309
323
  return this.rightOptions;
310
324
  }
311
325
  }
326
+ /**
327
+ * Check if the given item options element contains at least one expandable, non-disabled option.
328
+ */
329
+ hasExpandableOptions(options) {
330
+ if (!options)
331
+ return false;
332
+ const optionElements = options.querySelectorAll('ion-item-option');
333
+ return Array.from(optionElements).some((option) => {
334
+ return option.expandable === true && !option.disabled;
335
+ });
336
+ }
337
+ /**
338
+ * Returns a Promise that resolves after `ms` milliseconds, or rejects if the
339
+ * given AbortSignal is fired before the timer expires.
340
+ */
341
+ delay(ms, signal) {
342
+ return new Promise((resolve, reject) => {
343
+ const id = setTimeout(resolve, ms);
344
+ signal.addEventListener('abort', () => {
345
+ clearTimeout(id);
346
+ reject(new DOMException('Animation cancelled', 'AbortError'));
347
+ }, { once: true });
348
+ });
349
+ }
350
+ /**
351
+ * Animate the item to a specific position using CSS transitions.
352
+ * Returns a Promise that resolves when the animation completes, or rejects if
353
+ * the given AbortSignal is fired.
354
+ */
355
+ animateToPosition(position, duration, signal) {
356
+ return new Promise((resolve, reject) => {
357
+ if (!this.item) {
358
+ return resolve();
359
+ }
360
+ this.item.style.transition = `transform ${duration}ms ease-out`;
361
+ this.item.style.transform = `translate3d(${-position}px, 0, 0)`;
362
+ const id = setTimeout(resolve, duration);
363
+ signal.addEventListener('abort', () => {
364
+ clearTimeout(id);
365
+ reject(new DOMException('Animation cancelled', 'AbortError'));
366
+ }, { once: true });
367
+ });
368
+ }
369
+ /**
370
+ * Calculate the swipe threshold distance required to trigger a full swipe animation.
371
+ * Returns the maximum options width plus a margin to ensure it's achievable.
372
+ */
373
+ getSwipeThreshold(direction) {
374
+ const maxWidth = direction === 'end' ? this.optsWidthRightSide : this.optsWidthLeftSide;
375
+ return maxWidth + SWIPE_MARGIN;
376
+ }
377
+ /**
378
+ * Animate the item through a full swipe sequence: off-screen → trigger action → return.
379
+ * This is used when an expandable option is swiped beyond the threshold.
380
+ */
381
+ async animateFullSwipe(direction) {
382
+ const abortController = new AbortController();
383
+ this.animationAbortController = abortController;
384
+ const { signal } = abortController;
385
+ // Prevent interruption during animation
386
+ if (this.gesture) {
387
+ this.gesture.enable(false);
388
+ }
389
+ try {
390
+ const options = direction === 'end' ? this.rightOptions : this.leftOptions;
391
+ // Trigger expandable state without moving the item
392
+ // Set state directly so expandable option fills its container, starting from
393
+ // the exact position where the user released, without any visual snap.
394
+ this.state =
395
+ direction === 'end'
396
+ ? 8 /* SlidingState.End */ | 32 /* SlidingState.SwipeEnd */ | 128 /* SlidingState.AnimatingFullSwipe */
397
+ : 16 /* SlidingState.Start */ | 64 /* SlidingState.SwipeStart */ | 128 /* SlidingState.AnimatingFullSwipe */;
398
+ await this.delay(100, signal);
399
+ // Animate off-screen while maintaining the expanded state
400
+ const offScreenDistance = direction === 'end' ? window.innerWidth : -window.innerWidth;
401
+ await this.animateToPosition(offScreenDistance, 250, signal);
402
+ // Trigger action
403
+ if (options) {
404
+ options.fireSwipeEvent();
405
+ }
406
+ // Small delay before returning
407
+ await this.delay(300, signal);
408
+ // Return to closed state
409
+ await this.animateToPosition(0, 250, signal);
410
+ }
411
+ catch (_a) {
412
+ // Animation was aborted (e.g. component disconnected). finally handles cleanup.
413
+ }
414
+ finally {
415
+ this.animationAbortController = undefined;
416
+ // Reset state
417
+ if (this.item) {
418
+ this.item.style.transition = '';
419
+ this.item.style.transform = '';
420
+ }
421
+ this.openAmount = 0;
422
+ this.state = 2 /* SlidingState.Disabled */;
423
+ if (openSlidingItem === this.el) {
424
+ openSlidingItem = undefined;
425
+ }
426
+ if (this.gesture) {
427
+ this.gesture.enable(!this.disabled);
428
+ }
429
+ }
430
+ }
312
431
  async updateOptions() {
313
432
  var _a;
314
433
  const options = this.el.querySelectorAll('ion-item-options');
@@ -415,6 +534,23 @@ const ItemSliding = class {
415
534
  if (contentEl) {
416
535
  resetContentScrollY(contentEl, initialContentScrollY);
417
536
  }
537
+ // Check for full swipe conditions with expandable options
538
+ const rawSwipeDistance = Math.abs(gesture.deltaX);
539
+ const direction = gesture.deltaX < 0 ? 'end' : 'start';
540
+ const options = direction === 'end' ? this.rightOptions : this.leftOptions;
541
+ const hasExpandable = this.hasExpandableOptions(options);
542
+ const shouldTriggerFullSwipe = hasExpandable &&
543
+ (rawSwipeDistance > this.getSwipeThreshold(direction) ||
544
+ (Math.abs(gesture.velocityX) > 0.5 &&
545
+ rawSwipeDistance > (direction === 'end' ? this.optsWidthRightSide : this.optsWidthLeftSide) * 0.5));
546
+ if (shouldTriggerFullSwipe) {
547
+ this.animateFullSwipe(direction).catch(() => {
548
+ if (this.gesture) {
549
+ this.gesture.enable(!this.disabled);
550
+ }
551
+ });
552
+ return;
553
+ }
418
554
  const velocity = gesture.velocityX;
419
555
  let restingPoint = this.openAmount > 0 ? this.optsWidthRightSide : -this.optsWidthLeftSide;
420
556
  // Check if the drag didn't clear the buttons mid-point
@@ -522,7 +658,7 @@ const ItemSliding = class {
522
658
  }
523
659
  render() {
524
660
  const theme = getIonTheme(this);
525
- return (h(Host, { key: '79cd09dd43183008f470b31abb7b3606f653a98b', class: {
661
+ return (h(Host, { key: 'c945f30d9f7deb90d22064d4059e2b08f35614be', class: {
526
662
  [theme]: true,
527
663
  'item-sliding-active-slide': this.state !== 2 /* SlidingState.Disabled */,
528
664
  'item-sliding-active-options-end': (this.state & 8 /* SlidingState.End */) !== 0,