@jsenv/navi 0.29.60 → 0.29.62
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 +87 -16
- package/dist/jsenv_navi.js.map +6 -5
- package/docs/AI_INSTRUCTIONS.md +8 -0
- package/docs/actions.md +32 -0
- package/docs/drag_to_travel.md +68 -9
- package/docs/interactions.md +8 -0
- package/package.json +2 -2
package/dist/jsenv_navi.js
CHANGED
|
@@ -12622,10 +12622,10 @@ defineInteractionDetector({
|
|
|
12622
12622
|
if (canReorder) {
|
|
12623
12623
|
element.setAttribute(REORDERABLE_ATTRIBUTE, "");
|
|
12624
12624
|
}
|
|
12625
|
-
// What @jsenv/dom puts on a drag source:
|
|
12626
|
-
//
|
|
12627
|
-
//
|
|
12628
|
-
//
|
|
12625
|
+
// What @jsenv/dom puts on a drag source: the axes written in the DOM for
|
|
12626
|
+
// whoever else answers this press (a sheet pushed down to close it, a row of
|
|
12627
|
+
// slides), no iOS callout, the touch left to the scroll until the press
|
|
12628
|
+
// becomes a grab, and the listener that lets the grab take it back.
|
|
12629
12629
|
const unmarkDragSource = markDragSource(element, axes);
|
|
12630
12630
|
|
|
12631
12631
|
// What a release can mean, which is not all of what was declared: "grab" is a
|
|
@@ -24189,20 +24189,26 @@ const useUIStateController = (
|
|
|
24189
24189
|
e,
|
|
24190
24190
|
"dispatching synthetic input event without data for checkbox/radio",
|
|
24191
24191
|
);
|
|
24192
|
-
|
|
24192
|
+
dispatchSyntheticInput(
|
|
24193
|
+
el,
|
|
24194
|
+
new Event("input", { bubbles: true }),
|
|
24195
|
+
e,
|
|
24196
|
+
);
|
|
24193
24197
|
syntheticInputFired = true;
|
|
24194
24198
|
} else {
|
|
24195
24199
|
debugUIState(
|
|
24196
24200
|
e,
|
|
24197
24201
|
`dispatching synthetic input event with data "${newUIState}" for input`,
|
|
24198
24202
|
);
|
|
24199
|
-
|
|
24203
|
+
dispatchSyntheticInput(
|
|
24204
|
+
el,
|
|
24200
24205
|
new InputEvent("input", {
|
|
24201
24206
|
bubbles: true,
|
|
24202
24207
|
cancelable: true,
|
|
24203
24208
|
inputType: "insertText",
|
|
24204
24209
|
data: newUIState,
|
|
24205
24210
|
}),
|
|
24211
|
+
e,
|
|
24206
24212
|
);
|
|
24207
24213
|
syntheticInputFired = true;
|
|
24208
24214
|
}
|
|
@@ -24214,7 +24220,11 @@ const useUIStateController = (
|
|
|
24214
24220
|
// A plain Event, not an InputEvent: that is what the browser
|
|
24215
24221
|
// itself fires on a select, and input_effect reads the value off
|
|
24216
24222
|
// the element anyway.
|
|
24217
|
-
|
|
24223
|
+
dispatchSyntheticInput(
|
|
24224
|
+
el,
|
|
24225
|
+
new Event("input", { bubbles: true }),
|
|
24226
|
+
e,
|
|
24227
|
+
);
|
|
24218
24228
|
syntheticInputFired = true;
|
|
24219
24229
|
}
|
|
24220
24230
|
// TODO: textarea
|
|
@@ -25317,14 +25327,16 @@ const useUIFacadeStateController = (props, realUIStateController) => {
|
|
|
25317
25327
|
}
|
|
25318
25328
|
if (
|
|
25319
25329
|
silent &&
|
|
25320
|
-
child.uiState
|
|
25321
|
-
s.realUIStateController.uiState
|
|
25330
|
+
uiStateHoldsNothing(child.uiState) &&
|
|
25331
|
+
!uiStateHoldsNothing(s.realUIStateController.uiState)
|
|
25322
25332
|
) {
|
|
25323
25333
|
// A silent sync means the child's own structure changed (children
|
|
25324
25334
|
// mounted/unmounted), not that the user acted. A child that ends up
|
|
25325
25335
|
// with no value there is one that currently *cannot* express one —
|
|
25326
|
-
// a <List loading> holds no items yet
|
|
25327
|
-
//
|
|
25336
|
+
// a <List loading> holds no items yet, a popup whose items are gone
|
|
25337
|
+
// aggregates to the empty array its stateType falls back to — which
|
|
25338
|
+
// must not read as the user clearing the picker, nor fire its
|
|
25339
|
+
// uiAction.
|
|
25328
25340
|
return;
|
|
25329
25341
|
}
|
|
25330
25342
|
updatingRef.current = true;
|
|
@@ -25445,6 +25457,33 @@ const isInternalEvent = (e) => {
|
|
|
25445
25457
|
return INTERNAL_EVENT_SET.has(e.type);
|
|
25446
25458
|
};
|
|
25447
25459
|
|
|
25460
|
+
/**
|
|
25461
|
+
* The synthetic "input" event is how the new state reaches the outside world
|
|
25462
|
+
* (`uiAction` is called from the input handler it triggers). It carries what
|
|
25463
|
+
* caused the state change so a listener can tell a gesture apart from navi
|
|
25464
|
+
* talking to itself: `findEvent(e, "navi_clear_ui_state")` answers "the user
|
|
25465
|
+
* pressed the clear cross", the same way the facade hops are recognized.
|
|
25466
|
+
*/
|
|
25467
|
+
const dispatchSyntheticInput = (el, inputEvent, causeEvent) => {
|
|
25468
|
+
chainEvent(inputEvent, causeEvent);
|
|
25469
|
+
el.dispatchEvent(inputEvent);
|
|
25470
|
+
};
|
|
25471
|
+
|
|
25472
|
+
// What a control says when it has nothing to say: no value at all, or the empty
|
|
25473
|
+
// array/object a group falls back to while it has no child to aggregate.
|
|
25474
|
+
const uiStateHoldsNothing = (uiState) => {
|
|
25475
|
+
if (uiState === undefined) {
|
|
25476
|
+
return true;
|
|
25477
|
+
}
|
|
25478
|
+
if (Array.isArray(uiState)) {
|
|
25479
|
+
return uiState.length === 0;
|
|
25480
|
+
}
|
|
25481
|
+
if (uiState !== null && typeof uiState === "object") {
|
|
25482
|
+
return Object.keys(uiState).length === 0;
|
|
25483
|
+
}
|
|
25484
|
+
return false;
|
|
25485
|
+
};
|
|
25486
|
+
|
|
25448
25487
|
/**
|
|
25449
25488
|
* Core hooks for wiring navi field components to the action/validation system.
|
|
25450
25489
|
*
|
|
@@ -30296,8 +30335,40 @@ const useDialogProps = props => {
|
|
|
30296
30335
|
// argument at all) docks it against the viewport (layer="top"/isModal)
|
|
30297
30336
|
// or its own positioned ancestor (layer="local", the same
|
|
30298
30337
|
// positionedAncestor computed above), same mechanism as Popover's own
|
|
30299
|
-
// custom renderer.
|
|
30300
|
-
// from the result, same as popover.jsx
|
|
30338
|
+
// custom renderer. applyDialogPosition sets --container-position-remaining-height/-width
|
|
30339
|
+
// from the result, same as popover.jsx — except for layer="top", see its
|
|
30340
|
+
// own comment just above.
|
|
30341
|
+
// The placement is a snapshot taken on a debounce; the size caps must not
|
|
30342
|
+
// be one too. applyNewPosition writes --container-position-remaining-
|
|
30343
|
+
// height/width on every reposition, from what pickPositionRelativeTo
|
|
30344
|
+
// measured the container as at that instant. For layer="top" that value
|
|
30345
|
+
// says nothing --dialog-maxmax-height/width doesn't already say live: the
|
|
30346
|
+
// container IS the visual viewport and Dialog is never anchored, so the
|
|
30347
|
+
// "remaining" space is always the whole container net of its own margins
|
|
30348
|
+
// — the very definition of --dialog-maxmax-*, which tracks the visual
|
|
30349
|
+
// viewport through --navi-vvh/--navi-app-height without waiting for
|
|
30350
|
+
// anything. Identical values, one of them one debounce late.
|
|
30351
|
+
//
|
|
30352
|
+
// That lag stays invisible while the viewport SHRINKS — the two are
|
|
30353
|
+
// combined with min(), so the live var is the one that binds and the
|
|
30354
|
+
// dialog follows the mobile keyboard down, its top edge pinned. On the
|
|
30355
|
+
// way back up the stale pixel value is the smaller one, so it binds
|
|
30356
|
+
// instead and the dialog stays keyboard-sized until the debounced
|
|
30357
|
+
// reposition fires — which then places a still-shrunk box in a
|
|
30358
|
+
// full-height viewport (centered: visibly lower), grows it in the same
|
|
30359
|
+
// call, and has to slide it back up: the bounce. Dropping the property
|
|
30360
|
+
// for layer="top" leaves the live var alone in charge, so the height
|
|
30361
|
+
// comes back exactly the way it left, from the bottom edge.
|
|
30362
|
+
//
|
|
30363
|
+
// Kept for layer="local": there the container is a real element, its own
|
|
30364
|
+
// box is what the caps must read, and nothing in CSS tracks it.
|
|
30365
|
+
const applyDialogPosition = position => {
|
|
30366
|
+
applyNewPosition(dialogEl, position);
|
|
30367
|
+
if (isModal) {
|
|
30368
|
+
dialogEl.style.removeProperty("--container-position-remaining-height");
|
|
30369
|
+
dialogEl.style.removeProperty("--container-position-remaining-width");
|
|
30370
|
+
}
|
|
30371
|
+
};
|
|
30301
30372
|
const positionDialog = triggerEvent => {
|
|
30302
30373
|
const {
|
|
30303
30374
|
positionArea,
|
|
@@ -30327,8 +30398,8 @@ const useDialogProps = props => {
|
|
|
30327
30398
|
event: triggerEvent
|
|
30328
30399
|
};
|
|
30329
30400
|
let position = pickPositionRelativeTo(dialogEl, null, pickOptions);
|
|
30330
|
-
|
|
30331
|
-
//
|
|
30401
|
+
applyDialogPosition(position);
|
|
30402
|
+
// applyDialogPosition above just set --container-position-remaining-
|
|
30332
30403
|
// width/height to the real available space — narrower than whatever
|
|
30333
30404
|
// dialogEl measured at just before (nothing, on a first open — see
|
|
30334
30405
|
// popover.jsx's own identical comment on its own positionPopover for
|
|
@@ -30341,7 +30412,7 @@ const useDialogProps = props => {
|
|
|
30341
30412
|
// correct it — that one only reacts on the *next* animation frame.
|
|
30342
30413
|
if (dialogEl.offsetWidth !== position.width || dialogEl.offsetHeight !== position.height) {
|
|
30343
30414
|
position = pickPositionRelativeTo(dialogEl, null, pickOptions);
|
|
30344
|
-
|
|
30415
|
+
applyDialogPosition(position);
|
|
30345
30416
|
}
|
|
30346
30417
|
// A descendant's own visibleRectEffect (visible_rect.js — e.g. a
|
|
30347
30418
|
// Callout anchored to something inside this Dialog) knowing to
|