@jsenv/navi 0.29.440 → 0.29.441
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dev/jsenv_navi.js +134 -59
- package/dist/dev/jsenv_navi.js.map +5 -5
- package/dist/jsenv_navi.js +134 -59
- package/dist/jsenv_navi.js.map +5 -5
- package/package.json +1 -1
package/dist/dev/jsenv_navi.js
CHANGED
|
@@ -13381,6 +13381,53 @@ const findControlProxy = (el) => {
|
|
|
13381
13381
|
return null;
|
|
13382
13382
|
};
|
|
13383
13383
|
|
|
13384
|
+
/**
|
|
13385
|
+
* Tells a typed field's control that the user gave up on what they typed:
|
|
13386
|
+
* `navi_cancel` with its reason — Escape, or leaving the field invalid. What
|
|
13387
|
+
* giving up means is the control's to decide (onnavi_cancel in
|
|
13388
|
+
* control_hooks.jsx: resetOnCancel, cancelOnEscape, cancelOnBlurInvalid).
|
|
13389
|
+
*
|
|
13390
|
+
* Leaving a field empty is not a cancel: the "change" of that same blur has
|
|
13391
|
+
* already sent the empty value. A field that must not be left empty says
|
|
13392
|
+
* `required`, and leaving it is then a blur_invalid.
|
|
13393
|
+
*/
|
|
13394
|
+
const addInputCancel = (input, controller) => {
|
|
13395
|
+
const dispatchCancel = (detail) => {
|
|
13396
|
+
dispatchInternalCustomEvent(input, "navi_cancel", detail);
|
|
13397
|
+
};
|
|
13398
|
+
const onkeydown = (e) => {
|
|
13399
|
+
if (e.key !== "Escape") {
|
|
13400
|
+
return;
|
|
13401
|
+
}
|
|
13402
|
+
if (controller.rules.callout.callout) {
|
|
13403
|
+
// Escape closes the open callout first (callout.js listens on its anchor).
|
|
13404
|
+
return;
|
|
13405
|
+
}
|
|
13406
|
+
dispatchCancel({ event: e, reason: "escape_key" });
|
|
13407
|
+
};
|
|
13408
|
+
const onblur = (e) => {
|
|
13409
|
+
if (input.closest("[inert]")) {
|
|
13410
|
+
// The page took the field away (an edition ending, a side swapped out)
|
|
13411
|
+
// and the focus went with it: the user did not leave it.
|
|
13412
|
+
return;
|
|
13413
|
+
}
|
|
13414
|
+
const { failedConstraintInfo } = controller.rules.validation;
|
|
13415
|
+
if (failedConstraintInfo) {
|
|
13416
|
+
dispatchCancel({
|
|
13417
|
+
event: e,
|
|
13418
|
+
reason: "blur_invalid",
|
|
13419
|
+
failedConstraintInfo,
|
|
13420
|
+
});
|
|
13421
|
+
}
|
|
13422
|
+
};
|
|
13423
|
+
input.addEventListener("keydown", onkeydown);
|
|
13424
|
+
input.addEventListener("blur", onblur);
|
|
13425
|
+
return () => {
|
|
13426
|
+
input.removeEventListener("keydown", onkeydown);
|
|
13427
|
+
input.removeEventListener("blur", onblur);
|
|
13428
|
+
};
|
|
13429
|
+
};
|
|
13430
|
+
|
|
13384
13431
|
const addInputEffect = (
|
|
13385
13432
|
input,
|
|
13386
13433
|
callback,
|
|
@@ -13569,11 +13616,15 @@ const listenInputStateChange = (
|
|
|
13569
13616
|
callback(e);
|
|
13570
13617
|
};
|
|
13571
13618
|
input.addEventListener("input", oninput);
|
|
13572
|
-
|
|
13619
|
+
// Capture: the guard is set before the control reacts to the key. Its
|
|
13620
|
+
// reaction can end what the field is in (Enter sends, Escape cancels, an
|
|
13621
|
+
// edition closes and hands the focus away), and the "change" of that blur
|
|
13622
|
+
// arrives in the microtasks between two listeners of this keydown.
|
|
13623
|
+
input.addEventListener("keydown", onkeydown, { capture: true });
|
|
13573
13624
|
input.addEventListener("change", onchange);
|
|
13574
13625
|
addTeardown(() => {
|
|
13575
13626
|
input.removeEventListener("input", oninput);
|
|
13576
|
-
input.removeEventListener("keydown", onkeydown);
|
|
13627
|
+
input.removeEventListener("keydown", onkeydown, { capture: true });
|
|
13577
13628
|
input.removeEventListener("change", onchange);
|
|
13578
13629
|
});
|
|
13579
13630
|
|
|
@@ -46704,16 +46755,19 @@ const useControlProps = (props, {
|
|
|
46704
46755
|
// ref so the effect always fires the current render's reaction.
|
|
46705
46756
|
const applyEventReactionRef = useRef();
|
|
46706
46757
|
applyEventReactionRef.current = applyEventReaction;
|
|
46758
|
+
const isTypedField = controlType === "input" && !isCheckable && props.type !== "range";
|
|
46707
46759
|
const refCallback = useCallback(field => {
|
|
46708
|
-
|
|
46709
|
-
|
|
46710
|
-
}
|
|
46711
|
-
return addInputEffect(field, e => applyEventReactionRef.current("naviChange", e), {
|
|
46760
|
+
const removeInputCancel = isTypedField ? addInputCancel(field, uiStateController) : undefined;
|
|
46761
|
+
const removeInputEffect = hasNaviChangeEventReaction && actionEvent !== "custom" ? addInputEffect(field, e => applyEventReactionRef.current("naviChange", e), {
|
|
46712
46762
|
waitForChange: actionAfterChange,
|
|
46713
46763
|
debounce: actionDebounce,
|
|
46714
46764
|
debugInteraction
|
|
46715
|
-
});
|
|
46716
|
-
|
|
46765
|
+
}) : undefined;
|
|
46766
|
+
return () => {
|
|
46767
|
+
removeInputCancel?.();
|
|
46768
|
+
removeInputEffect?.();
|
|
46769
|
+
};
|
|
46770
|
+
}, [isTypedField, actionEvent, actionAfterChange, actionDebounce, hasNaviChangeEventReaction, ref]);
|
|
46717
46771
|
const refComposed = useComposeElementRef(refCallback, ref);
|
|
46718
46772
|
const onPaste = e => {
|
|
46719
46773
|
props.onPaste?.(e);
|
|
@@ -47676,7 +47730,7 @@ const useInteractiveProps = (props, {
|
|
|
47676
47730
|
}
|
|
47677
47731
|
if (
|
|
47678
47732
|
// error prevent cancellation until the user closes it (or something closes it)
|
|
47679
|
-
e.detail.failedConstraintInfo.
|
|
47733
|
+
e.detail.failedConstraintInfo.status === "error" && e.detail.failedConstraintInfo.reportStatus !== "closed") {
|
|
47680
47734
|
return;
|
|
47681
47735
|
}
|
|
47682
47736
|
}
|
|
@@ -61287,21 +61341,51 @@ const Editable = props => {
|
|
|
61287
61341
|
editingPreviousRef.current = editing;
|
|
61288
61342
|
}
|
|
61289
61343
|
|
|
61290
|
-
//
|
|
61344
|
+
// The field is mounted the whole time, so edition starting is when it
|
|
61345
|
+
// arrives, and Editable places the focus itself: a mount-time autofocus
|
|
61346
|
+
// would only ever see the first `editing`. Starting edition is a press aimed
|
|
61347
|
+
// at typing, so the field takes the keyboard whatever asked for it — a
|
|
61348
|
+
// click, Enter, a letter key.
|
|
61349
|
+
const focusedBeforeEditionRef = useRef(null);
|
|
61291
61350
|
useLayoutEffect(() => {
|
|
61351
|
+
const field = ref.current;
|
|
61292
61352
|
if (!editing) {
|
|
61353
|
+
const focusedBeforeEdition = focusedBeforeEditionRef.current;
|
|
61354
|
+
focusedBeforeEditionRef.current = null;
|
|
61355
|
+
if (!focusedBeforeEdition || !focusedBeforeEdition.isConnected) {
|
|
61356
|
+
return;
|
|
61357
|
+
}
|
|
61358
|
+
// The field just went inert while holding the focus (Enter, Escape, the
|
|
61359
|
+
// action ending): the focus goes back to where edition was asked from.
|
|
61360
|
+
// Focus the user took away, the blur that ended edition, stays where
|
|
61361
|
+
// they put it — on the body included.
|
|
61362
|
+
if (document.activeElement !== field) {
|
|
61363
|
+
return;
|
|
61364
|
+
}
|
|
61365
|
+
moveFocusTo(focusedBeforeEdition);
|
|
61293
61366
|
return;
|
|
61294
61367
|
}
|
|
61295
|
-
|
|
61296
|
-
if (
|
|
61297
|
-
|
|
61298
|
-
|
|
61299
|
-
|
|
61300
|
-
|
|
61301
|
-
|
|
61302
|
-
|
|
61303
|
-
|
|
61304
|
-
|
|
61368
|
+
focusedBeforeEditionRef.current = document.activeElement;
|
|
61369
|
+
if (!valueSignal) {
|
|
61370
|
+
// An edition starts from the value, not from what a cancelled one left
|
|
61371
|
+
// typed in the field. Done before the focus: written into a focused
|
|
61372
|
+
// field, it would read as a change to send (see input_effect.js).
|
|
61373
|
+
dispatchRequestResetUIState(field, editing.event);
|
|
61374
|
+
}
|
|
61375
|
+
moveFocusTo(field);
|
|
61376
|
+
const initialValue = editing.event?.detail?.initialValue;
|
|
61377
|
+
if (initialValue !== undefined) {
|
|
61378
|
+
// The key that started edition is its first keystroke.
|
|
61379
|
+
field.value = initialValue;
|
|
61380
|
+
field.dispatchEvent(new CustomEvent("input", {
|
|
61381
|
+
bubbles: false
|
|
61382
|
+
}));
|
|
61383
|
+
return;
|
|
61384
|
+
}
|
|
61385
|
+
if (autoFocusSelect) {
|
|
61386
|
+
field.select();
|
|
61387
|
+
// Keep the beginning of the text visible instead of scrolling to the end
|
|
61388
|
+
field.scrollLeft = 0;
|
|
61305
61389
|
}
|
|
61306
61390
|
}, [editing]);
|
|
61307
61391
|
const controlProps = {
|
|
@@ -61310,9 +61394,6 @@ const Editable = props => {
|
|
|
61310
61394
|
name,
|
|
61311
61395
|
value,
|
|
61312
61396
|
valueSignal,
|
|
61313
|
-
autoFocus: editing,
|
|
61314
|
-
autoFocusVisible: true,
|
|
61315
|
-
autoFocusSelect,
|
|
61316
61397
|
cancelOnEscape: true,
|
|
61317
61398
|
cancelOnBlurInvalid: true,
|
|
61318
61399
|
constraints,
|
|
@@ -61332,6 +61413,11 @@ const Editable = props => {
|
|
|
61332
61413
|
});
|
|
61333
61414
|
},
|
|
61334
61415
|
onBlur: e => {
|
|
61416
|
+
if (!editing) {
|
|
61417
|
+
// The focus handed back once edition ended (see the effect above):
|
|
61418
|
+
// the edition is already over, this blur is its consequence.
|
|
61419
|
+
return;
|
|
61420
|
+
}
|
|
61335
61421
|
let inputValue;
|
|
61336
61422
|
const valueWhenEditStart = valueWhenEditStartRef.current;
|
|
61337
61423
|
let inputValueWhenEditStart;
|
|
@@ -61398,7 +61484,7 @@ const Editable = props => {
|
|
|
61398
61484
|
// - input not focusable (via keyboard or anything)
|
|
61399
61485
|
// - cannot be interacted with pointer (click, hover, etc)
|
|
61400
61486
|
// - is ignored by screen readers
|
|
61401
|
-
inert: editing
|
|
61487
|
+
inert: !editing,
|
|
61402
61488
|
"data-editing": editing ? "" : undefined,
|
|
61403
61489
|
children: control
|
|
61404
61490
|
})]
|
|
@@ -92979,20 +93065,33 @@ const Table = props => {
|
|
|
92979
93065
|
columns,
|
|
92980
93066
|
canChangeColumnOrder: Boolean(onColumnOrderChange)
|
|
92981
93067
|
});
|
|
93068
|
+
|
|
93069
|
+
// Edition starts from a cell holding the focus itself. Once started, the
|
|
93070
|
+
// focus is in the cell's field and its keys are the field's: an Enter there
|
|
93071
|
+
// validates, it must not start edition again.
|
|
93072
|
+
const canStartEditing = () => dragContextValue.grabTarget === null && document.activeElement.tagName === "TD";
|
|
93073
|
+
// The key starting edition is its first keystroke, typed by Editable
|
|
93074
|
+
// (initialValue). Its default is prevented: the browser would type it a
|
|
93075
|
+
// second time into the field that takes the focus during this keydown.
|
|
93076
|
+
const startEditingWithKey = e => {
|
|
93077
|
+
e.preventDefault();
|
|
93078
|
+
document.activeElement.dispatchEvent(new CustomEvent("editrequested", {
|
|
93079
|
+
bubbles: false,
|
|
93080
|
+
detail: {
|
|
93081
|
+
initialValue: e.key
|
|
93082
|
+
}
|
|
93083
|
+
}));
|
|
93084
|
+
return true;
|
|
93085
|
+
};
|
|
92982
93086
|
useKeyboardShortcuts(ref, [...createSelectionKeyboardShortcuts(selectionController, {
|
|
92983
93087
|
toggleEnabled: true,
|
|
92984
93088
|
enabled: () => dragContextValue.grabTarget === null
|
|
92985
93089
|
}), {
|
|
92986
93090
|
key: "enter",
|
|
92987
93091
|
description: "Edit table cell content",
|
|
92988
|
-
enabled:
|
|
93092
|
+
enabled: canStartEditing,
|
|
92989
93093
|
handler: () => {
|
|
92990
|
-
|
|
92991
|
-
const activeCell = document.activeElement.closest("td");
|
|
92992
|
-
if (!activeCell) {
|
|
92993
|
-
return false;
|
|
92994
|
-
}
|
|
92995
|
-
activeCell.dispatchEvent(new CustomEvent("editrequested", {
|
|
93094
|
+
document.activeElement.dispatchEvent(new CustomEvent("editrequested", {
|
|
92996
93095
|
bubbles: false
|
|
92997
93096
|
}));
|
|
92998
93097
|
return true;
|
|
@@ -93000,37 +93099,13 @@ const Table = props => {
|
|
|
93000
93099
|
}, {
|
|
93001
93100
|
key: "a-z",
|
|
93002
93101
|
description: "Start editing table cell content",
|
|
93003
|
-
enabled:
|
|
93004
|
-
handler:
|
|
93005
|
-
const activeCell = document.activeElement.closest("td");
|
|
93006
|
-
if (!activeCell) {
|
|
93007
|
-
return false;
|
|
93008
|
-
}
|
|
93009
|
-
activeCell.dispatchEvent(new CustomEvent("editrequested", {
|
|
93010
|
-
bubbles: false,
|
|
93011
|
-
detail: {
|
|
93012
|
-
initialValue: e.key
|
|
93013
|
-
}
|
|
93014
|
-
}));
|
|
93015
|
-
return true;
|
|
93016
|
-
}
|
|
93102
|
+
enabled: canStartEditing,
|
|
93103
|
+
handler: startEditingWithKey
|
|
93017
93104
|
}, {
|
|
93018
93105
|
key: "0-9",
|
|
93019
93106
|
description: "Start editing table cell content",
|
|
93020
|
-
enabled:
|
|
93021
|
-
handler:
|
|
93022
|
-
const activeCell = document.activeElement.closest("td");
|
|
93023
|
-
if (!activeCell) {
|
|
93024
|
-
return false;
|
|
93025
|
-
}
|
|
93026
|
-
activeCell.dispatchEvent(new CustomEvent("editrequested", {
|
|
93027
|
-
bubbles: false,
|
|
93028
|
-
detail: {
|
|
93029
|
-
initialValue: e.key
|
|
93030
|
-
}
|
|
93031
|
-
}));
|
|
93032
|
-
return true;
|
|
93033
|
-
}
|
|
93107
|
+
enabled: canStartEditing,
|
|
93108
|
+
handler: startEditingWithKey
|
|
93034
93109
|
}]);
|
|
93035
93110
|
|
|
93036
93111
|
// resizing
|