@jsenv/navi 0.29.236 → 0.29.238
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.
- package/dist/dev/jsenv_navi.js +134 -71
- package/dist/dev/jsenv_navi.js.map +6 -6
- package/dist/jsenv_navi.js +76 -71
- package/dist/jsenv_navi.js.map +6 -6
- package/docs/AI_INSTRUCTIONS.md +5 -3
- package/docs/css_architecture.md +82 -10
- package/package.json +4 -4
package/dist/dev/jsenv_navi.js
CHANGED
|
@@ -6984,7 +6984,17 @@ const openCallout = (
|
|
|
6984
6984
|
`append callout into ${getElementSignature(calloutContainer)}`,
|
|
6985
6985
|
);
|
|
6986
6986
|
}
|
|
6987
|
+
let checkCalloutInsertionImpact;
|
|
6988
|
+
{
|
|
6989
|
+
checkCalloutInsertionImpact = startCalloutInsertionImpactCheck(
|
|
6990
|
+
calloutContainer,
|
|
6991
|
+
anchorElement,
|
|
6992
|
+
);
|
|
6993
|
+
}
|
|
6987
6994
|
calloutContainer.appendChild(calloutElement);
|
|
6995
|
+
if (checkCalloutInsertionImpact) {
|
|
6996
|
+
checkCalloutInsertionImpact();
|
|
6997
|
+
}
|
|
6988
6998
|
calloutElement.showPopover();
|
|
6989
6999
|
addTeardown(() => {
|
|
6990
7000
|
calloutElement.remove();
|
|
@@ -7550,6 +7560,54 @@ const canContainCallout = (element) => {
|
|
|
7550
7560
|
return true;
|
|
7551
7561
|
};
|
|
7552
7562
|
|
|
7563
|
+
// A callout is a popover with no layout box of its own, so putting it in the
|
|
7564
|
+
// tree must move nothing. Anything that does move means an app rule counting
|
|
7565
|
+
// element children — :last-child, :nth-child, :only-child, :empty, > * + * —
|
|
7566
|
+
// took the callout for one of them and changed subject. Invisible from the
|
|
7567
|
+
// app, where the rule and the markup both read fine.
|
|
7568
|
+
// Two boxes rather than every child of the container: a callout on a list row
|
|
7569
|
+
// would otherwise measure the whole list on each press.
|
|
7570
|
+
const startCalloutInsertionImpactCheck = (calloutContainer, anchorElement) => {
|
|
7571
|
+
if (!anchorElement) {
|
|
7572
|
+
return null;
|
|
7573
|
+
}
|
|
7574
|
+
const anchorBoxBefore = anchorElement.getBoundingClientRect();
|
|
7575
|
+
const containerBoxBefore = calloutContainer.getBoundingClientRect();
|
|
7576
|
+
return () => {
|
|
7577
|
+
const anchorBoxAfter = anchorElement.getBoundingClientRect();
|
|
7578
|
+
const containerBoxAfter = calloutContainer.getBoundingClientRect();
|
|
7579
|
+
let what;
|
|
7580
|
+
let boxBefore;
|
|
7581
|
+
let boxAfter;
|
|
7582
|
+
if (!isSameBox(anchorBoxBefore, anchorBoxAfter)) {
|
|
7583
|
+
what = "moved it";
|
|
7584
|
+
boxBefore = anchorBoxBefore;
|
|
7585
|
+
boxAfter = anchorBoxAfter;
|
|
7586
|
+
} else if (!isSameBox(containerBoxBefore, containerBoxAfter)) {
|
|
7587
|
+
what = `moved ${getElementSignature(calloutContainer)} around it`;
|
|
7588
|
+
boxBefore = containerBoxBefore;
|
|
7589
|
+
boxAfter = containerBoxAfter;
|
|
7590
|
+
} else {
|
|
7591
|
+
return;
|
|
7592
|
+
}
|
|
7593
|
+
console.warn(
|
|
7594
|
+
`opening a callout on ${getElementSignature(anchorElement)} ${what}: ${formatBox(boxBefore)} -> ${formatBox(boxAfter)}.
|
|
7595
|
+
A callout draws nothing in the flow, but while it is open it is an element child of ${getElementSignature(calloutContainer)}: a rule counting children there (:last-child, :nth-child, :only-child, :empty, > * + *) counts it too and matches something else than what it aims at.
|
|
7596
|
+
Say which children the rule counts: :nth-last-child(1 of S) / :nth-child(An+B of S), where S names yours (a class, or :not([navi-out-of-flow]) — the marker navi's out-of-flow elements carry). A sibling rule takes the marker between its compounds: > *:not([navi-out-of-flow]) + *:not([navi-out-of-flow]).
|
|
7597
|
+
See @jsenv/navi docs/css_architecture.md.`,
|
|
7598
|
+
);
|
|
7599
|
+
};
|
|
7600
|
+
};
|
|
7601
|
+
const isSameBox = (a, b) => {
|
|
7602
|
+
return (
|
|
7603
|
+
a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height
|
|
7604
|
+
);
|
|
7605
|
+
};
|
|
7606
|
+
const formatBox = ({ x, y, width, height }) => {
|
|
7607
|
+
const round = (value) => Math.round(value * 100) / 100;
|
|
7608
|
+
return `${round(width)}x${round(height)} at ${round(x)},${round(y)}`;
|
|
7609
|
+
};
|
|
7610
|
+
|
|
7553
7611
|
const escapeHtml = (string) => {
|
|
7554
7612
|
return string
|
|
7555
7613
|
.replace(/&/g, "&")
|
|
@@ -18950,6 +19008,20 @@ const resolveSpacingSize = (size, element, property = "padding") => {
|
|
|
18950
19008
|
}
|
|
18951
19009
|
return normalizeStyle(SIZE_MAP[size] || size, property, "js", element);
|
|
18952
19010
|
};
|
|
19011
|
+
// Whether a spacing is nothing at all, answered without resolving it. A
|
|
19012
|
+
// viewport or container length resolves through a computed-style read, and
|
|
19013
|
+
// asked during a render that read forces the browser to compute the style of
|
|
19014
|
+
// a tree still being built, stylesheets still being adopted. Zero is a fact
|
|
19015
|
+
// about the number written, whatever the unit; a scale keyword is never zero.
|
|
19016
|
+
const isZeroSpacingSize = (size) => {
|
|
19017
|
+
if (typeof size === "number") {
|
|
19018
|
+
return size === 0;
|
|
19019
|
+
}
|
|
19020
|
+
if (typeof size !== "string") {
|
|
19021
|
+
return false;
|
|
19022
|
+
}
|
|
19023
|
+
return parseFloat(size) === 0;
|
|
19024
|
+
};
|
|
18953
19025
|
|
|
18954
19026
|
const COLOR_KEYWORD_MAP = {
|
|
18955
19027
|
primary: "var(--navi-color-primary)",
|
|
@@ -39989,47 +40061,35 @@ const LoadingIndicatorFluid = ({
|
|
|
39989
40061
|
const [containerWidth, setContainerWidth] = useState(0);
|
|
39990
40062
|
const [containerHeight, setContainerHeight] = useState(0);
|
|
39991
40063
|
const [containerRadius, setContainerRadius] = useState(0);
|
|
40064
|
+
|
|
40065
|
+
// Sized by the observer alone, first frame included: an element gets an
|
|
40066
|
+
// observation for the size it has when first observed, delivered after the
|
|
40067
|
+
// frame's own layout and before its paint. Measuring here instead would
|
|
40068
|
+
// force a layout in the middle of the commit, on a tree the commit is still
|
|
40069
|
+
// writing to, and the frame would lay it out again. Inside the callback style
|
|
40070
|
+
// and layout are clean, so the radius read there is a lookup, not a recalc.
|
|
39992
40071
|
useLayoutEffect(() => {
|
|
39993
40072
|
const indicatorEl = ref.current;
|
|
39994
40073
|
if (!indicatorEl) {
|
|
39995
40074
|
return null;
|
|
39996
40075
|
}
|
|
39997
|
-
const {
|
|
39998
|
-
|
|
39999
|
-
|
|
40000
|
-
|
|
40001
|
-
|
|
40002
|
-
|
|
40003
|
-
|
|
40004
|
-
|
|
40005
|
-
|
|
40006
|
-
|
|
40007
|
-
|
|
40008
|
-
|
|
40009
|
-
|
|
40010
|
-
|
|
40011
|
-
let animationFrameId = null;
|
|
40012
|
-
// Create a resize observer to detect changes in the container's dimensions
|
|
40013
|
-
const resizeObserver = new ResizeObserver(entries => {
|
|
40014
|
-
// Use requestAnimationFrame to debounce updates
|
|
40015
|
-
if (animationFrameId) {
|
|
40016
|
-
cancelAnimationFrame(animationFrameId);
|
|
40017
|
-
}
|
|
40018
|
-
animationFrameId = requestAnimationFrame(() => {
|
|
40019
|
-
const [containerEntry] = entries;
|
|
40020
|
-
const {
|
|
40021
|
-
width,
|
|
40022
|
-
height
|
|
40023
|
-
} = containerEntry.contentRect;
|
|
40024
|
-
setContainerWidth(width);
|
|
40025
|
-
setContainerHeight(height);
|
|
40026
|
-
});
|
|
40076
|
+
const resizeObserver = new ResizeObserver(([containerEntry]) => {
|
|
40077
|
+
const {
|
|
40078
|
+
width,
|
|
40079
|
+
height
|
|
40080
|
+
} = containerEntry.contentRect;
|
|
40081
|
+
setContainerWidth(width);
|
|
40082
|
+
setContainerHeight(height);
|
|
40083
|
+
if (radius === undefined || radius === "inherit") {
|
|
40084
|
+
const parentEl = indicatorEl.parentElement;
|
|
40085
|
+
// The inline style says the radius as the caller wrote it; the
|
|
40086
|
+
// computed longhand otherwise, the shorthand computing to "" when the
|
|
40087
|
+
// four corners differ.
|
|
40088
|
+
setContainerRadius(parentEl.style.borderRadius || parentEl.style.borderTopLeftRadius || getComputedStyle(parentEl).borderTopLeftRadius || "0px");
|
|
40089
|
+
}
|
|
40027
40090
|
});
|
|
40028
40091
|
resizeObserver.observe(indicatorEl);
|
|
40029
40092
|
return () => {
|
|
40030
|
-
if (animationFrameId) {
|
|
40031
|
-
cancelAnimationFrame(animationFrameId);
|
|
40032
|
-
}
|
|
40033
40093
|
resizeObserver.disconnect();
|
|
40034
40094
|
};
|
|
40035
40095
|
}, []);
|
|
@@ -44371,6 +44431,13 @@ const measureDrawing = ({
|
|
|
44371
44431
|
* Measured rather than declared: a bar's size comes from a prop, a theme
|
|
44372
44432
|
* variable, its own content or the device's notch, and only the used value
|
|
44373
44433
|
* knows all four.
|
|
44434
|
+
*
|
|
44435
|
+
* Written once per task, after every bar has measured. The variable resizes
|
|
44436
|
+
* an ancestor of the bars — the scroll container takes its padding from it —
|
|
44437
|
+
* so a write landing between two measurements turns the second one into a
|
|
44438
|
+
* style recalc and a layout of the whole page. Sizes are queued, and the queue
|
|
44439
|
+
* lands in a microtask: still before the browser paints, so the content is
|
|
44440
|
+
* never seen under a bar, and after every read of the commit that queued them.
|
|
44374
44441
|
*/
|
|
44375
44442
|
|
|
44376
44443
|
// Several bars can share an edge — during a page transition the outgoing and
|
|
@@ -44384,10 +44451,14 @@ const sizeMapByArea = new Map();
|
|
|
44384
44451
|
// size again — the ones for the smaller bars, and the second half of a page
|
|
44385
44452
|
// transition.
|
|
44386
44453
|
const writtenValueByArea = new Map();
|
|
44454
|
+
// Sizes said and not yet written. One queue for both callers below: a bar
|
|
44455
|
+
// saying its size twice before the write keeps the last word, whoever said it.
|
|
44456
|
+
const pendingSizeMapByArea = new Map();
|
|
44457
|
+
let flushMicrotaskQueued = false;
|
|
44458
|
+
let flushFrame = null;
|
|
44387
44459
|
|
|
44388
|
-
// A size
|
|
44389
|
-
// the room it takes is given back
|
|
44390
|
-
// painted under it.
|
|
44460
|
+
// A size a render produced: a prop, the children or a theme variable changed
|
|
44461
|
+
// the bar, and the room it takes is given back before that commit is painted.
|
|
44391
44462
|
/**
|
|
44392
44463
|
* @param {"top"|"bottom"|"left"|"right"} area
|
|
44393
44464
|
* @param {Element} barElement - Which bar this size belongs to.
|
|
@@ -44395,41 +44466,47 @@ const writtenValueByArea = new Map();
|
|
|
44395
44466
|
* content.
|
|
44396
44467
|
*/
|
|
44397
44468
|
const setFixedBarSpace = (area, barElement, size) => {
|
|
44398
|
-
|
|
44399
|
-
|
|
44400
|
-
|
|
44401
|
-
}
|
|
44402
|
-
|
|
44403
|
-
|
|
44404
|
-
|
|
44405
|
-
|
|
44406
|
-
|
|
44407
|
-
|
|
44408
|
-
|
|
44409
|
-
//
|
|
44469
|
+
queueSize(area, barElement, size);
|
|
44470
|
+
if (flushMicrotaskQueued) {
|
|
44471
|
+
return;
|
|
44472
|
+
}
|
|
44473
|
+
flushMicrotaskQueued = true;
|
|
44474
|
+
queueMicrotask(() => {
|
|
44475
|
+
flushMicrotaskQueued = false;
|
|
44476
|
+
flushPendingSizes();
|
|
44477
|
+
});
|
|
44478
|
+
};
|
|
44479
|
+
|
|
44480
|
+
// A size nothing asked for, coming from a ResizeObserver. Mutating layout from
|
|
44481
|
+
// inside a resize callback is what makes the browser report "ResizeObserver
|
|
44482
|
+
// loop completed with undelivered notifications", so this write waits for the
|
|
44483
|
+
// frame that resize produced.
|
|
44410
44484
|
/**
|
|
44411
44485
|
* @param {"top"|"bottom"|"left"|"right"} area
|
|
44412
44486
|
* @param {Element} barElement - Which bar this size belongs to.
|
|
44413
44487
|
* @param {number} size - In px.
|
|
44414
44488
|
*/
|
|
44415
44489
|
const requestFixedBarSpace = (area, barElement, size) => {
|
|
44490
|
+
queueSize(area, barElement, size);
|
|
44491
|
+
if (flushFrame !== null) {
|
|
44492
|
+
return;
|
|
44493
|
+
}
|
|
44494
|
+
flushFrame = requestAnimationFrame(() => {
|
|
44495
|
+
flushFrame = null;
|
|
44496
|
+
flushPendingSizes();
|
|
44497
|
+
});
|
|
44498
|
+
};
|
|
44499
|
+
|
|
44500
|
+
const queueSize = (area, barElement, size) => {
|
|
44416
44501
|
let pendingSizeMap = pendingSizeMapByArea.get(area);
|
|
44417
44502
|
if (!pendingSizeMap) {
|
|
44418
44503
|
pendingSizeMap = new Map();
|
|
44419
44504
|
pendingSizeMapByArea.set(area, pendingSizeMap);
|
|
44420
44505
|
}
|
|
44421
44506
|
pendingSizeMap.set(barElement, size);
|
|
44422
|
-
if (flushFrame !== null) {
|
|
44423
|
-
return;
|
|
44424
|
-
}
|
|
44425
|
-
flushFrame = requestAnimationFrame(flushPendingSizes);
|
|
44426
44507
|
};
|
|
44427
44508
|
|
|
44428
|
-
const pendingSizeMapByArea = new Map();
|
|
44429
|
-
let flushFrame = null;
|
|
44430
|
-
|
|
44431
44509
|
const flushPendingSizes = () => {
|
|
44432
|
-
flushFrame = null;
|
|
44433
44510
|
for (const [area, pendingSizeMap] of pendingSizeMapByArea) {
|
|
44434
44511
|
for (const [barElement, size] of pendingSizeMap) {
|
|
44435
44512
|
storeSize(area, barElement, size);
|
|
@@ -44439,20 +44516,6 @@ const flushPendingSizes = () => {
|
|
|
44439
44516
|
pendingSizeMapByArea.clear();
|
|
44440
44517
|
};
|
|
44441
44518
|
|
|
44442
|
-
// What the bar itself just said wins over what its observer had queued about
|
|
44443
|
-
// it: a bar unmounting gives its room back, and a size queued for it before
|
|
44444
|
-
// that must not put it back.
|
|
44445
|
-
const dropPendingSize = (area, barElement) => {
|
|
44446
|
-
const pendingSizeMap = pendingSizeMapByArea.get(area);
|
|
44447
|
-
if (!pendingSizeMap) {
|
|
44448
|
-
return;
|
|
44449
|
-
}
|
|
44450
|
-
pendingSizeMap.delete(barElement);
|
|
44451
|
-
if (pendingSizeMap.size === 0) {
|
|
44452
|
-
pendingSizeMapByArea.delete(area);
|
|
44453
|
-
}
|
|
44454
|
-
};
|
|
44455
|
-
|
|
44456
44519
|
const storeSize = (area, barElement, size) => {
|
|
44457
44520
|
let sizeMap = sizeMapByArea.get(area);
|
|
44458
44521
|
if (!sizeMap) {
|
|
@@ -59662,7 +59725,7 @@ const useDialogProps = props => {
|
|
|
59662
59725
|
bottom: false,
|
|
59663
59726
|
left: false
|
|
59664
59727
|
};
|
|
59665
|
-
if (
|
|
59728
|
+
if (isZeroSpacingSize(marginWithContainer)) {
|
|
59666
59729
|
const {
|
|
59667
59730
|
y,
|
|
59668
59731
|
x
|