@jsenv/navi 0.29.10 → 0.29.12
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/README.md +9 -0
- package/dist/jsenv_navi.js +417 -49
- package/dist/jsenv_navi.js.map +18 -15
- package/dist/jsenv_navi_side_effects.js +8 -0
- package/dist/jsenv_navi_side_effects.js.map +2 -2
- package/docs/AI_INSTRUCTIONS.md +9 -0
- package/docs/MOBILE_LAYOUT_PITFALLS.md +69 -14
- package/docs/actions.md +250 -0
- package/docs/resource.md +267 -0
- package/docs/resource_dependencies.md +103 -0
- package/docs/resource_with_params.md +80 -0
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -9742,7 +9742,7 @@ const setupNetworkMonitoring = () => {
|
|
|
9742
9742
|
};
|
|
9743
9743
|
setupNetworkMonitoring();
|
|
9744
9744
|
|
|
9745
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
9745
|
+
installImportMetaCssBuild(import.meta);const css$Y = /* css */`
|
|
9746
9746
|
.navi_loading_indicator_fluid_container {
|
|
9747
9747
|
position: relative;
|
|
9748
9748
|
display: flex;
|
|
@@ -9774,7 +9774,7 @@ const LoadingIndicatorFluid = ({
|
|
|
9774
9774
|
visuallyHidden,
|
|
9775
9775
|
...rest
|
|
9776
9776
|
}) => {
|
|
9777
|
-
import.meta.css = [css$
|
|
9777
|
+
import.meta.css = [css$Y, "@jsenv/navi/src/graphic/loading/loading_indicator_fluid.jsx"];
|
|
9778
9778
|
const ref = useRef(null);
|
|
9779
9779
|
// The container dimensions can be deduced from the ref itself as the indicator is absolute inset 0
|
|
9780
9780
|
const [containerWidth, setContainerWidth] = useState(0);
|
|
@@ -9979,7 +9979,7 @@ const LoadingRectangleSvg = ({
|
|
|
9979
9979
|
});
|
|
9980
9980
|
};
|
|
9981
9981
|
|
|
9982
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
9982
|
+
installImportMetaCssBuild(import.meta);const css$X = /* css */`
|
|
9983
9983
|
.navi_loading_outline_wrapper {
|
|
9984
9984
|
position: absolute;
|
|
9985
9985
|
/* Controls place the outline slightly outside their box, right on top of
|
|
@@ -10016,7 +10016,7 @@ installImportMetaCssBuild(import.meta);const css$W = /* css */`
|
|
|
10016
10016
|
}
|
|
10017
10017
|
`;
|
|
10018
10018
|
const LoadingOutline = props => {
|
|
10019
|
-
import.meta.css = [css$
|
|
10019
|
+
import.meta.css = [css$X, "@jsenv/navi/src/graphic/loading/loading_outline.jsx"];
|
|
10020
10020
|
if (props.containerRef) {
|
|
10021
10021
|
const container = props.containerRef.current;
|
|
10022
10022
|
if (!container) {
|
|
@@ -10341,7 +10341,7 @@ const selectByTextStrings = (element, range, startText, endText) => {
|
|
|
10341
10341
|
};
|
|
10342
10342
|
|
|
10343
10343
|
installImportMetaCssBuild(import.meta);// https://jsfiddle.net/v5xzJ/4/
|
|
10344
|
-
const css$
|
|
10344
|
+
const css$W = /* css */`
|
|
10345
10345
|
@layer navi {
|
|
10346
10346
|
.navi_text {
|
|
10347
10347
|
&[data-skeleton] {
|
|
@@ -10847,7 +10847,7 @@ const TextShrinkWrap = props => {
|
|
|
10847
10847
|
});
|
|
10848
10848
|
};
|
|
10849
10849
|
const TextUI = props => {
|
|
10850
|
-
import.meta.css = [css$
|
|
10850
|
+
import.meta.css = [css$W, "@jsenv/navi/src/text/text.jsx"];
|
|
10851
10851
|
let {
|
|
10852
10852
|
ref,
|
|
10853
10853
|
spacing,
|
|
@@ -13561,7 +13561,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
13561
13561
|
* - Arrow automatically shows when pointing at a valid anchor element
|
|
13562
13562
|
* - Centers in viewport when no anchor element provided or anchor is too big
|
|
13563
13563
|
*/
|
|
13564
|
-
const css$
|
|
13564
|
+
const css$V = /* css */`
|
|
13565
13565
|
@layer navi {
|
|
13566
13566
|
.navi_callout {
|
|
13567
13567
|
/* A callout is parented to what it explains, so it inherits from it — and
|
|
@@ -13781,7 +13781,7 @@ const openCallout = (message, {
|
|
|
13781
13781
|
skipFocus = false,
|
|
13782
13782
|
debug = () => {}
|
|
13783
13783
|
} = {}) => {
|
|
13784
|
-
import.meta.css = [css$
|
|
13784
|
+
import.meta.css = [css$V, "@jsenv/navi/src/control/rules/callout/callout.js"];
|
|
13785
13785
|
if (debug === true) {
|
|
13786
13786
|
debug = (e, ...args) => console.debug(`"${e.type}" -> `, ...args);
|
|
13787
13787
|
}
|
|
@@ -22859,7 +22859,7 @@ const getAssociatedLabels = element => {
|
|
|
22859
22859
|
return [];
|
|
22860
22860
|
};
|
|
22861
22861
|
|
|
22862
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
22862
|
+
installImportMetaCssBuild(import.meta);const css$U = /* css */`
|
|
22863
22863
|
@layer navi {
|
|
22864
22864
|
.navi_button {
|
|
22865
22865
|
--button-border-radius: var(--navi-control-border-radius);
|
|
@@ -23252,7 +23252,7 @@ installImportMetaCssBuild(import.meta);const css$T = /* css */`
|
|
|
23252
23252
|
}
|
|
23253
23253
|
`;
|
|
23254
23254
|
const ButtonUI = props => {
|
|
23255
|
-
import.meta.css = [css$
|
|
23255
|
+
import.meta.css = [css$U, "@jsenv/navi/src/control/input/button_ui.jsx"];
|
|
23256
23256
|
const {
|
|
23257
23257
|
ref,
|
|
23258
23258
|
// href/link
|
|
@@ -25380,7 +25380,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
25380
25380
|
* reaches the real container.
|
|
25381
25381
|
*/
|
|
25382
25382
|
let openLocalDialogCount = 0;
|
|
25383
|
-
const css$
|
|
25383
|
+
const css$T = /* css */`
|
|
25384
25384
|
@layer navi {
|
|
25385
25385
|
.navi_dialog {
|
|
25386
25386
|
/* Min gap between the dialog and the edges of its container. Written
|
|
@@ -25790,7 +25790,7 @@ const css$S = /* css */`
|
|
|
25790
25790
|
* @param {import("ignore:preact").ComponentChildren} props.children
|
|
25791
25791
|
*/
|
|
25792
25792
|
const Dialog = props => {
|
|
25793
|
-
import.meta.css = [css$
|
|
25793
|
+
import.meta.css = [css$T, "@jsenv/navi/src/layout/dialog.jsx"];
|
|
25794
25794
|
if (props.openController) {
|
|
25795
25795
|
return jsx(ControlledDialog, {
|
|
25796
25796
|
...props
|
|
@@ -26608,7 +26608,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
26608
26608
|
* and applied.
|
|
26609
26609
|
*/
|
|
26610
26610
|
let openLocalPopoverCount = 0;
|
|
26611
|
-
const css$
|
|
26611
|
+
const css$S = /* css */`
|
|
26612
26612
|
@layer navi {
|
|
26613
26613
|
.navi_popover {
|
|
26614
26614
|
/* soft: user-configurable preferred max-height. Kept as a *default*
|
|
@@ -26978,7 +26978,7 @@ const css$R = /* css */`
|
|
|
26978
26978
|
* @param {import("ignore:preact").ComponentChildren} props.children
|
|
26979
26979
|
*/
|
|
26980
26980
|
const Popover = props => {
|
|
26981
|
-
import.meta.css = [css$
|
|
26981
|
+
import.meta.css = [css$S, "@jsenv/navi/src/layout/popover.jsx"];
|
|
26982
26982
|
if (props.openController) {
|
|
26983
26983
|
return jsx(ControlledPopover, {
|
|
26984
26984
|
...props
|
|
@@ -27942,7 +27942,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
27942
27942
|
* event, and a caller replacing the body entirely then has one protocol to
|
|
27943
27943
|
* follow — `--navi-confirm` for yes, anything that closes for no.
|
|
27944
27944
|
*/
|
|
27945
|
-
const css$
|
|
27945
|
+
const css$R = /* css */`
|
|
27946
27946
|
/* The width lives on the body rather than on the popup, so that custom
|
|
27947
27947
|
content (which replaces this body entirely) sizes itself instead of
|
|
27948
27948
|
inheriting a ceiling meant for a sentence-long question. */
|
|
@@ -28079,7 +28079,7 @@ const ConfirmPopup = ({
|
|
|
28079
28079
|
onAnswer,
|
|
28080
28080
|
onClosed
|
|
28081
28081
|
}) => {
|
|
28082
|
-
import.meta.css = [css$
|
|
28082
|
+
import.meta.css = [css$R, "@jsenv/navi/src/action/confirm_popup.jsx"];
|
|
28083
28083
|
const {
|
|
28084
28084
|
mode,
|
|
28085
28085
|
confirmLabel,
|
|
@@ -28163,7 +28163,7 @@ const defaultBody = (message, {
|
|
|
28163
28163
|
});
|
|
28164
28164
|
};
|
|
28165
28165
|
|
|
28166
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
28166
|
+
installImportMetaCssBuild(import.meta);const css$Q = /* css */`
|
|
28167
28167
|
.action_error {
|
|
28168
28168
|
margin-top: 0;
|
|
28169
28169
|
margin-bottom: 20px;
|
|
@@ -28188,7 +28188,7 @@ const ActionRenderer = ({
|
|
|
28188
28188
|
children,
|
|
28189
28189
|
disabled
|
|
28190
28190
|
}) => {
|
|
28191
|
-
import.meta.css = [css$
|
|
28191
|
+
import.meta.css = [css$Q, "@jsenv/navi/src/action/action_renderer.jsx"];
|
|
28192
28192
|
if (action === undefined) {
|
|
28193
28193
|
throw new Error("ActionRenderer requires an action to render, but none was provided.");
|
|
28194
28194
|
}
|
|
@@ -29676,6 +29676,36 @@ const debug$2 = (args) => {
|
|
|
29676
29676
|
}
|
|
29677
29677
|
};
|
|
29678
29678
|
|
|
29679
|
+
/**
|
|
29680
|
+
* Creates a reactive REST resource backed by a shared signal store.
|
|
29681
|
+
* Returns a `stateFacade` exposing one action per REST callback provided
|
|
29682
|
+
* (`USER.GET`, `USER.GET_MANY`, `USER.POST`, …) plus `.withParams()` and the
|
|
29683
|
+
* relationship methods `.one()`, `.many()`, `.scopedOne()`, `.scopedMany()`.
|
|
29684
|
+
*
|
|
29685
|
+
* Each REST callback receives the params passed to the action call and must return
|
|
29686
|
+
* the data that will be upserted into the store:
|
|
29687
|
+
* - GET / POST / PUT / PATCH → the full item object, e.g. `{ id, name }`
|
|
29688
|
+
* - DELETE → the id or `{ id }` of the removed item
|
|
29689
|
+
* - GET_MANY / POST_MANY / … → an array of item objects
|
|
29690
|
+
*
|
|
29691
|
+
* A sub-resource of the backend (`/games/:id/candidates`) must be modelled with a
|
|
29692
|
+
* relationship method, never as an `op`/`type` discriminator dispatched inside one
|
|
29693
|
+
* verb's callback.
|
|
29694
|
+
*
|
|
29695
|
+
* @param {string} name - resource name, used in action names and error messages
|
|
29696
|
+
* @param {Object} restCallbacks - `{ idKey, uniqueKeys, rerunOn, dependencies, GET, GET_MANY, POST, POST_MANY, PUT, PUT_MANY, PATCH, PATCH_MANY, DELETE, DELETE_MANY }`
|
|
29697
|
+
* @param {string} [restCallbacks.idKey] - primary key property, defaults to `"id"` (or the first `uniqueKeys` entry)
|
|
29698
|
+
* @param {string[]} [restCallbacks.uniqueKeys] - alternate keys the store can find an item by (e.g. `"username"`); a callback may return a different `id` to rename the item's primary key
|
|
29699
|
+
* @see docs/resource.md — relationships, callback return contracts, decision table
|
|
29700
|
+
*
|
|
29701
|
+
* @example
|
|
29702
|
+
* const USER = resource("user", {
|
|
29703
|
+
* GET: ({ id }) => fetchJson(`/users/${id}`),
|
|
29704
|
+
* GET_MANY: () => fetchJson(`/users`),
|
|
29705
|
+
* POST: (user) => fetchJson(`/users`, { method: "POST", body: user }),
|
|
29706
|
+
* DELETE: ({ id }) => fetchJson(`/users/${id}`, { method: "DELETE" }),
|
|
29707
|
+
* });
|
|
29708
|
+
*/
|
|
29679
29709
|
const resource = (
|
|
29680
29710
|
name,
|
|
29681
29711
|
{
|
|
@@ -29830,7 +29860,7 @@ const createResource = (
|
|
|
29830
29860
|
* @param {Object} params - Parameters to bind to all actions of this resource (required)
|
|
29831
29861
|
* @param {Object} options - Additional options for the parameterized resource
|
|
29832
29862
|
* @returns {Object} A new resource instance with parameter-bound actions and isolated lifecycle
|
|
29833
|
-
* @see
|
|
29863
|
+
* @see docs/resource_with_params.md for detailed documentation and examples
|
|
29834
29864
|
*
|
|
29835
29865
|
* @example
|
|
29836
29866
|
* const ROLE = resource("role", { GET: (params) => fetchRole(params) });
|
|
@@ -29877,6 +29907,31 @@ const createResource = (
|
|
|
29877
29907
|
};
|
|
29878
29908
|
stateFacade.withParams = withParams;
|
|
29879
29909
|
|
|
29910
|
+
/**
|
|
29911
|
+
* Links a property on each item to a single item in an independent child store.
|
|
29912
|
+
* The property is reactive: updating the child item anywhere propagates immediately.
|
|
29913
|
+
* The child resource exists independently — it is not owned by, nor deleted with, the parent.
|
|
29914
|
+
*
|
|
29915
|
+
* Use it when the child is a first-class entity with its own store, shared across
|
|
29916
|
+
* parents (a user referenced by many games). When the child only exists inside its
|
|
29917
|
+
* owner, use `.scopedOne()` instead.
|
|
29918
|
+
*
|
|
29919
|
+
* Callback return contracts:
|
|
29920
|
+
* - GET / PUT → the parent object with the relationship nested inside:
|
|
29921
|
+
* `async ({ id }) => ({ id, session: { id: 10, token: "abc" } })`; `null` for no relationship
|
|
29922
|
+
* - DELETE → the parent id (or `{ id }`); the property is set to `null`
|
|
29923
|
+
*
|
|
29924
|
+
* The backend may also embed the child inline in a parent GET/POST response — the
|
|
29925
|
+
* setter on the property upserts the nested object into the child store.
|
|
29926
|
+
*
|
|
29927
|
+
* Returns the child relationship resource, itself chainable:
|
|
29928
|
+
* `USER_SESSION.one("device", DEVICE)` adds a reactive `.device` property to each session.
|
|
29929
|
+
*
|
|
29930
|
+
* @param {string} propertyName - property holding the child on each parent item
|
|
29931
|
+
* @param {Object} childResource - the independent resource created by `resource()`
|
|
29932
|
+
* @param {Object} [restCallbacks] - `{ rerunOn, dependencies, GET, PUT, DELETE }`
|
|
29933
|
+
* @see docs/resource.md
|
|
29934
|
+
*/
|
|
29880
29935
|
stateFacade.one = (
|
|
29881
29936
|
propertyName,
|
|
29882
29937
|
childResource,
|
|
@@ -30044,6 +30099,29 @@ ${originalActionName} source location: ${locationInfo}`,
|
|
|
30044
30099
|
});
|
|
30045
30100
|
};
|
|
30046
30101
|
|
|
30102
|
+
/**
|
|
30103
|
+
* Links a property on each item to an array of items in an independent child store.
|
|
30104
|
+
* Items in the array are full entries in the shared child store — if the same item is
|
|
30105
|
+
* referenced by several parents, a single update propagates to all of them.
|
|
30106
|
+
*
|
|
30107
|
+
* Use it when children are first-class entities shared across parents (a game's players,
|
|
30108
|
+
* who are users). When the children only exist inside their owner, or when the relation
|
|
30109
|
+
* itself carries fields (`seen_at`, `slot`), use `.scopedMany()` instead.
|
|
30110
|
+
*
|
|
30111
|
+
* Callback return contracts:
|
|
30112
|
+
* - GET_MANY → the parent object with the array nested inside:
|
|
30113
|
+
* `async ({ id }) => ({ id, friends: [{ id: 2 }, { id: 3 }] })` — a full-parent
|
|
30114
|
+
* response is absorbed as-is; the array replaces the relationship
|
|
30115
|
+
* - GET / POST / PUT / PATCH → the child object; it is upserted into the child store
|
|
30116
|
+
* but does NOT join the parent's array, which only a GET_MANY refresh changes
|
|
30117
|
+
* - DELETE → `[parentId, childId]`
|
|
30118
|
+
* - DELETE_MANY → `[parentId, [childId, childId, …]]`
|
|
30119
|
+
*
|
|
30120
|
+
* @param {string} propertyName - property holding the child array on each parent item
|
|
30121
|
+
* @param {Object} childResource - the independent resource created by `resource()`
|
|
30122
|
+
* @param {Object} [restCallbacks] - `{ rerunOn, dependencies, GET, GET_MANY, POST, POST_MANY, PUT, PUT_MANY, PATCH, PATCH_MANY, DELETE, DELETE_MANY }`
|
|
30123
|
+
* @see docs/resource.md
|
|
30124
|
+
*/
|
|
30047
30125
|
stateFacade.many = (
|
|
30048
30126
|
propertyName,
|
|
30049
30127
|
childResource,
|
|
@@ -30340,6 +30418,29 @@ ${originalActionName} source location: ${locationInfo}`,
|
|
|
30340
30418
|
});
|
|
30341
30419
|
};
|
|
30342
30420
|
|
|
30421
|
+
/**
|
|
30422
|
+
* Attaches a single private sub-object to each item. The child has no identity outside
|
|
30423
|
+
* its owner and is not shared across items; each owner gets its own private signal.
|
|
30424
|
+
*
|
|
30425
|
+
* Use it for a sub-resource the backend exposes under the parent (`/users/:id/profile`)
|
|
30426
|
+
* whose content is meaningless without that parent.
|
|
30427
|
+
*
|
|
30428
|
+
* All callbacks must return `[ownerId, props | null]`:
|
|
30429
|
+
* - `GET: async ({ id }) => [id, { bio: "Hello", avatar: "alice.png" }]`
|
|
30430
|
+
* - `PATCH: async ({ id, bio }) => [id, { bio, avatar: "alice.png" }]`
|
|
30431
|
+
* - `DELETE: async ({ id }) => [id, null]`
|
|
30432
|
+
*
|
|
30433
|
+
* `ownerId` may also be `{ [uniqueKey]: value }` when the owner is known by an alternate key.
|
|
30434
|
+
* The property is `null` until a callback provides data; setting it to `null` clears it.
|
|
30435
|
+
* Mutations apply directly to the owner's signal, so the parent GET is never rerun.
|
|
30436
|
+
*
|
|
30437
|
+
* Returns the child relationship resource, itself chainable:
|
|
30438
|
+
* `USER_PROFILE.one("theme", THEME)` adds a reactive `.theme` property on each profile.
|
|
30439
|
+
*
|
|
30440
|
+
* @param {string} propertyName - property holding the sub-object on each owner item
|
|
30441
|
+
* @param {Object} [restCallbacks] - `{ idKey, rerunOn, dependencies, GET, POST, PUT, PATCH, DELETE }`
|
|
30442
|
+
* @see docs/resource.md
|
|
30443
|
+
*/
|
|
30343
30444
|
stateFacade.scopedOne = (
|
|
30344
30445
|
propertyName,
|
|
30345
30446
|
{
|
|
@@ -30461,6 +30562,36 @@ ${originalActionName} source location: ${locationInfo}`,
|
|
|
30461
30562
|
return childResource;
|
|
30462
30563
|
};
|
|
30463
30564
|
|
|
30565
|
+
/**
|
|
30566
|
+
* Attaches a private ordered collection of sub-objects to each item. The child objects
|
|
30567
|
+
* have no identity outside their owner — two owners can hold items with the same id that
|
|
30568
|
+
* are completely independent. Each owner gets its own private arraySignalStore.
|
|
30569
|
+
*
|
|
30570
|
+
* This is the shape for a backend sub-route (`/games/:id/candidates`,
|
|
30571
|
+
* `…/candidates/:userId/accept`) and for a relation carrying its own fields
|
|
30572
|
+
* (`candidate_since`, `seen_at`): those fields belong to the pair, not to a shared child
|
|
30573
|
+
* store where they would corrupt the entity for every other reader.
|
|
30574
|
+
*
|
|
30575
|
+
* All callbacks must return `[ownerId, ...rest]`:
|
|
30576
|
+
* - `GET_MANY: async ({ id }) => [id, [{ name: "id", type: "int" }, …]]` — replaces the collection
|
|
30577
|
+
* - `POST: async ({ id, name, type }) => [id, { name, type }]`
|
|
30578
|
+
* - `PUT: async ({ id, oldName, name, type }) => [id, oldName, { name, type }]` (id rename)
|
|
30579
|
+
* - `DELETE: async ({ id, name }) => [id, name]`
|
|
30580
|
+
* - `*_MANY: [ownerId, itemArray]` — any plural verb replaces the whole collection,
|
|
30581
|
+
* which is how a backend answering a sub-route with the refreshed parent is absorbed
|
|
30582
|
+
*
|
|
30583
|
+
* `ownerId` may also be `{ [uniqueKey]: value }` when the owner is known by an alternate key.
|
|
30584
|
+
* A singular POST upserts the child but does not append it to the collection; ordering is
|
|
30585
|
+
* the backend's, so the owner's GET is rerun instead (only when its last response embedded
|
|
30586
|
+
* `propertyName`), and the child's own GET_MANY reruns per its `rerunOn`.
|
|
30587
|
+
*
|
|
30588
|
+
* Returns the child relationship resource, itself chainable:
|
|
30589
|
+
* `TABLE_COLUMNS.one("dataType", DATA_TYPE)` adds a reactive `.dataType` property on each column.
|
|
30590
|
+
*
|
|
30591
|
+
* @param {string} propertyName - property holding the collection on each owner item
|
|
30592
|
+
* @param {Object} [restCallbacks] - `{ idKey, rerunOn, dependencies, GET, GET_MANY, POST, POST_MANY, PUT, PUT_MANY, PATCH, PATCH_MANY, DELETE, DELETE_MANY }`
|
|
30593
|
+
* @see docs/resource.md
|
|
30594
|
+
*/
|
|
30464
30595
|
stateFacade.scopedMany = (
|
|
30465
30596
|
propertyName,
|
|
30466
30597
|
{
|
|
@@ -35234,7 +35365,7 @@ const PhoneSvg = () => {
|
|
|
35234
35365
|
};
|
|
35235
35366
|
|
|
35236
35367
|
installImportMetaCssBuild(import.meta);// # TextAnchor — how it works
|
|
35237
|
-
const css$
|
|
35368
|
+
const css$P = /* css */`
|
|
35238
35369
|
.navi_text_anchor {
|
|
35239
35370
|
vertical-align: baseline;
|
|
35240
35371
|
user-select: none;
|
|
@@ -35269,7 +35400,7 @@ const TextAnchor = ({
|
|
|
35269
35400
|
textSize,
|
|
35270
35401
|
lineLayout
|
|
35271
35402
|
}) => {
|
|
35272
|
-
import.meta.css = [css$
|
|
35403
|
+
import.meta.css = [css$P, "@jsenv/navi/src/text/text_anchor.jsx"];
|
|
35273
35404
|
const anchorRef = useRef();
|
|
35274
35405
|
|
|
35275
35406
|
// Plain useLayoutEffect would also fire while an ancestor dialog/popover
|
|
@@ -35384,7 +35515,7 @@ const computeTopOffset = ({
|
|
|
35384
35515
|
};
|
|
35385
35516
|
const charTopCanvas = document.createElement("canvas");
|
|
35386
35517
|
|
|
35387
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
35518
|
+
installImportMetaCssBuild(import.meta);const css$O = /* css */`
|
|
35388
35519
|
@layer navi {
|
|
35389
35520
|
/* Ensure data attributes from box.jsx can win to update display */
|
|
35390
35521
|
.navi_icon {
|
|
@@ -35542,7 +35673,7 @@ const Icon = ({
|
|
|
35542
35673
|
fillLine,
|
|
35543
35674
|
...props
|
|
35544
35675
|
}) => {
|
|
35545
|
-
import.meta.css = [css$
|
|
35676
|
+
import.meta.css = [css$O, "@jsenv/navi/src/text/icon.jsx"];
|
|
35546
35677
|
const innerChildren = href ? jsx("svg", {
|
|
35547
35678
|
width: "100%",
|
|
35548
35679
|
height: "100%",
|
|
@@ -35695,7 +35826,7 @@ const useDimColorWhen = (elementRef, shouldDim) => {
|
|
|
35695
35826
|
});
|
|
35696
35827
|
};
|
|
35697
35828
|
|
|
35698
|
-
installImportMetaCssBuild(import.meta);const css$
|
|
35829
|
+
installImportMetaCssBuild(import.meta);const css$N = /* css */`
|
|
35699
35830
|
@layer navi {
|
|
35700
35831
|
.navi_link {
|
|
35701
35832
|
--link-border-radius: unset;
|
|
@@ -36134,7 +36265,7 @@ Object.assign(PSEUDO_CLASSES, {
|
|
|
36134
36265
|
* @param {boolean} [props.readOnly]
|
|
36135
36266
|
*/
|
|
36136
36267
|
const Link = props => {
|
|
36137
|
-
import.meta.css = [css$
|
|
36268
|
+
import.meta.css = [css$N, "@jsenv/navi/src/nav/link/link.jsx"];
|
|
36138
36269
|
if (props.route) {
|
|
36139
36270
|
return jsx(LinkWithRoute, {
|
|
36140
36271
|
...props
|
|
@@ -36369,7 +36500,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
36369
36500
|
* TabList component with support for horizontal and vertical layouts
|
|
36370
36501
|
* https://dribbble.com/search/tabs
|
|
36371
36502
|
*/
|
|
36372
|
-
const css$
|
|
36503
|
+
const css$M = /* css */`
|
|
36373
36504
|
@layer navi {
|
|
36374
36505
|
.navi_nav {
|
|
36375
36506
|
--nav-border: none;
|
|
@@ -36544,7 +36675,7 @@ const Nav = ({
|
|
|
36544
36675
|
// "before" or "after": which side the panel sits on, turning the nav into folder tabs
|
|
36545
36676
|
...props
|
|
36546
36677
|
}) => {
|
|
36547
|
-
import.meta.css = [css$
|
|
36678
|
+
import.meta.css = [css$M, "@jsenv/navi/src/nav/link/nav.jsx"];
|
|
36548
36679
|
children = toChildArray(children);
|
|
36549
36680
|
return jsx(Box, {
|
|
36550
36681
|
as: "nav",
|
|
@@ -36936,7 +37067,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
36936
37067
|
* Border width participates in layout (it is added to the tab and page
|
|
36937
37068
|
* padding): a thick border grows the binder rather than eating into the text.
|
|
36938
37069
|
*/
|
|
36939
|
-
const css$
|
|
37070
|
+
const css$L = /* css */`
|
|
36940
37071
|
@layer navi {
|
|
36941
37072
|
.navi_binder {
|
|
36942
37073
|
--binder-border-width: var(--navi-control-border-width);
|
|
@@ -37249,7 +37380,7 @@ const Binder = ({
|
|
|
37249
37380
|
pagePadding,
|
|
37250
37381
|
...props
|
|
37251
37382
|
}) => {
|
|
37252
|
-
import.meta.css = [css$
|
|
37383
|
+
import.meta.css = [css$L, "@jsenv/navi/src/nav/binder/binder.jsx"];
|
|
37253
37384
|
const items = toChildArray(children).map((child, index) => {
|
|
37254
37385
|
const {
|
|
37255
37386
|
value: itemValue,
|
|
@@ -37600,8 +37731,8 @@ const withPixelUnit = value => {
|
|
|
37600
37731
|
* because the document is the scrollport in the common case and an anchor
|
|
37601
37732
|
* landing under a bar is never what anyone wants.
|
|
37602
37733
|
*
|
|
37603
|
-
* The variables hold the
|
|
37604
|
-
* sets them.
|
|
37734
|
+
* The variables hold the measured size of the bars on that edge — see the
|
|
37735
|
+
* comment where FixedBar sets them.
|
|
37605
37736
|
*/
|
|
37606
37737
|
|
|
37607
37738
|
const FIXED_BAR_SPACE_CSS = /* css */ `
|
|
@@ -37631,17 +37762,42 @@ const FIXED_BAR_SPACE_CSS = /* css */ `
|
|
|
37631
37762
|
}
|
|
37632
37763
|
`;
|
|
37633
37764
|
|
|
37765
|
+
// Several bars can share an edge — during a page transition the outgoing and
|
|
37766
|
+
// the incoming one are both mounted. They are all pinned to that same edge, so
|
|
37767
|
+
// they overlap: the room to give back is the largest of them, not their sum,
|
|
37768
|
+
// and one leaving must leave the others' room in place.
|
|
37769
|
+
const sizeMapByArea = new Map();
|
|
37770
|
+
|
|
37634
37771
|
/**
|
|
37635
37772
|
* @param {"top"|"bottom"|"left"|"right"} area
|
|
37636
|
-
* @param {
|
|
37773
|
+
* @param {Element} barElement - Which bar this size belongs to.
|
|
37774
|
+
* @param {number|null} size - In px; `null` gives that bar's room back to the
|
|
37775
|
+
* content.
|
|
37637
37776
|
*/
|
|
37638
|
-
const setFixedBarSpace = (area,
|
|
37777
|
+
const setFixedBarSpace = (area, barElement, size) => {
|
|
37778
|
+
let sizeMap = sizeMapByArea.get(area);
|
|
37779
|
+
if (!sizeMap) {
|
|
37780
|
+
sizeMap = new Map();
|
|
37781
|
+
sizeMapByArea.set(area, sizeMap);
|
|
37782
|
+
}
|
|
37783
|
+
if (size === null) {
|
|
37784
|
+
sizeMap.delete(barElement);
|
|
37785
|
+
} else {
|
|
37786
|
+
sizeMap.set(barElement, size);
|
|
37787
|
+
}
|
|
37788
|
+
|
|
37789
|
+
let largestSize = 0;
|
|
37790
|
+
for (const barSize of sizeMap.values()) {
|
|
37791
|
+
if (barSize > largestSize) {
|
|
37792
|
+
largestSize = barSize;
|
|
37793
|
+
}
|
|
37794
|
+
}
|
|
37639
37795
|
const property = `--navi-fixed-bar-space-${area}`;
|
|
37640
37796
|
const { style } = document.documentElement;
|
|
37641
|
-
if (
|
|
37797
|
+
if (sizeMap.size === 0) {
|
|
37642
37798
|
style.removeProperty(property);
|
|
37643
37799
|
} else {
|
|
37644
|
-
style.setProperty(property,
|
|
37800
|
+
style.setProperty(property, `${largestSize}px`);
|
|
37645
37801
|
}
|
|
37646
37802
|
};
|
|
37647
37803
|
|
|
@@ -37671,7 +37827,7 @@ installImportMetaCssBuild(import.meta);/**
|
|
|
37671
37827
|
* into the size; a box-shadow draws the identical line and stays out of
|
|
37672
37828
|
* layout.
|
|
37673
37829
|
*/
|
|
37674
|
-
const css$
|
|
37830
|
+
const css$K = /* css */`
|
|
37675
37831
|
@layer navi {
|
|
37676
37832
|
:root {
|
|
37677
37833
|
--navi-fixed-bar-width: 56px;
|
|
@@ -37809,7 +37965,7 @@ const FixedBar = ({
|
|
|
37809
37965
|
border = true,
|
|
37810
37966
|
...props
|
|
37811
37967
|
}) => {
|
|
37812
|
-
import.meta.css = [css$
|
|
37968
|
+
import.meta.css = [css$K, "@jsenv/navi/src/layout/fixed_bar/fixed_bar.jsx"];
|
|
37813
37969
|
const defaultRef = useRef();
|
|
37814
37970
|
props.ref = props.ref || defaultRef;
|
|
37815
37971
|
// Whichever of width/height crosses the edge the bar sits on is what the
|
|
@@ -37818,6 +37974,9 @@ const FixedBar = ({
|
|
|
37818
37974
|
// rebuilt as a calc() expression, so a size coming from anywhere — a prop, a
|
|
37819
37975
|
// theme variable, the content itself — is reserved just the same, and each
|
|
37820
37976
|
// `env()` inset stays the browser's business alone.
|
|
37977
|
+
// And measured again whenever it changes: a ResizeObserver on the bar covers
|
|
37978
|
+
// in one go a size prop that changes, content arriving or leaving, a font
|
|
37979
|
+
// loading late, a rotation moving the notch.
|
|
37821
37980
|
const vertical = area === "left" || area === "right";
|
|
37822
37981
|
const {
|
|
37823
37982
|
ref
|
|
@@ -37827,15 +37986,21 @@ const FixedBar = ({
|
|
|
37827
37986
|
if (!barElement) {
|
|
37828
37987
|
return undefined;
|
|
37829
37988
|
}
|
|
37830
|
-
const {
|
|
37831
|
-
|
|
37832
|
-
|
|
37833
|
-
|
|
37834
|
-
|
|
37989
|
+
const publishSize = () => {
|
|
37990
|
+
const {
|
|
37991
|
+
width,
|
|
37992
|
+
height
|
|
37993
|
+
} = barElement.getBoundingClientRect();
|
|
37994
|
+
setFixedBarSpace(area, barElement, vertical ? width : height);
|
|
37995
|
+
};
|
|
37996
|
+
publishSize();
|
|
37997
|
+
const resizeObserver = new ResizeObserver(publishSize);
|
|
37998
|
+
resizeObserver.observe(barElement);
|
|
37835
37999
|
return () => {
|
|
37836
|
-
|
|
38000
|
+
resizeObserver.disconnect();
|
|
38001
|
+
setFixedBarSpace(area, barElement, null);
|
|
37837
38002
|
};
|
|
37838
|
-
}, [area]);
|
|
38003
|
+
}, [area, vertical]);
|
|
37839
38004
|
return jsx(Box, {
|
|
37840
38005
|
baseClassName: "navi_fixed_bar",
|
|
37841
38006
|
"data-area": area,
|
|
@@ -37846,6 +38011,178 @@ const FixedBar = ({
|
|
|
37846
38011
|
});
|
|
37847
38012
|
};
|
|
37848
38013
|
|
|
38014
|
+
/**
|
|
38015
|
+
* Finds the element that is too wide for the page and says which one it is.
|
|
38016
|
+
*
|
|
38017
|
+
* A page that overflows horizontally is a bug wherever it happens, and on
|
|
38018
|
+
* Chrome Android it is a catastrophic one: the layout viewport inflates to the
|
|
38019
|
+
* content and `position: fixed` centering goes with it (see
|
|
38020
|
+
* docs/MOBILE_LAYOUT_PITFALLS.md). The remedy there is a wrapper in
|
|
38021
|
+
* `overflow-x: clip`, and it works — at the price of making the cause
|
|
38022
|
+
* invisible: nothing sticks out anymore, so nothing says a fixed width, a
|
|
38023
|
+
* `min-width` or an unbreakable string is still oversized. This puts the
|
|
38024
|
+
* signal back, without giving up the net.
|
|
38025
|
+
*
|
|
38026
|
+
* What counts as "too wide" is measured against the box that clips, not
|
|
38027
|
+
* against the document: with `clip` there is no scrollable overflow to read
|
|
38028
|
+
* (`scrollWidth` reports none), so the rectangles of the descendants are what
|
|
38029
|
+
* tells.
|
|
38030
|
+
*
|
|
38031
|
+
* Two things are deliberately not reported, because they cannot reach the
|
|
38032
|
+
* document:
|
|
38033
|
+
* - anything inside a box that clips or scrolls on its own — a wide table in
|
|
38034
|
+
* its `overflow-x: auto` container is doing exactly what it should;
|
|
38035
|
+
* - anything out of flow in the viewport's own coordinates (`position: fixed`,
|
|
38036
|
+
* the top layer), which contributes nothing to the document's overflow.
|
|
38037
|
+
*/
|
|
38038
|
+
|
|
38039
|
+
|
|
38040
|
+
// Subpixel layout rounds rectangles up on boxes that fit exactly.
|
|
38041
|
+
const OVERFLOW_TOLERANCE = 1;
|
|
38042
|
+
|
|
38043
|
+
const css$J = /* css */ `
|
|
38044
|
+
[data-navi-overflow-x] {
|
|
38045
|
+
outline: 2px dashed #e74c3c;
|
|
38046
|
+
outline-offset: -2px;
|
|
38047
|
+
}
|
|
38048
|
+
`;
|
|
38049
|
+
|
|
38050
|
+
/**
|
|
38051
|
+
* @param {object} [options]
|
|
38052
|
+
* @param {Element} [options.root=document.body] The box the content must fit
|
|
38053
|
+
* in — the wrapper carrying `overflow-x: clip`, when there is one.
|
|
38054
|
+
* @param {boolean} [options.highlight=true] Outline the culprits on screen.
|
|
38055
|
+
* @param {(overflows: Array<{element: Element, overflow: number, side: "left"|"right"}>) => void} [options.onDetect]
|
|
38056
|
+
* Replaces the default console warning.
|
|
38057
|
+
* @returns {() => void} Stops watching.
|
|
38058
|
+
*/
|
|
38059
|
+
const detectHorizontalOverflow = ({
|
|
38060
|
+
root = document.body,
|
|
38061
|
+
highlight = true,
|
|
38062
|
+
onDetect = warnOverflows,
|
|
38063
|
+
} = {}) => {
|
|
38064
|
+
let styleEl = null;
|
|
38065
|
+
if (highlight) {
|
|
38066
|
+
styleEl = document.createElement("style");
|
|
38067
|
+
styleEl.textContent = css$J;
|
|
38068
|
+
document.head.appendChild(styleEl);
|
|
38069
|
+
}
|
|
38070
|
+
|
|
38071
|
+
let highlightedSet = new Set();
|
|
38072
|
+
const detect = () => {
|
|
38073
|
+
const overflows = findOverflows(root);
|
|
38074
|
+
if (highlight) {
|
|
38075
|
+
const nextHighlightedSet = new Set();
|
|
38076
|
+
for (const { element } of overflows) {
|
|
38077
|
+
element.setAttribute("data-navi-overflow-x", "");
|
|
38078
|
+
nextHighlightedSet.add(element);
|
|
38079
|
+
}
|
|
38080
|
+
for (const element of highlightedSet) {
|
|
38081
|
+
if (!nextHighlightedSet.has(element)) {
|
|
38082
|
+
element.removeAttribute("data-navi-overflow-x");
|
|
38083
|
+
}
|
|
38084
|
+
}
|
|
38085
|
+
highlightedSet = nextHighlightedSet;
|
|
38086
|
+
}
|
|
38087
|
+
if (overflows.length) {
|
|
38088
|
+
onDetect(overflows);
|
|
38089
|
+
}
|
|
38090
|
+
};
|
|
38091
|
+
|
|
38092
|
+
// Measuring inside a resize callback is what makes the loop; wait for the
|
|
38093
|
+
// frame that resize produced.
|
|
38094
|
+
let frame = null;
|
|
38095
|
+
const requestDetect = () => {
|
|
38096
|
+
if (frame !== null) {
|
|
38097
|
+
return;
|
|
38098
|
+
}
|
|
38099
|
+
frame = requestAnimationFrame(() => {
|
|
38100
|
+
frame = null;
|
|
38101
|
+
detect();
|
|
38102
|
+
});
|
|
38103
|
+
};
|
|
38104
|
+
|
|
38105
|
+
// Resize catches the window narrowing and the content growing (the root gets
|
|
38106
|
+
// taller); mutations catch a wide element arriving without changing the
|
|
38107
|
+
// root's size.
|
|
38108
|
+
const resizeObserver = new ResizeObserver(requestDetect);
|
|
38109
|
+
resizeObserver.observe(root);
|
|
38110
|
+
const mutationObserver = new MutationObserver(requestDetect);
|
|
38111
|
+
mutationObserver.observe(root, { subtree: true, childList: true });
|
|
38112
|
+
requestDetect();
|
|
38113
|
+
|
|
38114
|
+
return () => {
|
|
38115
|
+
resizeObserver.disconnect();
|
|
38116
|
+
mutationObserver.disconnect();
|
|
38117
|
+
if (frame !== null) {
|
|
38118
|
+
cancelAnimationFrame(frame);
|
|
38119
|
+
frame = null;
|
|
38120
|
+
}
|
|
38121
|
+
for (const element of highlightedSet) {
|
|
38122
|
+
element.removeAttribute("data-navi-overflow-x");
|
|
38123
|
+
}
|
|
38124
|
+
highlightedSet.clear();
|
|
38125
|
+
if (styleEl) {
|
|
38126
|
+
styleEl.remove();
|
|
38127
|
+
}
|
|
38128
|
+
};
|
|
38129
|
+
};
|
|
38130
|
+
|
|
38131
|
+
const warnOverflows = (overflows) => {
|
|
38132
|
+
for (const { element, overflow, side } of overflows) {
|
|
38133
|
+
console.warn(
|
|
38134
|
+
`${getElementSignature(element)} overflows the page by ${Math.round(
|
|
38135
|
+
overflow,
|
|
38136
|
+
)}px on the ${side}. Look for a width in px, a min-width, or an unbreakable string; if it is meant to be wider than the screen, give it its own "overflow-x: auto".`,
|
|
38137
|
+
element,
|
|
38138
|
+
);
|
|
38139
|
+
}
|
|
38140
|
+
};
|
|
38141
|
+
|
|
38142
|
+
const findOverflows = (root) => {
|
|
38143
|
+
const rootRect = root.getBoundingClientRect();
|
|
38144
|
+
const rootStyle = getComputedStyle(root);
|
|
38145
|
+
// Only the end side is watched: what sticks out there is what the document
|
|
38146
|
+
// grows to hold, and what inflates the layout viewport. Past the start edge
|
|
38147
|
+
// the content is simply unreachable, and that is where the offscreen
|
|
38148
|
+
// patterns (a label parked at `left: -9999px`) live.
|
|
38149
|
+
const side = rootStyle.direction === "rtl" ? "left" : "right";
|
|
38150
|
+
const overflows = [];
|
|
38151
|
+
const collect = (parentEl) => {
|
|
38152
|
+
for (const el of parentEl.children) {
|
|
38153
|
+
const style = getComputedStyle(el);
|
|
38154
|
+
if (style.display === "none" || style.position === "fixed") {
|
|
38155
|
+
continue;
|
|
38156
|
+
}
|
|
38157
|
+
if (el.hasAttribute("popover") || el.tagName === "DIALOG") {
|
|
38158
|
+
continue;
|
|
38159
|
+
}
|
|
38160
|
+
const rect = el.getBoundingClientRect();
|
|
38161
|
+
if (rect.width === 0 && rect.height === 0) {
|
|
38162
|
+
continue;
|
|
38163
|
+
}
|
|
38164
|
+
const overflow =
|
|
38165
|
+
side === "right"
|
|
38166
|
+
? rect.right - rootRect.right
|
|
38167
|
+
: rootRect.left - rect.left;
|
|
38168
|
+
if (overflow > OVERFLOW_TOLERANCE) {
|
|
38169
|
+
// The outermost box that sticks out is the one to fix; its children
|
|
38170
|
+
// stick out because it does.
|
|
38171
|
+
overflows.push({ element: el, overflow, side });
|
|
38172
|
+
continue;
|
|
38173
|
+
}
|
|
38174
|
+
// A box holding its own horizontal overflow cannot leak into the page,
|
|
38175
|
+
// whatever it holds — a wide table in its own scroll box is right.
|
|
38176
|
+
if (style.overflowX !== "visible") {
|
|
38177
|
+
continue;
|
|
38178
|
+
}
|
|
38179
|
+
collect(el);
|
|
38180
|
+
}
|
|
38181
|
+
};
|
|
38182
|
+
collect(root);
|
|
38183
|
+
return overflows;
|
|
38184
|
+
};
|
|
38185
|
+
|
|
37849
38186
|
const useFocusGroup = (
|
|
37850
38187
|
elementRef,
|
|
37851
38188
|
{
|
|
@@ -38447,7 +38784,7 @@ const useCheckableProps = (props, options) => {
|
|
|
38447
38784
|
installImportMetaCssBuild(import.meta);const css$F = /* css */`
|
|
38448
38785
|
@layer navi {
|
|
38449
38786
|
.navi_checkbox {
|
|
38450
|
-
--border-radius: var(--navi-
|
|
38787
|
+
--border-radius: var(--navi-checkbox-border-radius);
|
|
38451
38788
|
--border-width: var(--navi-control-border-width);
|
|
38452
38789
|
/* Focus outline */
|
|
38453
38790
|
--outline-width: var(--navi-focus-outline-width);
|
|
@@ -47048,7 +47385,7 @@ const css$u = /* css */`
|
|
|
47048
47385
|
around it does. Its own scroll box must then be transparent to
|
|
47049
47386
|
layout — otherwise it would cap the list at a height of its own and
|
|
47050
47387
|
start a second, nested scroll inside the page's. */
|
|
47051
|
-
&[data-scroller
|
|
47388
|
+
&[data-scroller] {
|
|
47052
47389
|
max-height: none;
|
|
47053
47390
|
overflow: visible;
|
|
47054
47391
|
|
|
@@ -47058,6 +47395,14 @@ const css$u = /* css */`
|
|
|
47058
47395
|
}
|
|
47059
47396
|
}
|
|
47060
47397
|
|
|
47398
|
+
/* Scrolling with the page means sticking to the viewport, and a FixedBar
|
|
47399
|
+
is in front of that viewport: without the offset a sticky label lands
|
|
47400
|
+
behind the bar. The bar publishes the room it takes (see
|
|
47401
|
+
fixed_bar_space.js) and it is 0px when there is no bar. */
|
|
47402
|
+
&[data-scroller="document"] {
|
|
47403
|
+
--x-list-group-label-top: var(--navi-fixed-bar-space-top, 0px);
|
|
47404
|
+
}
|
|
47405
|
+
|
|
47061
47406
|
&[data-expand-x] {
|
|
47062
47407
|
width: 100%;
|
|
47063
47408
|
}
|
|
@@ -47416,7 +47761,7 @@ const css$u = /* css */`
|
|
|
47416
47761
|
|
|
47417
47762
|
.navi_list_item_group_label {
|
|
47418
47763
|
position: sticky;
|
|
47419
|
-
top:
|
|
47764
|
+
top: var(--list-group-label-top, var(--x-list-group-label-top, 0px));
|
|
47420
47765
|
z-index: 1;
|
|
47421
47766
|
display: block;
|
|
47422
47767
|
background-color: var(--list-group-label-background-color);
|
|
@@ -47659,7 +48004,7 @@ const ListUI = props => {
|
|
|
47659
48004
|
baseClassName: "navi_list_container",
|
|
47660
48005
|
popover: popover,
|
|
47661
48006
|
"data-horizontal": horizontal ? "" : undefined,
|
|
47662
|
-
"data-scroller": scroller
|
|
48007
|
+
"data-scroller": getScrollerAttribute(scroller),
|
|
47663
48008
|
"data-expand-x": expandX || expand ? "" : undefined,
|
|
47664
48009
|
"data-expand-y": expandY || expand ? "" : undefined,
|
|
47665
48010
|
expandX: expandX,
|
|
@@ -47918,6 +48263,7 @@ const useListScrollSync = ({
|
|
|
47918
48263
|
setScrollerElResolved(current => current === scrollerElNow ? current : scrollerElNow);
|
|
47919
48264
|
};
|
|
47920
48265
|
useLayoutEffect(resolveScroller);
|
|
48266
|
+
useStickyScrollportWarning();
|
|
47921
48267
|
|
|
47922
48268
|
// The row the scroll holds onto across a change of geometry, and where it
|
|
47923
48269
|
// sat when that change was decided. Captured at the two moments the list
|
|
@@ -48695,6 +49041,28 @@ const getScrollerViewportRect = scrollerEl => {
|
|
|
48695
49041
|
}
|
|
48696
49042
|
return scrollerEl.getBoundingClientRect();
|
|
48697
49043
|
};
|
|
49044
|
+
const useStickyScrollportWarning = (ref, scroller) => {
|
|
49045
|
+
useRef(false);
|
|
49046
|
+
useLayoutEffect(() => {
|
|
49047
|
+
{
|
|
49048
|
+
return;
|
|
49049
|
+
}
|
|
49050
|
+
});
|
|
49051
|
+
};
|
|
49052
|
+
|
|
49053
|
+
// The CSS needs to tell "the page scrolls me" from "some box around me
|
|
49054
|
+
// scrolls me": only the first one sticks to the viewport, where the fixed bars
|
|
49055
|
+
// are.
|
|
49056
|
+
const getScrollerAttribute = scroller => {
|
|
49057
|
+
if (scroller === "self") {
|
|
49058
|
+
return undefined;
|
|
49059
|
+
}
|
|
49060
|
+
if (scroller === "document") {
|
|
49061
|
+
return "document";
|
|
49062
|
+
}
|
|
49063
|
+
return "parent";
|
|
49064
|
+
};
|
|
49065
|
+
|
|
48698
49066
|
// scroller="parent": the list virtualizes against the scroll box it lives in
|
|
48699
49067
|
// instead of one of its own. Which box that is can only be measured, and a
|
|
48700
49068
|
// measurement holds for the geometry it was taken on — see resolveScroller in
|
|
@@ -62783,5 +63151,5 @@ const UserSvg = () => jsx("svg", {
|
|
|
62783
63151
|
})
|
|
62784
63152
|
});
|
|
62785
63153
|
|
|
62786
|
-
export { ActionRenderer, ActiveKeyboardShortcuts, Address, Badge, BadgeCount, BadgeList, Binder, Box, Button, ButtonCopyToClipboard, Caption, CardLayout, CheckSvg, CheckboxGroup, CloseSvg, Code, Col, Colgroup, Color, ConstructionSvg, ControlGroup, DaySpin, Details, Dialog, Editable, ErrorBoundary, ErrorBoundaryContext, ExclamationSvg, EyeClosedSvg, EyeSvg, Field, FixedBar, Form, Group, Head, HeartSvg, HomeSvg, Icon, Image, Input, InputDuration, Interpolate, Label, Link, LinkAnchorSvg, LinkBlankTargetSvg, LinkCurrentSvg, List, ListItem, ListItemGroup, ListItems, Loading, LoadingDotsSvg, LoadingIndicator, LoadingIndicatorFluid, LoadingOutline, MessageBox, Meter, Nav, NaviDebug, NumberSpin, Paragraph, Picker, Popover, Popup, Quantity, RadioGroup, Route, RowNumberCol, RowNumberTableCell, SVGMaskOverlay, SearchSvg, SelectableInput, SelectionContext, Separator, SettingsSvg, SidePanel, Slide, SlideContainer, Spin, StarSvg, SummaryMarker, Svg, Table, TableCell, Tbody, Text, TextBox, Textarea, TextareaCharCount, Thead, Time, Title, Tr, UITransition, Unit, UserSvg, ViewportLayout, Wheel, WheelGroup, WheelItem, actionRunEffect, anyMatchingRouteSignal, applySearch, arraySignalMembership, coarsePointerSignal, compareTwoJsValues, createAction, createAvailableConstraint, createRequestCanceller, createSearch, createSelectionKeyboardShortcuts, createSlot, defineNaviConfirmPopupOptions, enableDebugActions, enableDebugOnDocumentLoading, ensureDocumentStartViewTransition, filterTableSelection, formatDatetime, formatDay, formatDayRelative, formatMonth, formatNumber, formatTime, formatTimeRelative, getNowHours, getNowHoursRoundedToStep, interpolateText, isCellSelected, isColumnSelected, isRowSelected, isToday, languagesSignal, localStorageSignal, moveArrayItemByIndex, navBack, navForward, navIntegratedVia, navTo, naviI18n, openCallout, rawUrlPart, registerGlobalConstraint, reload, rerunActions, resource, route, routeAction, setBaseUrl, setPreferredLanguage, setSupportedLanguages, setupRoutes, stateSignal, stopLoad, stringifyTableSelectionValue, swapArrayItemByIndex, syncOwnedResourceToSignals, syncResourceToSignals, updateActions, useActionStatus, useArraySignalMembership, useAsyncData, useCalloutRequestClose, useCancelPrevious, useCellGridFromRows, useConstraintValidityState, useDependenciesDiff, useDisplayedLayoutEffect, useDocumentResource, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useInputGroup, useKeyboardShortcuts, useNavState, useOrderedColumns, usePopupMode, useRouteStatus, useRunOnMount, useSearchText, useSelectableElement, useSelectionController, useSignalSync, useSlideValue, useStateArray, useTitleLevel, useUrlSearchParam, valueInLocalStorage, windowWidthSignal };
|
|
63154
|
+
export { ActionRenderer, ActiveKeyboardShortcuts, Address, Badge, BadgeCount, BadgeList, Binder, Box, Button, ButtonCopyToClipboard, Caption, CardLayout, CheckSvg, CheckboxGroup, CloseSvg, Code, Col, Colgroup, Color, ConstructionSvg, ControlGroup, DaySpin, Details, Dialog, Editable, ErrorBoundary, ErrorBoundaryContext, ExclamationSvg, EyeClosedSvg, EyeSvg, Field, FixedBar, Form, Group, Head, HeartSvg, HomeSvg, Icon, Image, Input, InputDuration, Interpolate, Label, Link, LinkAnchorSvg, LinkBlankTargetSvg, LinkCurrentSvg, List, ListItem, ListItemGroup, ListItems, Loading, LoadingDotsSvg, LoadingIndicator, LoadingIndicatorFluid, LoadingOutline, MessageBox, Meter, Nav, NaviDebug, NumberSpin, Paragraph, Picker, Popover, Popup, Quantity, RadioGroup, Route, RowNumberCol, RowNumberTableCell, SVGMaskOverlay, SearchSvg, SelectableInput, SelectionContext, Separator, SettingsSvg, SidePanel, Slide, SlideContainer, Spin, StarSvg, SummaryMarker, Svg, Table, TableCell, Tbody, Text, TextBox, Textarea, TextareaCharCount, Thead, Time, Title, Tr, UITransition, Unit, UserSvg, ViewportLayout, Wheel, WheelGroup, WheelItem, actionRunEffect, anyMatchingRouteSignal, applySearch, arraySignalMembership, coarsePointerSignal, compareTwoJsValues, createAction, createAvailableConstraint, createRequestCanceller, createSearch, createSelectionKeyboardShortcuts, createSlot, defineNaviConfirmPopupOptions, detectHorizontalOverflow, enableDebugActions, enableDebugOnDocumentLoading, ensureDocumentStartViewTransition, filterTableSelection, formatDatetime, formatDay, formatDayRelative, formatMonth, formatNumber, formatTime, formatTimeRelative, getNowHours, getNowHoursRoundedToStep, interpolateText, isCellSelected, isColumnSelected, isRowSelected, isToday, languagesSignal, localStorageSignal, moveArrayItemByIndex, navBack, navForward, navIntegratedVia, navTo, naviI18n, openCallout, rawUrlPart, registerGlobalConstraint, reload, rerunActions, resource, route, routeAction, setBaseUrl, setPreferredLanguage, setSupportedLanguages, setupRoutes, stateSignal, stopLoad, stringifyTableSelectionValue, swapArrayItemByIndex, syncOwnedResourceToSignals, syncResourceToSignals, updateActions, useActionStatus, useArraySignalMembership, useAsyncData, useCalloutRequestClose, useCancelPrevious, useCellGridFromRows, useConstraintValidityState, useDependenciesDiff, useDisplayedLayoutEffect, useDocumentResource, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useInputGroup, useKeyboardShortcuts, useNavState, useOrderedColumns, usePopupMode, useRouteStatus, useRunOnMount, useSearchText, useSelectableElement, useSelectionController, useSignalSync, useSlideValue, useStateArray, useTitleLevel, useUrlSearchParam, valueInLocalStorage, windowWidthSignal };
|
|
62787
63155
|
//# sourceMappingURL=jsenv_navi.js.map
|