@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.
@@ -47099,7 +47099,7 @@ const useRenderScope = (init, update) => {
47099
47099
  * interface UIStateController {
47100
47100
  * id: string;
47101
47101
  * controlType: string; // Used for debug logging
47102
- * uiStateSignal: Signal; // Accessed by button children to inherit parent value
47102
+ * uiStateSignal: Signal | null; // What a button child with no value of its own inherits (null: nothing)
47103
47103
  * registerChild(child): void; // Called on child mount
47104
47104
  * onChildUIAction(child, e, { stateChanged: boolean }): void; // Called when a child fires a UI action
47105
47105
  * unregisterChild(child): void; // Called on child unmount
@@ -49469,7 +49469,12 @@ const useUIFacadeStateController = (props, realUIStateController) => {
49469
49469
  props,
49470
49470
  pushStateDownToChild,
49471
49471
  ref: realUIStateController.ref,
49472
- uiStateSignal: realUIStateController.uiStateSignal,
49472
+ // Nothing for a button in the popup to inherit. The picker's value is
49473
+ // what its one child holds, and a button is refused as that child
49474
+ // (canRegisterAsFacadeChild): it answers for itself. Handed the value,
49475
+ // its action would run with the picker's selection over the params it
49476
+ // was bound with — a "refresh" in a sort picker sending the sort order.
49477
+ uiStateSignal: null,
49473
49478
  controlHostProps: realUIStateController.controlHostProps,
49474
49479
  readOnlyWithoutRun: realUIStateController.readOnlyWithoutRun,
49475
49480
  registerChild: (child) => {
@@ -49599,7 +49604,6 @@ const useUIFacadeStateController = (props, realUIStateController) => {
49599
49604
  (s) => {
49600
49605
  s.controller.props = props;
49601
49606
  s.controller.ref = realUIStateController.ref;
49602
- s.controller.uiStateSignal = realUIStateController.uiStateSignal;
49603
49607
  s.controller.controlHostProps = realUIStateController.controlHostProps;
49604
49608
  s.controller.readOnlyWithoutRun =
49605
49609
  realUIStateController.readOnlyWithoutRun;
@@ -55251,18 +55255,69 @@ const TextShrinkWrap = props => {
55251
55255
  shrinkWrap: undefined
55252
55256
  });
55253
55257
  };
55254
- // Whether the text is cut is read on the axis navi cuts it on: one line ends
55255
- // in an ellipsis on the inline axis, n lines are clamped on the block one. The
55256
- // 1px is the rounding of a box that exactly fits.
55258
+ // Whether the text is cut is read from its line boxes against the content box,
55259
+ // on the axis navi cuts it on: one line ends in an ellipsis on the inline axis,
55260
+ // n lines are clamped on the block one. Not from scrollWidth/scrollHeight: the
55261
+ // scroll extent holds the padding, so a text cut inside its end padding would
55262
+ // read as fitting.
55257
55263
  const isTextTruncated = (element, maxLines) => {
55258
55264
  if (maxLines === 1 || maxLines === "1") {
55259
- return element.scrollWidth - element.clientWidth > 1;
55265
+ const content = getContentEdges(element);
55266
+ // The ellipsis is painted over a line laid out at its full width. The
55267
+ // 0.5px absorbs the rounding of a box that exactly fits.
55268
+ return getLineRects(element).some(rect => rect.right - content.right > 0.5 || content.left - rect.left > 0.5);
55260
55269
  }
55261
55270
  if (maxLines > 1) {
55262
- return element.scrollHeight - element.clientHeight > 1;
55271
+ const content = getContentEdges(element);
55272
+ // A clamped line is still laid out, under the content box. Its middle is
55273
+ // read, not its top: under a line-height of 1 or less the glyphs of the
55274
+ // next line start above the bottom of the one before.
55275
+ return getLineRects(element).some(rect => rect.top + rect.height / 2 > content.bottom);
55263
55276
  }
55264
55277
  return false;
55265
55278
  };
55279
+ // Read from the in-flow children: an overlay positioned over the box (a loading
55280
+ // skeleton) fills the padding and is not part of a line.
55281
+ const getLineRects = element => {
55282
+ const range = document.createRange();
55283
+ const rects = [];
55284
+ for (const child of element.childNodes) {
55285
+ if (child.nodeType === Node.ELEMENT_NODE) {
55286
+ const {
55287
+ position
55288
+ } = window.getComputedStyle(child);
55289
+ if (position === "absolute" || position === "fixed") {
55290
+ continue;
55291
+ }
55292
+ }
55293
+ range.selectNode(child);
55294
+ rects.push(...range.getClientRects());
55295
+ }
55296
+ return rects;
55297
+ };
55298
+ // In the coordinates of the line rects: a transformed ancestor (a pressed
55299
+ // button's scale) shrinks the rects and the box, not the computed paddings.
55300
+ const getContentEdges = element => {
55301
+ const style = window.getComputedStyle(element);
55302
+ const box = element.getBoundingClientRect();
55303
+ const scaleX = scaleOf(box.width, element.offsetWidth);
55304
+ const scaleY = scaleOf(box.height, element.offsetHeight);
55305
+ const startX = parseFloat(style.borderLeftWidth) + parseFloat(style.paddingLeft);
55306
+ const endX = parseFloat(style.borderRightWidth) + parseFloat(style.paddingRight);
55307
+ const endY = parseFloat(style.borderBottomWidth) + parseFloat(style.paddingBottom);
55308
+ return {
55309
+ left: box.left + startX * scaleX,
55310
+ right: box.right - endX * scaleX,
55311
+ bottom: box.bottom - endY * scaleY
55312
+ };
55313
+ };
55314
+ const scaleOf = (screenSize, layoutSize) => {
55315
+ // A box with no size has no padding or border to scale either.
55316
+ if (layoutSize === 0) {
55317
+ return 1;
55318
+ }
55319
+ return screenSize / layoutSize;
55320
+ };
55266
55321
  const TextReportingTruncation = ({
55267
55322
  onTruncatedChange,
55268
55323
  ...props