@jsenv/dom 0.17.27 → 0.17.28

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 (2) hide show
  1. package/dist/jsenv_dom.js +59 -22
  2. package/package.json +1 -1
package/dist/jsenv_dom.js CHANGED
@@ -2702,6 +2702,12 @@ const normalizeStyle = (
2702
2702
 
2703
2703
  if (colorPropertySet.has(propertyName)) {
2704
2704
  if (typeof value === "string") {
2705
+ if (context === "css") {
2706
+ // A string is already what the DOM takes, and it takes more of them
2707
+ // (system colors, relative color syntax) than this parser knows;
2708
+ // parsing it to rgba and back would only cost.
2709
+ return value;
2710
+ }
2705
2711
  if (isCSSKeyword(value)) {
2706
2712
  return value;
2707
2713
  }
@@ -19720,14 +19726,22 @@ const getMaxWidth = (
19720
19726
  * Summing their `width` values therefore over-counts the true line width.
19721
19727
  *
19722
19728
  * Instead we compute the bounding extent per line: track the minimum `left`
19723
- * and maximum `right` across all rects sharing the same rounded `top`, then
19724
- * use `right - left` as the line width. This is correct regardless of nesting
19729
+ * and maximum `right` across all rects of the same line, then use
19730
+ * `right - left` as the line width. This is correct regardless of nesting
19725
19731
  * depth and works well for regular inline text content.
19726
19732
  *
19727
- * Limitation: rects are grouped by `Math.round(r.top)`, so elements on the
19728
- * same visual line but with slightly different baselines (e.g. an icon taller
19729
- * than surrounding text) could be counted as separate lines. This is unlikely
19730
- * to matter in practice for normal text rendering.
19733
+ * ## Implementation note lines are found by vertical overlap
19734
+ *
19735
+ * Boxes sharing a line do not share a `top`: an inline icon sized to 1em and
19736
+ * sitting on the baseline, a superscript, an inline-block, all start a pixel
19737
+ * or a fraction of one away from the text beside them (and that fraction
19738
+ * moves with a sub-pixel scroll offset). Grouping rects by their rounded
19739
+ * `top` would count such a box as a line of its own, and the "longest line"
19740
+ * would come out as the text without it — short enough to make it wrap once
19741
+ * applied as a width. A rect therefore joins the line it overlaps vertically
19742
+ * by more than half the smaller of the two heights: boxes on one line share
19743
+ * most of their height, while the text rects of two consecutive lines overlap
19744
+ * by a sliver at most (a line-height below the font's content height).
19731
19745
  *
19732
19746
  * Limitation: `range.getClientRects()` returns rects for text nodes and inline
19733
19747
  * boxes as laid out in the flow, ignoring any `overflow: hidden` or `max-width`
@@ -19745,31 +19759,41 @@ const measureLongestVisualLineWidth = (el) => {
19745
19759
  const range = document.createRange();
19746
19760
  range.selectNodeContents(el);
19747
19761
 
19748
- const lineBoundsByTop = new Map();
19749
- for (const r of range.getClientRects()) {
19750
- if (r.width === 0) {
19762
+ const lines = [];
19763
+ for (const rect of range.getClientRects()) {
19764
+ if (rect.width === 0) {
19751
19765
  continue;
19752
19766
  }
19753
- const top = Math.round(r.top);
19754
- const existing = lineBoundsByTop.get(top);
19755
- if (existing === undefined) {
19756
- lineBoundsByTop.set(top, { left: r.left, right: r.right });
19757
- } else {
19758
- if (r.left < existing.left) {
19759
- existing.left = r.left;
19760
- }
19761
- if (r.right > existing.right) {
19762
- existing.right = r.right;
19763
- }
19767
+ const line = lines.find((candidate) => sharesLine(candidate, rect));
19768
+ if (line === undefined) {
19769
+ lines.push({
19770
+ top: rect.top,
19771
+ bottom: rect.bottom,
19772
+ left: rect.left,
19773
+ right: rect.right,
19774
+ });
19775
+ continue;
19776
+ }
19777
+ if (rect.top < line.top) {
19778
+ line.top = rect.top;
19779
+ }
19780
+ if (rect.bottom > line.bottom) {
19781
+ line.bottom = rect.bottom;
19782
+ }
19783
+ if (rect.left < line.left) {
19784
+ line.left = rect.left;
19785
+ }
19786
+ if (rect.right > line.right) {
19787
+ line.right = rect.right;
19764
19788
  }
19765
19789
  }
19766
19790
 
19767
- if (lineBoundsByTop.size <= 1) {
19791
+ if (lines.length <= 1) {
19768
19792
  return null;
19769
19793
  }
19770
19794
 
19771
19795
  let longestLineWidth = 0;
19772
- for (const { left, right } of lineBoundsByTop.values()) {
19796
+ for (const { left, right } of lines) {
19773
19797
  const w = right - left;
19774
19798
  if (w > longestLineWidth) {
19775
19799
  longestLineWidth = w;
@@ -19778,6 +19802,19 @@ const measureLongestVisualLineWidth = (el) => {
19778
19802
  return longestLineWidth;
19779
19803
  };
19780
19804
 
19805
+ const sharesLine = (line, rect) => {
19806
+ const overlapTop = rect.top > line.top ? rect.top : line.top;
19807
+ const overlapBottom = rect.bottom < line.bottom ? rect.bottom : line.bottom;
19808
+ const overlap = overlapBottom - overlapTop;
19809
+ if (overlap <= 0) {
19810
+ return false;
19811
+ }
19812
+ const rectHeight = rect.bottom - rect.top;
19813
+ const lineHeight = line.bottom - line.top;
19814
+ const smallerHeight = rectHeight < lineHeight ? rectHeight : lineHeight;
19815
+ return overlap > smallerHeight / 2;
19816
+ };
19817
+
19781
19818
  // Measures the width of the widest row of direct children.
19782
19819
  // Uses children's bounding rects (which respect overflow:hidden / max-width)
19783
19820
  // rather than Range.getClientRects() which sees through clipping boundaries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/dom",
3
- "version": "0.17.27",
3
+ "version": "0.17.28",
4
4
  "type": "module",
5
5
  "description": "DOM utilities for writing frontend code",
6
6
  "repository": {