@jsenv/navi 0.29.509 → 0.29.510

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.
@@ -48488,7 +48488,7 @@ const useRenderScope = (init, update) => {
48488
48488
  * interface UIStateController {
48489
48489
  * id: string;
48490
48490
  * controlType: string; // Used for debug logging
48491
- * uiStateSignal: Signal; // Accessed by button children to inherit parent value
48491
+ * uiStateSignal: Signal | null; // What a button child with no value of its own inherits (null: nothing)
48492
48492
  * registerChild(child): void; // Called on child mount
48493
48493
  * onChildUIAction(child, e, { stateChanged: boolean }): void; // Called when a child fires a UI action
48494
48494
  * unregisterChild(child): void; // Called on child unmount
@@ -50894,7 +50894,12 @@ const useUIFacadeStateController = (props, realUIStateController) => {
50894
50894
  props,
50895
50895
  pushStateDownToChild,
50896
50896
  ref: realUIStateController.ref,
50897
- uiStateSignal: realUIStateController.uiStateSignal,
50897
+ // Nothing for a button in the popup to inherit. The picker's value is
50898
+ // what its one child holds, and a button is refused as that child
50899
+ // (canRegisterAsFacadeChild): it answers for itself. Handed the value,
50900
+ // its action would run with the picker's selection over the params it
50901
+ // was bound with — a "refresh" in a sort picker sending the sort order.
50902
+ uiStateSignal: null,
50898
50903
  controlHostProps: realUIStateController.controlHostProps,
50899
50904
  readOnlyWithoutRun: realUIStateController.readOnlyWithoutRun,
50900
50905
  registerChild: (child) => {
@@ -51025,7 +51030,6 @@ const useUIFacadeStateController = (props, realUIStateController) => {
51025
51030
  (s) => {
51026
51031
  s.controller.props = props;
51027
51032
  s.controller.ref = realUIStateController.ref;
51028
- s.controller.uiStateSignal = realUIStateController.uiStateSignal;
51029
51033
  s.controller.controlHostProps = realUIStateController.controlHostProps;
51030
51034
  s.controller.readOnlyWithoutRun =
51031
51035
  realUIStateController.readOnlyWithoutRun;
@@ -56781,18 +56785,69 @@ const TextShrinkWrap = props => {
56781
56785
  shrinkWrap: undefined
56782
56786
  });
56783
56787
  };
56784
- // Whether the text is cut is read on the axis navi cuts it on: one line ends
56785
- // in an ellipsis on the inline axis, n lines are clamped on the block one. The
56786
- // 1px is the rounding of a box that exactly fits.
56788
+ // Whether the text is cut is read from its line boxes against the content box,
56789
+ // on the axis navi cuts it on: one line ends in an ellipsis on the inline axis,
56790
+ // n lines are clamped on the block one. Not from scrollWidth/scrollHeight: the
56791
+ // scroll extent holds the padding, so a text cut inside its end padding would
56792
+ // read as fitting.
56787
56793
  const isTextTruncated = (element, maxLines) => {
56788
56794
  if (maxLines === 1 || maxLines === "1") {
56789
- return element.scrollWidth - element.clientWidth > 1;
56795
+ const content = getContentEdges(element);
56796
+ // The ellipsis is painted over a line laid out at its full width. The
56797
+ // 0.5px absorbs the rounding of a box that exactly fits.
56798
+ return getLineRects(element).some(rect => rect.right - content.right > 0.5 || content.left - rect.left > 0.5);
56790
56799
  }
56791
56800
  if (maxLines > 1) {
56792
- return element.scrollHeight - element.clientHeight > 1;
56801
+ const content = getContentEdges(element);
56802
+ // A clamped line is still laid out, under the content box. Its middle is
56803
+ // read, not its top: under a line-height of 1 or less the glyphs of the
56804
+ // next line start above the bottom of the one before.
56805
+ return getLineRects(element).some(rect => rect.top + rect.height / 2 > content.bottom);
56793
56806
  }
56794
56807
  return false;
56795
56808
  };
56809
+ // Read from the in-flow children: an overlay positioned over the box (a loading
56810
+ // skeleton) fills the padding and is not part of a line.
56811
+ const getLineRects = element => {
56812
+ const range = document.createRange();
56813
+ const rects = [];
56814
+ for (const child of element.childNodes) {
56815
+ if (child.nodeType === Node.ELEMENT_NODE) {
56816
+ const {
56817
+ position
56818
+ } = window.getComputedStyle(child);
56819
+ if (position === "absolute" || position === "fixed") {
56820
+ continue;
56821
+ }
56822
+ }
56823
+ range.selectNode(child);
56824
+ rects.push(...range.getClientRects());
56825
+ }
56826
+ return rects;
56827
+ };
56828
+ // In the coordinates of the line rects: a transformed ancestor (a pressed
56829
+ // button's scale) shrinks the rects and the box, not the computed paddings.
56830
+ const getContentEdges = element => {
56831
+ const style = window.getComputedStyle(element);
56832
+ const box = element.getBoundingClientRect();
56833
+ const scaleX = scaleOf(box.width, element.offsetWidth);
56834
+ const scaleY = scaleOf(box.height, element.offsetHeight);
56835
+ const startX = parseFloat(style.borderLeftWidth) + parseFloat(style.paddingLeft);
56836
+ const endX = parseFloat(style.borderRightWidth) + parseFloat(style.paddingRight);
56837
+ const endY = parseFloat(style.borderBottomWidth) + parseFloat(style.paddingBottom);
56838
+ return {
56839
+ left: box.left + startX * scaleX,
56840
+ right: box.right - endX * scaleX,
56841
+ bottom: box.bottom - endY * scaleY
56842
+ };
56843
+ };
56844
+ const scaleOf = (screenSize, layoutSize) => {
56845
+ // A box with no size has no padding or border to scale either.
56846
+ if (layoutSize === 0) {
56847
+ return 1;
56848
+ }
56849
+ return screenSize / layoutSize;
56850
+ };
56796
56851
  const TextReportingTruncation = ({
56797
56852
  onTruncatedChange,
56798
56853
  ...props