@jsenv/navi 0.29.440 → 0.29.442
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 +6 -6
- package/dist/jsenv_navi.js +134 -59
- package/dist/jsenv_navi.js.map +6 -6
- package/package.json +2 -2
package/dist/jsenv_navi.js
CHANGED
|
@@ -12814,6 +12814,53 @@ const findControlProxy = (el) => {
|
|
|
12814
12814
|
return null;
|
|
12815
12815
|
};
|
|
12816
12816
|
|
|
12817
|
+
/**
|
|
12818
|
+
* Tells a typed field's control that the user gave up on what they typed:
|
|
12819
|
+
* `navi_cancel` with its reason — Escape, or leaving the field invalid. What
|
|
12820
|
+
* giving up means is the control's to decide (onnavi_cancel in
|
|
12821
|
+
* control_hooks.jsx: resetOnCancel, cancelOnEscape, cancelOnBlurInvalid).
|
|
12822
|
+
*
|
|
12823
|
+
* Leaving a field empty is not a cancel: the "change" of that same blur has
|
|
12824
|
+
* already sent the empty value. A field that must not be left empty says
|
|
12825
|
+
* `required`, and leaving it is then a blur_invalid.
|
|
12826
|
+
*/
|
|
12827
|
+
const addInputCancel = (input, controller) => {
|
|
12828
|
+
const dispatchCancel = (detail) => {
|
|
12829
|
+
dispatchInternalCustomEvent(input, "navi_cancel", detail);
|
|
12830
|
+
};
|
|
12831
|
+
const onkeydown = (e) => {
|
|
12832
|
+
if (e.key !== "Escape") {
|
|
12833
|
+
return;
|
|
12834
|
+
}
|
|
12835
|
+
if (controller.rules.callout.callout) {
|
|
12836
|
+
// Escape closes the open callout first (callout.js listens on its anchor).
|
|
12837
|
+
return;
|
|
12838
|
+
}
|
|
12839
|
+
dispatchCancel({ event: e, reason: "escape_key" });
|
|
12840
|
+
};
|
|
12841
|
+
const onblur = (e) => {
|
|
12842
|
+
if (input.closest("[inert]")) {
|
|
12843
|
+
// The page took the field away (an edition ending, a side swapped out)
|
|
12844
|
+
// and the focus went with it: the user did not leave it.
|
|
12845
|
+
return;
|
|
12846
|
+
}
|
|
12847
|
+
const { failedConstraintInfo } = controller.rules.validation;
|
|
12848
|
+
if (failedConstraintInfo) {
|
|
12849
|
+
dispatchCancel({
|
|
12850
|
+
event: e,
|
|
12851
|
+
reason: "blur_invalid",
|
|
12852
|
+
failedConstraintInfo,
|
|
12853
|
+
});
|
|
12854
|
+
}
|
|
12855
|
+
};
|
|
12856
|
+
input.addEventListener("keydown", onkeydown);
|
|
12857
|
+
input.addEventListener("blur", onblur);
|
|
12858
|
+
return () => {
|
|
12859
|
+
input.removeEventListener("keydown", onkeydown);
|
|
12860
|
+
input.removeEventListener("blur", onblur);
|
|
12861
|
+
};
|
|
12862
|
+
};
|
|
12863
|
+
|
|
12817
12864
|
const addInputEffect = (
|
|
12818
12865
|
input,
|
|
12819
12866
|
callback,
|
|
@@ -13002,11 +13049,15 @@ const listenInputStateChange = (
|
|
|
13002
13049
|
callback(e);
|
|
13003
13050
|
};
|
|
13004
13051
|
input.addEventListener("input", oninput);
|
|
13005
|
-
|
|
13052
|
+
// Capture: the guard is set before the control reacts to the key. Its
|
|
13053
|
+
// reaction can end what the field is in (Enter sends, Escape cancels, an
|
|
13054
|
+
// edition closes and hands the focus away), and the "change" of that blur
|
|
13055
|
+
// arrives in the microtasks between two listeners of this keydown.
|
|
13056
|
+
input.addEventListener("keydown", onkeydown, { capture: true });
|
|
13006
13057
|
input.addEventListener("change", onchange);
|
|
13007
13058
|
addTeardown(() => {
|
|
13008
13059
|
input.removeEventListener("input", oninput);
|
|
13009
|
-
input.removeEventListener("keydown", onkeydown);
|
|
13060
|
+
input.removeEventListener("keydown", onkeydown, { capture: true });
|
|
13010
13061
|
input.removeEventListener("change", onchange);
|
|
13011
13062
|
});
|
|
13012
13063
|
|
|
@@ -45537,16 +45588,19 @@ const useControlProps = (props, {
|
|
|
45537
45588
|
// ref so the effect always fires the current render's reaction.
|
|
45538
45589
|
const applyEventReactionRef = useRef();
|
|
45539
45590
|
applyEventReactionRef.current = applyEventReaction;
|
|
45591
|
+
const isTypedField = controlType === "input" && !isCheckable && props.type !== "range";
|
|
45540
45592
|
const refCallback = useCallback(field => {
|
|
45541
|
-
|
|
45542
|
-
|
|
45543
|
-
}
|
|
45544
|
-
return addInputEffect(field, e => applyEventReactionRef.current("naviChange", e), {
|
|
45593
|
+
const removeInputCancel = isTypedField ? addInputCancel(field, uiStateController) : undefined;
|
|
45594
|
+
const removeInputEffect = hasNaviChangeEventReaction && actionEvent !== "custom" ? addInputEffect(field, e => applyEventReactionRef.current("naviChange", e), {
|
|
45545
45595
|
waitForChange: actionAfterChange,
|
|
45546
45596
|
debounce: actionDebounce,
|
|
45547
45597
|
debugInteraction
|
|
45548
|
-
});
|
|
45549
|
-
|
|
45598
|
+
}) : undefined;
|
|
45599
|
+
return () => {
|
|
45600
|
+
removeInputCancel?.();
|
|
45601
|
+
removeInputEffect?.();
|
|
45602
|
+
};
|
|
45603
|
+
}, [isTypedField, actionEvent, actionAfterChange, actionDebounce, hasNaviChangeEventReaction, ref]);
|
|
45550
45604
|
const refComposed = useComposeElementRef(refCallback, ref);
|
|
45551
45605
|
const onPaste = e => {
|
|
45552
45606
|
props.onPaste?.(e);
|
|
@@ -46488,7 +46542,7 @@ const useInteractiveProps = (props, {
|
|
|
46488
46542
|
}
|
|
46489
46543
|
if (
|
|
46490
46544
|
// error prevent cancellation until the user closes it (or something closes it)
|
|
46491
|
-
e.detail.failedConstraintInfo.
|
|
46545
|
+
e.detail.failedConstraintInfo.status === "error" && e.detail.failedConstraintInfo.reportStatus !== "closed") {
|
|
46492
46546
|
return;
|
|
46493
46547
|
}
|
|
46494
46548
|
}
|
|
@@ -60075,21 +60129,51 @@ const Editable = props => {
|
|
|
60075
60129
|
editingPreviousRef.current = editing;
|
|
60076
60130
|
}
|
|
60077
60131
|
|
|
60078
|
-
//
|
|
60132
|
+
// The field is mounted the whole time, so edition starting is when it
|
|
60133
|
+
// arrives, and Editable places the focus itself: a mount-time autofocus
|
|
60134
|
+
// would only ever see the first `editing`. Starting edition is a press aimed
|
|
60135
|
+
// at typing, so the field takes the keyboard whatever asked for it — a
|
|
60136
|
+
// click, Enter, a letter key.
|
|
60137
|
+
const focusedBeforeEditionRef = useRef(null);
|
|
60079
60138
|
useLayoutEffect(() => {
|
|
60139
|
+
const field = ref.current;
|
|
60080
60140
|
if (!editing) {
|
|
60141
|
+
const focusedBeforeEdition = focusedBeforeEditionRef.current;
|
|
60142
|
+
focusedBeforeEditionRef.current = null;
|
|
60143
|
+
if (!focusedBeforeEdition || !focusedBeforeEdition.isConnected) {
|
|
60144
|
+
return;
|
|
60145
|
+
}
|
|
60146
|
+
// The field just went inert while holding the focus (Enter, Escape, the
|
|
60147
|
+
// action ending): the focus goes back to where edition was asked from.
|
|
60148
|
+
// Focus the user took away, the blur that ended edition, stays where
|
|
60149
|
+
// they put it — on the body included.
|
|
60150
|
+
if (document.activeElement !== field) {
|
|
60151
|
+
return;
|
|
60152
|
+
}
|
|
60153
|
+
moveFocusTo(focusedBeforeEdition);
|
|
60081
60154
|
return;
|
|
60082
60155
|
}
|
|
60083
|
-
|
|
60084
|
-
if (
|
|
60085
|
-
|
|
60086
|
-
|
|
60087
|
-
|
|
60088
|
-
|
|
60089
|
-
|
|
60090
|
-
|
|
60091
|
-
|
|
60092
|
-
|
|
60156
|
+
focusedBeforeEditionRef.current = document.activeElement;
|
|
60157
|
+
if (!valueSignal) {
|
|
60158
|
+
// An edition starts from the value, not from what a cancelled one left
|
|
60159
|
+
// typed in the field. Done before the focus: written into a focused
|
|
60160
|
+
// field, it would read as a change to send (see input_effect.js).
|
|
60161
|
+
dispatchRequestResetUIState(field, editing.event);
|
|
60162
|
+
}
|
|
60163
|
+
moveFocusTo(field);
|
|
60164
|
+
const initialValue = editing.event?.detail?.initialValue;
|
|
60165
|
+
if (initialValue !== undefined) {
|
|
60166
|
+
// The key that started edition is its first keystroke.
|
|
60167
|
+
field.value = initialValue;
|
|
60168
|
+
field.dispatchEvent(new CustomEvent("input", {
|
|
60169
|
+
bubbles: false
|
|
60170
|
+
}));
|
|
60171
|
+
return;
|
|
60172
|
+
}
|
|
60173
|
+
if (autoFocusSelect) {
|
|
60174
|
+
field.select();
|
|
60175
|
+
// Keep the beginning of the text visible instead of scrolling to the end
|
|
60176
|
+
field.scrollLeft = 0;
|
|
60093
60177
|
}
|
|
60094
60178
|
}, [editing]);
|
|
60095
60179
|
const controlProps = {
|
|
@@ -60098,9 +60182,6 @@ const Editable = props => {
|
|
|
60098
60182
|
name,
|
|
60099
60183
|
value,
|
|
60100
60184
|
valueSignal,
|
|
60101
|
-
autoFocus: editing,
|
|
60102
|
-
autoFocusVisible: true,
|
|
60103
|
-
autoFocusSelect,
|
|
60104
60185
|
cancelOnEscape: true,
|
|
60105
60186
|
cancelOnBlurInvalid: true,
|
|
60106
60187
|
constraints,
|
|
@@ -60120,6 +60201,11 @@ const Editable = props => {
|
|
|
60120
60201
|
});
|
|
60121
60202
|
},
|
|
60122
60203
|
onBlur: e => {
|
|
60204
|
+
if (!editing) {
|
|
60205
|
+
// The focus handed back once edition ended (see the effect above):
|
|
60206
|
+
// the edition is already over, this blur is its consequence.
|
|
60207
|
+
return;
|
|
60208
|
+
}
|
|
60123
60209
|
let inputValue;
|
|
60124
60210
|
const valueWhenEditStart = valueWhenEditStartRef.current;
|
|
60125
60211
|
let inputValueWhenEditStart;
|
|
@@ -60186,7 +60272,7 @@ const Editable = props => {
|
|
|
60186
60272
|
// - input not focusable (via keyboard or anything)
|
|
60187
60273
|
// - cannot be interacted with pointer (click, hover, etc)
|
|
60188
60274
|
// - is ignored by screen readers
|
|
60189
|
-
inert: editing
|
|
60275
|
+
inert: !editing,
|
|
60190
60276
|
"data-editing": editing ? "" : undefined,
|
|
60191
60277
|
children: control
|
|
60192
60278
|
})]
|
|
@@ -90761,20 +90847,33 @@ const Table = props => {
|
|
|
90761
90847
|
columns,
|
|
90762
90848
|
canChangeColumnOrder: Boolean(onColumnOrderChange)
|
|
90763
90849
|
});
|
|
90850
|
+
|
|
90851
|
+
// Edition starts from a cell holding the focus itself. Once started, the
|
|
90852
|
+
// focus is in the cell's field and its keys are the field's: an Enter there
|
|
90853
|
+
// validates, it must not start edition again.
|
|
90854
|
+
const canStartEditing = () => dragContextValue.grabTarget === null && document.activeElement.tagName === "TD";
|
|
90855
|
+
// The key starting edition is its first keystroke, typed by Editable
|
|
90856
|
+
// (initialValue). Its default is prevented: the browser would type it a
|
|
90857
|
+
// second time into the field that takes the focus during this keydown.
|
|
90858
|
+
const startEditingWithKey = e => {
|
|
90859
|
+
e.preventDefault();
|
|
90860
|
+
document.activeElement.dispatchEvent(new CustomEvent("editrequested", {
|
|
90861
|
+
bubbles: false,
|
|
90862
|
+
detail: {
|
|
90863
|
+
initialValue: e.key
|
|
90864
|
+
}
|
|
90865
|
+
}));
|
|
90866
|
+
return true;
|
|
90867
|
+
};
|
|
90764
90868
|
useKeyboardShortcuts(ref, [...createSelectionKeyboardShortcuts(selectionController, {
|
|
90765
90869
|
toggleEnabled: true,
|
|
90766
90870
|
enabled: () => dragContextValue.grabTarget === null
|
|
90767
90871
|
}), {
|
|
90768
90872
|
key: "enter",
|
|
90769
90873
|
description: "Edit table cell content",
|
|
90770
|
-
enabled:
|
|
90874
|
+
enabled: canStartEditing,
|
|
90771
90875
|
handler: () => {
|
|
90772
|
-
|
|
90773
|
-
const activeCell = document.activeElement.closest("td");
|
|
90774
|
-
if (!activeCell) {
|
|
90775
|
-
return false;
|
|
90776
|
-
}
|
|
90777
|
-
activeCell.dispatchEvent(new CustomEvent("editrequested", {
|
|
90876
|
+
document.activeElement.dispatchEvent(new CustomEvent("editrequested", {
|
|
90778
90877
|
bubbles: false
|
|
90779
90878
|
}));
|
|
90780
90879
|
return true;
|
|
@@ -90782,37 +90881,13 @@ const Table = props => {
|
|
|
90782
90881
|
}, {
|
|
90783
90882
|
key: "a-z",
|
|
90784
90883
|
description: "Start editing table cell content",
|
|
90785
|
-
enabled:
|
|
90786
|
-
handler:
|
|
90787
|
-
const activeCell = document.activeElement.closest("td");
|
|
90788
|
-
if (!activeCell) {
|
|
90789
|
-
return false;
|
|
90790
|
-
}
|
|
90791
|
-
activeCell.dispatchEvent(new CustomEvent("editrequested", {
|
|
90792
|
-
bubbles: false,
|
|
90793
|
-
detail: {
|
|
90794
|
-
initialValue: e.key
|
|
90795
|
-
}
|
|
90796
|
-
}));
|
|
90797
|
-
return true;
|
|
90798
|
-
}
|
|
90884
|
+
enabled: canStartEditing,
|
|
90885
|
+
handler: startEditingWithKey
|
|
90799
90886
|
}, {
|
|
90800
90887
|
key: "0-9",
|
|
90801
90888
|
description: "Start editing table cell content",
|
|
90802
|
-
enabled:
|
|
90803
|
-
handler:
|
|
90804
|
-
const activeCell = document.activeElement.closest("td");
|
|
90805
|
-
if (!activeCell) {
|
|
90806
|
-
return false;
|
|
90807
|
-
}
|
|
90808
|
-
activeCell.dispatchEvent(new CustomEvent("editrequested", {
|
|
90809
|
-
bubbles: false,
|
|
90810
|
-
detail: {
|
|
90811
|
-
initialValue: e.key
|
|
90812
|
-
}
|
|
90813
|
-
}));
|
|
90814
|
-
return true;
|
|
90815
|
-
}
|
|
90889
|
+
enabled: canStartEditing,
|
|
90890
|
+
handler: startEditingWithKey
|
|
90816
90891
|
}]);
|
|
90817
90892
|
|
|
90818
90893
|
// resizing
|