@capgo/capacitor-sheets 8.1.0 → 8.2.1

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.
package/README.md CHANGED
@@ -1,19 +1,32 @@
1
1
  # @capgo/capacitor-sheets
2
2
 
3
- <a href="https://capgo.app/">
3
+ <a href="https://capgo.app/?ref=plugin_sheets">
4
4
  <img
5
5
  src="https://capgo.app/readme-banner.svg?repo=Cap-go/capacitor-sheets"
6
- alt="Capgo - Instant updates for Capacitor"
6
+ alt="Capgo - Build Capacitor apps with AI, ship updates instantly"
7
7
  />
8
8
  </a>
9
9
 
10
+ <div align="center">
11
+ <p>
12
+ <b>Capgo</b>: push fixes to your Capacitor users in minutes, build signed iOS and Android apps without a Mac, and
13
+ roll back in one click.
14
+ </p>
15
+ <h2>
16
+ <a href="https://capgo.app/register/?ref=plugin_sheets">➡️ Get started for free</a>
17
+ </h2>
18
+ <p>14-day unlimited free trial. No credit card required</p>
19
+ </div>
20
+
21
+ > 🤖 **Building with an AI coding agent?** Install [Capgo skills](https://capgo.app/skills/?ref=plugin_sheets) so Claude Code, Codex, Cursor, Copilot, or Gemini CLI know how to install and use `@capgo/capacitor-sheets` in your Capacitor app: `npx skills add Cap-go/capgo-skills`. Then ask your agent: _"Add @capgo/capacitor-sheets to my Capacitor app and wire it up."_ [Agent setup guides](https://capgo.app/agents/?ref=plugin_sheets) · [Hosted MCP](https://capgo.app/docs/ai/mcp/?ref=plugin_sheets)
22
+
10
23
  Framework-agnostic sheets, drawers, dialogs, scroll helpers, and overlay primitives for Capacitor apps.
11
24
 
12
25
  This package is inspired by the public Silk feature surface, but it is not a wrapper around Silk and does not include Silk source code. It uses platform web APIs and custom elements so the same primitives work in React, Vue, Angular, Svelte, Solid, or plain TypeScript.
13
26
 
14
27
  ## Usecase Demos
15
28
 
16
- Each usecase has its own animated WebP demo. These match the public Silk-style example set: long sheets, detents, sidebars, bottom/top sheets, keyboard-aware sheets, toasts, detached sheets, full pages, stacking, depth, parallax, lightboxes, persistent detents, and cards.
29
+ Each usecase has its own animated WebP demo. These match the public Silk-style example set: long sheets, detents, sidebars, bottom/top sheets, keyboard-aware sheets, toasts, detached sheets, full pages, stacking, depth, parallax, lightboxes, persistent detents, and cards..
17
30
 
18
31
  <table>
19
32
  <tr>
@@ -529,6 +529,9 @@ function releaseThemeColorDimmer(documentRef) {
529
529
  }
530
530
 
531
531
  // src/core/sheet-controller.ts
532
+ var inertDepth = /* @__PURE__ */ new WeakMap();
533
+ var inertOriginal = /* @__PURE__ */ new WeakMap();
534
+ var inertHolders = /* @__PURE__ */ new WeakMap();
532
535
  var defaultOptions = {
533
536
  contentPlacement: "bottom",
534
537
  sheetRole: "dialog",
@@ -713,24 +716,32 @@ var SheetController = class {
713
716
  pointerTravel = null;
714
717
  connected = false;
715
718
  lockedScroll = false;
716
- inertedElements = /* @__PURE__ */ new Map();
719
+ inertHeldElements = /* @__PURE__ */ new Set();
720
+ remeasureFrame = null;
721
+ activeMotion = null;
722
+ pendingSettle = null;
723
+ /** Entering completion deferred by a drag that interrupted present(); runs once the sheet settles. */
724
+ deferredEnteringComplete = null;
725
+ /** Resolves the in-flight present() after entering lifecycle completes or is abandoned. */
726
+ settlePresentPromise = null;
717
727
  previousFocus = null;
718
728
  hasThemeDimmer = false;
719
729
  stackId = null;
720
730
  documentListenersAttached = false;
721
731
  viewListenersElement = null;
722
732
  visualViewportListenersAttached = false;
723
- handleVisualViewportResize = () => {
733
+ handleVisualViewportResize = (event) => {
724
734
  const view = this.parts.view;
725
735
  const visualViewport = this.root.ownerDocument.defaultView?.visualViewport;
726
736
  if (!view) return;
737
+ const viewportChanged = event !== void 0;
727
738
  if (!visualViewport || this.options.nativeFocusScrollPrevention === false) {
728
- this.setKeyboardOffset("0em");
739
+ this.setKeyboardOffset("0em", viewportChanged);
729
740
  return;
730
741
  }
731
742
  const layoutHeight = this.root.ownerDocument.documentElement.clientHeight;
732
743
  const keyboardOffset = Math.max(0, layoutHeight - visualViewport.height - visualViewport.offsetTop);
733
- this.setKeyboardOffset(`${toEm(view, keyboardOffset)}em`);
744
+ this.setKeyboardOffset(`${toEm(view, keyboardOffset)}em`, viewportChanged);
734
745
  };
735
746
  handleFocusIn = (event) => {
736
747
  if (!this.presented || this.options.nativeFocusScrollPrevention === false) return;
@@ -761,10 +772,18 @@ var SheetController = class {
761
772
  if (!this.presented || this.options.swipe === false || this.options.contentPlacement === "center") return;
762
773
  if (!(event.target instanceof Element)) return;
763
774
  if (!this.parts.content?.contains(event.target) && event.target !== this.parts.backdrop) return;
775
+ if (this.pointerTravel && !event.isPrimary) return;
764
776
  if (this.options.nativeEdgeSwipePrevention !== false && this.isNativeEdgePointer(event)) {
765
777
  event.preventDefault();
766
778
  }
779
+ this.syncOffsetFromRenderedPosition();
780
+ const interruptedMotion = this.activeMotion ? this.pendingSettle : null;
781
+ this.pendingSettle = null;
782
+ this.cancelActiveMotion();
783
+ this.applyOffset(this.currentOffsetPx, this.status);
767
784
  this.remeasure();
785
+ const axis = isVerticalTrack(this.activeTrack) ? "y" : "x";
786
+ const startPos = axis === "y" ? event.clientY : event.clientX;
768
787
  this.pointerTravel = {
769
788
  id: event.pointerId,
770
789
  startOffsetPx: this.currentOffsetPx,
@@ -774,7 +793,9 @@ var SheetController = class {
774
793
  lastY: event.clientY,
775
794
  lastTime: performance.now(),
776
795
  velocity: 0,
777
- dragging: false
796
+ dragging: false,
797
+ samples: [{ t: performance.now(), pos: startPos }],
798
+ interruptedMotion
778
799
  };
779
800
  };
780
801
  handlePointerMove = (event) => {
@@ -802,6 +823,8 @@ var SheetController = class {
802
823
  travel.lastX = event.clientX;
803
824
  travel.lastY = event.clientY;
804
825
  travel.lastTime = now;
826
+ travel.samples.push({ t: now, pos: current });
827
+ if (travel.samples.length > 24) travel.samples.shift();
805
828
  const nextOffset = this.limitOffset(travel.startOffsetPx + delta);
806
829
  this.applyOffset(nextOffset, "dragging");
807
830
  };
@@ -813,9 +836,17 @@ var SheetController = class {
813
836
  currentTarget.releasePointerCapture(event.pointerId);
814
837
  }
815
838
  this.pointerTravel = null;
816
- if (!travel.dragging) return;
817
- const nearest = this.nearestDetentForOffset(this.currentOffsetPx, travel.velocity);
839
+ if (!travel.dragging) {
840
+ if (travel.interruptedMotion && this.presented) {
841
+ void this.runSettleMotion(travel.interruptedMotion);
842
+ }
843
+ return;
844
+ }
845
+ const nearest = this.nearestDetentForOffset(this.currentOffsetPx, this.releaseVelocity(travel));
818
846
  dispatch(this.root, "cap-sheet-drag-end", this.getTravelEvent());
847
+ if (travel.interruptedMotion?.status === "entering") {
848
+ this.deferredEnteringComplete = travel.interruptedMotion.complete;
849
+ }
819
850
  if (nearest === 0 && this.options.swipeDismissal !== false) {
820
851
  void this.dismiss({ source: "gesture" });
821
852
  return;
@@ -900,6 +931,8 @@ var SheetController = class {
900
931
  this.remeasure();
901
932
  this.applyOffset(this.detentOffsetsPx[0] || this.hiddenOffsetPx, this.status);
902
933
  this.updateDomState(false);
934
+ } else if (this.presented && affectsDismissedOffset(options)) {
935
+ this.scheduleRemeasure();
903
936
  }
904
937
  this.registerStack();
905
938
  }
@@ -954,6 +987,7 @@ var SheetController = class {
954
987
  }
955
988
  this.previousFocus = this.root.ownerDocument.activeElement;
956
989
  this.presented = true;
990
+ this.abandonDeferredEntering();
957
991
  this.activeDetent = clamp(options.detent ?? this.resolveInitialDetent(), 1, this.detentOffsetsPx.length - 1);
958
992
  this.updateDetentState();
959
993
  this.updateDomState(true);
@@ -962,15 +996,33 @@ var SheetController = class {
962
996
  this.retainThemeDimmer();
963
997
  this.setStatus("entering");
964
998
  dispatch(this.root, "cap-sheet-present", this.getTravelEvent());
965
- await this.animateTo(this.detentOffsetsPx[this.activeDetent] || 0, {
966
- ...this.options.enteringAnimationSettings || {},
967
- ...options.animation || {}
999
+ const source = options.source || "programmatic";
1000
+ let resolvePresent;
1001
+ const presentLifecycleSettled = new Promise((resolve) => {
1002
+ resolvePresent = resolve;
968
1003
  });
969
- this.setStatus("idle");
970
- this.focusInitialElement();
971
- this.emitPresentedChange(true, options.source || "programmatic");
972
- this.emitActiveDetentChange(this.activeDetent, 0);
973
- this.updateStack();
1004
+ this.settlePresentPromise = () => {
1005
+ resolvePresent();
1006
+ };
1007
+ const complete = () => {
1008
+ this.setStatus("idle");
1009
+ this.focusInitialElement();
1010
+ this.emitPresentedChange(true, source);
1011
+ this.emitActiveDetentChange(this.activeDetent, 0);
1012
+ this.updateStack();
1013
+ this.releasePresentAwait();
1014
+ };
1015
+ const finished = await this.runSettleMotion({
1016
+ status: "entering",
1017
+ animation: {
1018
+ ...this.options.enteringAnimationSettings || {},
1019
+ ...options.animation || {}
1020
+ },
1021
+ complete
1022
+ });
1023
+ if (!finished) {
1024
+ await presentLifecycleSettled;
1025
+ }
974
1026
  }
975
1027
  /** Dismiss the sheet. */
976
1028
  async dismiss(options = {}) {
@@ -979,6 +1031,8 @@ var SheetController = class {
979
1031
  const previous = this.activeDetent;
980
1032
  this.presented = false;
981
1033
  this.activeDetent = 0;
1034
+ this.pendingSettle = null;
1035
+ this.abandonDeferredEntering();
982
1036
  this.updateDetentState();
983
1037
  this.setStatus("exiting");
984
1038
  dispatch(this.root, "cap-sheet-dismiss", this.getTravelEvent());
@@ -1019,15 +1073,49 @@ var SheetController = class {
1019
1073
  const previous = this.activeDetent;
1020
1074
  this.activeDetent = target;
1021
1075
  this.updateDetentState();
1022
- this.setStatus("settling");
1023
- await this.animateTo(this.detentOffsetsPx[target] || 0, {
1024
- ...this.options.steppingAnimationSettings || {},
1025
- ...options.animation || {}
1076
+ await this.runSettleMotion({
1077
+ status: "settling",
1078
+ animation: {
1079
+ ...this.options.steppingAnimationSettings || {},
1080
+ ...options.animation || {}
1081
+ },
1082
+ complete: () => {
1083
+ const enteringComplete = this.deferredEnteringComplete;
1084
+ if (enteringComplete) {
1085
+ this.deferredEnteringComplete = null;
1086
+ enteringComplete();
1087
+ return;
1088
+ }
1089
+ this.setStatus("idle");
1090
+ if (previous !== target) {
1091
+ this.emitActiveDetentChange(target, previous);
1092
+ }
1093
+ }
1026
1094
  });
1027
- this.setStatus("idle");
1028
- if (previous !== target) {
1029
- this.emitActiveDetentChange(target, previous);
1030
- }
1095
+ }
1096
+ /**
1097
+ * Animate to the active detent and run the completion once the motion finishes.
1098
+ * When the motion is cancelled by a pointerdown, the settle stays pending so a tap
1099
+ * that never becomes a drag can resume it.
1100
+ */
1101
+ releasePresentAwait() {
1102
+ if (!this.settlePresentPromise) return;
1103
+ const settle = this.settlePresentPromise;
1104
+ this.settlePresentPromise = null;
1105
+ settle();
1106
+ }
1107
+ abandonDeferredEntering() {
1108
+ this.deferredEnteringComplete = null;
1109
+ this.releasePresentAwait();
1110
+ }
1111
+ async runSettleMotion(motion) {
1112
+ this.setStatus(motion.status);
1113
+ this.pendingSettle = motion;
1114
+ const finished = await this.animateTo(this.detentOffsetsPx[this.activeDetent] || 0, motion.animation);
1115
+ if (!finished) return false;
1116
+ if (this.pendingSettle === motion) this.pendingSettle = null;
1117
+ motion.complete();
1118
+ return true;
1031
1119
  }
1032
1120
  /** Step up or down one detent. */
1033
1121
  async step(direction = "up", options = {}) {
@@ -1159,7 +1247,7 @@ var SheetController = class {
1159
1247
  const resolved = resolveAnimation(settings);
1160
1248
  if (!content || shouldSkipMotion(resolved.skip)) {
1161
1249
  this.applyOffset(targetOffsetPx, "idle");
1162
- return;
1250
+ return true;
1163
1251
  }
1164
1252
  const startOffset = this.currentOffsetPx;
1165
1253
  const fromProgress = this.getProgressForOffset(startOffset);
@@ -1184,7 +1272,18 @@ var SheetController = class {
1184
1272
  );
1185
1273
  const started = performance.now();
1186
1274
  let frame;
1275
+ let cancelled = false;
1276
+ const cancelMotion = () => {
1277
+ if (cancelled) return;
1278
+ cancelled = true;
1279
+ cancelAnimationFrame(frame);
1280
+ contentAnimation?.cancel();
1281
+ backdropAnimation?.cancel();
1282
+ this.activeMotion = null;
1283
+ };
1284
+ this.activeMotion = { cancel: cancelMotion };
1187
1285
  const tick = () => {
1286
+ if (cancelled) return;
1188
1287
  const progress = duration === 0 ? 1 : clamp((performance.now() - started) / duration, 0, 1);
1189
1288
  const offset = startOffset + (targetOffsetPx - startOffset) * progress;
1190
1289
  this.currentOffsetPx = offset;
@@ -1198,10 +1297,13 @@ var SheetController = class {
1198
1297
  contentAnimation?.finished,
1199
1298
  backdropAnimation ? backdropAnimation.finished : Promise.resolve()
1200
1299
  ]);
1300
+ if (cancelled) return false;
1201
1301
  cancelAnimationFrame(frame);
1202
1302
  contentAnimation?.cancel();
1203
1303
  backdropAnimation?.cancel();
1304
+ this.activeMotion = null;
1204
1305
  this.applyOffset(targetOffsetPx, "idle");
1306
+ return true;
1205
1307
  }
1206
1308
  getProgressForOffset(offsetPx) {
1207
1309
  if (this.options.contentPlacement === "center") {
@@ -1425,47 +1527,152 @@ var SheetController = class {
1425
1527
  documentLockSnapshot.delete(doc);
1426
1528
  this.lockedScroll = false;
1427
1529
  }
1428
- setKeyboardOffset(value) {
1530
+ setKeyboardOffset(value, viewportChanged = false) {
1429
1531
  const view = this.parts.view;
1430
1532
  if (!view) return;
1431
- if (view.style.getPropertyValue("--cap-sheet-keyboard-offset") !== value) {
1432
- view.style.setProperty("--cap-sheet-keyboard-offset", value);
1433
- }
1533
+ const changed = view.style.getPropertyValue("--cap-sheet-keyboard-offset") !== value;
1534
+ if (changed) view.style.setProperty("--cap-sheet-keyboard-offset", value);
1535
+ if (changed || viewportChanged) this.scheduleRemeasure();
1536
+ }
1537
+ scheduleRemeasure() {
1434
1538
  if (!this.presented) return;
1435
- requestAnimationFrame(() => {
1539
+ if (this.remeasureFrame !== null) return;
1540
+ this.remeasureFrame = requestAnimationFrame(() => {
1541
+ this.remeasureFrame = null;
1436
1542
  if (!this.presented) return;
1437
1543
  this.remeasure();
1544
+ if (this.status === "dragging") return;
1438
1545
  this.applyOffset(this.detentOffsetsPx[this.activeDetent] || 0, this.status);
1439
1546
  });
1440
1547
  }
1548
+ cancelActiveMotion() {
1549
+ this.activeMotion?.cancel();
1550
+ this.activeMotion = null;
1551
+ }
1552
+ syncOffsetFromRenderedPosition() {
1553
+ const content = this.parts.content;
1554
+ if (!content || this.options.contentPlacement === "center") return;
1555
+ const offsetPx = this.readOffsetPxFromTransform(content);
1556
+ if (offsetPx === null) return;
1557
+ this.currentOffsetPx = offsetPx;
1558
+ }
1559
+ readOffsetPxFromTransform(content) {
1560
+ const axis = isVerticalTrack(this.activeTrack) ? "y" : "x";
1561
+ const computed = this.root.ownerDocument.defaultView?.getComputedStyle(content).transform;
1562
+ const inline = content.style.transform;
1563
+ const transform = computed && computed !== "none" ? computed : inline && inline !== "none" ? inline : "";
1564
+ if (!transform) return this.currentOffsetPx;
1565
+ const translateMatch = transform.match(/translate3d\(\s*([^,]+)\s*,\s*([^,]+)\s*,/);
1566
+ if (translateMatch) {
1567
+ const raw = axis === "y" ? translateMatch[2] : translateMatch[1];
1568
+ const emMatch = raw.trim().match(/^(-?[\d.]+)em$/);
1569
+ if (emMatch) return Number.parseFloat(emMatch[1]) * getEmPx(content);
1570
+ const pxMatch = raw.trim().match(/^(-?[\d.]+)px$/);
1571
+ if (pxMatch) return Number.parseFloat(pxMatch[1]);
1572
+ }
1573
+ const matrixMatch = transform.match(/^matrix\(([^)]+)\)$/);
1574
+ if (matrixMatch) {
1575
+ const values = matrixMatch[1].split(",").map((part) => Number.parseFloat(part.trim()));
1576
+ const translation = axis === "y" ? values[5] : values[4];
1577
+ if (Number.isFinite(translation)) return translation;
1578
+ }
1579
+ const matrix3dMatch = transform.match(/^matrix3d\(([^)]+)\)$/);
1580
+ if (matrix3dMatch) {
1581
+ const values = matrix3dMatch[1].split(",").map((part) => Number.parseFloat(part.trim()));
1582
+ const translation = axis === "y" ? values[13] : values[12];
1583
+ if (Number.isFinite(translation)) return translation;
1584
+ }
1585
+ return null;
1586
+ }
1587
+ releaseVelocity(travel) {
1588
+ const windowMs = 100;
1589
+ const now = performance.now();
1590
+ const samples = travel.samples.filter((sample) => now - sample.t <= windowMs);
1591
+ if (samples.length < 2) return 0;
1592
+ const first = samples[0];
1593
+ const last = samples[samples.length - 1];
1594
+ const elapsed = Math.max(last.t - first.t, 1);
1595
+ return (last.pos - first.pos) / elapsed * 1e3;
1596
+ }
1597
+ getAllowedOutsideElements() {
1598
+ const allowed = /* @__PURE__ */ new Set();
1599
+ if (this.parts.view) allowed.add(this.parts.view);
1600
+ for (const element of this.parts.islands) allowed.add(element);
1601
+ for (const element of this.parts.externalOverlays) allowed.add(element);
1602
+ return allowed;
1603
+ }
1604
+ applyInertForElement(element, allowed) {
1605
+ const isAllowed = Array.from(allowed).some((candidate) => element === candidate || element.contains(candidate));
1606
+ if (isAllowed) {
1607
+ element.inert = false;
1608
+ element.removeAttribute("aria-hidden");
1609
+ return;
1610
+ }
1611
+ element.inert = true;
1612
+ element.setAttribute("aria-hidden", "true");
1613
+ }
1441
1614
  applyInert() {
1442
1615
  if (this.options.inertOutside === false || !this.parts.view) return;
1443
- const body = this.root.ownerDocument.body;
1444
- const allowed = /* @__PURE__ */ new Set([this.parts.view, ...this.parts.islands, ...this.parts.externalOverlays]);
1445
- this.clearInert();
1616
+ const doc = this.root.ownerDocument;
1617
+ const body = doc.body;
1618
+ const allowed = this.getAllowedOutsideElements();
1619
+ this.releaseInertHold();
1620
+ const holders = inertHolders.get(doc) || /* @__PURE__ */ new Set();
1621
+ holders.add(this);
1622
+ inertHolders.set(doc, holders);
1446
1623
  for (const child of Array.from(body.children)) {
1447
1624
  if (!(child instanceof HTMLElement)) continue;
1448
- this.inertedElements.set(child, { inert: child.inert, ariaHidden: child.getAttribute("aria-hidden") });
1449
- const isAllowed = Array.from(allowed).some((element) => child === element || child.contains(element));
1450
- if (isAllowed) {
1451
- child.inert = false;
1452
- child.removeAttribute("aria-hidden");
1453
- continue;
1625
+ const depth = inertDepth.get(child) ?? 0;
1626
+ if (depth === 0) {
1627
+ inertOriginal.set(child, { inert: child.inert, ariaHidden: child.getAttribute("aria-hidden") });
1454
1628
  }
1455
- child.inert = true;
1456
- child.setAttribute("aria-hidden", "true");
1629
+ inertDepth.set(child, depth + 1);
1630
+ this.inertHeldElements.add(child);
1631
+ this.applyInertForElement(child, allowed);
1457
1632
  }
1458
1633
  }
1459
1634
  clearInert() {
1460
- for (const [element, state] of this.inertedElements) {
1461
- element.inert = state.inert;
1462
- if (state.ariaHidden === null) {
1463
- element.removeAttribute("aria-hidden");
1635
+ this.releaseInertHold();
1636
+ }
1637
+ releaseInertHold() {
1638
+ if (this.inertHeldElements.size === 0) return;
1639
+ const doc = this.root.ownerDocument;
1640
+ const holders = inertHolders.get(doc);
1641
+ holders?.delete(this);
1642
+ if (holders?.size === 0) inertHolders.delete(doc);
1643
+ for (const element of this.inertHeldElements) {
1644
+ const depth = Math.max((inertDepth.get(element) ?? 1) - 1, 0);
1645
+ if (depth === 0) {
1646
+ inertDepth.delete(element);
1647
+ const snapshot = inertOriginal.get(element);
1648
+ if (snapshot) {
1649
+ element.inert = snapshot.inert;
1650
+ if (snapshot.ariaHidden === null) {
1651
+ element.removeAttribute("aria-hidden");
1652
+ } else {
1653
+ element.setAttribute("aria-hidden", snapshot.ariaHidden);
1654
+ }
1655
+ inertOriginal.delete(element);
1656
+ }
1464
1657
  } else {
1465
- element.setAttribute("aria-hidden", state.ariaHidden);
1658
+ inertDepth.set(element, depth);
1466
1659
  }
1467
1660
  }
1468
- this.inertedElements.clear();
1661
+ this.inertHeldElements.clear();
1662
+ this.reapplyDocumentInert(doc);
1663
+ }
1664
+ reapplyDocumentInert(doc) {
1665
+ const holders = inertHolders.get(doc);
1666
+ if (!holders?.size) return;
1667
+ const top = Array.from(holders).filter((controller) => controller.presented && controller.options.inertOutside !== false).sort((left, right) => right.getStackZIndex() - left.getStackZIndex())[0];
1668
+ if (!top) return;
1669
+ const allowed = top.getAllowedOutsideElements();
1670
+ const body = doc.body;
1671
+ for (const child of Array.from(body.children)) {
1672
+ if (!(child instanceof HTMLElement)) continue;
1673
+ if ((inertDepth.get(child) ?? 0) === 0) continue;
1674
+ top.applyInertForElement(child, allowed);
1675
+ }
1469
1676
  }
1470
1677
  retainThemeDimmer() {
1471
1678
  if (this.hasThemeDimmer || this.options.themeColorDimming !== "auto") return;