@leavepulse/ui 0.94.1 → 0.95.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.
Files changed (92) hide show
  1. package/dist/assets/{LpTable-DxnthVz4.css → LpTable-BJjNEvgJ.css} +57 -30
  2. package/dist/chunks/{LpAddressInput-BCqR0zmv.js → LpAddressInput-C5VIgRp0.js} +1 -1
  3. package/dist/chunks/{LpAppShell-DlpQierx.js → LpAppShell-C4b3ySa_.js} +3 -3
  4. package/dist/chunks/{LpCodeBlock-BgWcg2mk.js → LpCodeBlock-DTU40x8P.js} +1 -1
  5. package/dist/chunks/{LpCodeDisplay-DBi1YPLp.js → LpCodeDisplay-F2hjLb0X.js} +2 -2
  6. package/dist/chunks/{LpConfirmDialog-y6k0ryWR.js → LpConfirmDialog-DrHcIxRn.js} +1 -1
  7. package/dist/chunks/{LpConfirmHost-D-X0tDym.js → LpConfirmHost-D0Cd8FA_.js} +1 -1
  8. package/dist/chunks/{LpCopyButton-BGzw95Xc.js → LpCopyButton-CiqUS4SX.js} +1 -1
  9. package/dist/chunks/{LpDataList-B56Gu6Yt.js → LpDataList-Cit9fOSp.js} +2 -2
  10. package/dist/chunks/{LpDrawer-BkWjFdV0.js → LpDrawer-BHlqi62K.js} +1 -1
  11. package/dist/chunks/{LpFileTree-BN_zybBN.js → LpFileTree-gVjF_b6r.js} +1 -1
  12. package/dist/chunks/{LpInspector-oTfkOdYd.js → LpInspector-DSL1lINx.js} +2 -2
  13. package/dist/chunks/{LpLightbox-DPgz7IsT.js → LpLightbox--EwvQGe-.js} +1 -1
  14. package/dist/chunks/{LpLogViewer-BuIzr_6h.js → LpLogViewer-DSq9e8mT.js} +1 -1
  15. package/dist/chunks/{LpMediaGallery-CTejKF2-.js → LpMediaGallery-DlYJf3DS.js} +1 -1
  16. package/dist/chunks/{LpModal-D1E7rQzw.js → LpModal-COMHrBKL.js} +1 -1
  17. package/dist/chunks/{LpModalNav-BFjiHWbr.js → LpModalNav-Cj02cfhL.js} +2 -2
  18. package/dist/chunks/{LpNotificationBell-KRgdRZs-.js → LpNotificationBell-DNGijtmW.js} +1 -1
  19. package/dist/chunks/{LpNotificationFeed-CMGYU8c-.js → LpNotificationFeed-BoovbtfL.js} +3 -3
  20. package/dist/chunks/{LpPageScrollbar-C9sTeU-2.js → LpPageScrollbar-BniFeL2U.js} +5 -1
  21. package/dist/chunks/{LpPaletteTheme-CLwO-SDf.js → LpPaletteTheme-_FwkJO2o.js} +1 -1
  22. package/dist/chunks/{LpPasswordInput-LPVI7D3v.js → LpPasswordInput-BBDz2zyU.js} +1 -1
  23. package/dist/chunks/{LpPie-CsiBXe-e.js → LpPie-CtC4XdhC.js} +1 -1
  24. package/dist/chunks/{LpPipeline-DVcoSs--.js → LpPipeline-aCALaUY8.js} +1 -1
  25. package/dist/chunks/{LpRelativeTime-CN5bWD73.js → LpRelativeTime-9MHlbq6F.js} +1 -1
  26. package/dist/chunks/{LpScrollArea-CqvwfZs2.js → LpScrollArea-B_1KZOSS.js} +11 -1
  27. package/dist/chunks/{LpSecretField-BwrZ4TfT.js → LpSecretField-C9vwMFQ6.js} +4 -4
  28. package/dist/chunks/{LpSidebar-BKhVDsMt.js → LpSidebar-DPri9uyz.js} +3 -3
  29. package/dist/chunks/{LpSidebarNav-DbCap8z1.js → LpSidebarNav-CV1rU6La.js} +1 -1
  30. package/dist/chunks/{LpThemeStudio-BPPctXgd.js → LpThemeStudio-yiEdFJQL.js} +1 -1
  31. package/dist/chunks/{LpThemeSwitcher-B3c44xFH.js → LpThemeSwitcher-DKkCFaFk.js} +1 -1
  32. package/dist/chunks/{LpToaster-Cdx1tnHB.js → LpToaster-C7hHkGOA.js} +1 -1
  33. package/dist/chunks/LpTooltip-Bt1ZFViJ.js +333 -0
  34. package/dist/chunks/{LpTruncate-C_C8eiIE.js → LpTruncate-DEHWL1OA.js} +2 -2
  35. package/dist/chunks/{LpUptimeBar-Dt6SY_-m.js → LpUptimeBar-CXgzU5ym.js} +1 -1
  36. package/dist/chunks/{LpWorkspaceSwitcher-DCIiOQt_.js → LpWorkspaceSwitcher-CrUVSI7x.js} +1 -1
  37. package/dist/chunks/{RevealToggle-BsYxY6Oz.js → RevealToggle-D9Vs88kM.js} +1 -1
  38. package/dist/components/LpAddressInput.vue.js +1 -1
  39. package/dist/components/LpAppShell.vue.js +1 -1
  40. package/dist/components/LpCodeBlock.vue.js +1 -1
  41. package/dist/components/LpCodeDisplay.vue.js +1 -1
  42. package/dist/components/LpCommandPalette.vue.js +1 -1
  43. package/dist/components/LpConfirmDialog.vue.js +1 -1
  44. package/dist/components/LpConfirmHost.vue.js +1 -1
  45. package/dist/components/LpCopyButton.vue.js +1 -1
  46. package/dist/components/LpDataList.vue.js +1 -1
  47. package/dist/components/LpDock.vue.js +1 -1
  48. package/dist/components/LpDrawer.vue.js +1 -1
  49. package/dist/components/LpFileTree.vue.js +1 -1
  50. package/dist/components/LpInspector.vue.js +1 -1
  51. package/dist/components/LpLightbox.vue.js +1 -1
  52. package/dist/components/LpLogViewer.vue.js +1 -1
  53. package/dist/components/LpMediaGallery.vue.js +1 -1
  54. package/dist/components/LpModal.vue.js +1 -1
  55. package/dist/components/LpModalNav.vue.js +1 -1
  56. package/dist/components/LpNotificationBell.vue.js +1 -1
  57. package/dist/components/LpNotificationFeed.vue.js +1 -1
  58. package/dist/components/LpPageScrollbar.vue.js +1 -1
  59. package/dist/components/LpPaletteTheme.vue.js +1 -1
  60. package/dist/components/LpPasswordInput.vue.js +1 -1
  61. package/dist/components/LpPie.vue.js +1 -1
  62. package/dist/components/LpPipeline.vue.js +1 -1
  63. package/dist/components/LpRelativeTime.vue.js +1 -1
  64. package/dist/components/LpScrollArea.vue.js +1 -1
  65. package/dist/components/LpSecretField.vue.js +1 -1
  66. package/dist/components/LpShortcutsDialog.vue.js +1 -1
  67. package/dist/components/LpSidebar.vue.js +1 -1
  68. package/dist/components/LpSidebarNav.vue.js +1 -1
  69. package/dist/components/LpTable.vue.js +38 -12
  70. package/dist/components/LpThemeStudio.vue.js +1 -1
  71. package/dist/components/LpThemeSwitcher.vue.js +1 -1
  72. package/dist/components/LpToaster.vue.js +1 -1
  73. package/dist/components/LpTooltip.vue.d.ts +3 -3
  74. package/dist/components/LpTooltip.vue.js +1 -1
  75. package/dist/components/LpTruncate.vue.js +1 -1
  76. package/dist/components/LpUptimeBar.vue.js +1 -1
  77. package/dist/components/LpWorkspaceSwitcher.vue.js +1 -1
  78. package/dist/components/RevealToggle.vue.js +1 -1
  79. package/dist/components/tableFeatures.d.ts +1 -0
  80. package/dist/components/tableFeatures.js +25 -0
  81. package/dist/composables/useShift.js +28 -11
  82. package/dist/index.d.ts +2 -0
  83. package/dist/index.js +36 -34
  84. package/package.json +1 -1
  85. package/src/components/LpPageScrollbar.vue +14 -1
  86. package/src/components/LpScrollArea.vue +18 -1
  87. package/src/components/LpTable.vue +76 -4
  88. package/src/components/LpTooltip.vue +153 -14
  89. package/src/components/tableFeatures.ts +36 -0
  90. package/src/composables/useShift.ts +45 -15
  91. package/src/index.ts +2 -0
  92. package/dist/chunks/LpTooltip-BKde8Xjv.js +0 -230
@@ -1,7 +1,7 @@
1
1
  import { defineComponent, computed, useAttrs, ref, shallowRef, watch, onMounted, useSlots, onBeforeUnmount, useId, h, openBlock, createElementBlock, normalizeClass, createVNode, withCtx, renderSlot, createCommentVNode, createBlock, createTextVNode, toDisplayString, Fragment, renderList, createElementVNode, Transition, mergeProps, normalizeStyle, withModifiers, TransitionGroup, unref, nextTick } from "vue";
2
2
  import { usePaletteScope, suppressPaletteDiscovery, usePaletteCommands } from "../composables/usePaletteCommands.js";
3
3
  import { menuPaletteCommands } from "./commandPalette.js";
4
- import { _ as _sfc_main$1 } from "../chunks/LpDataList-B56Gu6Yt.js";
4
+ import { _ as _sfc_main$1 } from "../chunks/LpDataList-Cit9fOSp.js";
5
5
  import { _ as _sfc_main$d } from "../chunks/LpCheckbox-C9VnPIQf.js";
6
6
  import { _ as _sfc_main$c } from "../chunks/LpContextMenu-DEFirphS.js";
7
7
  import { _ as _sfc_main$g } from "../chunks/LpEditable-DfRUAGkA.js";
@@ -11,7 +11,7 @@ import { _ as _sfc_main$5 } from "../chunks/LpIcon-CxHFPwKt.js";
11
11
  import { _ as _sfc_main$e } from "../chunks/LpSkeleton-leTl53V9.js";
12
12
  import { _ as _sfc_main$h } from "../chunks/LpPagination-DGLntZJN.js";
13
13
  import { _ as _sfc_main$6 } from "../chunks/LpPopover-w2rr4x7n.js";
14
- import { _ as _sfc_main$b } from "../chunks/LpScrollArea-CqvwfZs2.js";
14
+ import { _ as _sfc_main$b } from "../chunks/LpScrollArea-B_1KZOSS.js";
15
15
  import { _ as _sfc_main$9 } from "../chunks/LpRollingText-B54Pje7P.js";
16
16
  import { _ as _sfc_main$2 } from "../chunks/LpShift-DAgS_d2x.js";
17
17
  import { _ as _sfc_main$7 } from "../chunks/LpTableFilter-CILHffC1.js";
@@ -25,16 +25,17 @@ import { usePaintSelect } from "../composables/usePaintSelect.js";
25
25
  import { useRovingFocus } from "../composables/useRovingFocus.js";
26
26
  import { useLabels } from "../composables/useLpConfig.js";
27
27
  import { useResizeObserver } from "../composables/useResizeObserver.js";
28
- import { orderPinned, pinOffsets, groupRows, rowWindow, presetHidden, sortByKeys, nextSorts, clampColumnWidth } from "./tableFeatures.js";
28
+ import { orderPinned, pinOffsets, groupRows, rowWindow, rowChanges, presetHidden, sortByKeys, nextSorts, clampColumnWidth } from "./tableFeatures.js";
29
29
  import { devWarn } from "../composables/devWarn.js";
30
30
  import { startPointerDrag } from "../composables/pointerDrag.js";
31
- import { isHTMLElement, nodeWindow } from "../composables/realm.js";
31
+ import { nodeWindow, isHTMLElement } from "../composables/realm.js";
32
+ import { prefersReducedMotion } from "../composables/easing.js";
32
33
  import { _ as _sfc_main$4 } from "../chunks/LpButton-BvVoF-PO.js";
33
34
  import { toCsv, downloadFile, fileStem } from "./exportData.js";
34
35
  import { toHiddenSets, sameHidden, summarizeShown, parseTableState, mergeRestored, serializeTableState, fromHiddenSets } from "./tableFilter.js";
35
36
  import { resolveRowMenu } from "./tableRowMenu.js";
36
37
  import { _ as _export_sfc } from "../chunks/_plugin-vue_export-helper-1tPrXgE0.js";
37
- import '../assets/LpTable-DxnthVz4.css';const _hoisted_1 = {
38
+ import '../assets/LpTable-BJjNEvgJ.css';const _hoisted_1 = {
38
39
  key: 0,
39
40
  class: "flex min-h-8 flex-wrap items-center gap-x-3 gap-y-2 pb-3 text-xs"
40
41
  };
@@ -1044,6 +1045,29 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1044
1045
  emit("rowClick", row);
1045
1046
  }
1046
1047
  });
1048
+ const ROW_MOTION_LIMIT = 12;
1049
+ const rowsEl = shallowRef(null);
1050
+ function setRowsEl(el) {
1051
+ const node = el?.$el ?? null;
1052
+ rowsEl.value = node;
1053
+ roving.setContainer(node);
1054
+ }
1055
+ const motionIds = computed(() => (headerless.value ? allEntries.value : viewEntries.value).map((entry) => entry.id));
1056
+ let shownIds = [];
1057
+ const bigSwap = computed(() => rowChanges(shownIds, motionIds.value) > ROW_MOTION_LIMIT);
1058
+ let settleFlip = false;
1059
+ watch(
1060
+ motionIds,
1061
+ (ids) => {
1062
+ const eased = bigSwap.value && shownIds.length > 0 && ids.length > 0;
1063
+ shownIds = ids;
1064
+ const body = rowsEl.value;
1065
+ if (!eased || !body || props.virtualRowHeight || prefersReducedMotion(nodeWindow(body))) return;
1066
+ settleFlip = !settleFlip;
1067
+ body.dataset.lpSettle = settleFlip ? "a" : "b";
1068
+ },
1069
+ { flush: "post", immediate: true }
1070
+ );
1047
1071
  function onRowKeydown(event) {
1048
1072
  if (event.key === " " && props.selectable && roving.activeId.value) {
1049
1073
  event.preventDefault();
@@ -1437,10 +1461,11 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1437
1461
  createVNode(TransitionGroup, {
1438
1462
  tag: "tbody",
1439
1463
  name: "lp-row",
1440
- css: animating.value && !__props.virtualRowHeight,
1464
+ css: animating.value && !__props.virtualRowHeight && !bigSwap.value,
1465
+ "move-class": bigSwap.value ? "lp-row-still" : void 0,
1441
1466
  class: normalizeClass(busy.value && displayRows.value.length ? "opacity-60 transition-opacity" : "transition-opacity"),
1442
1467
  "aria-busy": busy.value || void 0,
1443
- ref: (el) => unref(roving).setContainer(el?.$el ?? null)
1468
+ ref: setRowsEl
1444
1469
  }, {
1445
1470
  default: withCtx(() => [
1446
1471
  displayRows.value.length === 0 && busy.value ? (openBlock(true), createElementBlock(Fragment, { key: 0 }, renderList(__props.skeletonRows, (n) => {
@@ -1647,7 +1672,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1647
1672
  ])) : createCommentVNode("", true)
1648
1673
  ]),
1649
1674
  _: 3
1650
- }, 8, ["css", "class", "aria-busy"])
1675
+ }, 8, ["css", "move-class", "class", "aria-busy"])
1651
1676
  ], 14, _hoisted_11)
1652
1677
  ]),
1653
1678
  _: 3
@@ -1669,7 +1694,8 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1669
1694
  key: activeLayout.value,
1670
1695
  tag: "div",
1671
1696
  name: "lp-row",
1672
- css: animating.value
1697
+ css: animating.value && !bigSwap.value,
1698
+ "move-class": bigSwap.value ? "lp-row-still" : void 0
1673
1699
  }, scrollAttrs.value, {
1674
1700
  class: ["min-h-0 transition-opacity", [
1675
1701
  busy.value && displayRows.value.length ? "opacity-60" : "",
@@ -1677,7 +1703,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1677
1703
  ]],
1678
1704
  style: activeLayout.value === "cards" ? { gridTemplateColumns: `repeat(auto-fill, minmax(min(${__props.cardWidth}, 100%), 1fr))` } : void 0,
1679
1705
  "aria-busy": busy.value || void 0,
1680
- ref: (el) => unref(roving).setContainer(el?.$el ?? null)
1706
+ ref: setRowsEl
1681
1707
  }), {
1682
1708
  default: withCtx(() => [
1683
1709
  displayRows.value.length === 0 && busy.value ? (openBlock(true), createElementBlock(Fragment, { key: 0 }, renderList(__props.skeletonRows, (n) => {
@@ -1896,7 +1922,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1896
1922
  }), 128))
1897
1923
  ]),
1898
1924
  _: 3
1899
- }, 16, ["css", "class", "style", "aria-busy"]))
1925
+ }, 16, ["css", "move-class", "class", "style", "aria-busy"]))
1900
1926
  ]),
1901
1927
  _: 3
1902
1928
  }, 8, ["items", "no-hint", "title", "subtitle", "title-icon", "title-image"]))
@@ -1915,7 +1941,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1915
1941
  };
1916
1942
  }
1917
1943
  });
1918
- const LpTable = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-a346ca36"]]);
1944
+ const LpTable = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-a366adee"]]);
1919
1945
  export {
1920
1946
  LpTable as default
1921
1947
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpThemeStudio-BPPctXgd.js";
1
+ import { _ as _sfc_main } from "../chunks/LpThemeStudio-yiEdFJQL.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpThemeSwitcher-B3c44xFH.js";
1
+ import { _ as _sfc_main } from "../chunks/LpThemeSwitcher-DKkCFaFk.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpToaster-Cdx1tnHB.js";
1
+ import { _ as _sfc_main } from "../chunks/LpToaster-C7hHkGOA.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -46,11 +46,11 @@ type __VLS_Props = {
46
46
  */
47
47
  touch?: "hold" | "none";
48
48
  };
49
- declare var __VLS_24: {}, __VLS_41: {};
49
+ declare var __VLS_9: {}, __VLS_50: {};
50
50
  type __VLS_Slots = {} & {
51
- default?: (props: typeof __VLS_24) => any;
51
+ default?: (props: typeof __VLS_9) => any;
52
52
  } & {
53
- content?: (props: typeof __VLS_41) => any;
53
+ content?: (props: typeof __VLS_50) => any;
54
54
  };
55
55
  declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
56
56
  touch: "hold" | "none";
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpTooltip-BKde8Xjv.js";
1
+ import { _ as _sfc_main } from "../chunks/LpTooltip-Bt1ZFViJ.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpTruncate-C_C8eiIE.js";
1
+ import { _ as _sfc_main } from "../chunks/LpTruncate-DEHWL1OA.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpUptimeBar-Dt6SY_-m.js";
1
+ import { _ as _sfc_main } from "../chunks/LpUptimeBar-CXgzU5ym.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpWorkspaceSwitcher-DCIiOQt_.js";
1
+ import { _ as _sfc_main } from "../chunks/LpWorkspaceSwitcher-CrUVSI7x.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/RevealToggle-BsYxY6Oz.js";
1
+ import { _ as _sfc_main } from "../chunks/RevealToggle-D9Vs88kM.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -65,3 +65,4 @@ export declare function presetHidden(preset: Pick<ColumnPreset, "columns">, colu
65
65
  * - `list`: one compact line per row with no header, for narrow places.
66
66
  */
67
67
  export type TableLayout = "table" | "cards" | "list";
68
+ export declare function rowChanges(before: readonly string[], after: readonly string[]): number;
@@ -74,6 +74,30 @@ function presetHidden(preset, columns) {
74
74
  const hidden = columns.filter((c) => !c.alwaysVisible && !shown.has(c.key)).map((c) => c.key);
75
75
  return hidden.length === columns.length ? /* @__PURE__ */ new Set() : new Set(hidden);
76
76
  }
77
+ function rowChanges(before, after) {
78
+ const old = /* @__PURE__ */ new Map();
79
+ before.forEach((id, i) => old.set(id, i));
80
+ let entered = 0;
81
+ const kept = [];
82
+ for (const id of after) {
83
+ const at = old.get(id);
84
+ if (at === void 0) entered++;
85
+ else kept.push(at);
86
+ }
87
+ const left = before.length - kept.length;
88
+ const tails = [];
89
+ for (const at of kept) {
90
+ let lo = 0;
91
+ let hi = tails.length;
92
+ while (lo < hi) {
93
+ const mid = lo + hi >> 1;
94
+ if (tails[mid] < at) lo = mid + 1;
95
+ else hi = mid;
96
+ }
97
+ tails[lo] = at;
98
+ }
99
+ return entered + left + (kept.length - tails.length);
100
+ }
77
101
  export {
78
102
  MIN_COLUMN_PX,
79
103
  clampColumnWidth,
@@ -82,6 +106,7 @@ export {
82
106
  orderPinned,
83
107
  pinOffsets,
84
108
  presetHidden,
109
+ rowChanges,
85
110
  rowWindow,
86
111
  sortByKeys
87
112
  };
@@ -1,6 +1,23 @@
1
1
  import { ref, watch, toValue, onBeforeUnmount, nextTick } from "vue";
2
2
  import { nodeWindow, isElement } from "./realm.js";
3
3
  import { prefersReducedMotion } from "./easing.js";
4
+ const firstReads = /* @__PURE__ */ new Map();
5
+ function firstRead(win, pass) {
6
+ let queue = firstReads.get(win);
7
+ if (!queue) {
8
+ const batch2 = /* @__PURE__ */ new Set();
9
+ queue = batch2;
10
+ firstReads.set(win, batch2);
11
+ win.requestAnimationFrame(() => {
12
+ firstReads.delete(win);
13
+ for (const p of batch2) p.read();
14
+ for (const p of batch2) p.write();
15
+ });
16
+ }
17
+ queue.add(pass);
18
+ const batch = queue;
19
+ return () => batch.delete(pass);
20
+ }
4
21
  const TWEEN_MS = 160;
5
22
  const RETARGET_AFTER_MS = 40;
6
23
  function useShift(options = {}) {
@@ -9,7 +26,7 @@ function useShift(options = {}) {
9
26
  const resizing = ref(false);
10
27
  let ro;
11
28
  let mo;
12
- let raf = 0;
29
+ let cancelFirstRead;
13
30
  let settleTimer;
14
31
  let lastHeight = 0;
15
32
  let lastWidth = 0;
@@ -95,7 +112,8 @@ function useShift(options = {}) {
95
112
  mo?.disconnect();
96
113
  ro = void 0;
97
114
  mo = void 0;
98
- cancelAnimationFrame(raf);
115
+ cancelFirstRead?.();
116
+ cancelFirstRead = void 0;
99
117
  clearTimeout(settleTimer);
100
118
  tweening.value = false;
101
119
  resizing.value = false;
@@ -109,17 +127,16 @@ function useShift(options = {}) {
109
127
  ([node, enabled]) => {
110
128
  teardown();
111
129
  if (!enabled || !node) return;
112
- lastHeight = node.offsetHeight;
113
- lastWidth = node.offsetWidth;
114
- laidOut = node.getClientRects().length > 0;
115
130
  scrollHost = node;
116
131
  node.addEventListener("scroll", rememberScroll, { capture: true, passive: true });
117
- if (wantsHeight()) node.style.height = `${lastHeight}px`;
118
- if (wantsWidth()) node.style.width = `${lastWidth}px`;
119
- raf = requestAnimationFrame(() => {
120
- tweening.value = true;
121
- if (wantsHeight()) node.style.height = "";
122
- if (wantsWidth()) node.style.width = "";
132
+ cancelFirstRead = firstRead(nodeWindow(node), {
133
+ read: () => {
134
+ if (inFlight) return;
135
+ lastHeight = node.offsetHeight;
136
+ lastWidth = node.offsetWidth;
137
+ laidOut = node.getClientRects().length > 0;
138
+ },
139
+ write: () => tweening.value = true
123
140
  });
124
141
  const watchChildren = () => {
125
142
  if (!ro) return;
package/dist/index.d.ts CHANGED
@@ -196,6 +196,8 @@ export type { SortState, TableColumn, TableListMode, TablePage, TableQuery } fro
196
196
  export type { TableLabels } from "./components/LpTable.vue";
197
197
  export type { TableCellEdit, TableExportOptions, TableSelectionToken } from "./components/LpTable.vue";
198
198
  export type { ColumnPin, ColumnPreset, TableLayout } from "./components/tableFeatures";
199
+ export { rowWindow } from "./components/tableFeatures";
200
+ export type { RowWindow } from "./components/tableFeatures";
199
201
  export { compareValues } from "./components/tableSort";
200
202
  export { filterRows, showOnly, shownValues } from "./components/tableFilter";
201
203
  export type { TableFilters } from "./components/tableFilter";
package/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { _ } from "./chunks/LayoutCanvas-j4TZqx1A.js";
2
- import { _ as _2 } from "./chunks/LpAddressInput-BCqR0zmv.js";
2
+ import { _ as _2 } from "./chunks/LpAddressInput-C5VIgRp0.js";
3
3
  import { _ as _3 } from "./chunks/LpMapPicker-V0Lybi10.js";
4
4
  import { OSM_TILES, createNominatimProvider, emptyAddress, formatAddress, project, tileUrl, unproject } from "./components/address.js";
5
5
  import { _ as _4 } from "./chunks/LpActionBar-43WHmTsM.js";
6
6
  import { _ as _5 } from "./chunks/LpAlert-Q0h7Gucu.js";
7
- import { _ as _6 } from "./chunks/LpAppShell-DlpQierx.js";
7
+ import { _ as _6 } from "./chunks/LpAppShell-C4b3ySa_.js";
8
8
  import { _ as _7 } from "./chunks/LpAutocomplete-YPxAPHEo.js";
9
9
  import { _ as _8 } from "./chunks/LpAvatar-CfLBqlY1.js";
10
10
  import { _ as _9 } from "./chunks/LpBadge-Bh5roBr_.js";
@@ -19,7 +19,7 @@ import { chartView, lttbIndices, minMaxBuckets, panRange, slotOf, sourceOf, zoom
19
19
  import { useChartSync } from "./composables/useChartSync.js";
20
20
  import { createLinkedView, linkChart, linkLog, linkPipeline, linkTable, linkTopology, provideLinkedView, useLinkedView } from "./composables/useLinkedView.js";
21
21
  import { _ as _14 } from "./chunks/LpCheckbox-C9VnPIQf.js";
22
- import { _ as _15 } from "./chunks/LpCodeBlock-BgWcg2mk.js";
22
+ import { _ as _15 } from "./chunks/LpCodeBlock-DTU40x8P.js";
23
23
  import { tokenizeLines } from "./components/codeHighlight.js";
24
24
  import { _ as _16 } from "./chunks/LpColorPicker-KGMG8QEh.js";
25
25
  import { DEFAULT_SWATCHES, clampHsv, formatColor, hsvToHsl, hsvToRgb, isLight, normalizeHue, opaqueCss, parseColor, parseHex, rgbToHex, rgbToHsv } from "./components/color.js";
@@ -33,8 +33,8 @@ import { default as default6 } from "./components/LpPaletteStats.vue.js";
33
33
  import { default as default7 } from "./components/LpPaletteSwatches.vue.js";
34
34
  import { default as default8 } from "./components/LpKeys.vue.js";
35
35
  import { comboKeys, comboSteps } from "./components/keys.js";
36
- import { _ as _18 } from "./chunks/LpConfirmDialog-y6k0ryWR.js";
37
- import { _ as _19 } from "./chunks/LpConfirmHost-D-X0tDym.js";
36
+ import { _ as _18 } from "./chunks/LpConfirmDialog-DrHcIxRn.js";
37
+ import { _ as _19 } from "./chunks/LpConfirmHost-D0Cd8FA_.js";
38
38
  import { confirm, useConfirm } from "./components/confirm.js";
39
39
  import { _ as _20 } from "./chunks/LpContextMenu-DEFirphS.js";
40
40
  import { _ as _21 } from "./chunks/LpDatePicker-Dlyvgsan.js";
@@ -43,11 +43,11 @@ import { _ as _23 } from "./chunks/LpRangeCalendar-9BvaLZji.js";
43
43
  import { clampRange, dayAllowed, defaultDatePresets, defaultRangePresets, formatRange, normalizeRange, sameRange } from "./components/dateRange.js";
44
44
  import { _ as _24 } from "./chunks/LpDisclosure-D4JZ-o8K.js";
45
45
  import { _ as _25 } from "./chunks/LpDivider-CUX46KR0.js";
46
- import { _ as _26 } from "./chunks/LpDrawer-BkWjFdV0.js";
46
+ import { _ as _26 } from "./chunks/LpDrawer-BHlqi62K.js";
47
47
  import { _ as _27 } from "./chunks/LpDropdownMenu-CdCe7D2d.js";
48
48
  import { default as default9 } from "./components/LpDropzone.vue.js";
49
49
  import { _ as _28 } from "./chunks/LpEmptyState-DLOm07KH.js";
50
- import { _ as _29 } from "./chunks/LpFileTree-BN_zybBN.js";
50
+ import { _ as _29 } from "./chunks/LpFileTree-gVjF_b6r.js";
51
51
  import { ancestorIds, checkStateOf, checkedCount, computeStats, fileIcon, formatModified, formatSize, sortNodes, subtreeIds, subtreeSize } from "./components/fileTree.js";
52
52
  import { _ as _30 } from "./chunks/LpFieldNotice-DUPo1Yii.js";
53
53
  import { _ as _31 } from "./chunks/LpFormField-jbM7K1Hg.js";
@@ -56,25 +56,25 @@ import { i, b, a, c } from "./chunks/icons-C8ccRCKw.js";
56
56
  import { default as default10 } from "./components/LpImageEditor.vue.js";
57
57
  import { _ as _33 } from "./chunks/LpInfraNode-tD5x5YOy.js";
58
58
  import { _ as _34 } from "./chunks/LpInput-DGMpxrcg.js";
59
- import { _ as _35 } from "./chunks/LpLightbox-DPgz7IsT.js";
59
+ import { _ as _35 } from "./chunks/LpLightbox--EwvQGe-.js";
60
60
  import { fileNameOf, isVideoItem } from "./components/lightbox.js";
61
61
  import { _ as _36 } from "./chunks/LpLink-Krkjhe_y.js";
62
- import { _ as _37 } from "./chunks/LpLogViewer-BuIzr_6h.js";
62
+ import { _ as _37 } from "./chunks/LpLogViewer-DSq9e8mT.js";
63
63
  import { _ as _38 } from "./chunks/LpLogInspector-BhJTJIst.js";
64
64
  import { compilePattern, findIds, messageShape, structuredPayload, timeMs } from "./components/logTools.js";
65
65
  import { countLevels, matchPositions, matchesTerm, ringWindow, stepMatch } from "./components/logSearch.js";
66
- import { _ as _39 } from "./chunks/LpMediaGallery-CTejKF2-.js";
67
- import { _ as _40 } from "./chunks/LpModal-D1E7rQzw.js";
68
- import { _ as _41 } from "./chunks/LpModalNav-BFjiHWbr.js";
69
- import { _ as _42 } from "./chunks/LpNotificationBell-KRgdRZs-.js";
70
- import { _ as _43 } from "./chunks/LpNotificationFeed-CMGYU8c-.js";
66
+ import { _ as _39 } from "./chunks/LpMediaGallery-DlYJf3DS.js";
67
+ import { _ as _40 } from "./chunks/LpModal-COMHrBKL.js";
68
+ import { _ as _41 } from "./chunks/LpModalNav-Cj02cfhL.js";
69
+ import { _ as _42 } from "./chunks/LpNotificationBell-DNGijtmW.js";
70
+ import { _ as _43 } from "./chunks/LpNotificationFeed-BoovbtfL.js";
71
71
  import { _ as _44 } from "./chunks/LpNumberField-C0LSu-tl.js";
72
72
  import { _ as _45 } from "./chunks/LpTimeField-Dio7Tj9A.js";
73
73
  import { _ as _46 } from "./chunks/LpNumberFlow-Vqj0tP7B.js";
74
74
  import { _ as _47 } from "./chunks/LpOtpInput-C14XagVB.js";
75
75
  import { _ as _48 } from "./chunks/LpPagination-DGLntZJN.js";
76
- import { _ as _49 } from "./chunks/LpPasswordInput-LPVI7D3v.js";
77
- import { _ as _50 } from "./chunks/LpPie-CsiBXe-e.js";
76
+ import { _ as _49 } from "./chunks/LpPasswordInput-BBDz2zyU.js";
77
+ import { _ as _50 } from "./chunks/LpPie-CtC4XdhC.js";
78
78
  import { _ as _51 } from "./chunks/LpPhoneInput-BdglZsGH.js";
79
79
  import { filterCountries, flagEmoji, loadCountries, matchCountryByValue } from "./components/countries.js";
80
80
  import { _ as _52 } from "./chunks/LpPopover-w2rr4x7n.js";
@@ -82,9 +82,9 @@ import { _ as _53 } from "./chunks/LpProgress-D_MSjowb.js";
82
82
  import { _ as _54 } from "./chunks/LpRadio-4KyxeSz4.js";
83
83
  import { _ as _55 } from "./chunks/LpRadioGroup-Cr_fmrBY.js";
84
84
  import { _ as _56 } from "./chunks/LpRollingText-B54Pje7P.js";
85
- import { _ as _57 } from "./chunks/LpScrollArea-CqvwfZs2.js";
85
+ import { _ as _57 } from "./chunks/LpScrollArea-B_1KZOSS.js";
86
86
  import { _ as _58 } from "./chunks/LpScrollStrip-D4tsOfAQ.js";
87
- import { _ as _59 } from "./chunks/LpPageScrollbar-C9sTeU-2.js";
87
+ import { _ as _59 } from "./chunks/LpPageScrollbar-BniFeL2U.js";
88
88
  import { _ as _60 } from "./chunks/LpSegmented-DzFYP0ZF.js";
89
89
  import { _ as _61 } from "./chunks/LpSection-CwsfNR7N.js";
90
90
  import { _ as _62 } from "./chunks/LpSelect-Dffg5r4I.js";
@@ -94,7 +94,7 @@ import { _ as _64 } from "./chunks/LpShift-DAgS_d2x.js";
94
94
  import { _ as _65 } from "./chunks/LpSwap-M7nFeW3C.js";
95
95
  import { _ as _66 } from "./chunks/LpHintExample-YzCXiSUp.js";
96
96
  import { default as default11 } from "./components/LpShortcutsDialog.vue.js";
97
- import { _ as _67 } from "./chunks/LpSidebar-BKhVDsMt.js";
97
+ import { _ as _67 } from "./chunks/LpSidebar-DPri9uyz.js";
98
98
  import { _ as _68 } from "./chunks/LpContextSwitcher-COw6GHPW.js";
99
99
  import { _ as _69 } from "./chunks/LpSkeleton-leTl53V9.js";
100
100
  import { _ as _70 } from "./chunks/LpSlider-DUd4gXnw.js";
@@ -112,6 +112,7 @@ import { _ as _76 } from "./chunks/LpStat-SyVb5JXm.js";
112
112
  import { _ as _77 } from "./chunks/LpStepper-DBNRA6Q2.js";
113
113
  import { _ as _78 } from "./chunks/LpSwitch-D-mcaA6C.js";
114
114
  import { default as default13 } from "./components/LpTable.vue.js";
115
+ import { rowWindow } from "./components/tableFeatures.js";
115
116
  import { compareValues } from "./components/tableSort.js";
116
117
  import { filterRows, showOnly, shownValues } from "./components/tableFilter.js";
117
118
  import { useTableFilterChoice } from "./composables/useTableFilterChoice.js";
@@ -122,10 +123,10 @@ import { _ as _80 } from "./chunks/LpBottomNav-9AqvZlRW.js";
122
123
  import { _ as _81 } from "./chunks/LpTagsInput-DD8-xe3F.js";
123
124
  import { addTags, splitTags } from "./components/tags.js";
124
125
  import { _ as _82 } from "./chunks/LpTextarea-CQg-dVxN.js";
125
- import { _ as _83 } from "./chunks/LpThemeSwitcher-B3c44xFH.js";
126
+ import { _ as _83 } from "./chunks/LpThemeSwitcher-DKkCFaFk.js";
126
127
  import { _ as _84 } from "./chunks/LpTilt-B7KHX1zH.js";
127
- import { _ as _85 } from "./chunks/LpToaster-Cdx1tnHB.js";
128
- import { _ as _86 } from "./chunks/LpTooltip-BKde8Xjv.js";
128
+ import { _ as _85 } from "./chunks/LpToaster-C7hHkGOA.js";
129
+ import { _ as _86 } from "./chunks/LpTooltip-Bt1ZFViJ.js";
129
130
  import { _ as _87 } from "./chunks/LpTour-BGUKUxSZ.js";
130
131
  import { holeOf, openContextMenu, panelsAround } from "./components/tour.js";
131
132
  import { _ as _88 } from "./chunks/LpHintLayer-nuMqfzvy.js";
@@ -134,13 +135,13 @@ import { HINT_SIGNALS, contextMenuTip, createHintSystem, dropzoneTip, hintKey, h
134
135
  import { _ as _90 } from "./chunks/LpTooltipProvider-CftjV7Xc.js";
135
136
  import { _ as _91 } from "./chunks/LpTopologyCanvas-B1YFfMaq.js";
136
137
  import { clusterGraph, diffTopology, findPath, layeredLayout, neighborhood, topologyToSvg } from "./components/topologyGraph.js";
137
- import { _ as _92 } from "./chunks/LpUptimeBar-Dt6SY_-m.js";
138
+ import { _ as _92 } from "./chunks/LpUptimeBar-CXgzU5ym.js";
138
139
  import { addLeaf, addTab, countBlocks, countLeaves, deserializeLayout, makeLayout, moveLeaf, removeBlock, removeLeaf, reorderTab, resizeAt, serializeLayout, setActiveTab } from "./layout/tree.js";
139
140
  import { blockTitle, defineBlocks } from "./layout/registry.js";
140
141
  import { layoutController, useLayout } from "./layout/useLayout.js";
141
142
  import { useWorkspaces } from "./layout/useWorkspaces.js";
142
- import { _ as _93 } from "./chunks/LpWorkspaceSwitcher-DCIiOQt_.js";
143
- import { _ as _94 } from "./chunks/LpInspector-oTfkOdYd.js";
143
+ import { _ as _93 } from "./chunks/LpWorkspaceSwitcher-CrUVSI7x.js";
144
+ import { _ as _94 } from "./chunks/LpInspector-DSL1lINx.js";
144
145
  import { _ as _95 } from "./chunks/LpWindow-DsPeiDCZ.js";
145
146
  import { WINDOW_DOCKS, clampRect, dockAt, dockedRect, isWindowRect, resizeRect, snapRect, useWindowLayer } from "./composables/windowing.js";
146
147
  import { THEME_COOKIE, applyTheme, applyThemeWithTransition, bootstrapTheme, cacheSystemThemes, normalizeTheme, parseTheme, prefersDarkScheme, revealTransition, serializeTheme, themeEntries, themeFromCookie, themeToCssRule, themeToCssVars, useTheme } from "./theme/useTheme.js";
@@ -192,14 +193,14 @@ import { useScrollStrip } from "./composables/useScrollStrip.js";
192
193
  import { useToast } from "./composables/useToast.js";
193
194
  import { _ as _96 } from "./chunks/LpThemeProvider-XK6ZeWAE.js";
194
195
  import { _ as _97 } from "./chunks/LpFrame-Bv5Fphte.js";
195
- import { _ as _98 } from "./chunks/LpThemeStudio-BPPctXgd.js";
196
+ import { _ as _98 } from "./chunks/LpThemeStudio-yiEdFJQL.js";
196
197
  import { _ as _99 } from "./chunks/LpThemeStudioSection-B7bt8Iyg.js";
197
198
  import { isInternalHref, resolveLink } from "./components/link.js";
198
199
  import { _ as _100 } from "./chunks/LpRepeater-C0rns2xW.js";
199
200
  import { _ as _101 } from "./chunks/LpGauge-KZaQ_FDR.js";
200
201
  import { _ as _102 } from "./chunks/LpQuota-BaCq5YOQ.js";
201
202
  import { _ as _103 } from "./chunks/LpStatus-C6GqSHJj.js";
202
- import { _ as _104 } from "./chunks/LpRelativeTime-CN5bWD73.js";
203
+ import { _ as _104 } from "./chunks/LpRelativeTime-9MHlbq6F.js";
203
204
  import { _ as _105 } from "./chunks/LpActivityLine-Ea-t5Vza.js";
204
205
  import { TONE_COLOR, TONE_SOFT, arcPath, capacityThresholds, meterFraction, toneAt } from "./components/meter.js";
205
206
  import { TONES, TONE_ALIASES, toneLook, toneOf } from "./components/tone.js";
@@ -207,7 +208,7 @@ import { formatAmount, quotaSegments, quotaState } from "./components/quota.js";
207
208
  import { compactNumber, decimalMark, formatBytes } from "./components/format.js";
208
209
  import { STATUS_LABELS, STATUS_LOOK } from "./components/status.js";
209
210
  import { formatRelativeTime } from "./components/relativeTime.js";
210
- import { _ as _106 } from "./chunks/LpPipeline-DVcoSs--.js";
211
+ import { _ as _106 } from "./chunks/LpPipeline-aCALaUY8.js";
211
212
  import { _ as _107 } from "./chunks/LpStepMark-CPGVj1XG.js";
212
213
  import { childProgress, connectorFilled, currentStepIndex, isSettled, overallStatus, stepElapsed } from "./components/pipeline.js";
213
214
  import { _ as _108 } from "./chunks/LpLoader-Bf1yZwaU.js";
@@ -216,12 +217,12 @@ import { useAppUpdate } from "./composables/useAppUpdate.js";
216
217
  import { useNow } from "./composables/useNow.js";
217
218
  import { _ as _109 } from "./chunks/LpCountdown-DVl7vXI1.js";
218
219
  import { DEFAULT_MILESTONES, DEFAULT_UNIT_LABELS, ceilToSecond, crossedMilestone, formatElapsed, formatTime, msToNextSecond, splitTime, timeParts } from "./components/timeSpan.js";
219
- import { _ as _110 } from "./chunks/LpCopyButton-BGzw95Xc.js";
220
- import { _ as _111 } from "./chunks/LpSecretField-BwrZ4TfT.js";
221
- import { _ as _112 } from "./chunks/LpDataList-B56Gu6Yt.js";
220
+ import { _ as _110 } from "./chunks/LpCopyButton-CiqUS4SX.js";
221
+ import { _ as _111 } from "./chunks/LpSecretField-C9vwMFQ6.js";
222
+ import { _ as _112 } from "./chunks/LpDataList-Cit9fOSp.js";
222
223
  import { _ as _113 } from "./chunks/LpEditable-DfRUAGkA.js";
223
- import { _ as _114 } from "./chunks/LpTruncate-C_C8eiIE.js";
224
- import { _ as _115 } from "./chunks/LpCodeDisplay-DBi1YPLp.js";
224
+ import { _ as _114 } from "./chunks/LpTruncate-DEHWL1OA.js";
225
+ import { _ as _115 } from "./chunks/LpCodeDisplay-F2hjLb0X.js";
225
226
  import { formatRemaining, groupCode, middleTruncate, normalizeCode } from "./components/fieldText.js";
226
227
  import { _ as _116 } from "./chunks/LpHoverCard-DYguY2AP.js";
227
228
  import { _ as _117 } from "./chunks/LpIdenticon-Dbbr4fvt.js";
@@ -251,7 +252,7 @@ import { _ as _129 } from "./chunks/LpCommandScope-DRUvHiX7.js";
251
252
  import { createPaletteRegistry, mergePaletteCommands, usePaletteCommands, vPalette } from "./composables/usePaletteCommands.js";
252
253
  import { commandShortcuts, commandToHotkey, commandToMenuItem, commandToPalette, useCommands } from "./composables/useCommands.js";
253
254
  import { usePaletteTheme } from "./theme/palette.js";
254
- import { _ as _130 } from "./chunks/LpPaletteTheme-CLwO-SDf.js";
255
+ import { _ as _130 } from "./chunks/LpPaletteTheme-_FwkJO2o.js";
255
256
  const uk = {
256
257
  actionBar: { dismiss: "Закрити" },
257
258
  activityLine: { busy: "Завантаження" },
@@ -1245,6 +1246,7 @@ export {
1245
1246
  rgbToHsv,
1246
1247
  ringWindow,
1247
1248
  rose,
1249
+ rowWindow,
1248
1250
  rubberBand,
1249
1251
  rubberClamp,
1250
1252
  runningHintSystem,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leavepulse/ui",
3
- "version": "0.94.1",
3
+ "version": "0.95.1",
4
4
  "type": "module",
5
5
  "description": "LeavePulse token-driven Vue component kit.",
6
6
  "repository": {
@@ -75,9 +75,22 @@ function measure() {
75
75
  const height = Math.max(MIN_THUMB_PX, Math.round((track * view.clientHeight) / view.scrollHeight))
76
76
  thumb.value = { height, top: Math.round(((track - height) * view.scrollTop) / range) }
77
77
  }
78
+ /*
79
+ * At most once a frame, and never in the middle of someone's render. A scroll
80
+ * or a resize of the window asks for the next frame. A change of the page's
81
+ * size is measured where the observer reports it, which is after the browser
82
+ * has laid the page out: the sizes are read for free there. Put off to the
83
+ * next frame instead, the read landed after whatever that frame had changed
84
+ * first (a list's rows starting their entrance) and laid the page out again
85
+ * for it, the cost of a whole table inside the frame that showed it.
86
+ */
78
87
  function measureSoon() {
79
88
  if (!frame && doc) frame = nodeWindow(doc).requestAnimationFrame(measure)
80
89
  }
90
+ function measureLaidOut() {
91
+ if (frame && doc) nodeWindow(doc).cancelAnimationFrame(frame)
92
+ measure()
93
+ }
81
94
 
82
95
  function onScroll() {
83
96
  measureSoon()
@@ -146,7 +159,7 @@ onMounted(() => {
146
159
  const win = nodeWindow(doc)
147
160
  win.addEventListener("scroll", onScroll, { passive: true })
148
161
  win.addEventListener("resize", measureSoon)
149
- sizes = new win.ResizeObserver(measureSoon)
162
+ sizes = new win.ResizeObserver(measureLaidOut)
150
163
  sizes.observe(doc.body)
151
164
  if (host) sizes.observe(host)
152
165
  measure()
@@ -155,8 +155,12 @@ function onViewportScroll(event: Event) {
155
155
  // state has to track content size even where the fade is off.
156
156
  let edgeResize: ResizeObserver | undefined
157
157
  let edgeMutation: MutationObserver | undefined
158
+ let edgeFrame = 0
159
+ let edgeWindow: Window | undefined
158
160
 
159
161
  function stopEdgeWatch() {
162
+ if (edgeFrame) edgeWindow?.cancelAnimationFrame(edgeFrame)
163
+ edgeFrame = 0
160
164
  edgeResize?.disconnect()
161
165
  edgeMutation?.disconnect()
162
166
  edgeResize = undefined
@@ -187,6 +191,14 @@ watch(
187
191
  // Rows arriving after the first paint are the common case — a list waiting
188
192
  // on a query renders empty, and observing only the boxes that existed then
189
193
  // left the fade off until something else happened to trigger a measure.
194
+ //
195
+ // Measured in the next frame, not here: a mutation is reported straight
196
+ // after the render that made it, before the browser has laid anything out,
197
+ // so reading the sizes here laid out fifty fresh table rows inside the
198
+ // click that brought them. The resize observer, which reports after layout,
199
+ // usually measures first anyway; the frame covers a change that resized
200
+ // nothing it watches.
201
+ edgeWindow = win
190
202
  if (win.MutationObserver) {
191
203
  edgeMutation = new win.MutationObserver(() => {
192
204
  if (edgeResize) {
@@ -194,7 +206,12 @@ watch(
194
206
  edgeResize.observe(el)
195
207
  for (const child of Array.from(el.children)) edgeResize.observe(child)
196
208
  }
197
- remeasure()
209
+ if (!edgeFrame) {
210
+ edgeFrame = win.requestAnimationFrame(() => {
211
+ edgeFrame = 0
212
+ remeasure()
213
+ })
214
+ }
198
215
  })
199
216
  edgeMutation.observe(el, { childList: true, subtree: true })
200
217
  }