@jsenv/navi 0.29.471 → 0.29.473
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 +69 -22
- package/dist/dev/jsenv_navi.js.map +8 -8
- package/dist/jsenv_navi.js +60 -20
- package/dist/jsenv_navi.js.map +8 -8
- package/package.json +1 -1
package/dist/dev/jsenv_navi.js
CHANGED
|
@@ -43943,6 +43943,7 @@ const useRenderScope = (init, update) => {
|
|
|
43943
43943
|
* registerChild(child): void; // Called on child mount
|
|
43944
43944
|
* onChildUIAction(child, e, { stateChanged: boolean }): void; // Called when a child fires a UI action
|
|
43945
43945
|
* unregisterChild(child): void; // Called on child unmount
|
|
43946
|
+
* collectsChild(child): boolean; // Whether registerChild(child) would put it in a value, here or above
|
|
43946
43947
|
* props: Object;
|
|
43947
43948
|
* ref: Ref; // Used to dispatch DOM events
|
|
43948
43949
|
* getManagedControls(): UIStateController[]; // Returns controls whose validity is managed by this controller
|
|
@@ -44667,6 +44668,10 @@ const useUIStateController = (
|
|
|
44667
44668
|
unregisterChild: (childUIStateController) => {
|
|
44668
44669
|
s.parentUIStateController?.unregisterChild(childUIStateController);
|
|
44669
44670
|
},
|
|
44671
|
+
collectsChild: (childUIStateController) =>
|
|
44672
|
+
Boolean(
|
|
44673
|
+
s.parentUIStateController?.collectsChild(childUIStateController),
|
|
44674
|
+
),
|
|
44670
44675
|
onChildUIAction: (childUIStateController, e, options) => {
|
|
44671
44676
|
s.parentUIStateController?.onChildUIAction(
|
|
44672
44677
|
childUIStateController,
|
|
@@ -45053,10 +45058,13 @@ const readNamedChildUIStates = (children, { warnNameless }) => {
|
|
|
45053
45058
|
// Looked up in useUIGroupStateController to fill in omitted aggregateChildStates /
|
|
45054
45059
|
// distributeChildUIState. If neither a default nor an explicit impl is found for a
|
|
45055
45060
|
// group, creation throws so the caller knows it must supply them.
|
|
45061
|
+
// The radio/checkbox filters read `props.type`, which controlHostProps copies:
|
|
45062
|
+
// a child is also asked about while rendering, before its first render has
|
|
45063
|
+
// built controlHostProps (see collectsChild).
|
|
45056
45064
|
const GROUP_DEFAULTS = {
|
|
45057
45065
|
radio_group: {
|
|
45058
45066
|
childControlFilter: (child) =>
|
|
45059
|
-
child.controlType === "input" && child.
|
|
45067
|
+
child.controlType === "input" && child.props?.type === "radio",
|
|
45060
45068
|
aggregateChildStates: keptOrFirstDefinedChildUIState,
|
|
45061
45069
|
distributeChildUIState: (newUIState, childUIStateController) => {
|
|
45062
45070
|
const childSelected = childUIStateController.props.value === newUIState;
|
|
@@ -45068,8 +45076,7 @@ const GROUP_DEFAULTS = {
|
|
|
45068
45076
|
},
|
|
45069
45077
|
checkbox_group: {
|
|
45070
45078
|
childControlFilter: (child) =>
|
|
45071
|
-
child.controlType === "input" &&
|
|
45072
|
-
child.controlHostProps?.type === "checkbox",
|
|
45079
|
+
child.controlType === "input" && child.props?.type === "checkbox",
|
|
45073
45080
|
aggregateChildStates: keptAndCheckedChildUIStates,
|
|
45074
45081
|
distributeChildUIState: (newUIState, childUIStateController) => {
|
|
45075
45082
|
const childSelected =
|
|
@@ -45614,6 +45621,9 @@ const useUIGroupStateController = (
|
|
|
45614
45621
|
|
|
45615
45622
|
const controller = {
|
|
45616
45623
|
controlType,
|
|
45624
|
+
// Published from the first render, like a leaf's: a group asks its
|
|
45625
|
+
// parent whether anyone listens before its first update.
|
|
45626
|
+
parentUIStateController,
|
|
45617
45627
|
id,
|
|
45618
45628
|
name,
|
|
45619
45629
|
value,
|
|
@@ -45837,6 +45847,18 @@ const useUIGroupStateController = (
|
|
|
45837
45847
|
notifyExternal: "silent",
|
|
45838
45848
|
});
|
|
45839
45849
|
},
|
|
45850
|
+
// registerChild above, asked ahead: monitored here, handed up, or
|
|
45851
|
+
// captured and dropped.
|
|
45852
|
+
collectsChild: (childUIStateController) => {
|
|
45853
|
+
if (isMonitoringChild(childUIStateController)) {
|
|
45854
|
+
return true;
|
|
45855
|
+
}
|
|
45856
|
+
const currentParent = s.parentUIStateController;
|
|
45857
|
+
if (allowCapture || !currentParent) {
|
|
45858
|
+
return false;
|
|
45859
|
+
}
|
|
45860
|
+
return currentParent.collectsChild(childUIStateController);
|
|
45861
|
+
},
|
|
45840
45862
|
onChildUIAction: (
|
|
45841
45863
|
childUIStateController,
|
|
45842
45864
|
e,
|
|
@@ -46314,6 +46336,9 @@ const useUIFacadeStateController = (props, realUIStateController) => {
|
|
|
46314
46336
|
s.realUIStateController.facadeChild = null;
|
|
46315
46337
|
}
|
|
46316
46338
|
},
|
|
46339
|
+
// A refused child is not handed further up: the popup's controls
|
|
46340
|
+
// answer to the picker or to nobody.
|
|
46341
|
+
collectsChild: (child) => canRegisterAsFacadeChild(child),
|
|
46317
46342
|
getManagedControls: () => {
|
|
46318
46343
|
const child = firstChildControllerRef.current;
|
|
46319
46344
|
if (!child) {
|
|
@@ -46934,8 +46959,6 @@ const useControlProps = (props, {
|
|
|
46934
46959
|
const controlInfo = createControlInfo(props, {
|
|
46935
46960
|
controlType
|
|
46936
46961
|
});
|
|
46937
|
-
const readOnlyUncontrolled = useReadOnlyUncontrolled(props, controlInfo);
|
|
46938
|
-
controlInfo.readOnlyUncontrolled = readOnlyUncontrolled;
|
|
46939
46962
|
const uiStateController = useUIStateController(props, {
|
|
46940
46963
|
controlInfo,
|
|
46941
46964
|
syncDomState,
|
|
@@ -46943,6 +46966,9 @@ const useControlProps = (props, {
|
|
|
46943
46966
|
persists,
|
|
46944
46967
|
uiActionInternal
|
|
46945
46968
|
});
|
|
46969
|
+
// After the controller: whether a group listens is asked of the parent it
|
|
46970
|
+
// registers into. Read by useInteractiveProps below.
|
|
46971
|
+
controlInfo.readOnlyUncontrolled = useReadOnlyUncontrolled(props, controlInfo, uiStateController);
|
|
46946
46972
|
// What the control draws is what it holds by itself, and this read is what
|
|
46947
46973
|
// re-renders it when that changes. A button inheriting the value of the
|
|
46948
46974
|
// control around it (a submit in a form, see ownUIStateSignal) draws none of
|
|
@@ -47948,16 +47974,21 @@ const INPUT_TYPE_SUPPORTING_READONLY_SET = new Set(["text", "date", "datetime-lo
|
|
|
47948
47974
|
// warning below so a group can ask the same question a single control does: a
|
|
47949
47975
|
// control with a `value` and nobody listening cannot be changed by hand, and
|
|
47950
47976
|
// that is true whatever the control is.
|
|
47951
|
-
|
|
47977
|
+
// The group is asked, not assumed from a controller being above: a control in
|
|
47978
|
+
// between may only pass registration along (a <Details> holds none of the
|
|
47979
|
+
// values it folds), and a group may hand the control further up or drop it.
|
|
47980
|
+
const useIsControlListenedTo = (props, uiStateController) => {
|
|
47952
47981
|
const isProxy = Boolean(props["navi-control-proxy-for"]);
|
|
47953
47982
|
const formContext = useContext(FormContext);
|
|
47954
|
-
const
|
|
47983
|
+
const {
|
|
47984
|
+
parentUIStateController
|
|
47985
|
+
} = uiStateController;
|
|
47955
47986
|
return Boolean(props.signal ||
|
|
47956
47987
|
// a bound signal is written back on uiAction → interactive
|
|
47957
|
-
props.uiAction || props.action || formContext || parentUIStateController || isProxy || props.command);
|
|
47988
|
+
props.uiAction || props.action || formContext || parentUIStateController?.collectsChild(uiStateController) || isProxy || props.command);
|
|
47958
47989
|
};
|
|
47959
|
-
const useReadOnlyUncontrolled = (props, controlInfo) => {
|
|
47960
|
-
const listenedTo = useIsControlListenedTo(props);
|
|
47990
|
+
const useReadOnlyUncontrolled = (props, controlInfo, uiStateController) => {
|
|
47991
|
+
const listenedTo = useIsControlListenedTo(props, uiStateController);
|
|
47961
47992
|
const warnedRef = useRef(false);
|
|
47962
47993
|
if (!controlInfo.hasStateProp) {
|
|
47963
47994
|
return false;
|
|
@@ -48025,7 +48056,7 @@ const useControlgroupProps = (props, {
|
|
|
48025
48056
|
// makes the group read-only so children don't appear interactive when they
|
|
48026
48057
|
// can't change. A form or a group around it IS someone listening — that is
|
|
48027
48058
|
// what will send the value and hand a new one back.
|
|
48028
|
-
const listenedTo = useIsControlListenedTo(props);
|
|
48059
|
+
const listenedTo = useIsControlListenedTo(props, uiGroupStateController);
|
|
48029
48060
|
const implicitReadOnlyWarnedRef = useRef(false);
|
|
48030
48061
|
const implicitReadOnly = uiGroupStateController.hasValueProp && !listenedTo;
|
|
48031
48062
|
if (implicitReadOnly && !props.readOnly) {
|
|
@@ -52857,10 +52888,16 @@ const LinkWithRoute = ({
|
|
|
52857
52888
|
currentExcept,
|
|
52858
52889
|
currentAlso,
|
|
52859
52890
|
children,
|
|
52891
|
+
// Kept out of `rest`: spread after the route's url, an `href={undefined}`
|
|
52892
|
+
// riding along would erase it.
|
|
52893
|
+
href,
|
|
52860
52894
|
...rest
|
|
52861
52895
|
}) => {
|
|
52862
52896
|
{
|
|
52863
52897
|
assertRoute(route);
|
|
52898
|
+
if (href !== undefined) {
|
|
52899
|
+
console.warn(`A link with a "route" ignores its "href" (${href}): the route builds the address.`);
|
|
52900
|
+
}
|
|
52864
52901
|
}
|
|
52865
52902
|
const url = route.buildUrl(routeParams);
|
|
52866
52903
|
{
|
|
@@ -58039,15 +58076,21 @@ const ButtonRouteResolver = props => {
|
|
|
58039
58076
|
};
|
|
58040
58077
|
const ButtonWithRoute = props => {
|
|
58041
58078
|
const Next = useNextResolver();
|
|
58079
|
+
// `href` is kept out of `rest`: spread after the route's url, an
|
|
58080
|
+
// `href={undefined}` riding along would erase it.
|
|
58042
58081
|
const {
|
|
58043
58082
|
route,
|
|
58044
58083
|
routeParams,
|
|
58045
58084
|
children,
|
|
58046
58085
|
pseudoState,
|
|
58086
|
+
href,
|
|
58047
58087
|
...rest
|
|
58048
58088
|
} = props;
|
|
58049
58089
|
{
|
|
58050
58090
|
assertRoute(route);
|
|
58091
|
+
if (href !== undefined) {
|
|
58092
|
+
console.warn(`A button with a "route" ignores its "href" (${href}): the route builds the address.`);
|
|
58093
|
+
}
|
|
58051
58094
|
}
|
|
58052
58095
|
const url = route.buildUrl(routeParams);
|
|
58053
58096
|
const linkMatching = useRouteCurrent(route, routeParams);
|
|
@@ -74434,10 +74477,18 @@ const PickerCustom = props => {
|
|
|
74434
74477
|
props.id = props.id || controlId || idDefault;
|
|
74435
74478
|
// Given no value, the picker holds what the control in its popup holds (see
|
|
74436
74479
|
// the `mount` decision below) — and a group around it collects that from the
|
|
74437
|
-
// moment both exist.
|
|
74438
|
-
|
|
74480
|
+
// moment both exist. A picker that picks nothing holds none: its popup's
|
|
74481
|
+
// controls are never adopted (see canRegisterAsFacadeChild).
|
|
74482
|
+
const valueInPopup = !props.picksNothing && !isControlValueGivenByProps(props);
|
|
74483
|
+
// Asked of the chain, not read off the context: a control above may only
|
|
74484
|
+
// pass registration along (a <Details> holds none of the values it folds),
|
|
74485
|
+
// and a group may hand a picker further up or drop it. "picker" is what the
|
|
74486
|
+
// picker's own controller registers as (see picker.jsx).
|
|
74439
74487
|
const parentController = useContext(ParentUIStateControllerContext);
|
|
74440
|
-
const answersToGroup =
|
|
74488
|
+
const answersToGroup = !props.standalone && Boolean(parentController?.collectsChild({
|
|
74489
|
+
controlType: "picker",
|
|
74490
|
+
props
|
|
74491
|
+
}));
|
|
74441
74492
|
// Same narrow-container/maxWidth-compact heuristic Popup itself uses (see
|
|
74442
74493
|
// popup_mode.jsx's own useResolvedPopupMode) — frozen for the lifetime of an opening
|
|
74443
74494
|
// (computed when closed, stable while open, so a screen resize mid-session
|
|
@@ -75631,12 +75682,7 @@ const PickerConfirmResolver = props => {
|
|
|
75631
75682
|
mode: mode,
|
|
75632
75683
|
focusCapture: focusCapture,
|
|
75633
75684
|
openWhileReadOnly: openWhileReadOnly,
|
|
75634
|
-
onConfirm: onConfirm
|
|
75635
|
-
// A question holds no value to read before it is asked: the popup is
|
|
75636
|
-
// built on the first open, like that of a picker told its value (see
|
|
75637
|
-
// the `mount` resolution in picker_custom.jsx).
|
|
75638
|
-
,
|
|
75639
|
-
mount: MOUNT_DEFAULT,
|
|
75685
|
+
onConfirm: onConfirm,
|
|
75640
75686
|
message: undefined,
|
|
75641
75687
|
confirmLabel: undefined,
|
|
75642
75688
|
cancelLabel: undefined,
|
|
@@ -87594,8 +87640,9 @@ const PickerFirstResolver = props => {
|
|
|
87594
87640
|
* it (no `ui`, a `Picker.UI.*`, a component `ui`), when there is a clear
|
|
87595
87641
|
* cross, or when a form or group around the picker collects it; when an
|
|
87596
87642
|
* element `ui` calls `usePickerState()`. Read by nothing, it waits for the
|
|
87597
|
-
* first open, like a picker told a value. A `
|
|
87598
|
-
* builds them early: its popup
|
|
87643
|
+
* first open, like a picker told a value. A `picksNothing` picker (and so
|
|
87644
|
+
* `type="confirm"`) never builds them early: its popup holds no value, there
|
|
87645
|
+
* is nothing to read.
|
|
87599
87646
|
*/
|
|
87600
87647
|
// Every picker type is resolved into one of these before it gets here (see
|
|
87601
87648
|
// PickerTypeResolver and resolveInputProps): a native input type, or navi_js
|