@jsenv/navi 0.29.11 → 0.29.14
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 +281 -17
- package/dist/jsenv_navi.js.map +8 -6
- 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/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/README.md
CHANGED
|
@@ -12,6 +12,15 @@ Routes are flexible: you can create route groups to share logic, state, or UI ac
|
|
|
12
12
|
|
|
13
13
|
Actions are async operations with lifecycle management — pending, success, error. You can declare actions that run when navigating to a route, and any component can subscribe to them via `useAsyncData` to reflect what is happening: loading states, results, errors. No manual wiring.
|
|
14
14
|
|
|
15
|
+
## REST state
|
|
16
|
+
|
|
17
|
+
`resource()` turns a REST endpoint into a reactive store: one action per verb, a
|
|
18
|
+
shared signal store every component reads from, and automatic invalidation
|
|
19
|
+
between related actions. Parent/child relations are first-class — `.one`,
|
|
20
|
+
`.many`, `.scopedOne`, `.scopedMany` model a backend sub-resource such as
|
|
21
|
+
`/games/:id/candidates` rather than leaving you to hand-roll it. See
|
|
22
|
+
[docs/resource.md](./docs/resource.md).
|
|
23
|
+
|
|
15
24
|
## Layout & Typography
|
|
16
25
|
|
|
17
26
|
**`Box`** is the main layout primitive. It wraps CSS Flexbox with a friendlier API: `flex` for horizontal layout, `flex="y"` for vertical (no more guessing what `flex-direction: column` does visually). Supports `grid`, `inline`, alignment via `alignX`/`alignY`, and spacing props.
|
package/dist/jsenv_navi.js
CHANGED
|
@@ -12042,6 +12042,13 @@ const mergeActionParams = (currentParams, newParams) => {
|
|
|
12042
12042
|
if (currentParams === NO_PARAMS) {
|
|
12043
12043
|
return newParams;
|
|
12044
12044
|
}
|
|
12045
|
+
if (newParams === undefined) {
|
|
12046
|
+
// Every control binds its action to its UI state signal; a control that carries
|
|
12047
|
+
// no value (a button) has an undefined one. It contributes no params, which must
|
|
12048
|
+
// not be confused with "the params are undefined" — the params bound by
|
|
12049
|
+
// bindParams stay in place.
|
|
12050
|
+
return currentParams;
|
|
12051
|
+
}
|
|
12045
12052
|
return mergeTwoJsValues(currentParams, newParams);
|
|
12046
12053
|
};
|
|
12047
12054
|
|
|
@@ -29676,6 +29683,36 @@ const debug$2 = (args) => {
|
|
|
29676
29683
|
}
|
|
29677
29684
|
};
|
|
29678
29685
|
|
|
29686
|
+
/**
|
|
29687
|
+
* Creates a reactive REST resource backed by a shared signal store.
|
|
29688
|
+
* Returns a `stateFacade` exposing one action per REST callback provided
|
|
29689
|
+
* (`USER.GET`, `USER.GET_MANY`, `USER.POST`, …) plus `.withParams()` and the
|
|
29690
|
+
* relationship methods `.one()`, `.many()`, `.scopedOne()`, `.scopedMany()`.
|
|
29691
|
+
*
|
|
29692
|
+
* Each REST callback receives the params passed to the action call and must return
|
|
29693
|
+
* the data that will be upserted into the store:
|
|
29694
|
+
* - GET / POST / PUT / PATCH → the full item object, e.g. `{ id, name }`
|
|
29695
|
+
* - DELETE → the id or `{ id }` of the removed item
|
|
29696
|
+
* - GET_MANY / POST_MANY / … → an array of item objects
|
|
29697
|
+
*
|
|
29698
|
+
* A sub-resource of the backend (`/games/:id/candidates`) must be modelled with a
|
|
29699
|
+
* relationship method, never as an `op`/`type` discriminator dispatched inside one
|
|
29700
|
+
* verb's callback.
|
|
29701
|
+
*
|
|
29702
|
+
* @param {string} name - resource name, used in action names and error messages
|
|
29703
|
+
* @param {Object} restCallbacks - `{ idKey, uniqueKeys, rerunOn, dependencies, GET, GET_MANY, POST, POST_MANY, PUT, PUT_MANY, PATCH, PATCH_MANY, DELETE, DELETE_MANY }`
|
|
29704
|
+
* @param {string} [restCallbacks.idKey] - primary key property, defaults to `"id"` (or the first `uniqueKeys` entry)
|
|
29705
|
+
* @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
|
|
29706
|
+
* @see docs/resource.md — relationships, callback return contracts, decision table
|
|
29707
|
+
*
|
|
29708
|
+
* @example
|
|
29709
|
+
* const USER = resource("user", {
|
|
29710
|
+
* GET: ({ id }) => fetchJson(`/users/${id}`),
|
|
29711
|
+
* GET_MANY: () => fetchJson(`/users`),
|
|
29712
|
+
* POST: (user) => fetchJson(`/users`, { method: "POST", body: user }),
|
|
29713
|
+
* DELETE: ({ id }) => fetchJson(`/users/${id}`, { method: "DELETE" }),
|
|
29714
|
+
* });
|
|
29715
|
+
*/
|
|
29679
29716
|
const resource = (
|
|
29680
29717
|
name,
|
|
29681
29718
|
{
|
|
@@ -29830,7 +29867,7 @@ const createResource = (
|
|
|
29830
29867
|
* @param {Object} params - Parameters to bind to all actions of this resource (required)
|
|
29831
29868
|
* @param {Object} options - Additional options for the parameterized resource
|
|
29832
29869
|
* @returns {Object} A new resource instance with parameter-bound actions and isolated lifecycle
|
|
29833
|
-
* @see
|
|
29870
|
+
* @see docs/resource_with_params.md for detailed documentation and examples
|
|
29834
29871
|
*
|
|
29835
29872
|
* @example
|
|
29836
29873
|
* const ROLE = resource("role", { GET: (params) => fetchRole(params) });
|
|
@@ -29877,6 +29914,31 @@ const createResource = (
|
|
|
29877
29914
|
};
|
|
29878
29915
|
stateFacade.withParams = withParams;
|
|
29879
29916
|
|
|
29917
|
+
/**
|
|
29918
|
+
* Links a property on each item to a single item in an independent child store.
|
|
29919
|
+
* The property is reactive: updating the child item anywhere propagates immediately.
|
|
29920
|
+
* The child resource exists independently — it is not owned by, nor deleted with, the parent.
|
|
29921
|
+
*
|
|
29922
|
+
* Use it when the child is a first-class entity with its own store, shared across
|
|
29923
|
+
* parents (a user referenced by many games). When the child only exists inside its
|
|
29924
|
+
* owner, use `.scopedOne()` instead.
|
|
29925
|
+
*
|
|
29926
|
+
* Callback return contracts:
|
|
29927
|
+
* - GET / PUT → the parent object with the relationship nested inside:
|
|
29928
|
+
* `async ({ id }) => ({ id, session: { id: 10, token: "abc" } })`; `null` for no relationship
|
|
29929
|
+
* - DELETE → the parent id (or `{ id }`); the property is set to `null`
|
|
29930
|
+
*
|
|
29931
|
+
* The backend may also embed the child inline in a parent GET/POST response — the
|
|
29932
|
+
* setter on the property upserts the nested object into the child store.
|
|
29933
|
+
*
|
|
29934
|
+
* Returns the child relationship resource, itself chainable:
|
|
29935
|
+
* `USER_SESSION.one("device", DEVICE)` adds a reactive `.device` property to each session.
|
|
29936
|
+
*
|
|
29937
|
+
* @param {string} propertyName - property holding the child on each parent item
|
|
29938
|
+
* @param {Object} childResource - the independent resource created by `resource()`
|
|
29939
|
+
* @param {Object} [restCallbacks] - `{ rerunOn, dependencies, GET, PUT, DELETE }`
|
|
29940
|
+
* @see docs/resource.md
|
|
29941
|
+
*/
|
|
29880
29942
|
stateFacade.one = (
|
|
29881
29943
|
propertyName,
|
|
29882
29944
|
childResource,
|
|
@@ -30044,6 +30106,29 @@ ${originalActionName} source location: ${locationInfo}`,
|
|
|
30044
30106
|
});
|
|
30045
30107
|
};
|
|
30046
30108
|
|
|
30109
|
+
/**
|
|
30110
|
+
* Links a property on each item to an array of items in an independent child store.
|
|
30111
|
+
* Items in the array are full entries in the shared child store — if the same item is
|
|
30112
|
+
* referenced by several parents, a single update propagates to all of them.
|
|
30113
|
+
*
|
|
30114
|
+
* Use it when children are first-class entities shared across parents (a game's players,
|
|
30115
|
+
* who are users). When the children only exist inside their owner, or when the relation
|
|
30116
|
+
* itself carries fields (`seen_at`, `slot`), use `.scopedMany()` instead.
|
|
30117
|
+
*
|
|
30118
|
+
* Callback return contracts:
|
|
30119
|
+
* - GET_MANY → the parent object with the array nested inside:
|
|
30120
|
+
* `async ({ id }) => ({ id, friends: [{ id: 2 }, { id: 3 }] })` — a full-parent
|
|
30121
|
+
* response is absorbed as-is; the array replaces the relationship
|
|
30122
|
+
* - GET / POST / PUT / PATCH → the child object; it is upserted into the child store
|
|
30123
|
+
* but does NOT join the parent's array, which only a GET_MANY refresh changes
|
|
30124
|
+
* - DELETE → `[parentId, childId]`
|
|
30125
|
+
* - DELETE_MANY → `[parentId, [childId, childId, …]]`
|
|
30126
|
+
*
|
|
30127
|
+
* @param {string} propertyName - property holding the child array on each parent item
|
|
30128
|
+
* @param {Object} childResource - the independent resource created by `resource()`
|
|
30129
|
+
* @param {Object} [restCallbacks] - `{ rerunOn, dependencies, GET, GET_MANY, POST, POST_MANY, PUT, PUT_MANY, PATCH, PATCH_MANY, DELETE, DELETE_MANY }`
|
|
30130
|
+
* @see docs/resource.md
|
|
30131
|
+
*/
|
|
30047
30132
|
stateFacade.many = (
|
|
30048
30133
|
propertyName,
|
|
30049
30134
|
childResource,
|
|
@@ -30340,6 +30425,29 @@ ${originalActionName} source location: ${locationInfo}`,
|
|
|
30340
30425
|
});
|
|
30341
30426
|
};
|
|
30342
30427
|
|
|
30428
|
+
/**
|
|
30429
|
+
* Attaches a single private sub-object to each item. The child has no identity outside
|
|
30430
|
+
* its owner and is not shared across items; each owner gets its own private signal.
|
|
30431
|
+
*
|
|
30432
|
+
* Use it for a sub-resource the backend exposes under the parent (`/users/:id/profile`)
|
|
30433
|
+
* whose content is meaningless without that parent.
|
|
30434
|
+
*
|
|
30435
|
+
* All callbacks must return `[ownerId, props | null]`:
|
|
30436
|
+
* - `GET: async ({ id }) => [id, { bio: "Hello", avatar: "alice.png" }]`
|
|
30437
|
+
* - `PATCH: async ({ id, bio }) => [id, { bio, avatar: "alice.png" }]`
|
|
30438
|
+
* - `DELETE: async ({ id }) => [id, null]`
|
|
30439
|
+
*
|
|
30440
|
+
* `ownerId` may also be `{ [uniqueKey]: value }` when the owner is known by an alternate key.
|
|
30441
|
+
* The property is `null` until a callback provides data; setting it to `null` clears it.
|
|
30442
|
+
* Mutations apply directly to the owner's signal, so the parent GET is never rerun.
|
|
30443
|
+
*
|
|
30444
|
+
* Returns the child relationship resource, itself chainable:
|
|
30445
|
+
* `USER_PROFILE.one("theme", THEME)` adds a reactive `.theme` property on each profile.
|
|
30446
|
+
*
|
|
30447
|
+
* @param {string} propertyName - property holding the sub-object on each owner item
|
|
30448
|
+
* @param {Object} [restCallbacks] - `{ idKey, rerunOn, dependencies, GET, POST, PUT, PATCH, DELETE }`
|
|
30449
|
+
* @see docs/resource.md
|
|
30450
|
+
*/
|
|
30343
30451
|
stateFacade.scopedOne = (
|
|
30344
30452
|
propertyName,
|
|
30345
30453
|
{
|
|
@@ -30461,6 +30569,36 @@ ${originalActionName} source location: ${locationInfo}`,
|
|
|
30461
30569
|
return childResource;
|
|
30462
30570
|
};
|
|
30463
30571
|
|
|
30572
|
+
/**
|
|
30573
|
+
* Attaches a private ordered collection of sub-objects to each item. The child objects
|
|
30574
|
+
* have no identity outside their owner — two owners can hold items with the same id that
|
|
30575
|
+
* are completely independent. Each owner gets its own private arraySignalStore.
|
|
30576
|
+
*
|
|
30577
|
+
* This is the shape for a backend sub-route (`/games/:id/candidates`,
|
|
30578
|
+
* `…/candidates/:userId/accept`) and for a relation carrying its own fields
|
|
30579
|
+
* (`candidate_since`, `seen_at`): those fields belong to the pair, not to a shared child
|
|
30580
|
+
* store where they would corrupt the entity for every other reader.
|
|
30581
|
+
*
|
|
30582
|
+
* All callbacks must return `[ownerId, ...rest]`:
|
|
30583
|
+
* - `GET_MANY: async ({ id }) => [id, [{ name: "id", type: "int" }, …]]` — replaces the collection
|
|
30584
|
+
* - `POST: async ({ id, name, type }) => [id, { name, type }]`
|
|
30585
|
+
* - `PUT: async ({ id, oldName, name, type }) => [id, oldName, { name, type }]` (id rename)
|
|
30586
|
+
* - `DELETE: async ({ id, name }) => [id, name]`
|
|
30587
|
+
* - `*_MANY: [ownerId, itemArray]` — any plural verb replaces the whole collection,
|
|
30588
|
+
* which is how a backend answering a sub-route with the refreshed parent is absorbed
|
|
30589
|
+
*
|
|
30590
|
+
* `ownerId` may also be `{ [uniqueKey]: value }` when the owner is known by an alternate key.
|
|
30591
|
+
* A singular POST upserts the child but does not append it to the collection; ordering is
|
|
30592
|
+
* the backend's, so the owner's GET is rerun instead (only when its last response embedded
|
|
30593
|
+
* `propertyName`), and the child's own GET_MANY reruns per its `rerunOn`.
|
|
30594
|
+
*
|
|
30595
|
+
* Returns the child relationship resource, itself chainable:
|
|
30596
|
+
* `TABLE_COLUMNS.one("dataType", DATA_TYPE)` adds a reactive `.dataType` property on each column.
|
|
30597
|
+
*
|
|
30598
|
+
* @param {string} propertyName - property holding the collection on each owner item
|
|
30599
|
+
* @param {Object} [restCallbacks] - `{ idKey, rerunOn, dependencies, GET, GET_MANY, POST, POST_MANY, PUT, PUT_MANY, PATCH, PATCH_MANY, DELETE, DELETE_MANY }`
|
|
30600
|
+
* @see docs/resource.md
|
|
30601
|
+
*/
|
|
30464
30602
|
stateFacade.scopedMany = (
|
|
30465
30603
|
propertyName,
|
|
30466
30604
|
{
|
|
@@ -37600,8 +37738,8 @@ const withPixelUnit = value => {
|
|
|
37600
37738
|
* because the document is the scrollport in the common case and an anchor
|
|
37601
37739
|
* landing under a bar is never what anyone wants.
|
|
37602
37740
|
*
|
|
37603
|
-
* The variables hold the
|
|
37604
|
-
* sets them.
|
|
37741
|
+
* The variables hold the measured size of the bars on that edge — see the
|
|
37742
|
+
* comment where FixedBar sets them.
|
|
37605
37743
|
*/
|
|
37606
37744
|
|
|
37607
37745
|
const FIXED_BAR_SPACE_CSS = /* css */ `
|
|
@@ -37631,18 +37769,122 @@ const FIXED_BAR_SPACE_CSS = /* css */ `
|
|
|
37631
37769
|
}
|
|
37632
37770
|
`;
|
|
37633
37771
|
|
|
37772
|
+
// Several bars can share an edge — during a page transition the outgoing and
|
|
37773
|
+
// the incoming one are both mounted. They are all pinned to that same edge, so
|
|
37774
|
+
// they overlap: the room to give back is the largest of them, not their sum,
|
|
37775
|
+
// and one leaving must leave the others' room in place.
|
|
37776
|
+
const sizeMapByArea = new Map();
|
|
37777
|
+
// What is currently on <html> for each area (absent = the variable is not set).
|
|
37778
|
+
// Writing the value that is already there would invalidate layout for nothing,
|
|
37779
|
+
// and several bars sharing an edge means most calls compute the same largest
|
|
37780
|
+
// size again — the ones for the smaller bars, and the second half of a page
|
|
37781
|
+
// transition.
|
|
37782
|
+
const writtenValueByArea = new Map();
|
|
37783
|
+
|
|
37784
|
+
// A size that must land before the next paint: a render changed the bar, so
|
|
37785
|
+
// the room it takes is given back in that same commit and the content is never
|
|
37786
|
+
// painted under it.
|
|
37634
37787
|
/**
|
|
37635
37788
|
* @param {"top"|"bottom"|"left"|"right"} area
|
|
37636
|
-
* @param {
|
|
37789
|
+
* @param {Element} barElement - Which bar this size belongs to.
|
|
37790
|
+
* @param {number|null} size - In px; `null` gives that bar's room back to the
|
|
37791
|
+
* content.
|
|
37637
37792
|
*/
|
|
37638
|
-
const setFixedBarSpace = (area,
|
|
37793
|
+
const setFixedBarSpace = (area, barElement, size) => {
|
|
37794
|
+
dropPendingSize(area, barElement);
|
|
37795
|
+
storeSize(area, barElement, size);
|
|
37796
|
+
writeSpace(area);
|
|
37797
|
+
};
|
|
37798
|
+
|
|
37799
|
+
// A size nothing asked for, coming from a ResizeObserver. Writing the variable
|
|
37800
|
+
// resizes an ANCESTOR of the bars — the scroll container takes its padding
|
|
37801
|
+
// from it — and mutating layout from inside a resize callback is what makes
|
|
37802
|
+
// the browser report "ResizeObserver loop completed with undelivered
|
|
37803
|
+
// notifications". So the write waits for the frame that resize produced.
|
|
37804
|
+
// Queued here rather than deferred by each bar on its own, so the bars sharing
|
|
37805
|
+
// an edge resolve to a single write instead of one per bar.
|
|
37806
|
+
/**
|
|
37807
|
+
* @param {"top"|"bottom"|"left"|"right"} area
|
|
37808
|
+
* @param {Element} barElement - Which bar this size belongs to.
|
|
37809
|
+
* @param {number} size - In px.
|
|
37810
|
+
*/
|
|
37811
|
+
const requestFixedBarSpace = (area, barElement, size) => {
|
|
37812
|
+
let pendingSizeMap = pendingSizeMapByArea.get(area);
|
|
37813
|
+
if (!pendingSizeMap) {
|
|
37814
|
+
pendingSizeMap = new Map();
|
|
37815
|
+
pendingSizeMapByArea.set(area, pendingSizeMap);
|
|
37816
|
+
}
|
|
37817
|
+
pendingSizeMap.set(barElement, size);
|
|
37818
|
+
if (flushFrame !== null) {
|
|
37819
|
+
return;
|
|
37820
|
+
}
|
|
37821
|
+
flushFrame = requestAnimationFrame(flushPendingSizes);
|
|
37822
|
+
};
|
|
37823
|
+
|
|
37824
|
+
const pendingSizeMapByArea = new Map();
|
|
37825
|
+
let flushFrame = null;
|
|
37826
|
+
|
|
37827
|
+
const flushPendingSizes = () => {
|
|
37828
|
+
flushFrame = null;
|
|
37829
|
+
for (const [area, pendingSizeMap] of pendingSizeMapByArea) {
|
|
37830
|
+
for (const [barElement, size] of pendingSizeMap) {
|
|
37831
|
+
storeSize(area, barElement, size);
|
|
37832
|
+
}
|
|
37833
|
+
writeSpace(area);
|
|
37834
|
+
}
|
|
37835
|
+
pendingSizeMapByArea.clear();
|
|
37836
|
+
};
|
|
37837
|
+
|
|
37838
|
+
// What the bar itself just said wins over what its observer had queued about
|
|
37839
|
+
// it: a bar unmounting gives its room back, and a size queued for it before
|
|
37840
|
+
// that must not put it back.
|
|
37841
|
+
const dropPendingSize = (area, barElement) => {
|
|
37842
|
+
const pendingSizeMap = pendingSizeMapByArea.get(area);
|
|
37843
|
+
if (!pendingSizeMap) {
|
|
37844
|
+
return;
|
|
37845
|
+
}
|
|
37846
|
+
pendingSizeMap.delete(barElement);
|
|
37847
|
+
if (pendingSizeMap.size === 0) {
|
|
37848
|
+
pendingSizeMapByArea.delete(area);
|
|
37849
|
+
}
|
|
37850
|
+
};
|
|
37851
|
+
|
|
37852
|
+
const storeSize = (area, barElement, size) => {
|
|
37853
|
+
let sizeMap = sizeMapByArea.get(area);
|
|
37854
|
+
if (!sizeMap) {
|
|
37855
|
+
sizeMap = new Map();
|
|
37856
|
+
sizeMapByArea.set(area, sizeMap);
|
|
37857
|
+
}
|
|
37858
|
+
if (size === null) {
|
|
37859
|
+
sizeMap.delete(barElement);
|
|
37860
|
+
} else {
|
|
37861
|
+
sizeMap.set(barElement, size);
|
|
37862
|
+
}
|
|
37863
|
+
};
|
|
37864
|
+
|
|
37865
|
+
const writeSpace = (area) => {
|
|
37866
|
+
const sizeMap = sizeMapByArea.get(area);
|
|
37867
|
+
let largestSize = 0;
|
|
37868
|
+
for (const barSize of sizeMap.values()) {
|
|
37869
|
+
if (barSize > largestSize) {
|
|
37870
|
+
largestSize = barSize;
|
|
37871
|
+
}
|
|
37872
|
+
}
|
|
37639
37873
|
const property = `--navi-fixed-bar-space-${area}`;
|
|
37640
37874
|
const { style } = document.documentElement;
|
|
37641
|
-
if (
|
|
37642
|
-
|
|
37643
|
-
|
|
37644
|
-
|
|
37875
|
+
if (sizeMap.size === 0) {
|
|
37876
|
+
if (writtenValueByArea.has(area)) {
|
|
37877
|
+
writtenValueByArea.delete(area);
|
|
37878
|
+
style.removeProperty(property);
|
|
37879
|
+
}
|
|
37880
|
+
return;
|
|
37881
|
+
}
|
|
37882
|
+
const value = `${largestSize}px`;
|
|
37883
|
+
if (writtenValueByArea.get(area) === value) {
|
|
37884
|
+
return;
|
|
37645
37885
|
}
|
|
37886
|
+
writtenValueByArea.set(area, value);
|
|
37887
|
+
style.setProperty(property, value);
|
|
37646
37888
|
};
|
|
37647
37889
|
|
|
37648
37890
|
installImportMetaCssBuild(import.meta);/**
|
|
@@ -37822,20 +38064,42 @@ const FixedBar = ({
|
|
|
37822
38064
|
const {
|
|
37823
38065
|
ref
|
|
37824
38066
|
} = props;
|
|
38067
|
+
const measureSpace = barElement => {
|
|
38068
|
+
const {
|
|
38069
|
+
width,
|
|
38070
|
+
height
|
|
38071
|
+
} = barElement.getBoundingClientRect();
|
|
38072
|
+
return vertical ? width : height;
|
|
38073
|
+
};
|
|
38074
|
+
|
|
38075
|
+
// Anything a render can change — a size prop, the children, a theme variable
|
|
38076
|
+
// — is measured in that same commit, before paint: no frame where the
|
|
38077
|
+
// content sits under the bar, and nothing written from inside an observer.
|
|
38078
|
+
useLayoutEffect(() => {
|
|
38079
|
+
const barElement = ref.current;
|
|
38080
|
+
if (!barElement) {
|
|
38081
|
+
return;
|
|
38082
|
+
}
|
|
38083
|
+
setFixedBarSpace(area, barElement, measureSpace(barElement));
|
|
38084
|
+
});
|
|
38085
|
+
|
|
38086
|
+
// What no render caused: a font loading late, content arriving from outside,
|
|
38087
|
+
// a rotation moving the notch. Measuring here is safe; the write is what has
|
|
38088
|
+
// to wait, and fixed_bar_space.js is the one that holds it back.
|
|
37825
38089
|
useLayoutEffect(() => {
|
|
37826
38090
|
const barElement = ref.current;
|
|
37827
38091
|
if (!barElement) {
|
|
37828
38092
|
return undefined;
|
|
37829
38093
|
}
|
|
37830
|
-
const {
|
|
37831
|
-
|
|
37832
|
-
|
|
37833
|
-
|
|
37834
|
-
setFixedBarSpace(area, `${vertical ? width : height}px`);
|
|
38094
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
38095
|
+
requestFixedBarSpace(area, barElement, measureSpace(barElement));
|
|
38096
|
+
});
|
|
38097
|
+
resizeObserver.observe(barElement);
|
|
37835
38098
|
return () => {
|
|
37836
|
-
|
|
38099
|
+
resizeObserver.disconnect();
|
|
38100
|
+
setFixedBarSpace(area, barElement, null);
|
|
37837
38101
|
};
|
|
37838
|
-
}, [area]);
|
|
38102
|
+
}, [area, vertical]);
|
|
37839
38103
|
return jsx(Box, {
|
|
37840
38104
|
baseClassName: "navi_fixed_bar",
|
|
37841
38105
|
"data-area": area,
|
|
@@ -38619,7 +38883,7 @@ const useCheckableProps = (props, options) => {
|
|
|
38619
38883
|
installImportMetaCssBuild(import.meta);const css$F = /* css */`
|
|
38620
38884
|
@layer navi {
|
|
38621
38885
|
.navi_checkbox {
|
|
38622
|
-
--border-radius: var(--navi-
|
|
38886
|
+
--border-radius: var(--navi-checkbox-border-radius);
|
|
38623
38887
|
--border-width: var(--navi-control-border-width);
|
|
38624
38888
|
/* Focus outline */
|
|
38625
38889
|
--outline-width: var(--navi-focus-outline-width);
|