@jsenv/navi 0.29.134 → 0.29.136
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/jsenv_navi.js +807 -277
- package/dist/jsenv_navi.js.map +62 -67
- package/dist/jsenv_navi_side_effects.js +32 -0
- package/dist/jsenv_navi_side_effects.js.map +3 -3
- package/docs/control_group.md +17 -7
- package/docs/css_architecture.md +34 -3
- package/docs/route_transitions.md +71 -6
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -373,6 +373,9 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
373
373
|
const URL_TARGET_ATTRIBUTE = "data-url-target";
|
|
374
374
|
const css$14 = /* css */`
|
|
375
375
|
@layer navi {
|
|
376
|
+
/* Layered whole: the mark is navi's suggestion for "here is what the URL
|
|
377
|
+
pointed at", not something it needs. An app replaces it, or removes it
|
|
378
|
+
with animation: none, from an unlayered rule of any weight. */
|
|
376
379
|
[${URL_TARGET_ATTRIBUTE}] {
|
|
377
380
|
animation: navi_url_target var(--navi-url-target-duration, 2000ms)
|
|
378
381
|
ease-out;
|
|
@@ -5979,25 +5982,32 @@ const css$12 = /* css */`
|
|
|
5979
5982
|
border-radius: 50%;
|
|
5980
5983
|
}
|
|
5981
5984
|
|
|
5985
|
+
/* The svg takes the whole square and the glyph keeps its share of it
|
|
5986
|
+
inside the viewBox (see CALLOUT_STATUS_GLYPH_VIEWBOX). Sized as a
|
|
5987
|
+
fraction of the square instead, the svg box lands on a fractional
|
|
5988
|
+
device pixel and the rasteriser snaps the glyph a pixel off centre —
|
|
5989
|
+
visible on a small badge, where the glyph is only a few pixels wide. */
|
|
5982
5990
|
svg {
|
|
5983
|
-
width:
|
|
5984
|
-
height:
|
|
5991
|
+
width: 100%;
|
|
5992
|
+
height: 100%;
|
|
5985
5993
|
}
|
|
5986
5994
|
}
|
|
5987
|
-
/* Inside an <Icon>, the icon box is the size: fill it.
|
|
5988
|
-
own share of it — the Icon's rule sizing any svg it holds to the whole
|
|
5989
|
-
box is for an svg that IS the icon, and this one sits in a square. */
|
|
5995
|
+
/* Inside an <Icon>, the icon box is the size: fill it. */
|
|
5990
5996
|
.navi_icon > .navi_callout_status_icon {
|
|
5991
5997
|
width: 100%;
|
|
5992
5998
|
height: 100%;
|
|
5993
5999
|
}
|
|
5994
|
-
.navi_icon[data-icon-char] .navi_callout_status_icon svg {
|
|
5995
|
-
width: auto;
|
|
5996
|
-
height: 55%;
|
|
5997
|
-
}
|
|
5998
6000
|
`;
|
|
5999
|
-
|
|
6000
|
-
|
|
6001
|
+
|
|
6002
|
+
/**
|
|
6003
|
+
* The "!" on a square, viewBox and path both. The box is square and holds the
|
|
6004
|
+
* glyph's margin: the ink is 298 of 540 units tall (55%), centered. Drawn this
|
|
6005
|
+
* way rather than tight around the ink so whoever shows it can give the svg the
|
|
6006
|
+
* whole square — the glyph is then placed by the vector rasteriser, which has
|
|
6007
|
+
* no device pixel grid to miss, at any square size down to a fractional one.
|
|
6008
|
+
*/
|
|
6009
|
+
const CALLOUT_STATUS_GLYPH_VIEWBOX = "0 0 540 540";
|
|
6010
|
+
const CALLOUT_STATUS_GLYPH_PATH = "m232.5,121 8,196h59l8-196zm36.5,224a37,37 0 1,0 2,0z";
|
|
6001
6011
|
|
|
6002
6012
|
/**
|
|
6003
6013
|
* @type {import("ignore:preact").FunctionComponent<{
|
|
@@ -6048,7 +6058,8 @@ const css$11 = /* css */`
|
|
|
6048
6058
|
/* A callout is parented to what it explains, so it inherits from it — and
|
|
6049
6059
|
an element that suppressed text selection (a list row, a drag source)
|
|
6050
6060
|
would make its own explanation unselectable. The message is text one
|
|
6051
|
-
copies.
|
|
6061
|
+
copies. Layered: an app that has a reason to lock its own callouts down
|
|
6062
|
+
says so and wins. */
|
|
6052
6063
|
user-select: text;
|
|
6053
6064
|
|
|
6054
6065
|
--callout-success-color: var(--navi-callout-success-color);
|
|
@@ -6208,9 +6219,11 @@ const css$11 = /* css */`
|
|
|
6208
6219
|
display: none;
|
|
6209
6220
|
}
|
|
6210
6221
|
|
|
6222
|
+
/* The glyph keeps its share of the square inside the viewBox
|
|
6223
|
+
(see CALLOUT_STATUS_GLYPH_VIEWBOX), so the svg takes all of it. */
|
|
6211
6224
|
svg {
|
|
6212
|
-
width:
|
|
6213
|
-
height:
|
|
6225
|
+
width: 100%;
|
|
6226
|
+
height: 100%;
|
|
6214
6227
|
color: white;
|
|
6215
6228
|
}
|
|
6216
6229
|
}
|
|
@@ -6957,6 +6970,7 @@ const calloutTemplate = /* html */`
|
|
|
6957
6970
|
<div
|
|
6958
6971
|
class="navi_callout"
|
|
6959
6972
|
popover="manual"
|
|
6973
|
+
navi-out-of-flow=""
|
|
6960
6974
|
aria-expanded="true"
|
|
6961
6975
|
>
|
|
6962
6976
|
<div class="navi_callout_box">
|
|
@@ -27055,12 +27069,190 @@ const releaseTransitionDestination = (owner) => {
|
|
|
27055
27069
|
publishDestination(null);
|
|
27056
27070
|
};
|
|
27057
27071
|
|
|
27072
|
+
/**
|
|
27073
|
+
* The furniture around the pages — the fixed bars — photographed with them for
|
|
27074
|
+
* the length of a route transition, so that a piece of it belonging to ONE of
|
|
27075
|
+
* the two states takes part in the movement instead of appearing or vanishing
|
|
27076
|
+
* in a frame.
|
|
27077
|
+
*
|
|
27078
|
+
* Whether a bar is the frame or part of what changes is a fact about the PAIR
|
|
27079
|
+
* of states, never about the bar: the top bar of a list is the frame while one
|
|
27080
|
+
* goes into a row of that list, and part of what changes when the next page
|
|
27081
|
+
* takes the whole screen. So nothing is declared — a name is written on every
|
|
27082
|
+
* bar for the length of the movement, and the browser derives the rest:
|
|
27083
|
+
*
|
|
27084
|
+
* - a bar the two states SHARE is one element wearing one name on both sides.
|
|
27085
|
+
* The browser pairs the two pictures into one group and holds it where it
|
|
27086
|
+
* stands: the frame, and the pages move behind it exactly as before.
|
|
27087
|
+
* - a bar only one state has never meets a counterpart. It belongs to the page
|
|
27088
|
+
* that has it, so it travels with that page — leaving by the keyframes the
|
|
27089
|
+
* page being left leaves by, arriving by the ones the page arriving arrives
|
|
27090
|
+
* by — instead of going out with the render. Under the pages, so a page
|
|
27091
|
+
* coming over it covers it (see the z-order in route_transition.jsx).
|
|
27092
|
+
*
|
|
27093
|
+
* Which keyframes those are is published by the movement itself
|
|
27094
|
+
* (--navi-route-transition-leave / -enter, see route_transition.jsx): a
|
|
27095
|
+
* selector cannot say "the bars that have no counterpart" — the names are per
|
|
27096
|
+
* element, so there is nothing static to write — so the rule is written for
|
|
27097
|
+
* the length of the movement, over the names this one turned out to have.
|
|
27098
|
+
*
|
|
27099
|
+
* The name is per ELEMENT and kept for the element's whole life, which is what
|
|
27100
|
+
* makes "shared" mean shared: several bars can live on one edge
|
|
27101
|
+
* (layout/fixed_bar/fixed_bar_space.js), so a bar leaving and another one
|
|
27102
|
+
* arriving on that same edge are two names and two movements, not one bar
|
|
27103
|
+
* changing its mind.
|
|
27104
|
+
*
|
|
27105
|
+
* Worn only while a transition of navi's plays: named the rest of the time, a
|
|
27106
|
+
* bar would be captured during every view transition the APPLICATION starts —
|
|
27107
|
+
* dead to the pointer and drawn in the top layer for the length of a movement
|
|
27108
|
+
* that has nothing to do with it. Being unable to answer a press is the price
|
|
27109
|
+
* of being photographed, and a route transition is where it costs nothing:
|
|
27110
|
+
* both pages are pictures for those few hundred milliseconds anyway.
|
|
27111
|
+
*/
|
|
27112
|
+
|
|
27113
|
+
// What counts as furniture: what is pinned to an edge of the window and gives
|
|
27114
|
+
// its room back to the content (layout/fixed_bar/fixed_bar.jsx). A sticky row
|
|
27115
|
+
// inside the pages needs none of this — it lives in the area, so it is already
|
|
27116
|
+
// part of the pages' own picture.
|
|
27117
|
+
const FURNITURE_SELECTOR = ".navi_fixed_bar";
|
|
27118
|
+
// Worn by the root for the length of a route transition (route_transition.jsx
|
|
27119
|
+
// owns it). Written out rather than imported: importing the module that owns
|
|
27120
|
+
// it back into this one would close a cycle.
|
|
27121
|
+
const TRANSITION_ATTRIBUTE$1 = "data-navi-route-transition";
|
|
27122
|
+
const NAME_PROPERTY = "view-transition-name";
|
|
27123
|
+
const FURNITURE_NAME_PREFIX = "navi-transition-furniture-";
|
|
27124
|
+
|
|
27125
|
+
const nameByElement = new WeakMap();
|
|
27126
|
+
let nameCount = 0;
|
|
27127
|
+
|
|
27128
|
+
// Whose movement the names belong to, for the same reason the window has an
|
|
27129
|
+
// owner (transition_window.js): a movement ending after another has replaced
|
|
27130
|
+
// it must not strip what the new one is wearing.
|
|
27131
|
+
let furnitureOwner = null;
|
|
27132
|
+
let namedElements = new Set();
|
|
27133
|
+
// The rule giving the one-sided bars their movement, written for one movement
|
|
27134
|
+
// and taken down with it.
|
|
27135
|
+
let travelStyleElement = null;
|
|
27136
|
+
|
|
27137
|
+
/**
|
|
27138
|
+
* Name what stands around the area, before the picture of the state being left
|
|
27139
|
+
* is taken: the browser reads the names off the DOM as it stands when the
|
|
27140
|
+
* transition starts.
|
|
27141
|
+
*/
|
|
27142
|
+
const nameTransitionFurniture = (owner, areaElement) => {
|
|
27143
|
+
if (owner !== furnitureOwner) {
|
|
27144
|
+
furnitureOwner = owner;
|
|
27145
|
+
namedElements = new Set();
|
|
27146
|
+
}
|
|
27147
|
+
nameFurnitureAround(areaElement);
|
|
27148
|
+
};
|
|
27149
|
+
|
|
27150
|
+
/**
|
|
27151
|
+
* The state arriving has rendered and the second picture has not been taken:
|
|
27152
|
+
* the one moment both states of the furniture are known. A bar that just
|
|
27153
|
+
* mounted is named — one that survived the render keeps the name it has, which
|
|
27154
|
+
* is what pairs its two pictures — and the bars only one of the two states has
|
|
27155
|
+
* are given the movement of the page they belong to.
|
|
27156
|
+
*/
|
|
27157
|
+
const holdTransitionFurniture = (owner, areaElement) => {
|
|
27158
|
+
if (owner !== furnitureOwner) {
|
|
27159
|
+
return;
|
|
27160
|
+
}
|
|
27161
|
+
const namesLeaving = [];
|
|
27162
|
+
for (const element of namedElements) {
|
|
27163
|
+
if (!element.isConnected) {
|
|
27164
|
+
namesLeaving.push(nameByElement.get(element));
|
|
27165
|
+
}
|
|
27166
|
+
}
|
|
27167
|
+
const namesArriving = nameFurnitureAround(areaElement);
|
|
27168
|
+
const cssText = `${travelRule("old", namesLeaving, "--navi-route-transition-leave")}${travelRule("new", namesArriving, "--navi-route-transition-enter")}`;
|
|
27169
|
+
if (!cssText) {
|
|
27170
|
+
return;
|
|
27171
|
+
}
|
|
27172
|
+
travelStyleElement = document.createElement("style");
|
|
27173
|
+
travelStyleElement.textContent = cssText;
|
|
27174
|
+
document.head.appendChild(travelStyleElement);
|
|
27175
|
+
};
|
|
27176
|
+
|
|
27177
|
+
// Returns the elements it had to name, which are the ones the state arriving
|
|
27178
|
+
// brought: everything else was already wearing its name from the first pass.
|
|
27179
|
+
const nameFurnitureAround = (areaElement) => {
|
|
27180
|
+
const namesAdded = [];
|
|
27181
|
+
for (const element of document.querySelectorAll(FURNITURE_SELECTOR)) {
|
|
27182
|
+
if (namedElements.has(element)) {
|
|
27183
|
+
continue;
|
|
27184
|
+
}
|
|
27185
|
+
// Inside the area it is not furniture, it is the page: naming it would
|
|
27186
|
+
// punch a hole in the picture the movement is played on.
|
|
27187
|
+
if (areaElement.contains(element)) {
|
|
27188
|
+
continue;
|
|
27189
|
+
}
|
|
27190
|
+
let name = nameByElement.get(element);
|
|
27191
|
+
if (!name) {
|
|
27192
|
+
// A name the application wrote itself answers for that element, and it
|
|
27193
|
+
// is saying something navi is not: it wants that bar moved on the pages'
|
|
27194
|
+
// clock, by its own CSS. Asked only of a bar navi has never named — a
|
|
27195
|
+
// bar wearing one of ITS names is one this movement inherited from the
|
|
27196
|
+
// one it interrupted, and taking it for the application's would leave it
|
|
27197
|
+
// named for the rest of the document's life.
|
|
27198
|
+
if (getComputedStyle(element).viewTransitionName !== "none") {
|
|
27199
|
+
continue;
|
|
27200
|
+
}
|
|
27201
|
+
nameCount++;
|
|
27202
|
+
name = `${FURNITURE_NAME_PREFIX}${nameCount}`;
|
|
27203
|
+
nameByElement.set(element, name);
|
|
27204
|
+
}
|
|
27205
|
+
element.style.setProperty(NAME_PROPERTY, name);
|
|
27206
|
+
namedElements.add(element);
|
|
27207
|
+
namesAdded.push(name);
|
|
27208
|
+
}
|
|
27209
|
+
return namesAdded;
|
|
27210
|
+
};
|
|
27211
|
+
|
|
27212
|
+
// The movement, played on pictures no static rule can name. The keyframes are
|
|
27213
|
+
// read rather than guessed: a type navi ships publishes them, and so may one
|
|
27214
|
+
// an application writes — a type that publishes nothing leaves its furniture
|
|
27215
|
+
// to the browser's own fade, which is what the rule not being written means.
|
|
27216
|
+
const travelRule = (side, names, movementProperty) => {
|
|
27217
|
+
if (names.length === 0) {
|
|
27218
|
+
return "";
|
|
27219
|
+
}
|
|
27220
|
+
const animationName = getComputedStyle(document.documentElement)
|
|
27221
|
+
.getPropertyValue(movementProperty)
|
|
27222
|
+
.trim();
|
|
27223
|
+
if (!animationName) {
|
|
27224
|
+
return "";
|
|
27225
|
+
}
|
|
27226
|
+
const selector = names
|
|
27227
|
+
.map(
|
|
27228
|
+
(name) =>
|
|
27229
|
+
`:root[${TRANSITION_ATTRIBUTE$1}]::view-transition-${side}(${name})`,
|
|
27230
|
+
)
|
|
27231
|
+
.join(",");
|
|
27232
|
+
return `${selector}{animation-name:${animationName};animation-timing-function:ease;animation-fill-mode:both}`;
|
|
27233
|
+
};
|
|
27234
|
+
|
|
27235
|
+
const releaseTransitionFurniture = (owner) => {
|
|
27236
|
+
if (owner !== furnitureOwner) {
|
|
27237
|
+
return;
|
|
27238
|
+
}
|
|
27239
|
+
furnitureOwner = null;
|
|
27240
|
+
for (const element of namedElements) {
|
|
27241
|
+
element.style.removeProperty(NAME_PROPERTY);
|
|
27242
|
+
}
|
|
27243
|
+
namedElements = new Set();
|
|
27244
|
+
if (travelStyleElement) {
|
|
27245
|
+
travelStyleElement.remove();
|
|
27246
|
+
travelStyleElement = null;
|
|
27247
|
+
}
|
|
27248
|
+
};
|
|
27249
|
+
|
|
27058
27250
|
/**
|
|
27059
27251
|
* The window two pages are seen through while one replaces the other, measured
|
|
27060
27252
|
* once and published for the length of the movement.
|
|
27061
27253
|
*
|
|
27062
|
-
* Both ways of moving from one route to another need the same
|
|
27063
|
-
*
|
|
27254
|
+
* Both ways of moving from one route to another need the same numbers, so they
|
|
27255
|
+
* are written under the same names and read by the same CSS formulas —
|
|
27064
27256
|
* `route_travel.jsx` for a row a finger pushes, `route_transition.jsx` for a
|
|
27065
27257
|
* relation between two pages. Only one of them ever plays at a time (there is
|
|
27066
27258
|
* one view transition per document), which is what lets them share the names.
|
|
@@ -27071,16 +27263,25 @@ const releaseTransitionDestination = (owner) => {
|
|
|
27071
27263
|
* pictures of a transition are drawn in the top layer, above everything, so
|
|
27072
27264
|
* no overflow of the document reaches them: where to cut them can only be
|
|
27073
27265
|
* said from outside the document, and this is where that outside is known.
|
|
27074
|
-
* - **the
|
|
27075
|
-
* browser the
|
|
27076
|
-
*
|
|
27077
|
-
*
|
|
27078
|
-
*
|
|
27079
|
-
*
|
|
27080
|
-
*
|
|
27081
|
-
* same place in the
|
|
27082
|
-
*
|
|
27083
|
-
*
|
|
27266
|
+
* - **the box is the two states TOGETHER**, not either one of them. The
|
|
27267
|
+
* browser puts the group where the arriving state stands and animates it
|
|
27268
|
+
* from where the state being left stood; both are wrong for the same reason
|
|
27269
|
+
* — a window that is one of the two states cuts the other. Held at the
|
|
27270
|
+
* rectangle that contains both, nothing either picture holds is ever cut by
|
|
27271
|
+
* a box it never had, and the user never watches the window get anywhere.
|
|
27272
|
+
* - **where each state WAS**, which is not where the window stands: the two
|
|
27273
|
+
* states are at the same place in the layout without being at the same place
|
|
27274
|
+
* in the window — one page is scrolled and the other is not, one has a top
|
|
27275
|
+
* bar over it and the other has the screen. Each picture is placed at its
|
|
27276
|
+
* own corner inside the window.
|
|
27277
|
+
* - **the band the state being left kept free**, next to the one the arriving
|
|
27278
|
+
* state keeps free. A band is furniture — a fixed bar, a sticky row — and
|
|
27279
|
+
* the pictures are cut at it so they are not watched painting over it. Read
|
|
27280
|
+
* live it describes the arriving state alone, and a piece of furniture that
|
|
27281
|
+
* belongs to only ONE of the two states is then treated as the frame of
|
|
27282
|
+
* both: the page being left is cut at a bar it never had. Photographed here
|
|
27283
|
+
* at the one moment the state being left still exists, the cut can be taken
|
|
27284
|
+
* at what is furniture on BOTH sides (see the clip formulas).
|
|
27084
27285
|
*
|
|
27085
27286
|
* Only the measuring needs JS, and only for the one moment both states exist:
|
|
27086
27287
|
* the page arriving is in the DOM and the transition has not started playing.
|
|
@@ -27107,8 +27308,32 @@ const releaseTransitionDestination = (owner) => {
|
|
|
27107
27308
|
* Each one is a distance inward from the band the safe area already leaves
|
|
27108
27309
|
* free, so a row states its own height and nothing else: what the bars above
|
|
27109
27310
|
* it take is already counted.
|
|
27311
|
+
*
|
|
27312
|
+
* Registered as lengths for the same reason the safe area is (safe_area.js):
|
|
27313
|
+
* the band has to be readable in pixels at the moment a page leaves.
|
|
27110
27314
|
*/
|
|
27111
27315
|
const TRANSITION_WINDOW_CSS = /* css */ `
|
|
27316
|
+
@property --navi-transition-cover-top {
|
|
27317
|
+
syntax: "<length>";
|
|
27318
|
+
inherits: true;
|
|
27319
|
+
initial-value: 0px;
|
|
27320
|
+
}
|
|
27321
|
+
@property --navi-transition-cover-right {
|
|
27322
|
+
syntax: "<length>";
|
|
27323
|
+
inherits: true;
|
|
27324
|
+
initial-value: 0px;
|
|
27325
|
+
}
|
|
27326
|
+
@property --navi-transition-cover-bottom {
|
|
27327
|
+
syntax: "<length>";
|
|
27328
|
+
inherits: true;
|
|
27329
|
+
initial-value: 0px;
|
|
27330
|
+
}
|
|
27331
|
+
@property --navi-transition-cover-left {
|
|
27332
|
+
syntax: "<length>";
|
|
27333
|
+
inherits: true;
|
|
27334
|
+
initial-value: 0px;
|
|
27335
|
+
}
|
|
27336
|
+
|
|
27112
27337
|
@layer navi {
|
|
27113
27338
|
:root {
|
|
27114
27339
|
--navi-transition-cover-top: 0px;
|
|
@@ -27125,6 +27350,12 @@ const WINDOW_WIDTH_PROPERTY = "--navi-transition-window-width";
|
|
|
27125
27350
|
const WINDOW_HEIGHT_PROPERTY = "--navi-transition-window-height";
|
|
27126
27351
|
const WINDOW_OLD_TOP_PROPERTY = "--navi-transition-window-old-top";
|
|
27127
27352
|
const WINDOW_OLD_LEFT_PROPERTY = "--navi-transition-window-old-left";
|
|
27353
|
+
const WINDOW_NEW_TOP_PROPERTY = "--navi-transition-window-new-top";
|
|
27354
|
+
const WINDOW_NEW_LEFT_PROPERTY = "--navi-transition-window-new-left";
|
|
27355
|
+
const OLD_BAND_TOP_PROPERTY = "--navi-transition-old-band-top";
|
|
27356
|
+
const OLD_BAND_RIGHT_PROPERTY = "--navi-transition-old-band-right";
|
|
27357
|
+
const OLD_BAND_BOTTOM_PROPERTY = "--navi-transition-old-band-bottom";
|
|
27358
|
+
const OLD_BAND_LEFT_PROPERTY = "--navi-transition-old-band-left";
|
|
27128
27359
|
const WINDOW_PROPERTIES = [
|
|
27129
27360
|
WINDOW_TOP_PROPERTY,
|
|
27130
27361
|
WINDOW_LEFT_PROPERTY,
|
|
@@ -27132,6 +27363,12 @@ const WINDOW_PROPERTIES = [
|
|
|
27132
27363
|
WINDOW_HEIGHT_PROPERTY,
|
|
27133
27364
|
WINDOW_OLD_TOP_PROPERTY,
|
|
27134
27365
|
WINDOW_OLD_LEFT_PROPERTY,
|
|
27366
|
+
WINDOW_NEW_TOP_PROPERTY,
|
|
27367
|
+
WINDOW_NEW_LEFT_PROPERTY,
|
|
27368
|
+
OLD_BAND_TOP_PROPERTY,
|
|
27369
|
+
OLD_BAND_RIGHT_PROPERTY,
|
|
27370
|
+
OLD_BAND_BOTTOM_PROPERTY,
|
|
27371
|
+
OLD_BAND_LEFT_PROPERTY,
|
|
27135
27372
|
];
|
|
27136
27373
|
|
|
27137
27374
|
// Whose numbers are currently published. The window belongs to the movement
|
|
@@ -27140,21 +27377,50 @@ const WINDOW_PROPERTIES = [
|
|
|
27140
27377
|
// on.
|
|
27141
27378
|
let windowOwner = null;
|
|
27142
27379
|
|
|
27143
|
-
|
|
27380
|
+
/**
|
|
27381
|
+
* The state being left, taken while rendering is held — the page arriving is
|
|
27382
|
+
* not in the DOM yet, so this is still the one going away. Where its box
|
|
27383
|
+
* stands and what its furniture left free, which the arriving state answers
|
|
27384
|
+
* for differently and which nothing can be read back from once it is gone.
|
|
27385
|
+
*/
|
|
27386
|
+
const measureTransitionWindowState = (element) => {
|
|
27387
|
+
return {
|
|
27388
|
+
rect: element.getBoundingClientRect(),
|
|
27389
|
+
band: readBand(),
|
|
27390
|
+
};
|
|
27391
|
+
};
|
|
27392
|
+
|
|
27393
|
+
const holdTransitionWindow = (owner, element, stateBefore) => {
|
|
27394
|
+
const rectBefore = stateBefore.rect;
|
|
27395
|
+
const bandBefore = stateBefore.band;
|
|
27144
27396
|
const rectAfter = element.getBoundingClientRect();
|
|
27145
|
-
|
|
27146
|
-
//
|
|
27147
|
-
// would
|
|
27148
|
-
|
|
27149
|
-
|
|
27397
|
+
const bandAfter = readBand();
|
|
27398
|
+
// The rectangle that contains both states. It cannot be measured from one
|
|
27399
|
+
// side alone: a page arriving shorter than the one it replaces would cut the
|
|
27400
|
+
// one leaving, a page arriving taller would be cut itself, and either one
|
|
27401
|
+
// standing higher up than the other would be cut across.
|
|
27402
|
+
const top = rectBefore.top < rectAfter.top ? rectBefore.top : rectAfter.top;
|
|
27403
|
+
const left =
|
|
27404
|
+
rectBefore.left < rectAfter.left ? rectBefore.left : rectAfter.left;
|
|
27405
|
+
const bottom =
|
|
27406
|
+
rectBefore.bottom > rectAfter.bottom ? rectBefore.bottom : rectAfter.bottom;
|
|
27407
|
+
const right =
|
|
27408
|
+
rectBefore.right > rectAfter.right ? rectBefore.right : rectAfter.right;
|
|
27150
27409
|
windowOwner = owner;
|
|
27151
27410
|
const { style } = document.documentElement;
|
|
27152
|
-
style.setProperty(WINDOW_TOP_PROPERTY, `${
|
|
27153
|
-
style.setProperty(WINDOW_LEFT_PROPERTY, `${
|
|
27154
|
-
style.setProperty(WINDOW_WIDTH_PROPERTY, `${
|
|
27155
|
-
style.setProperty(WINDOW_HEIGHT_PROPERTY, `${
|
|
27411
|
+
style.setProperty(WINDOW_TOP_PROPERTY, `${top}px`);
|
|
27412
|
+
style.setProperty(WINDOW_LEFT_PROPERTY, `${left}px`);
|
|
27413
|
+
style.setProperty(WINDOW_WIDTH_PROPERTY, `${right - left}px`);
|
|
27414
|
+
style.setProperty(WINDOW_HEIGHT_PROPERTY, `${bottom - top}px`);
|
|
27156
27415
|
style.setProperty(WINDOW_OLD_TOP_PROPERTY, `${rectBefore.top}px`);
|
|
27157
27416
|
style.setProperty(WINDOW_OLD_LEFT_PROPERTY, `${rectBefore.left}px`);
|
|
27417
|
+
style.setProperty(WINDOW_NEW_TOP_PROPERTY, `${rectAfter.top}px`);
|
|
27418
|
+
style.setProperty(WINDOW_NEW_LEFT_PROPERTY, `${rectAfter.left}px`);
|
|
27419
|
+
const oldBand = bandTheCutMayBeRelaxedTo(bandBefore, bandAfter, rectAfter);
|
|
27420
|
+
style.setProperty(OLD_BAND_TOP_PROPERTY, `${oldBand.top}px`);
|
|
27421
|
+
style.setProperty(OLD_BAND_RIGHT_PROPERTY, `${oldBand.right}px`);
|
|
27422
|
+
style.setProperty(OLD_BAND_BOTTOM_PROPERTY, `${oldBand.bottom}px`);
|
|
27423
|
+
style.setProperty(OLD_BAND_LEFT_PROPERTY, `${oldBand.left}px`);
|
|
27158
27424
|
};
|
|
27159
27425
|
|
|
27160
27426
|
// The live layout takes the box back. A discontinuity by construction — the
|
|
@@ -27172,6 +27438,65 @@ const releaseTransitionWindow = (owner) => {
|
|
|
27172
27438
|
}
|
|
27173
27439
|
};
|
|
27174
27440
|
|
|
27441
|
+
// A band nothing can be wider than, published for an edge nothing is known
|
|
27442
|
+
// about: taken as the smaller of the two, it leaves the live band standing on
|
|
27443
|
+
// its own, which is the movement navi played before it knew any of this.
|
|
27444
|
+
const BAND_UNKNOWN = 1e6;
|
|
27445
|
+
|
|
27446
|
+
// What the furniture leaves free on each edge, as a distance inward from that
|
|
27447
|
+
// edge of the window: the app's safe area (everything pinned to the window's
|
|
27448
|
+
// edges) plus what covers the box from inside the document.
|
|
27449
|
+
const readBand = () => {
|
|
27450
|
+
const computedStyle = getComputedStyle(document.documentElement);
|
|
27451
|
+
const readEdge = (edge) => {
|
|
27452
|
+
const safeArea = parseFloat(
|
|
27453
|
+
computedStyle.getPropertyValue(`--navi-safe-area-inset-${edge}`),
|
|
27454
|
+
);
|
|
27455
|
+
const cover = parseFloat(
|
|
27456
|
+
computedStyle.getPropertyValue(`--navi-transition-cover-${edge}`),
|
|
27457
|
+
);
|
|
27458
|
+
// A browser that cannot register a custom property hands back the calc()
|
|
27459
|
+
// it was written as rather than what it computes to (see safe_area.js).
|
|
27460
|
+
if (!Number.isFinite(safeArea) || !Number.isFinite(cover)) {
|
|
27461
|
+
return BAND_UNKNOWN;
|
|
27462
|
+
}
|
|
27463
|
+
return safeArea + cover;
|
|
27464
|
+
};
|
|
27465
|
+
return {
|
|
27466
|
+
top: readEdge("top"),
|
|
27467
|
+
right: readEdge("right"),
|
|
27468
|
+
bottom: readEdge("bottom"),
|
|
27469
|
+
left: readEdge("left"),
|
|
27470
|
+
};
|
|
27471
|
+
};
|
|
27472
|
+
|
|
27473
|
+
// How far the cut may be opened for the picture being left — the band its own
|
|
27474
|
+
// state kept free, which is the whole point of photographing it: furniture
|
|
27475
|
+
// present in only one of the two states is not the frame, it is part of what
|
|
27476
|
+
// changes, and cutting the page being left at a bar it never had shows its
|
|
27477
|
+
// header being sliced instead of leaving.
|
|
27478
|
+
//
|
|
27479
|
+
// Never past what the picture ARRIVING needs, though: the cut is one line for
|
|
27480
|
+
// both pictures, so opening it for one opens it for the other. A box that runs
|
|
27481
|
+
// under the furniture it arrives beside — a page scrolled below a top bar —
|
|
27482
|
+
// would then be watched painting over that bar for the length of the movement,
|
|
27483
|
+
// which is what the cut exists to prevent. On such an edge the arriving band
|
|
27484
|
+
// stands, and the page being left is cut as it always was.
|
|
27485
|
+
const bandTheCutMayBeRelaxedTo = (bandBefore, bandAfter, rectAfter) => {
|
|
27486
|
+
return {
|
|
27487
|
+
top: rectAfter.top < bandAfter.top ? bandAfter.top : bandBefore.top,
|
|
27488
|
+
right:
|
|
27489
|
+
rectAfter.right > window.innerWidth - bandAfter.right
|
|
27490
|
+
? bandAfter.right
|
|
27491
|
+
: bandBefore.right,
|
|
27492
|
+
bottom:
|
|
27493
|
+
rectAfter.bottom > window.innerHeight - bandAfter.bottom
|
|
27494
|
+
? bandAfter.bottom
|
|
27495
|
+
: bandBefore.bottom,
|
|
27496
|
+
left: rectAfter.left < bandAfter.left ? bandAfter.left : bandBefore.left,
|
|
27497
|
+
};
|
|
27498
|
+
};
|
|
27499
|
+
|
|
27175
27500
|
installImportMetaCssBuild(import.meta);/**
|
|
27176
27501
|
* How two routes move against each other, said one relation at a time —
|
|
27177
27502
|
* without putting them in a row, and without a box in the tree.
|
|
@@ -27302,18 +27627,47 @@ const css$10 = /* css */`
|
|
|
27302
27627
|
the document animates, and the document belongs to the application the
|
|
27303
27628
|
rest of the time. */
|
|
27304
27629
|
:root[data-navi-route-transition] {
|
|
27305
|
-
/* With an area marked, the page AROUND it is not taken as
|
|
27306
|
-
exactly one of the two names below exists at a time, and the
|
|
27307
|
-
can be written once for both. It is also
|
|
27308
|
-
|
|
27309
|
-
|
|
27310
|
-
|
|
27311
|
-
|
|
27312
|
-
|
|
27313
|
-
|
|
27314
|
-
|
|
27630
|
+
/* With an area marked, the page AROUND it is not taken as one picture —
|
|
27631
|
+
so exactly one of the two names below exists at a time, and the
|
|
27632
|
+
movements can be written once for both. It is also the only way the
|
|
27633
|
+
furniture can take part in the movement: photographed with the whole
|
|
27634
|
+
document it is one picture the size of the screen, and a bar can be
|
|
27635
|
+
neither held where it stands nor moved with the page it belongs to
|
|
27636
|
+
inside it. Each bar is photographed on its own instead (see
|
|
27637
|
+
transition_furniture.js), and nothing shows through where the pages are
|
|
27638
|
+
— their two pictures cover the area's rectangle between them at every
|
|
27639
|
+
moment. */
|
|
27315
27640
|
&[data-navi-route-transition-target="area"] {
|
|
27316
27641
|
view-transition-name: none;
|
|
27642
|
+
|
|
27643
|
+
/* The pages travel OVER the furniture. Everything else captured while an
|
|
27644
|
+
area is marked is a fixed bar wearing a name of navi's own for the
|
|
27645
|
+
length of the movement (transition_furniture.js): a bar the two states
|
|
27646
|
+
share is one group the browser holds where it stands, and a bar only
|
|
27647
|
+
one of them has stands there too, with no counterpart to move to. Both
|
|
27648
|
+
belong under the pages — that is what lets a page come over a bar that
|
|
27649
|
+
is going away, and a page leaving uncover the bar arriving behind it.
|
|
27650
|
+
|
|
27651
|
+
Ordered here rather than left to the DOM, which decides it otherwise:
|
|
27652
|
+
where an application puts its bars relative to the area is its own
|
|
27653
|
+
business. A name and \`*\` weigh the same, so the two rules are read in
|
|
27654
|
+
the order they are written. */
|
|
27655
|
+
&::view-transition-group(*) {
|
|
27656
|
+
z-index: 0;
|
|
27657
|
+
}
|
|
27658
|
+
&::view-transition-group(navi-route-transition) {
|
|
27659
|
+
z-index: 1;
|
|
27660
|
+
}
|
|
27661
|
+
|
|
27662
|
+
/* And on the transition's own clock, whatever was captured. How long the
|
|
27663
|
+
movement lasts is a fact about the movement, not about the pages: a bar
|
|
27664
|
+
left on the browser's own 250ms would be gone a third of the way into a
|
|
27665
|
+
longer one, instead of being covered by the page coming over it. */
|
|
27666
|
+
&::view-transition-group(*),
|
|
27667
|
+
&::view-transition-old(*),
|
|
27668
|
+
&::view-transition-new(*) {
|
|
27669
|
+
animation-duration: var(--navi-route-transition-duration, 300ms);
|
|
27670
|
+
}
|
|
27317
27671
|
}
|
|
27318
27672
|
|
|
27319
27673
|
&::view-transition-old(root),
|
|
@@ -27342,11 +27696,22 @@ const css$10 = /* css */`
|
|
|
27342
27696
|
overflow: clip;
|
|
27343
27697
|
}
|
|
27344
27698
|
&::view-transition-group(navi-route-transition) {
|
|
27345
|
-
/* Held still for the whole transition, at the
|
|
27346
|
-
|
|
27347
|
-
|
|
27348
|
-
|
|
27349
|
-
|
|
27699
|
+
/* Held still for the whole transition, at the rectangle that contains
|
|
27700
|
+
both states (see transition_window.js). Held by dropping the group's
|
|
27701
|
+
animation rather than by winning against it with !important. The
|
|
27702
|
+
browser puts the group where the ARRIVING area stands, so it is moved
|
|
27703
|
+
from there back to the window's own corner. */
|
|
27704
|
+
top: calc(
|
|
27705
|
+
var(--navi-transition-window-top) - var(
|
|
27706
|
+
--navi-transition-window-new-top
|
|
27707
|
+
)
|
|
27708
|
+
);
|
|
27709
|
+
left: calc(
|
|
27710
|
+
var(--navi-transition-window-left) - var(
|
|
27711
|
+
--navi-transition-window-new-left
|
|
27712
|
+
)
|
|
27713
|
+
);
|
|
27714
|
+
width: var(--navi-transition-window-width);
|
|
27350
27715
|
height: var(--navi-transition-window-height);
|
|
27351
27716
|
animation-name: none;
|
|
27352
27717
|
|
|
@@ -27365,31 +27730,51 @@ const css$10 = /* css */`
|
|
|
27365
27730
|
the pages covers the top of the area exactly as a fixed bar covers the
|
|
27366
27731
|
top of the screen. Both are read rather than asked for, so one that
|
|
27367
27732
|
grows, shrinks or unmounts mid-transition is followed without anything
|
|
27368
|
-
being told.
|
|
27369
|
-
|
|
27733
|
+
being told.
|
|
27734
|
+
|
|
27735
|
+
Read live, though, they describe the state ARRIVING and nothing else,
|
|
27736
|
+
so the cut is taken at the smaller of that and the band the state
|
|
27737
|
+
being left kept free (--navi-transition-old-band-*, photographed while
|
|
27738
|
+
both still existed). Furniture standing in BOTH states is the frame:
|
|
27739
|
+
the pages move behind it and are cut at it. Furniture standing in one
|
|
27740
|
+
of them is part of what changes, and cutting the page being left at a
|
|
27741
|
+
bar it never had shows its own header being sliced instead of
|
|
27742
|
+
leaving. */
|
|
27370
27743
|
--navi-route-transition-clip-top: max(
|
|
27371
27744
|
0px,
|
|
27372
|
-
|
|
27373
|
-
|
|
27745
|
+
min(
|
|
27746
|
+
var(--navi-safe-area-inset-top) + var(--navi-transition-cover-top),
|
|
27747
|
+
var(--navi-transition-old-band-top)
|
|
27748
|
+
) - var(--navi-transition-window-top)
|
|
27374
27749
|
);
|
|
27375
27750
|
--navi-route-transition-clip-left: max(
|
|
27376
27751
|
0px,
|
|
27377
|
-
|
|
27378
|
-
|
|
27752
|
+
min(
|
|
27753
|
+
var(--navi-safe-area-inset-left) + var(--navi-transition-cover-left),
|
|
27754
|
+
var(--navi-transition-old-band-left)
|
|
27755
|
+
) - var(--navi-transition-window-left)
|
|
27379
27756
|
);
|
|
27380
27757
|
--navi-route-transition-clip-bottom: max(
|
|
27381
27758
|
0px,
|
|
27382
27759
|
var(--navi-transition-window-top) +
|
|
27383
27760
|
var(--navi-transition-window-height) +
|
|
27384
|
-
|
|
27385
|
-
|
|
27761
|
+
min(
|
|
27762
|
+
var(--navi-safe-area-inset-bottom) +
|
|
27763
|
+
var(--navi-transition-cover-bottom),
|
|
27764
|
+
var(--navi-transition-old-band-bottom)
|
|
27765
|
+
) -
|
|
27766
|
+
100dvh
|
|
27386
27767
|
);
|
|
27387
27768
|
--navi-route-transition-clip-right: max(
|
|
27388
27769
|
0px,
|
|
27389
27770
|
var(--navi-transition-window-left) +
|
|
27390
27771
|
var(--navi-transition-window-width) +
|
|
27391
|
-
|
|
27392
|
-
|
|
27772
|
+
min(
|
|
27773
|
+
var(--navi-safe-area-inset-right) +
|
|
27774
|
+
var(--navi-transition-cover-right),
|
|
27775
|
+
var(--navi-transition-old-band-right)
|
|
27776
|
+
) -
|
|
27777
|
+
100dvw
|
|
27393
27778
|
);
|
|
27394
27779
|
clip-path: inset(
|
|
27395
27780
|
var(--navi-route-transition-clip-top)
|
|
@@ -27416,10 +27801,12 @@ const css$10 = /* css */`
|
|
|
27416
27801
|
) - var(--navi-route-transition-clip-bottom)
|
|
27417
27802
|
);
|
|
27418
27803
|
}
|
|
27804
|
+
/* Each picture at the corner its own state stood at, which is not the
|
|
27805
|
+
window's: the window contains both states, and a state that is scrolled
|
|
27806
|
+
— or that stands under a bar the other one does not have — is somewhere
|
|
27807
|
+
inside it (see transition_window.js). Offset here rather than by
|
|
27808
|
+
\`translate\`, which the movement itself uses. */
|
|
27419
27809
|
&::view-transition-old(navi-route-transition) {
|
|
27420
|
-
/* Where the area WAS on screen, which is not where the window stands
|
|
27421
|
-
(see transition_window.js). Offset here rather than by \`translate\`,
|
|
27422
|
-
which the movement itself uses. */
|
|
27423
27810
|
top: calc(
|
|
27424
27811
|
var(--navi-transition-window-old-top) - var(
|
|
27425
27812
|
--navi-transition-window-top
|
|
@@ -27431,6 +27818,18 @@ const css$10 = /* css */`
|
|
|
27431
27818
|
)
|
|
27432
27819
|
);
|
|
27433
27820
|
}
|
|
27821
|
+
&::view-transition-new(navi-route-transition) {
|
|
27822
|
+
top: calc(
|
|
27823
|
+
var(--navi-transition-window-new-top) - var(
|
|
27824
|
+
--navi-transition-window-top
|
|
27825
|
+
)
|
|
27826
|
+
);
|
|
27827
|
+
left: calc(
|
|
27828
|
+
var(--navi-transition-window-new-left) - var(
|
|
27829
|
+
--navi-transition-window-left
|
|
27830
|
+
)
|
|
27831
|
+
);
|
|
27832
|
+
}
|
|
27434
27833
|
|
|
27435
27834
|
/* ------------------------------------------------------------------
|
|
27436
27835
|
The movements. One of \`root\` and \`navi-route-transition\` exists at a
|
|
@@ -27459,47 +27858,29 @@ const css$10 = /* css */`
|
|
|
27459
27858
|
object-position: top left;
|
|
27460
27859
|
/* Two pages crossing are two solid things, and seeing through one to
|
|
27461
27860
|
the other says they are the same page changing its mind. A movement
|
|
27462
|
-
that
|
|
27463
|
-
|
|
27861
|
+
that fades on one of its two sides wants the opposite, and says so —
|
|
27862
|
+
see zoom below. */
|
|
27464
27863
|
mix-blend-mode: normal;
|
|
27465
27864
|
animation-fill-mode: both;
|
|
27466
27865
|
}
|
|
27467
27866
|
}
|
|
27468
27867
|
|
|
27469
|
-
/*
|
|
27470
|
-
|
|
27471
|
-
|
|
27472
|
-
|
|
27473
|
-
|
|
27474
|
-
|
|
27475
|
-
|
|
27476
|
-
|
|
27477
|
-
&::view-transition-old(navi-route-transition),
|
|
27478
|
-
&::view-transition-new(navi-route-transition) {
|
|
27479
|
-
animation-timing-function: ease;
|
|
27480
|
-
}
|
|
27481
|
-
}
|
|
27482
|
-
|
|
27868
|
+
/* Which keyframes a page leaves and arrives by, said as a VALUE on the
|
|
27869
|
+
root rather than only as a rule on the pictures. That pair IS the
|
|
27870
|
+
movement, and the pages are not the only thing playing it: a fixed bar
|
|
27871
|
+
the two states do not share travels with the page it belongs to, and its
|
|
27872
|
+
picture wears a name nobody can write a selector for (it is per element
|
|
27873
|
+
— see transition_furniture.js). Read from here, it is given the same
|
|
27874
|
+
two. A type an application defines its own way is free to publish them
|
|
27875
|
+
and have its furniture travel too. */
|
|
27483
27876
|
&[data-navi-route-transition-type="slide-x"] {
|
|
27484
27877
|
&[data-navi-route-transition="forward"] {
|
|
27485
|
-
|
|
27486
|
-
|
|
27487
|
-
animation-name: navi-route-transition-leave-towards-start;
|
|
27488
|
-
}
|
|
27489
|
-
&::view-transition-new(root),
|
|
27490
|
-
&::view-transition-new(navi-route-transition) {
|
|
27491
|
-
animation-name: navi-route-transition-enter-from-end;
|
|
27492
|
-
}
|
|
27878
|
+
--navi-route-transition-leave: navi-route-transition-leave-towards-start;
|
|
27879
|
+
--navi-route-transition-enter: navi-route-transition-enter-from-end;
|
|
27493
27880
|
}
|
|
27494
27881
|
&[data-navi-route-transition="back"] {
|
|
27495
|
-
|
|
27496
|
-
|
|
27497
|
-
animation-name: navi-route-transition-leave-towards-end;
|
|
27498
|
-
}
|
|
27499
|
-
&::view-transition-new(root),
|
|
27500
|
-
&::view-transition-new(navi-route-transition) {
|
|
27501
|
-
animation-name: navi-route-transition-enter-from-start;
|
|
27502
|
-
}
|
|
27882
|
+
--navi-route-transition-leave: navi-route-transition-leave-towards-end;
|
|
27883
|
+
--navi-route-transition-enter: navi-route-transition-enter-from-start;
|
|
27503
27884
|
}
|
|
27504
27885
|
}
|
|
27505
27886
|
|
|
@@ -27508,24 +27889,12 @@ const css$10 = /* css */`
|
|
|
27508
27889
|
coming up from below. */
|
|
27509
27890
|
&[data-navi-route-transition-type="slide-y"] {
|
|
27510
27891
|
&[data-navi-route-transition="forward"] {
|
|
27511
|
-
|
|
27512
|
-
|
|
27513
|
-
animation-name: navi-route-transition-leave-towards-top;
|
|
27514
|
-
}
|
|
27515
|
-
&::view-transition-new(root),
|
|
27516
|
-
&::view-transition-new(navi-route-transition) {
|
|
27517
|
-
animation-name: navi-route-transition-enter-from-bottom;
|
|
27518
|
-
}
|
|
27892
|
+
--navi-route-transition-leave: navi-route-transition-leave-towards-top;
|
|
27893
|
+
--navi-route-transition-enter: navi-route-transition-enter-from-bottom;
|
|
27519
27894
|
}
|
|
27520
27895
|
&[data-navi-route-transition="back"] {
|
|
27521
|
-
|
|
27522
|
-
|
|
27523
|
-
animation-name: navi-route-transition-leave-towards-bottom;
|
|
27524
|
-
}
|
|
27525
|
-
&::view-transition-new(root),
|
|
27526
|
-
&::view-transition-new(navi-route-transition) {
|
|
27527
|
-
animation-name: navi-route-transition-enter-from-top;
|
|
27528
|
-
}
|
|
27896
|
+
--navi-route-transition-leave: navi-route-transition-leave-towards-bottom;
|
|
27897
|
+
--navi-route-transition-enter: navi-route-transition-enter-from-top;
|
|
27529
27898
|
}
|
|
27530
27899
|
}
|
|
27531
27900
|
|
|
@@ -27536,79 +27905,90 @@ const css$10 = /* css */`
|
|
|
27536
27905
|
fade. */
|
|
27537
27906
|
&[data-navi-route-transition-type="cover-x"] {
|
|
27538
27907
|
&[data-navi-route-transition="forward"] {
|
|
27539
|
-
|
|
27540
|
-
|
|
27541
|
-
animation-name: navi-route-transition-still;
|
|
27542
|
-
}
|
|
27543
|
-
&::view-transition-new(root),
|
|
27544
|
-
&::view-transition-new(navi-route-transition) {
|
|
27545
|
-
animation-name: navi-route-transition-enter-from-end;
|
|
27546
|
-
}
|
|
27908
|
+
--navi-route-transition-leave: navi-route-transition-still;
|
|
27909
|
+
--navi-route-transition-enter: navi-route-transition-enter-from-end;
|
|
27547
27910
|
}
|
|
27548
27911
|
&[data-navi-route-transition="back"] {
|
|
27912
|
+
--navi-route-transition-leave: navi-route-transition-leave-towards-end;
|
|
27913
|
+
--navi-route-transition-enter: navi-route-transition-still;
|
|
27549
27914
|
&::view-transition-old(root),
|
|
27550
27915
|
&::view-transition-old(navi-route-transition) {
|
|
27551
27916
|
/* The page leaving is the cover: it must slide off ABOVE the one it
|
|
27552
27917
|
uncovers, against the browser's default of drawing the new page on
|
|
27553
27918
|
top. */
|
|
27554
27919
|
z-index: 1;
|
|
27555
|
-
animation-name: navi-route-transition-leave-towards-end;
|
|
27556
|
-
}
|
|
27557
|
-
&::view-transition-new(root),
|
|
27558
|
-
&::view-transition-new(navi-route-transition) {
|
|
27559
|
-
animation-name: navi-route-transition-still;
|
|
27560
27920
|
}
|
|
27561
27921
|
}
|
|
27562
27922
|
}
|
|
27563
27923
|
&[data-navi-route-transition-type="cover-y"] {
|
|
27564
27924
|
&[data-navi-route-transition="forward"] {
|
|
27565
|
-
|
|
27566
|
-
|
|
27567
|
-
animation-name: navi-route-transition-still;
|
|
27568
|
-
}
|
|
27569
|
-
&::view-transition-new(root),
|
|
27570
|
-
&::view-transition-new(navi-route-transition) {
|
|
27571
|
-
animation-name: navi-route-transition-enter-from-bottom;
|
|
27572
|
-
}
|
|
27925
|
+
--navi-route-transition-leave: navi-route-transition-still;
|
|
27926
|
+
--navi-route-transition-enter: navi-route-transition-enter-from-bottom;
|
|
27573
27927
|
}
|
|
27574
27928
|
&[data-navi-route-transition="back"] {
|
|
27929
|
+
--navi-route-transition-leave: navi-route-transition-leave-towards-bottom;
|
|
27930
|
+
--navi-route-transition-enter: navi-route-transition-still;
|
|
27575
27931
|
&::view-transition-old(root),
|
|
27576
27932
|
&::view-transition-old(navi-route-transition) {
|
|
27577
27933
|
z-index: 1;
|
|
27578
|
-
animation-name: navi-route-transition-leave-towards-bottom;
|
|
27579
|
-
}
|
|
27580
|
-
&::view-transition-new(root),
|
|
27581
|
-
&::view-transition-new(navi-route-transition) {
|
|
27582
|
-
animation-name: navi-route-transition-still;
|
|
27583
27934
|
}
|
|
27584
27935
|
}
|
|
27585
27936
|
}
|
|
27586
27937
|
|
|
27587
27938
|
/* Going deeper is coming closer: the page arriving lands from slightly too
|
|
27588
27939
|
big, and going back it is the page leaving that grows away. The other
|
|
27589
|
-
side
|
|
27940
|
+
side fades, which is what the browser would have done there anyway —
|
|
27941
|
+
written out so that this type says both of its halves like every
|
|
27942
|
+
other. */
|
|
27590
27943
|
&[data-navi-route-transition-type="zoom"] {
|
|
27944
|
+
&[data-navi-route-transition="forward"] {
|
|
27945
|
+
--navi-route-transition-leave: navi-route-transition-fade-out;
|
|
27946
|
+
--navi-route-transition-enter: navi-route-transition-zoom-in;
|
|
27947
|
+
}
|
|
27948
|
+
&[data-navi-route-transition="back"] {
|
|
27949
|
+
--navi-route-transition-leave: navi-route-transition-zoom-out;
|
|
27950
|
+
--navi-route-transition-enter: navi-route-transition-fade-in;
|
|
27951
|
+
}
|
|
27591
27952
|
&::view-transition-old(root),
|
|
27592
27953
|
&::view-transition-new(root),
|
|
27593
27954
|
&::view-transition-old(navi-route-transition),
|
|
27594
27955
|
&::view-transition-new(navi-route-transition) {
|
|
27595
|
-
/* One side of this one is
|
|
27596
|
-
|
|
27597
|
-
|
|
27598
|
-
movement. */
|
|
27956
|
+
/* One side of this one is a fade, and a fade is two half-transparent
|
|
27957
|
+
pictures: they must ADD up rather than cover each other, or the page
|
|
27958
|
+
behind shows through the middle of the movement. */
|
|
27599
27959
|
mix-blend-mode: plus-lighter;
|
|
27600
27960
|
}
|
|
27601
|
-
|
|
27602
|
-
|
|
27603
|
-
|
|
27604
|
-
|
|
27605
|
-
|
|
27961
|
+
}
|
|
27962
|
+
|
|
27963
|
+
/* The pages play what the type published. Written once for the types navi
|
|
27964
|
+
ships and for those only: a type an application defines writes its own
|
|
27965
|
+
rule, and one of navi's here would race it on cascade order. */
|
|
27966
|
+
&[data-navi-route-transition-type="slide-x"],
|
|
27967
|
+
&[data-navi-route-transition-type="slide-y"],
|
|
27968
|
+
&[data-navi-route-transition-type="cover-x"],
|
|
27969
|
+
&[data-navi-route-transition-type="cover-y"],
|
|
27970
|
+
&[data-navi-route-transition-type="zoom"] {
|
|
27971
|
+
&::view-transition-old(root),
|
|
27972
|
+
&::view-transition-old(navi-route-transition) {
|
|
27973
|
+
animation-name: var(--navi-route-transition-leave);
|
|
27606
27974
|
}
|
|
27607
|
-
|
|
27608
|
-
|
|
27609
|
-
|
|
27610
|
-
|
|
27611
|
-
|
|
27975
|
+
&::view-transition-new(root),
|
|
27976
|
+
&::view-transition-new(navi-route-transition) {
|
|
27977
|
+
animation-name: var(--navi-route-transition-enter);
|
|
27978
|
+
}
|
|
27979
|
+
}
|
|
27980
|
+
|
|
27981
|
+
/* Eased, which is a taste about THESE four: a custom type says its own
|
|
27982
|
+
curve, and zoom keeps the browser's. */
|
|
27983
|
+
&[data-navi-route-transition-type="slide-x"],
|
|
27984
|
+
&[data-navi-route-transition-type="slide-y"],
|
|
27985
|
+
&[data-navi-route-transition-type="cover-x"],
|
|
27986
|
+
&[data-navi-route-transition-type="cover-y"] {
|
|
27987
|
+
&::view-transition-old(root),
|
|
27988
|
+
&::view-transition-new(root),
|
|
27989
|
+
&::view-transition-old(navi-route-transition),
|
|
27990
|
+
&::view-transition-new(navi-route-transition) {
|
|
27991
|
+
animation-timing-function: ease;
|
|
27612
27992
|
}
|
|
27613
27993
|
}
|
|
27614
27994
|
}
|
|
@@ -27675,6 +28055,19 @@ const css$10 = /* css */`
|
|
|
27675
28055
|
translate: 0 0;
|
|
27676
28056
|
}
|
|
27677
28057
|
}
|
|
28058
|
+
/* The browser's own fade, written out: a movement says both of its halves,
|
|
28059
|
+
so that whoever else plays it (transition_furniture.js) can be given the
|
|
28060
|
+
same one. */
|
|
28061
|
+
@keyframes navi-route-transition-fade-out {
|
|
28062
|
+
to {
|
|
28063
|
+
opacity: 0;
|
|
28064
|
+
}
|
|
28065
|
+
}
|
|
28066
|
+
@keyframes navi-route-transition-fade-in {
|
|
28067
|
+
from {
|
|
28068
|
+
opacity: 0;
|
|
28069
|
+
}
|
|
28070
|
+
}
|
|
27678
28071
|
`;
|
|
27679
28072
|
|
|
27680
28073
|
/**
|
|
@@ -28113,12 +28506,17 @@ const beginTransition = ({
|
|
|
28113
28506
|
warnOnce("several-areas", `${areaElements.length} elements carry ${TRANSITION_AREA_ATTRIBUTE}. They all take the same view-transition-name, and a name belongs to one element at a time: the browser refuses EVERY view transition of the document while this holds. Mark the one element the pages live in.`);
|
|
28114
28507
|
}
|
|
28115
28508
|
const areaElement = areaElements.length > 0 ? areaElements[0] : null;
|
|
28116
|
-
// The area as it stands before anything moves
|
|
28117
|
-
// still the page being
|
|
28118
|
-
|
|
28509
|
+
// The area as it stands before anything moves, and the band the furniture
|
|
28510
|
+
// around it leaves free: rendering is held, so both are still the page being
|
|
28511
|
+
// left (see transition_window.js).
|
|
28512
|
+
let areaStateBefore = null;
|
|
28119
28513
|
if (areaElement) {
|
|
28120
28514
|
documentElement.setAttribute(TRANSITION_TARGET_ATTRIBUTE, "area");
|
|
28121
|
-
|
|
28515
|
+
// Said before the picture is taken, like every name (see
|
|
28516
|
+
// transition_furniture.js): what the bars are wearing when the transition
|
|
28517
|
+
// starts is what the browser photographs.
|
|
28518
|
+
nameTransitionFurniture(transition, areaElement);
|
|
28519
|
+
areaStateBefore = measureTransitionWindowState(areaElement);
|
|
28122
28520
|
}
|
|
28123
28521
|
// A duration of this relation's own, worn for the length of the transition —
|
|
28124
28522
|
// and whatever the application had written inline put back afterwards, not
|
|
@@ -28188,7 +28586,8 @@ const beginTransition = ({
|
|
|
28188
28586
|
// The page arriving is in the DOM and the transition has not started
|
|
28189
28587
|
// playing: the one moment both states of the area can be known.
|
|
28190
28588
|
if (areaElement) {
|
|
28191
|
-
|
|
28589
|
+
holdTransitionFurniture(transition, areaElement);
|
|
28590
|
+
holdTransitionWindow(transition, areaElement, areaStateBefore);
|
|
28192
28591
|
}
|
|
28193
28592
|
});
|
|
28194
28593
|
const end = () => {
|
|
@@ -28206,6 +28605,7 @@ const beginTransition = ({
|
|
|
28206
28605
|
documentElement.removeAttribute(TRANSITION_TARGET_ATTRIBUTE);
|
|
28207
28606
|
releaseTransitionWindow(transition);
|
|
28208
28607
|
releaseTransitionDestination(transition);
|
|
28608
|
+
releaseTransitionFurniture(transition);
|
|
28209
28609
|
if (restoreDuration) {
|
|
28210
28610
|
restoreDuration();
|
|
28211
28611
|
}
|
|
@@ -28248,7 +28648,7 @@ const warnAboutNamesEscapingArea = (areaElement, capturedNames) => {
|
|
|
28248
28648
|
}
|
|
28249
28649
|
let escapedName = null;
|
|
28250
28650
|
for (const name of capturedNames) {
|
|
28251
|
-
if (name === "root" || name === AREA_NAME) {
|
|
28651
|
+
if (name === "root" || name === AREA_NAME || name.startsWith(FURNITURE_NAME_PREFIX)) {
|
|
28252
28652
|
continue;
|
|
28253
28653
|
}
|
|
28254
28654
|
escapedName = name;
|
|
@@ -28333,16 +28733,37 @@ const pageIsCurrent$1 = ({
|
|
|
28333
28733
|
};
|
|
28334
28734
|
// The FIRST page that answers, and every page read all the same: a page that
|
|
28335
28735
|
// is not the current one today is the one that must wake the reader tomorrow.
|
|
28736
|
+
//
|
|
28737
|
+
// Two of them answering at once is the one thing this reading cannot get right.
|
|
28738
|
+
// Relations are declared one pair at a time, so the row read here is the order
|
|
28739
|
+
// they happened to be written in — an order nothing in the application shows —
|
|
28740
|
+
// and the movement then played is the one written for whichever page was
|
|
28741
|
+
// mentioned first. A movement is a movement: it looks deliberate, which is why
|
|
28742
|
+
// the overlap is said out loud here, where both pages are known.
|
|
28336
28743
|
const currentPageIndex$1 = pages => {
|
|
28337
28744
|
let currentIndex = -1;
|
|
28338
28745
|
for (let i = 0; i < pages.length; i++) {
|
|
28339
28746
|
const isCurrent = pageIsCurrent$1(pages[i]);
|
|
28340
|
-
if (isCurrent
|
|
28747
|
+
if (!isCurrent) {
|
|
28748
|
+
continue;
|
|
28749
|
+
}
|
|
28750
|
+
if (currentIndex === -1) {
|
|
28341
28751
|
currentIndex = i;
|
|
28752
|
+
continue;
|
|
28342
28753
|
}
|
|
28754
|
+
warnPagesBothCurrent(pages[currentIndex], pages[i]);
|
|
28343
28755
|
}
|
|
28344
28756
|
return currentIndex;
|
|
28345
28757
|
};
|
|
28758
|
+
const warnPagesBothCurrent = (pageKept, pageIgnored) => {
|
|
28759
|
+
const kept = describePage(pageKept);
|
|
28760
|
+
const ignored = describePage(pageIgnored);
|
|
28761
|
+
warnOnce(`both-current:${kept}|${ignored}`, `${kept} and ${ignored} are both current on "${window.location.pathname}": two relations claim this url. A relation is resolved through which page is current, so the one mentioned first in a defineRouteTransition call wins — ${kept} — and its movement plays whichever of the two the application is really showing. Make one of them decline this url with a param constraint (see navigation.md, "Which values a param accepts").`);
|
|
28762
|
+
};
|
|
28763
|
+
const describePage = ({
|
|
28764
|
+
route,
|
|
28765
|
+
params
|
|
28766
|
+
}) => params ? `${route} with ${JSON.stringify(params)}` : `${route}`;
|
|
28346
28767
|
|
|
28347
28768
|
installImportMetaCssBuild(import.meta);/**
|
|
28348
28769
|
* Dragging from one route to the next, when the tabs of a page are URLs.
|
|
@@ -28480,17 +28901,16 @@ const css$$ = /* css */`
|
|
|
28480
28901
|
same page changing its mind. */
|
|
28481
28902
|
mix-blend-mode: normal;
|
|
28482
28903
|
}
|
|
28904
|
+
/* Each picture at the corner its own box stood at, which is not the
|
|
28905
|
+
window's: the window contains both boxes, and the two are at the same
|
|
28906
|
+
place in the layout without being at the same place in the window — one
|
|
28907
|
+
page is scrolled and the other is not, so the box being left starts
|
|
28908
|
+
higher up. Left at the window's own corner the page being left would be
|
|
28909
|
+
seen jumping back to its top before it even begins to leave. Offset here
|
|
28910
|
+
rather than by \`translate\`, which the movement itself uses, and at its
|
|
28911
|
+
own size rather than the group's so that nothing is cut off the far side
|
|
28912
|
+
of the shift (see transition_window.js). */
|
|
28483
28913
|
&::view-transition-old(navi-route-travel) {
|
|
28484
|
-
/* Where the page being left WAS on screen, which is not where the group
|
|
28485
|
-
stands: the group is at the arriving box (its position animation is
|
|
28486
|
-
dropped along with its height one, below), and the two boxes are at the
|
|
28487
|
-
same place in the layout without being at the same place in the window
|
|
28488
|
-
— one page is scrolled and the other is not, so the box being left
|
|
28489
|
-
starts higher up. Left at the group's own corner the page being left
|
|
28490
|
-
would be seen jumping back to its top before it even begins to leave.
|
|
28491
|
-
Offset here rather than by \`translate\`, which the movement itself uses,
|
|
28492
|
-
and at its own size rather than the group's so that nothing is cut off
|
|
28493
|
-
the far side of the shift (see transition_window.js). */
|
|
28494
28914
|
top: calc(
|
|
28495
28915
|
var(--navi-transition-window-old-top) - var(
|
|
28496
28916
|
--navi-transition-window-top
|
|
@@ -28503,6 +28923,19 @@ const css$$ = /* css */`
|
|
|
28503
28923
|
);
|
|
28504
28924
|
width: auto;
|
|
28505
28925
|
}
|
|
28926
|
+
&::view-transition-new(navi-route-travel) {
|
|
28927
|
+
top: calc(
|
|
28928
|
+
var(--navi-transition-window-new-top) - var(
|
|
28929
|
+
--navi-transition-window-top
|
|
28930
|
+
)
|
|
28931
|
+
);
|
|
28932
|
+
left: calc(
|
|
28933
|
+
var(--navi-transition-window-new-left) - var(
|
|
28934
|
+
--navi-transition-window-left
|
|
28935
|
+
)
|
|
28936
|
+
);
|
|
28937
|
+
width: auto;
|
|
28938
|
+
}
|
|
28506
28939
|
/* The pages are cut at the edge of the box they travel in. Said HERE and
|
|
28507
28940
|
nowhere else: these pictures are drawn in the top layer, so no overflow
|
|
28508
28941
|
on any element of the document — not the box's own, not a frame around
|
|
@@ -28514,17 +28947,28 @@ const css$$ = /* css */`
|
|
|
28514
28947
|
}
|
|
28515
28948
|
&::view-transition-group(navi-route-travel) {
|
|
28516
28949
|
/* The window the two pictures are seen through, held still for the whole
|
|
28517
|
-
travel at the
|
|
28518
|
-
is what CLIPS, and the browser
|
|
28519
|
-
left to the box arriving — so the
|
|
28520
|
-
|
|
28521
|
-
|
|
28522
|
-
|
|
28523
|
-
|
|
28524
|
-
|
|
28525
|
-
|
|
28526
|
-
|
|
28527
|
-
|
|
28950
|
+
travel at the rectangle that contains both boxes (see
|
|
28951
|
+
transition_window.js): the group is what CLIPS, and the browser
|
|
28952
|
+
animates it from the box being left to the box arriving — so the
|
|
28953
|
+
window moves and shrinks under the pictures, cutting the page leaving
|
|
28954
|
+
from the bottom, progressively. The box does end up at the arriving
|
|
28955
|
+
page's height, and that is right; what must not happen is the user
|
|
28956
|
+
watching it get there.
|
|
28957
|
+
|
|
28958
|
+
Held by dropping the group's animation rather than by winning against
|
|
28959
|
+
it with !important. The browser puts the group where the ARRIVING box
|
|
28960
|
+
stands, so it is moved from there back to the window's own corner. */
|
|
28961
|
+
top: calc(
|
|
28962
|
+
var(--navi-transition-window-top) - var(
|
|
28963
|
+
--navi-transition-window-new-top
|
|
28964
|
+
)
|
|
28965
|
+
);
|
|
28966
|
+
left: calc(
|
|
28967
|
+
var(--navi-transition-window-left) - var(
|
|
28968
|
+
--navi-transition-window-new-left
|
|
28969
|
+
)
|
|
28970
|
+
);
|
|
28971
|
+
width: var(--navi-transition-window-width);
|
|
28528
28972
|
height: var(--navi-transition-window-height);
|
|
28529
28973
|
|
|
28530
28974
|
/* Cut at what covers the box, on top of being cut at the box. The
|
|
@@ -28542,31 +28986,51 @@ const css$$ = /* css */`
|
|
|
28542
28986
|
above the pages covers the top of the box exactly as a fixed bar covers
|
|
28543
28987
|
the top of the screen. Both are read rather than asked for, so one that
|
|
28544
28988
|
grows, shrinks or unmounts mid-travel is followed without anything
|
|
28545
|
-
being told.
|
|
28546
|
-
|
|
28989
|
+
being told.
|
|
28990
|
+
|
|
28991
|
+
Read live, though, they describe the state ARRIVING and nothing else,
|
|
28992
|
+
so the cut is taken at the smaller of that and the band the state
|
|
28993
|
+
being left kept free (--navi-transition-old-band-*, photographed while
|
|
28994
|
+
both still existed). Furniture standing in BOTH states is the frame:
|
|
28995
|
+
the pages move behind it and are cut at it. Furniture standing in one
|
|
28996
|
+
of them is part of what changes, and cutting the page being left at a
|
|
28997
|
+
bar it never had shows its own header being sliced instead of
|
|
28998
|
+
leaving. */
|
|
28547
28999
|
--navi-route-travel-clip-top: max(
|
|
28548
29000
|
0px,
|
|
28549
|
-
|
|
28550
|
-
|
|
29001
|
+
min(
|
|
29002
|
+
var(--navi-safe-area-inset-top) + var(--navi-transition-cover-top),
|
|
29003
|
+
var(--navi-transition-old-band-top)
|
|
29004
|
+
) - var(--navi-transition-window-top)
|
|
28551
29005
|
);
|
|
28552
29006
|
--navi-route-travel-clip-left: max(
|
|
28553
29007
|
0px,
|
|
28554
|
-
|
|
28555
|
-
|
|
29008
|
+
min(
|
|
29009
|
+
var(--navi-safe-area-inset-left) + var(--navi-transition-cover-left),
|
|
29010
|
+
var(--navi-transition-old-band-left)
|
|
29011
|
+
) - var(--navi-transition-window-left)
|
|
28556
29012
|
);
|
|
28557
29013
|
--navi-route-travel-clip-bottom: max(
|
|
28558
29014
|
0px,
|
|
28559
29015
|
var(--navi-transition-window-top) +
|
|
28560
29016
|
var(--navi-transition-window-height) +
|
|
28561
|
-
|
|
28562
|
-
|
|
29017
|
+
min(
|
|
29018
|
+
var(--navi-safe-area-inset-bottom) +
|
|
29019
|
+
var(--navi-transition-cover-bottom),
|
|
29020
|
+
var(--navi-transition-old-band-bottom)
|
|
29021
|
+
) -
|
|
29022
|
+
100dvh
|
|
28563
29023
|
);
|
|
28564
29024
|
--navi-route-travel-clip-right: max(
|
|
28565
29025
|
0px,
|
|
28566
29026
|
var(--navi-transition-window-left) +
|
|
28567
29027
|
var(--navi-transition-window-width) +
|
|
28568
|
-
|
|
28569
|
-
|
|
29028
|
+
min(
|
|
29029
|
+
var(--navi-safe-area-inset-right) +
|
|
29030
|
+
var(--navi-transition-cover-right),
|
|
29031
|
+
var(--navi-transition-old-band-right)
|
|
29032
|
+
) -
|
|
29033
|
+
100dvw
|
|
28570
29034
|
);
|
|
28571
29035
|
clip-path: inset(
|
|
28572
29036
|
var(--navi-route-travel-clip-top) var(--navi-route-travel-clip-right)
|
|
@@ -28903,9 +29367,10 @@ const RouteTravel = ({
|
|
|
28903
29367
|
document.documentElement.setAttribute(DRAGGED_ATTRIBUTE, "");
|
|
28904
29368
|
}
|
|
28905
29369
|
pageAskedForRef.current = page;
|
|
28906
|
-
// The box as it stands before anything moves
|
|
28907
|
-
//
|
|
28908
|
-
|
|
29370
|
+
// The box as it stands before anything moves, and the band the furniture
|
|
29371
|
+
// around it leaves free: rendering is held, so both are still the page
|
|
29372
|
+
// being left (see transition_window.js).
|
|
29373
|
+
const stateBefore = measureTransitionWindowState(elementRef.current);
|
|
28909
29374
|
// The hold a navigation already took, if this travel is the answer to one:
|
|
28910
29375
|
// taking another would be taking a hold on a page that is holding still.
|
|
28911
29376
|
const releaseRendering = takeoverRoutingRenderingHold();
|
|
@@ -28948,7 +29413,7 @@ const RouteTravel = ({
|
|
|
28948
29413
|
// which is what the box is measured through: the two states are at the
|
|
28949
29414
|
// same place in the layout without being at the same place in the window
|
|
28950
29415
|
// (see transition_window.js).
|
|
28951
|
-
holdTransitionWindow(travel, elementRef.current,
|
|
29416
|
+
holdTransitionWindow(travel, elementRef.current, stateBefore);
|
|
28952
29417
|
});
|
|
28953
29418
|
travel.viewTransition = viewTransition;
|
|
28954
29419
|
if (scrub) {
|
|
@@ -38375,6 +38840,9 @@ const selectByTextStrings = (element, range, startText, endText) => {
|
|
|
38375
38840
|
installImportMetaCssBuild(import.meta);// https://jsfiddle.net/v5xzJ/4/
|
|
38376
38841
|
const css$X = /* css */`
|
|
38377
38842
|
@layer navi {
|
|
38843
|
+
/* Same reason as .navi_icon below: the display here is a starting point,
|
|
38844
|
+
and box.jsx's unlayered [navi-box-flow] attributes have to win over it.
|
|
38845
|
+
The rest is appearance a caller may want back. */
|
|
38378
38846
|
.navi_text {
|
|
38379
38847
|
&[data-skeleton] {
|
|
38380
38848
|
border-radius: 0.2em;
|
|
@@ -44646,6 +45114,11 @@ const CheckboxPseudoElements = ["::-navi-loader", "::-navi-checkmark"];
|
|
|
44646
45114
|
|
|
44647
45115
|
installImportMetaCssBuild(import.meta);const css$M = /* css */`
|
|
44648
45116
|
@layer navi {
|
|
45117
|
+
/* Layered on purpose, rules included: a Field and its Label are chrome an
|
|
45118
|
+
app restyles wholesale (its own label color, its own spacing), and
|
|
45119
|
+
nothing declared here is structural — a cursor, a dimmed color, and
|
|
45120
|
+
spacing that already has --spacing-with-control as its knob. So an app
|
|
45121
|
+
rule of any weight takes them back, no specificity games. */
|
|
44649
45122
|
.navi_label {
|
|
44650
45123
|
--label-required-indicator-color: var(--navi-color-danger, #b42318);
|
|
44651
45124
|
|
|
@@ -46296,7 +46769,11 @@ installImportMetaCssBuild(import.meta);const css$J = /* css */`
|
|
|
46296
46769
|
--button-color-disabled: var(--button-color-readonly);
|
|
46297
46770
|
|
|
46298
46771
|
/* Here to be easy to override */
|
|
46299
|
-
|
|
46772
|
+
/* Layered, this one and the three below: display so box.jsx's unlayered
|
|
46773
|
+
[navi-box-flow] can put the button inline-flex, the font and the line
|
|
46774
|
+
so an app's own button typography wins without having to go through
|
|
46775
|
+
the --button-* variables. */
|
|
46776
|
+
display: inline-block;
|
|
46300
46777
|
font-size: var(--button-font-size);
|
|
46301
46778
|
font-family: var(--button-font-family);
|
|
46302
46779
|
/* A form control comes with a line of its own from the browser, and that
|
|
@@ -49850,70 +50327,95 @@ const css$G = /* css */`
|
|
|
49850
50327
|
controls answer it, an app never writes it. Whoever answers it also
|
|
49851
50328
|
stops it (see .navi_button_content in button_ui.jsx), so a button
|
|
49852
50329
|
deeper in — the clear cross in a picker's slot, the Save of a form in
|
|
49853
|
-
a popup the member opens — never mistakes itself for the seam.
|
|
49854
|
-
|
|
49855
|
-
|
|
49856
|
-
|
|
49857
|
-
|
|
49858
|
-
|
|
49859
|
-
|
|
49860
|
-
|
|
49861
|
-
|
|
49862
|
-
|
|
49863
|
-
|
|
49864
|
-
|
|
49865
|
-
|
|
49866
|
-
|
|
49867
|
-
|
|
49868
|
-
|
|
49869
|
-
|
|
49870
|
-
|
|
49871
|
-
|
|
49872
|
-
|
|
49873
|
-
|
|
49874
|
-
|
|
49875
|
-
|
|
49876
|
-
|
|
49877
|
-
|
|
49878
|
-
|
|
49879
|
-
|
|
49880
|
-
|
|
49881
|
-
|
|
49882
|
-
|
|
49883
|
-
|
|
49884
|
-
|
|
49885
|
-
|
|
49886
|
-
|
|
49887
|
-
|
|
49888
|
-
|
|
49889
|
-
|
|
49890
|
-
|
|
49891
|
-
|
|
49892
|
-
|
|
49893
|
-
|
|
49894
|
-
|
|
49895
|
-
|
|
49896
|
-
|
|
49897
|
-
|
|
49898
|
-
|
|
49899
|
-
|
|
49900
|
-
|
|
50330
|
+
a popup the member opens — never mistakes itself for the seam.
|
|
50331
|
+
Both are asked of members only (see just below for what one is): the
|
|
50332
|
+
second one alone being reset by a popup would leave the first one
|
|
50333
|
+
reaching it. */
|
|
50334
|
+
|
|
50335
|
+
/* What the group counts as a member — a child on its line, spelled
|
|
50336
|
+
*:not([navi-out-of-flow]) here and at every seam below. Not every child
|
|
50337
|
+
is one. A popup renders inside its opener's own subtree — a Dialog
|
|
50338
|
+
written next to the Button that opens it lands as a child of the group
|
|
50339
|
+
itself, and brings its backdrop (and, for layer="local", its clip
|
|
50340
|
+
wrapper) along; a callout anchored on a <button> is mounted in that
|
|
50341
|
+
button's parent, which is the group too. All of them are out of flow:
|
|
50342
|
+
none is ever at a seam, and none is between two members either. Counted
|
|
50343
|
+
as members they take a corner meant for a real one — a dialog in the
|
|
50344
|
+
middle of a row comes out square — and, worse, they move the real ones:
|
|
50345
|
+
a row of one button plus the dialog it opens has no :only-child left, so
|
|
50346
|
+
the lone button squares the side it joins nothing on. Read off a marker
|
|
50347
|
+
the elements set themselves (see popover.jsx, dialog.jsx, callout.js)
|
|
50348
|
+
rather than named class by class here: a layout component knowing the
|
|
50349
|
+
private classes of every component that can open something is exactly
|
|
50350
|
+
what the corner claims above exist to avoid. */
|
|
50351
|
+
> *:not([navi-out-of-flow]) {
|
|
50352
|
+
/* Members overlap by the width of one border, so along each seam one of
|
|
50353
|
+
the two borders covers the other. Whichever member the user is on has
|
|
50354
|
+
to be the one on top: it is the one whose border changes color, and
|
|
50355
|
+
the one whose focus ring goes all the way around — half a ring, cut by
|
|
50356
|
+
the neighbour painted after it, is what this avoids. z-index needs a
|
|
50357
|
+
positioned element to mean anything, hence position: relative.
|
|
50358
|
+
Deliberately not paired with isolation: isolate — a stacking context
|
|
50359
|
+
here would also trap the popup of a picker held in the group, which
|
|
50360
|
+
counts on its own band reaching the whole page. What keeps these
|
|
50361
|
+
values from escaping is instead that everything they could reach is a
|
|
50362
|
+
band above them (see navi_z_indexes.js). */
|
|
50363
|
+
&:hover,
|
|
50364
|
+
&[data-hover] {
|
|
50365
|
+
position: relative;
|
|
50366
|
+
z-index: var(--navi-z-index-control-hovered);
|
|
50367
|
+
}
|
|
50368
|
+
|
|
50369
|
+
/* Three spellings for one thing — the member showing a focus ring. Some
|
|
50370
|
+
controls take the focus on their own root (a button); others wrap a
|
|
50371
|
+
real input and draw the ring on their frame while the keyboard is held
|
|
50372
|
+
somewhere inside (a picker, a spin). The ring is what must not be
|
|
50373
|
+
sliced, so the member holding it is raised whether it wears the state
|
|
50374
|
+
itself or merely contains it. */
|
|
50375
|
+
&:focus-visible,
|
|
50376
|
+
&[data-focus-visible],
|
|
50377
|
+
&:has([data-focus-visible]) {
|
|
50378
|
+
position: relative;
|
|
50379
|
+
z-index: var(--navi-z-index-control-focused);
|
|
50380
|
+
}
|
|
50381
|
+
|
|
50382
|
+
/* The member holding something open. Neither of the two above covers it:
|
|
50383
|
+
the click that opened the popup gives no focus ring, the focus itself
|
|
50384
|
+
left for the popup's content, and the pointer is free to travel to a
|
|
50385
|
+
neighbour — yet the member keeps the border color its open state gives
|
|
50386
|
+
it, and that border is exactly what the neighbour painted after it
|
|
50387
|
+
slices. Read as a state, not as a pseudo-class: :active only lasts as
|
|
50388
|
+
long as the button is held down, and while it is held :hover is true
|
|
50389
|
+
anyway, so it would add nothing here.
|
|
50390
|
+
|
|
50391
|
+
:has, for the same reason as focus-visible above — the group member
|
|
50392
|
+
can be an enrobage around the control that expands — and reaching a
|
|
50393
|
+
popup held inline (a Popover with layer="local" renders inside its
|
|
50394
|
+
member) costs nothing: that popup only reads expanded while its own
|
|
50395
|
+
member is, which is the member this raises. A popup that is a child of
|
|
50396
|
+
the group itself wears aria-expanded too, and is not a member:
|
|
50397
|
+
position/z-index here would fight the placement it does its own way. */
|
|
50398
|
+
&[aria-expanded="true"],
|
|
50399
|
+
&:has([aria-expanded="true"]) {
|
|
50400
|
+
position: relative;
|
|
50401
|
+
z-index: var(--navi-z-index-control-expanded);
|
|
50402
|
+
}
|
|
49901
50403
|
}
|
|
49902
50404
|
|
|
49903
|
-
/*
|
|
50405
|
+
/* Where two members meet, stated as the relationship itself rather than as
|
|
50406
|
+
positions in the child list: the corner a member loses is the one facing
|
|
50407
|
+
a member, and the child list holds more than members (see above).
|
|
50408
|
+
A group of one — or of one member and the popup it opens — matches
|
|
50409
|
+
neither rule and keeps the radius it has on its own. */
|
|
50410
|
+
|
|
50411
|
+
/* Horizontal (default) */
|
|
49904
50412
|
&:not([data-vertical]) {
|
|
49905
|
-
|
|
50413
|
+
/* A member with a member before it: its left corners are on that seam,
|
|
50414
|
+
and it is the one pulled back so the two borders there become one
|
|
50415
|
+
line — same relationship, so the same rule. */
|
|
50416
|
+
> *:not([navi-out-of-flow]) ~ *:not([navi-out-of-flow]) {
|
|
49906
50417
|
margin-left: calc(var(--border-width, var(--group-border-width)) * -1);
|
|
49907
|
-
}
|
|
49908
|
-
> *:first-child:not(:only-child) {
|
|
49909
|
-
--x-corner-top-right-radius: 0;
|
|
49910
|
-
--x-corner-bottom-right-radius: 0;
|
|
49911
|
-
|
|
49912
|
-
border-top-right-radius: 0 !important;
|
|
49913
|
-
border-bottom-right-radius: 0 !important;
|
|
49914
|
-
}
|
|
49915
50418
|
|
|
49916
|
-
> *:last-child:not(:only-child) {
|
|
49917
50419
|
--x-corner-top-left-radius: 0;
|
|
49918
50420
|
--x-corner-bottom-left-radius: 0;
|
|
49919
50421
|
|
|
@@ -49921,30 +50423,23 @@ const css$G = /* css */`
|
|
|
49921
50423
|
border-bottom-left-radius: 0 !important;
|
|
49922
50424
|
}
|
|
49923
50425
|
|
|
49924
|
-
|
|
49925
|
-
|
|
50426
|
+
/* A member with a member after it: its right corners are on that seam. */
|
|
50427
|
+
> *:not([navi-out-of-flow]):has(~ *:not([navi-out-of-flow])) {
|
|
49926
50428
|
--x-corner-top-right-radius: 0;
|
|
49927
50429
|
--x-corner-bottom-right-radius: 0;
|
|
49928
|
-
--x-corner-bottom-left-radius: 0;
|
|
49929
50430
|
|
|
49930
|
-
border-radius: 0 !important;
|
|
50431
|
+
border-top-right-radius: 0 !important;
|
|
50432
|
+
border-bottom-right-radius: 0 !important;
|
|
49931
50433
|
}
|
|
49932
50434
|
}
|
|
49933
50435
|
|
|
49934
|
-
/* Vertical
|
|
50436
|
+
/* Vertical */
|
|
49935
50437
|
&[data-vertical] {
|
|
49936
|
-
|
|
50438
|
+
/* A member with a member above it — see the horizontal block's own
|
|
50439
|
+
comments, this is the same thing turned a quarter. */
|
|
50440
|
+
> *:not([navi-out-of-flow]) ~ *:not([navi-out-of-flow]) {
|
|
49937
50441
|
margin-top: calc(var(--border-width, var(--group-border-width)) * -1);
|
|
49938
|
-
}
|
|
49939
|
-
> *:first-child:not(:only-child) {
|
|
49940
|
-
--x-corner-bottom-right-radius: 0;
|
|
49941
|
-
--x-corner-bottom-left-radius: 0;
|
|
49942
|
-
|
|
49943
|
-
border-bottom-right-radius: 0 !important;
|
|
49944
|
-
border-bottom-left-radius: 0 !important;
|
|
49945
|
-
}
|
|
49946
50442
|
|
|
49947
|
-
> *:last-child:not(:only-child) {
|
|
49948
50443
|
--x-corner-top-left-radius: 0;
|
|
49949
50444
|
--x-corner-top-right-radius: 0;
|
|
49950
50445
|
|
|
@@ -49952,13 +50447,13 @@ const css$G = /* css */`
|
|
|
49952
50447
|
border-top-right-radius: 0 !important;
|
|
49953
50448
|
}
|
|
49954
50449
|
|
|
49955
|
-
|
|
49956
|
-
|
|
49957
|
-
--x-corner-top-right-radius: 0;
|
|
50450
|
+
/* A member with a member below it. */
|
|
50451
|
+
> *:not([navi-out-of-flow]):has(~ *:not([navi-out-of-flow])) {
|
|
49958
50452
|
--x-corner-bottom-right-radius: 0;
|
|
49959
50453
|
--x-corner-bottom-left-radius: 0;
|
|
49960
50454
|
|
|
49961
|
-
border-radius: 0 !important;
|
|
50455
|
+
border-bottom-right-radius: 0 !important;
|
|
50456
|
+
border-bottom-left-radius: 0 !important;
|
|
49962
50457
|
}
|
|
49963
50458
|
}
|
|
49964
50459
|
}
|
|
@@ -55974,7 +56469,13 @@ const DialogLocal = props => {
|
|
|
55974
56469
|
children: [backdropProps && jsx(Box, {
|
|
55975
56470
|
...backdropProps
|
|
55976
56471
|
}), jsx("div", {
|
|
55977
|
-
className: "navi_dialog_clip_wrapper"
|
|
56472
|
+
className: "navi_dialog_clip_wrapper"
|
|
56473
|
+
// Out of flow like the dialog it holds — see the dialog's own
|
|
56474
|
+
// contentProps for what reads the marker.
|
|
56475
|
+
// eslint-disable-next-line react/no-unknown-property
|
|
56476
|
+
,
|
|
56477
|
+
|
|
56478
|
+
"navi-out-of-flow": "",
|
|
55978
56479
|
children: jsx(Box, {
|
|
55979
56480
|
...contentProps
|
|
55980
56481
|
})
|
|
@@ -56653,6 +57154,9 @@ const useDialogProps = props => {
|
|
|
56653
57154
|
// real element we render ourselves.
|
|
56654
57155
|
Object.assign(backdropProps, {
|
|
56655
57156
|
"ref": backdropRef,
|
|
57157
|
+
// Out of flow like the popup it belongs to — see the content element's own
|
|
57158
|
+
// note for what the marker is read by.
|
|
57159
|
+
"navi-out-of-flow": "",
|
|
56656
57160
|
"baseClassName": "navi_dialog_backdrop",
|
|
56657
57161
|
"aria-hidden": "true",
|
|
56658
57162
|
// Recomputed fresh on every render from openController.opened (not
|
|
@@ -56726,6 +57230,13 @@ const useDialogProps = props => {
|
|
|
56726
57230
|
onNaviCommand(e);
|
|
56727
57231
|
rest.onnavi_command?.(e);
|
|
56728
57232
|
},
|
|
57233
|
+
// Not a child on the line of whatever holds it: a popup renders inside its
|
|
57234
|
+
// opener's own subtree, so a Dialog written next to the control that opens it
|
|
57235
|
+
// is a child of that control's own parent. A Group frames the children on
|
|
57236
|
+
// its line and this is not one of them — it is in the top layer, or
|
|
57237
|
+
// absolutely placed by its own code — so it says so once, here (group.jsx
|
|
57238
|
+
// reads this attribute to tell a member from anything else on its line).
|
|
57239
|
+
"navi-out-of-flow": "",
|
|
56729
57240
|
"baseClassName": "navi_dialog",
|
|
56730
57241
|
"pseudoClasses": DIALOG_PSEUDO_CLASSES,
|
|
56731
57242
|
// Distinguishes the two renderers for the CSS above (position: fixed
|
|
@@ -57436,7 +57947,13 @@ const PopoverCustom = props => {
|
|
|
57436
57947
|
children: [backdropProps && jsx(Box, {
|
|
57437
57948
|
...backdropProps
|
|
57438
57949
|
}), jsx("div", {
|
|
57439
|
-
className: "navi_popover_clip_wrapper"
|
|
57950
|
+
className: "navi_popover_clip_wrapper"
|
|
57951
|
+
// Out of flow like the popover it holds — see the popover's own
|
|
57952
|
+
// contentProps for what reads the marker.
|
|
57953
|
+
// eslint-disable-next-line react/no-unknown-property
|
|
57954
|
+
,
|
|
57955
|
+
|
|
57956
|
+
"navi-out-of-flow": "",
|
|
57440
57957
|
children: jsx(Box, {
|
|
57441
57958
|
...contentProps
|
|
57442
57959
|
})
|
|
@@ -58172,6 +58689,9 @@ const usePopoverProps = props => {
|
|
|
58172
58689
|
Object.assign(backdropProps, {
|
|
58173
58690
|
"ref": backdropRef,
|
|
58174
58691
|
"id": backdropId,
|
|
58692
|
+
// Out of flow like the popup it belongs to — see the content element's own
|
|
58693
|
+
// note for what the marker is read by.
|
|
58694
|
+
"navi-out-of-flow": "",
|
|
58175
58695
|
"baseClassName": "navi_popover_backdrop",
|
|
58176
58696
|
"aria-hidden": "true",
|
|
58177
58697
|
// Recomputed fresh on every render from openController.opened (not a
|
|
@@ -58267,6 +58787,13 @@ const usePopoverProps = props => {
|
|
|
58267
58787
|
// it contains claim header/footer/body (see box.jsx) — a popup is always a
|
|
58268
58788
|
// scrolling area, so it says so once, here.
|
|
58269
58789
|
"overflow": "auto",
|
|
58790
|
+
// Not a child on the line of whatever holds it: a popup renders inside its
|
|
58791
|
+
// opener's own subtree, so a Popover written next to the control that opens it
|
|
58792
|
+
// is a child of that control's own parent. A Group frames the children on
|
|
58793
|
+
// its line and this is not one of them — it is in the top layer, or
|
|
58794
|
+
// absolutely placed by its own code — so it says so once, here (group.jsx
|
|
58795
|
+
// reads this attribute to tell a member from anything else on its line).
|
|
58796
|
+
"navi-out-of-flow": "",
|
|
58270
58797
|
"baseClassName": "navi_popover",
|
|
58271
58798
|
"pseudoClasses": POPOVER_PSEUDO_CLASSES,
|
|
58272
58799
|
"onKeyDown": e => {
|
|
@@ -58377,7 +58904,10 @@ const css$C = /* css */`
|
|
|
58377
58904
|
opens it (a Picker holds its popup children inside its root), and
|
|
58378
58905
|
line-height inherits as computed: the control's line is a length
|
|
58379
58906
|
(--navi-control-line-height), and inherited it would arrive as that
|
|
58380
|
-
control's pixels — a 12px caption on an 18px picker's 23px rows.
|
|
58907
|
+
control's pixels — a 12px caption on an 18px picker's 23px rows.
|
|
58908
|
+
Layered, like the dialog padding below: both are what navi puts there
|
|
58909
|
+
in the absence of anything else, and an app writing its own line or
|
|
58910
|
+
its own popup padding is meant to win. */
|
|
58381
58911
|
line-height: var(--navi-line-height);
|
|
58382
58912
|
|
|
58383
58913
|
&.navi_popover {
|