@jsenv/navi 0.29.377 → 0.29.379
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 +87 -11
- package/dist/dev/jsenv_navi.js.map +7 -7
- package/dist/jsenv_navi.js +81 -11
- package/dist/jsenv_navi.js.map +7 -7
- package/docs/AI_INSTRUCTIONS.md +3 -1
- package/docs/popup_lift.md +5 -1
- package/docs/typography.md +28 -3
- package/package.json +1 -1
package/dist/dev/jsenv_navi.js
CHANGED
|
@@ -45318,6 +45318,10 @@ time.navi_text {
|
|
|
45318
45318
|
border-radius: inherit;
|
|
45319
45319
|
}
|
|
45320
45320
|
|
|
45321
|
+
&[data-text-clamp] {
|
|
45322
|
+
min-width: 0;
|
|
45323
|
+
}
|
|
45324
|
+
|
|
45321
45325
|
&[data-text-overflow] {
|
|
45322
45326
|
text-overflow: ellipsis;
|
|
45323
45327
|
overflow-wrap: normal;
|
|
@@ -45691,6 +45695,10 @@ const shouldInjectSpacingBetween = (left, right) => {
|
|
|
45691
45695
|
* n lines. This is the only prop to use for that — `Box`'s `lineClamp` /
|
|
45692
45696
|
* `overflowEllipsis` are raw CSS mappings meant for elements that are not a
|
|
45693
45697
|
* `Text`, and `lineClamp={1}` is never the single-line truncation you want.
|
|
45698
|
+
* Either value states the `white-space` it needs — one line does not wrap,
|
|
45699
|
+
* n lines may — so it holds inside a single-line ancestor too (a `Picker`'s
|
|
45700
|
+
* value, a `Time`); an explicit `noWrap`/`pre`/`preLine` on this `Text`
|
|
45701
|
+
* still wins.
|
|
45694
45702
|
* Truncation only happens if the element may become narrower than its
|
|
45695
45703
|
* content: `maxLines` sets `min-width: 0` here, but each `Box` between this
|
|
45696
45704
|
* one and the element that carries the width must set it too.
|
|
@@ -45753,6 +45761,25 @@ const shouldInjectSpacingBetween = (left, right) => {
|
|
|
45753
45761
|
* internally for overlays such as the skeleton container.
|
|
45754
45762
|
*/
|
|
45755
45763
|
const Text = props => {
|
|
45764
|
+
if (props.maxLines > 1) {
|
|
45765
|
+
props = {
|
|
45766
|
+
...props,
|
|
45767
|
+
"data-text-clamp": ""
|
|
45768
|
+
};
|
|
45769
|
+
if (!saysWhiteSpace(props)) {
|
|
45770
|
+
// n lines means "you may wrap". white-space is inherited, so left unsaid
|
|
45771
|
+
// a single-line ancestor (a Picker's value, a Time) leaves the clamp one
|
|
45772
|
+
// line to cut, and the text runs past its box. Same tag rule as
|
|
45773
|
+
// TextOverflow: a paragraph keeps its line breaks.
|
|
45774
|
+
props = props.as === "p" ? {
|
|
45775
|
+
...props,
|
|
45776
|
+
preLine: true
|
|
45777
|
+
} : {
|
|
45778
|
+
...props,
|
|
45779
|
+
noWrap: false
|
|
45780
|
+
};
|
|
45781
|
+
}
|
|
45782
|
+
}
|
|
45756
45783
|
if (props.loading || props.skeleton) {
|
|
45757
45784
|
return jsx(TextSkeleton, {
|
|
45758
45785
|
...props
|
|
@@ -45964,6 +45991,14 @@ const TextOverflow = ({
|
|
|
45964
45991
|
children: children
|
|
45965
45992
|
});
|
|
45966
45993
|
};
|
|
45994
|
+
const saysWhiteSpace = ({
|
|
45995
|
+
noWrap,
|
|
45996
|
+
pre,
|
|
45997
|
+
preWrap,
|
|
45998
|
+
preLine
|
|
45999
|
+
}) => {
|
|
46000
|
+
return noWrap !== undefined || pre !== undefined || preWrap !== undefined || preLine !== undefined;
|
|
46001
|
+
};
|
|
45967
46002
|
const TextWithSelectRange = ({
|
|
45968
46003
|
ref,
|
|
45969
46004
|
selectRange,
|
|
@@ -60001,9 +60036,11 @@ const TimeRelative = ({
|
|
|
60001
60036
|
});
|
|
60002
60037
|
};
|
|
60003
60038
|
const TimeText = props => {
|
|
60039
|
+
// A date stays on one line, unless the caller grants it several.
|
|
60040
|
+
const noWrap = props.maxLines > 1 ? undefined : true;
|
|
60004
60041
|
return jsx(Text, {
|
|
60005
60042
|
as: "time",
|
|
60006
|
-
noWrap:
|
|
60043
|
+
noWrap: noWrap,
|
|
60007
60044
|
...props
|
|
60008
60045
|
});
|
|
60009
60046
|
};
|
|
@@ -61244,6 +61281,10 @@ const ROOT_ATTRIBUTE = "data-navi-popup-lift";
|
|
|
61244
61281
|
// What the two boxes are to each other (Dialog's `lift`: "box" | "scene"),
|
|
61245
61282
|
// worn by the root too — it decides how each picture sits in the moving box.
|
|
61246
61283
|
const KIND_ATTRIBUTE = "data-navi-popup-lift-kind";
|
|
61284
|
+
// Worn by the root when the two pictures fit the moving box the whole way
|
|
61285
|
+
// (see picturesFitTheBox): the box's edge then has nothing to cut but the
|
|
61286
|
+
// pictures' own ink — a halo, a shadow — and the CSS lets it through.
|
|
61287
|
+
const FIT_ATTRIBUTE = "data-navi-popup-lift-fit";
|
|
61247
61288
|
// The one node that IS the anchor once in front: the popup itself, or a node
|
|
61248
61289
|
// inside it.
|
|
61249
61290
|
const TARGET_SELECTOR = "[data-lift]";
|
|
@@ -61363,6 +61404,7 @@ const liftPopupFromAnchor = (
|
|
|
61363
61404
|
giveBackNameArriving?.();
|
|
61364
61405
|
root.removeAttribute(ROOT_ATTRIBUTE);
|
|
61365
61406
|
root.removeAttribute(KIND_ATTRIBUTE);
|
|
61407
|
+
root.removeAttribute(FIT_ATTRIBUTE);
|
|
61366
61408
|
root.style.removeProperty(DURATION_PROPERTY);
|
|
61367
61409
|
root.style.removeProperty(BORDER_RADIUS_PROPERTY);
|
|
61368
61410
|
root.style.removeProperty(BACKGROUND_COLOR_PROPERTY);
|
|
@@ -61374,7 +61416,7 @@ const liftPopupFromAnchor = (
|
|
|
61374
61416
|
releaseScrollHold?.();
|
|
61375
61417
|
releaseScrollHold = trapScrollInside(popupEl, { backdrop: true });
|
|
61376
61418
|
const room = measureRoomBetweenBars();
|
|
61377
|
-
const boxLeaving =
|
|
61419
|
+
const boxLeaving = elementLeaving.getBoundingClientRect();
|
|
61378
61420
|
let boxArriving = null;
|
|
61379
61421
|
let cornersArriving = null;
|
|
61380
61422
|
const viewTransition = startViewTransition(async () => {
|
|
@@ -61392,8 +61434,9 @@ const liftPopupFromAnchor = (
|
|
|
61392
61434
|
if (elementArriving) {
|
|
61393
61435
|
giveBackNameArriving = wearLiftName(elementArriving);
|
|
61394
61436
|
cornersArriving = readCorners(elementArriving);
|
|
61395
|
-
|
|
61396
|
-
|
|
61437
|
+
boxArriving = elementArriving.getBoundingClientRect();
|
|
61438
|
+
if (picturesFitTheBox(lift, boxLeaving, boxArriving)) {
|
|
61439
|
+
root.setAttribute(FIT_ATTRIBUTE, "");
|
|
61397
61440
|
}
|
|
61398
61441
|
}
|
|
61399
61442
|
});
|
|
@@ -61697,6 +61740,30 @@ const animateMovingBox = ({ cornersFrom, cornersTo, room, boxFrom, boxTo }) => {
|
|
|
61697
61740
|
});
|
|
61698
61741
|
};
|
|
61699
61742
|
|
|
61743
|
+
// Whether both pictures fit the moving box from one end to the other. The
|
|
61744
|
+
// box's clip (dialog.jsx) is its edge for a picture larger than the box: what
|
|
61745
|
+
// a card gaining room uncovers, what a scene shows past the band cut from it.
|
|
61746
|
+
// Under "box" each picture keeps its own size, so both fit only when the two
|
|
61747
|
+
// boxes are the same size; under "scene" both are drawn as wide as the box
|
|
61748
|
+
// and centred, so they fit when the two boxes have the same aspect ratio —
|
|
61749
|
+
// each picture, drawn at the other box's width, is then that box's height.
|
|
61750
|
+
// A picture's ink past its own box (a halo, a shadow) is not content the clip
|
|
61751
|
+
// is there to cut, so when they fit, the clip is lifted.
|
|
61752
|
+
const picturesFitTheBox = (lift, boxFrom, boxTo) => {
|
|
61753
|
+
if (lift === "scene") {
|
|
61754
|
+
const heightFromAtWidthTo = (boxTo.width * boxFrom.height) / boxFrom.width;
|
|
61755
|
+
const heightToAtWidthFrom = (boxFrom.width * boxTo.height) / boxTo.width;
|
|
61756
|
+
return (
|
|
61757
|
+
Math.abs(heightFromAtWidthTo - boxTo.height) < 1 &&
|
|
61758
|
+
Math.abs(heightToAtWidthFrom - boxFrom.height) < 1
|
|
61759
|
+
);
|
|
61760
|
+
}
|
|
61761
|
+
return (
|
|
61762
|
+
Math.abs(boxFrom.width - boxTo.width) < 1 &&
|
|
61763
|
+
Math.abs(boxFrom.height - boxTo.height) < 1
|
|
61764
|
+
);
|
|
61765
|
+
};
|
|
61766
|
+
|
|
61700
61767
|
const insetToRoom = (room, box) =>
|
|
61701
61768
|
`inset(${room.top - box.top}px ${box.right - room.right}px ${box.bottom - room.bottom}px ${room.left - box.left}px)`;
|
|
61702
61769
|
|
|
@@ -62345,6 +62412,12 @@ const css$E = /* css */`
|
|
|
62345
62412
|
the box, and a change of width would read as the same card zooming
|
|
62346
62413
|
then swapping.
|
|
62347
62414
|
|
|
62415
|
+
A picture holds the element's ink past its own box too — a halo, a
|
|
62416
|
+
shadow — and the clip cuts that along with the content, at the box's
|
|
62417
|
+
corners. When both pictures fit the box the whole way (popup_lift.js,
|
|
62418
|
+
picturesFitTheBox) there is no content to uncover, only ink to lose:
|
|
62419
|
+
the clip is lifted and the box carries its ink along.
|
|
62420
|
+
|
|
62348
62421
|
The box paints what the lifted card paints behind its content
|
|
62349
62422
|
(published by popup_lift.js): where it has grown past the picture it
|
|
62350
62423
|
carries, it is the card that has grown. Its corners are those of the
|
|
@@ -62362,6 +62435,9 @@ const css$E = /* css */`
|
|
|
62362
62435
|
block-size: auto;
|
|
62363
62436
|
inline-size: auto;
|
|
62364
62437
|
}
|
|
62438
|
+
&[data-navi-popup-lift-fit]::view-transition-image-pair(navi-popup-lift) {
|
|
62439
|
+
overflow: visible;
|
|
62440
|
+
}
|
|
62365
62441
|
|
|
62366
62442
|
/* The card leaves as itself and arrives as what it became: the picture
|
|
62367
62443
|
being left stays whole for most of the way, the arriving one only shows
|
|
@@ -76389,6 +76465,12 @@ const PickerFirstResolver = props => {
|
|
|
76389
76465
|
* press, the way a click is the innermost target's. Being a gesture, the open goes through
|
|
76390
76466
|
* the interaction gate as one: a `readOnly` picker refuses it and says so
|
|
76391
76467
|
* where the finger is, whatever `openWhileReadOnly` says.
|
|
76468
|
+
* @param {number} [maxLines=1] How many lines the value may take before it is
|
|
76469
|
+
* cut with an ellipsis — `Text`'s `maxLines`, put on the value box, and
|
|
76470
|
+
* granted to a `BadgeList` in the `ui`, which caps its rows to it (see
|
|
76471
|
+
* max_lines_context.js). `variant="text"` and `variant="bare"` are not
|
|
76472
|
+
* clamped: their drawing is the caller's own, a `Text` in the `ui` cuts
|
|
76473
|
+
* itself with its own `maxLines`. See docs/typography.md.
|
|
76392
76474
|
* @param {import("ignore:preact").ComponentChildren | "default" | import("ignore:preact").ComponentType} [ui] What the
|
|
76393
76475
|
* trigger draws in place of the value's default rendering (a date, a list
|
|
76394
76476
|
* joined by commas…) — and then all it draws: `placeholder` is not shown
|
|
@@ -78160,13 +78242,7 @@ const renderDayDefault = (day, {
|
|
|
78160
78242
|
type: "date",
|
|
78161
78243
|
format: format,
|
|
78162
78244
|
dayLabel: true,
|
|
78163
|
-
lang: lang
|
|
78164
|
-
// A Time keeps its date on one line by default; here a day too long for
|
|
78165
|
-
// the box is meant to wrap, so that is undone — except for maxLines={1},
|
|
78166
|
-
// which IS one line and cuts it itself (see Text's own TextOverflow):
|
|
78167
|
-
// saying "you may wrap" there would undo the truncation instead.
|
|
78168
|
-
,
|
|
78169
|
-
noWrap: maxLines === 1 || maxLines === "1" ? undefined : false,
|
|
78245
|
+
lang: lang,
|
|
78170
78246
|
maxLines: maxLines,
|
|
78171
78247
|
children: day
|
|
78172
78248
|
});
|