@jsenv/navi 0.29.12 → 0.29.14

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.
@@ -12042,6 +12042,13 @@ const mergeActionParams = (currentParams, newParams) => {
12042
12042
  if (currentParams === NO_PARAMS) {
12043
12043
  return newParams;
12044
12044
  }
12045
+ if (newParams === undefined) {
12046
+ // Every control binds its action to its UI state signal; a control that carries
12047
+ // no value (a button) has an undefined one. It contributes no params, which must
12048
+ // not be confused with "the params are undefined" — the params bound by
12049
+ // bindParams stay in place.
12050
+ return currentParams;
12051
+ }
12045
12052
  return mergeTwoJsValues(currentParams, newParams);
12046
12053
  };
12047
12054
 
@@ -37767,7 +37774,16 @@ const FIXED_BAR_SPACE_CSS = /* css */ `
37767
37774
  // they overlap: the room to give back is the largest of them, not their sum,
37768
37775
  // and one leaving must leave the others' room in place.
37769
37776
  const sizeMapByArea = new Map();
37770
-
37777
+ // What is currently on <html> for each area (absent = the variable is not set).
37778
+ // Writing the value that is already there would invalidate layout for nothing,
37779
+ // and several bars sharing an edge means most calls compute the same largest
37780
+ // size again — the ones for the smaller bars, and the second half of a page
37781
+ // transition.
37782
+ const writtenValueByArea = new Map();
37783
+
37784
+ // A size that must land before the next paint: a render changed the bar, so
37785
+ // the room it takes is given back in that same commit and the content is never
37786
+ // painted under it.
37771
37787
  /**
37772
37788
  * @param {"top"|"bottom"|"left"|"right"} area
37773
37789
  * @param {Element} barElement - Which bar this size belongs to.
@@ -37775,6 +37791,65 @@ const sizeMapByArea = new Map();
37775
37791
  * content.
37776
37792
  */
37777
37793
  const setFixedBarSpace = (area, barElement, size) => {
37794
+ dropPendingSize(area, barElement);
37795
+ storeSize(area, barElement, size);
37796
+ writeSpace(area);
37797
+ };
37798
+
37799
+ // A size nothing asked for, coming from a ResizeObserver. Writing the variable
37800
+ // resizes an ANCESTOR of the bars — the scroll container takes its padding
37801
+ // from it — and mutating layout from inside a resize callback is what makes
37802
+ // the browser report "ResizeObserver loop completed with undelivered
37803
+ // notifications". So the write waits for the frame that resize produced.
37804
+ // Queued here rather than deferred by each bar on its own, so the bars sharing
37805
+ // an edge resolve to a single write instead of one per bar.
37806
+ /**
37807
+ * @param {"top"|"bottom"|"left"|"right"} area
37808
+ * @param {Element} barElement - Which bar this size belongs to.
37809
+ * @param {number} size - In px.
37810
+ */
37811
+ const requestFixedBarSpace = (area, barElement, size) => {
37812
+ let pendingSizeMap = pendingSizeMapByArea.get(area);
37813
+ if (!pendingSizeMap) {
37814
+ pendingSizeMap = new Map();
37815
+ pendingSizeMapByArea.set(area, pendingSizeMap);
37816
+ }
37817
+ pendingSizeMap.set(barElement, size);
37818
+ if (flushFrame !== null) {
37819
+ return;
37820
+ }
37821
+ flushFrame = requestAnimationFrame(flushPendingSizes);
37822
+ };
37823
+
37824
+ const pendingSizeMapByArea = new Map();
37825
+ let flushFrame = null;
37826
+
37827
+ const flushPendingSizes = () => {
37828
+ flushFrame = null;
37829
+ for (const [area, pendingSizeMap] of pendingSizeMapByArea) {
37830
+ for (const [barElement, size] of pendingSizeMap) {
37831
+ storeSize(area, barElement, size);
37832
+ }
37833
+ writeSpace(area);
37834
+ }
37835
+ pendingSizeMapByArea.clear();
37836
+ };
37837
+
37838
+ // What the bar itself just said wins over what its observer had queued about
37839
+ // it: a bar unmounting gives its room back, and a size queued for it before
37840
+ // that must not put it back.
37841
+ const dropPendingSize = (area, barElement) => {
37842
+ const pendingSizeMap = pendingSizeMapByArea.get(area);
37843
+ if (!pendingSizeMap) {
37844
+ return;
37845
+ }
37846
+ pendingSizeMap.delete(barElement);
37847
+ if (pendingSizeMap.size === 0) {
37848
+ pendingSizeMapByArea.delete(area);
37849
+ }
37850
+ };
37851
+
37852
+ const storeSize = (area, barElement, size) => {
37778
37853
  let sizeMap = sizeMapByArea.get(area);
37779
37854
  if (!sizeMap) {
37780
37855
  sizeMap = new Map();
@@ -37785,7 +37860,10 @@ const setFixedBarSpace = (area, barElement, size) => {
37785
37860
  } else {
37786
37861
  sizeMap.set(barElement, size);
37787
37862
  }
37863
+ };
37788
37864
 
37865
+ const writeSpace = (area) => {
37866
+ const sizeMap = sizeMapByArea.get(area);
37789
37867
  let largestSize = 0;
37790
37868
  for (const barSize of sizeMap.values()) {
37791
37869
  if (barSize > largestSize) {
@@ -37795,10 +37873,18 @@ const setFixedBarSpace = (area, barElement, size) => {
37795
37873
  const property = `--navi-fixed-bar-space-${area}`;
37796
37874
  const { style } = document.documentElement;
37797
37875
  if (sizeMap.size === 0) {
37798
- style.removeProperty(property);
37799
- } else {
37800
- style.setProperty(property, `${largestSize}px`);
37876
+ if (writtenValueByArea.has(area)) {
37877
+ writtenValueByArea.delete(area);
37878
+ style.removeProperty(property);
37879
+ }
37880
+ return;
37881
+ }
37882
+ const value = `${largestSize}px`;
37883
+ if (writtenValueByArea.get(area) === value) {
37884
+ return;
37801
37885
  }
37886
+ writtenValueByArea.set(area, value);
37887
+ style.setProperty(property, value);
37802
37888
  };
37803
37889
 
37804
37890
  installImportMetaCssBuild(import.meta);/**
@@ -37974,27 +38060,40 @@ const FixedBar = ({
37974
38060
  // rebuilt as a calc() expression, so a size coming from anywhere — a prop, a
37975
38061
  // theme variable, the content itself — is reserved just the same, and each
37976
38062
  // `env()` inset stays the browser's business alone.
37977
- // And measured again whenever it changes: a ResizeObserver on the bar covers
37978
- // in one go a size prop that changes, content arriving or leaving, a font
37979
- // loading late, a rotation moving the notch.
37980
38063
  const vertical = area === "left" || area === "right";
37981
38064
  const {
37982
38065
  ref
37983
38066
  } = props;
38067
+ const measureSpace = barElement => {
38068
+ const {
38069
+ width,
38070
+ height
38071
+ } = barElement.getBoundingClientRect();
38072
+ return vertical ? width : height;
38073
+ };
38074
+
38075
+ // Anything a render can change — a size prop, the children, a theme variable
38076
+ // — is measured in that same commit, before paint: no frame where the
38077
+ // content sits under the bar, and nothing written from inside an observer.
38078
+ useLayoutEffect(() => {
38079
+ const barElement = ref.current;
38080
+ if (!barElement) {
38081
+ return;
38082
+ }
38083
+ setFixedBarSpace(area, barElement, measureSpace(barElement));
38084
+ });
38085
+
38086
+ // What no render caused: a font loading late, content arriving from outside,
38087
+ // a rotation moving the notch. Measuring here is safe; the write is what has
38088
+ // to wait, and fixed_bar_space.js is the one that holds it back.
37984
38089
  useLayoutEffect(() => {
37985
38090
  const barElement = ref.current;
37986
38091
  if (!barElement) {
37987
38092
  return undefined;
37988
38093
  }
37989
- const publishSize = () => {
37990
- const {
37991
- width,
37992
- height
37993
- } = barElement.getBoundingClientRect();
37994
- setFixedBarSpace(area, barElement, vertical ? width : height);
37995
- };
37996
- publishSize();
37997
- const resizeObserver = new ResizeObserver(publishSize);
38094
+ const resizeObserver = new ResizeObserver(() => {
38095
+ requestFixedBarSpace(area, barElement, measureSpace(barElement));
38096
+ });
37998
38097
  resizeObserver.observe(barElement);
37999
38098
  return () => {
38000
38099
  resizeObserver.disconnect();