@jsenv/navi 0.27.65 → 0.27.67

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.
@@ -17418,7 +17418,7 @@ const createCalloutManager = (
17418
17418
  return;
17419
17419
  }
17420
17420
  const resolvedAnchorElement =
17421
- anchorElement || controller.elementRef.current;
17421
+ anchorElement || controller.ref.current;
17422
17422
  const removeCloseOnCleanup = addTeardown?.(() => {
17423
17423
  requestCloseCallout(new CustomEvent("cleanup"), "cleanup");
17424
17424
  });
@@ -17443,7 +17443,7 @@ const createCalloutManager = (
17443
17443
  tokenData.onClose?.();
17444
17444
  }
17445
17445
  tokens.clear();
17446
- const element = controller.elementRef.current;
17446
+ const element = controller.ref.current;
17447
17447
  if (
17448
17448
  shouldTransferFocusFromCallout &&
17449
17449
  element &&
@@ -19491,7 +19491,7 @@ const REQUIRED_CONSTRAINT = {
19491
19491
  }
19492
19492
  return {
19493
19493
  message: naviI18n("constraint.required.radio"),
19494
- target: field.elementRef.current,
19494
+ target: field.ref.current,
19495
19495
  };
19496
19496
  }
19497
19497
  if (type === "radio") {
@@ -19505,7 +19505,7 @@ const REQUIRED_CONSTRAINT = {
19505
19505
  }
19506
19506
  return {
19507
19507
  message: naviI18n("constraint.required.radio"),
19508
- target: field.elementRef.current,
19508
+ target: field.ref.current,
19509
19509
  };
19510
19510
  }
19511
19511
 
@@ -19517,7 +19517,7 @@ const REQUIRED_CONSTRAINT = {
19517
19517
  }
19518
19518
  return {
19519
19519
  message: naviI18n("constraint.required.checkbox_group"),
19520
- target: field.elementRef.current,
19520
+ target: field.ref.current,
19521
19521
  };
19522
19522
  }
19523
19523
  if (type === "checkbox") {
@@ -20356,7 +20356,7 @@ const createControlValidation = (
20356
20356
 
20357
20357
  const checkValidity = ({
20358
20358
  event,
20359
- requester = controller.elementRef.current,
20359
+ requester = controller.ref.current,
20360
20360
  fromRequestAction,
20361
20361
  } = {}) => {
20362
20362
  if (fromRequestAction) {
@@ -20399,7 +20399,7 @@ const createControlValidation = (
20399
20399
  ...DEFAULT_CONSTRAINT_SET,
20400
20400
  ...dynamicConstraintSet,
20401
20401
  ]);
20402
- const elementSig = getElementSignature(controller.elementRef.current);
20402
+ const elementSig = getElementSignature(controller.ref.current);
20403
20403
  debugUIState(
20404
20404
  event,
20405
20405
  `check ${elementSig}: ${constraintSet.size} constraints`,
@@ -20454,7 +20454,7 @@ const createControlValidation = (
20454
20454
  if (activeFailedConstraintInfo) {
20455
20455
  const titleLess = controller.controlHostProps.title === undefined;
20456
20456
  if (titleLess) {
20457
- const element = controller.elementRef.current;
20457
+ const element = controller.ref.current;
20458
20458
  if (element) {
20459
20459
  element.setAttribute(
20460
20460
  "title",
@@ -20465,7 +20465,7 @@ const createControlValidation = (
20465
20465
  } else {
20466
20466
  const titleLess = controller.controlHostProps.title === undefined;
20467
20467
  if (titleLess) {
20468
- const element = controller.elementRef.current;
20468
+ const element = controller.ref.current;
20469
20469
  if (element) {
20470
20470
  element.removeAttribute("title");
20471
20471
  }
@@ -20479,7 +20479,7 @@ const createControlValidation = (
20479
20479
  !compareTwoJsValues(constraintValidityState, newConstraintValidityState)
20480
20480
  ) {
20481
20481
  constraintValidityState = newConstraintValidityState;
20482
- const element = controller.elementRef.current;
20482
+ const element = controller.ref.current;
20483
20483
  if (element) {
20484
20484
  debugUIState(
20485
20485
  event,
@@ -20529,7 +20529,7 @@ const createControlValidation = (
20529
20529
  event,
20530
20530
  { report = false, fromRequestAction = false } = {},
20531
20531
  ) => {
20532
- const elementSig = getElementSignature(controller.elementRef.current);
20532
+ const elementSig = getElementSignature(controller.ref.current);
20533
20533
  const isValid = checkValidity({ event, fromRequestAction });
20534
20534
  if (failingManagedControlValidity) {
20535
20535
  // Group/form case: find the actual failing leaf and report on it.
@@ -21022,7 +21022,7 @@ const createControlInteraction = (
21022
21022
  // Keep title attribute in sync for accessibility.
21023
21023
  const titleLess = !controller.controlHostProps?.title;
21024
21024
  if (titleLess) {
21025
- const element = controller.elementRef.current;
21025
+ const element = controller.ref.current;
21026
21026
  if (element) {
21027
21027
  if (interactionFailedConstraintInfo) {
21028
21028
  element.setAttribute(
@@ -21236,7 +21236,7 @@ const tryActionAfterInteractionAllowed = (
21236
21236
  if (controller) {
21237
21237
  const proxyTargetController = findControlProxyTargetController(controller);
21238
21238
  if (proxyTargetController) {
21239
- elementForAction = proxyTargetController.elementRef.current;
21239
+ elementForAction = proxyTargetController.ref.current;
21240
21240
  }
21241
21241
  const activeController = proxyTargetController ?? controller;
21242
21242
  uiState = activeController?.uiState;
@@ -22359,7 +22359,7 @@ const createControlRules = (
22359
22359
  * onChildUIAction(child, e, { stateChanged: boolean }): void; // Called when a child fires a UI action
22360
22360
  * unregisterChild(child): void; // Called on child unmount
22361
22361
  * props: Object;
22362
- * elementRef: Ref; // Used to dispatch DOM events
22362
+ * ref: Ref; // Used to dispatch DOM events
22363
22363
  * getManagedControls(): UIStateController[]; // Returns controls whose validity is managed by this controller
22364
22364
  * }
22365
22365
  * ```
@@ -22395,7 +22395,7 @@ const ParentUIStateControllerContext = createContext();
22395
22395
  * **internalBehavior events** (e.g. radio_sibling_uncheck, state_prop re-sync):
22396
22396
  * skip reactions and parent notification — they are programmatic, not user-initiated.
22397
22397
  *
22398
- * The controller exposes `elementRef` so parent groups can dispatch DOM events on children
22398
+ * The controller exposes `ref` so parent groups can dispatch DOM events on children
22399
22399
  * (e.g. `resetUIState` cascading `navi_reset_ui_state`).
22400
22400
  */
22401
22401
  const useUIStateController = (
@@ -22417,7 +22417,6 @@ const useUIStateController = (
22417
22417
  const parentUIStateController = useContext(ParentUIStateControllerContext);
22418
22418
  const formContext = useContext(FormContext);
22419
22419
  const { id, uiAction } = props;
22420
- const ref = props.ref;
22421
22420
  const isProxy = Boolean(props["navi-control-proxy-for"]);
22422
22421
  if (persists === undefined && formContext) {
22423
22422
  persists = true;
@@ -22436,7 +22435,7 @@ const useUIStateController = (
22436
22435
  );
22437
22436
  useLayoutEffect(() => {
22438
22437
  const controller = uiStateControllerRef.current;
22439
- const el = ref.current;
22438
+ const el = controller.ref.current;
22440
22439
  if (el) {
22441
22440
  el.__uiStateController__ = controller;
22442
22441
  }
@@ -22483,6 +22482,13 @@ const useUIStateController = (
22483
22482
  // not the resolved/curated host props. useInteractiveProps overwrites
22484
22483
  // uiStateController.controlHostProps with the resolved subset on every render.
22485
22484
  uiStateController.props = props;
22485
+ // Re-sync to this render's ref object. It's normally stable, but it can
22486
+ // legitimately change identity (e.g. switching from an internal fallback
22487
+ // ref to a forwarded one, or across an interrupted/resumed render such as
22488
+ // a Suspense boundary resolving) — if we kept the original ref forever,
22489
+ // `ref.current` would be stuck at whatever it was at creation time, even
22490
+ // after the controller has moved on to a different, live DOM node.
22491
+ uiStateController.ref = props.ref;
22486
22492
  uiStateController.id = props.id; // never suppoed to changed, not supported for now
22487
22493
  uiStateController.name = props.name;
22488
22494
 
@@ -22508,11 +22514,12 @@ const useUIStateController = (
22508
22514
  parentUIStateController,
22509
22515
  isProxy,
22510
22516
  allowNameless,
22511
- elementRef: ref,
22512
- props,
22513
22517
 
22518
+ props,
22519
+ ref: props.ref,
22514
22520
  id: props.id,
22515
22521
  name: props.name,
22522
+
22516
22523
  state: stateInitial,
22517
22524
  uiState: stateInitial,
22518
22525
  uiStateSignal,
@@ -22557,7 +22564,7 @@ const useUIStateController = (
22557
22564
  if (skipCommand) ; else {
22558
22565
  const command = uiStateController.controlHostProps.command;
22559
22566
  if (command) {
22560
- const element = uiStateController.elementRef.current;
22567
+ const element = uiStateController.ref.current;
22561
22568
  if (element) {
22562
22569
  debugUIState(
22563
22570
  `triggering command "${command}" for "${controlType}"`,
@@ -22580,7 +22587,9 @@ const useUIStateController = (
22580
22587
  return false;
22581
22588
  }
22582
22589
  }
22583
- const controllerSig = getElementSignature(e.currentTarget || ref.current);
22590
+ const controllerSig = getElementSignature(
22591
+ e.currentTarget || uiStateController.ref.current,
22592
+ );
22584
22593
  // if (persists) {
22585
22594
  // setNavState(prop);
22586
22595
  // }
@@ -22664,7 +22673,7 @@ const useUIStateController = (
22664
22673
  }
22665
22674
  debugUIState(e, `publishUIState(${JSON.stringify(newUIState)})`);
22666
22675
  publishUIState(newUIState, e);
22667
- const el = ref.current;
22676
+ const el = uiStateController.ref.current;
22668
22677
  // Always notify the element that its UI state changed.
22669
22678
  // Listeners use this to stay in sync (e.g. input_effect.js tracks currentState,
22670
22679
  // useUIState subscribes for reactive updates). Separate from navi_set_ui_state
@@ -22767,7 +22776,7 @@ const useUIStateController = (
22767
22776
  if (targetController) {
22768
22777
  debugUIState(
22769
22778
  e,
22770
- `forwarding set_ui_state "${newUIState}" to ${getElementSignature(targetController.elementRef.current)}`,
22779
+ `forwarding set_ui_state "${newUIState}" to ${getElementSignature(targetController.ref.current)}`,
22771
22780
  );
22772
22781
  const forwardEvent = new CustomEvent("proxy_forward_set_ui_state", {
22773
22782
  detail: {},
@@ -23227,6 +23236,10 @@ const useUIGroupStateController = (
23227
23236
  const prevValue = existingController.value;
23228
23237
  const prevHasValueProp = existingController.hasValueProp;
23229
23238
  existingController.props = props;
23239
+ // Re-sync to this render's ref object — see the matching comment in
23240
+ // useUIStateController._checkForUpdates for why this can't be captured
23241
+ // once at creation time and left untouched.
23242
+ existingController.ref = ref;
23230
23243
  existingController.id = id;
23231
23244
  existingController.name = name;
23232
23245
  existingController.value = value;
@@ -23282,7 +23295,7 @@ const useUIGroupStateController = (
23282
23295
  uiState: fallbackState,
23283
23296
  uiStateSignal,
23284
23297
  wantRequesterButtonState,
23285
- elementRef: ref,
23298
+ ref,
23286
23299
  getPropFromState: (uiState) => uiState,
23287
23300
  distributeChildUIState: resolvedDistributeChildUIState,
23288
23301
  // Cascades newUIState to each monitored child via resolvedDistributeChildUIState,
@@ -23655,7 +23668,7 @@ const useUIFacadeStateController = (props, realUIStateController) => {
23655
23668
  const facadeUIStateController = {
23656
23669
  controlType: "facade",
23657
23670
  props,
23658
- elementRef: realUIStateController.elementRef,
23671
+ ref: realUIStateController.ref,
23659
23672
  uiStateSignal: realUIStateController.uiStateSignal,
23660
23673
  registerChild: (child) => {
23661
23674
  if (!canRegisterAsFacadeChild(child)) {
@@ -23977,7 +23990,7 @@ const useControlProps = (props, {
23977
23990
  return true;
23978
23991
  };
23979
23992
  const syncUIStateWithDOM = e => {
23980
- const controlEl = e.currentTarget || uiStateController.elementRef.current;
23993
+ const controlEl = e.currentTarget || uiStateController.ref.current;
23981
23994
  const value = readControlValue(controlEl);
23982
23995
  uiStateController.setUIState(value, e);
23983
23996
  };
@@ -23987,7 +24000,6 @@ const useControlProps = (props, {
23987
24000
  const triggerUIAction = e => {
23988
24001
  syncUIStateWithDOM(e);
23989
24002
  };
23990
- const wasCheckedAtMousedownRef = useRef(false);
23991
24003
  const getDefaultEventReactionDefinitions = () => {
23992
24004
  const keyDownDefault = e => {
23993
24005
  const defaultAction = getKeyboardEventDefaultAction(e);
@@ -24202,16 +24214,20 @@ const useControlProps = (props, {
24202
24214
  }
24203
24215
  return keyDownDefault(e);
24204
24216
  },
24205
- mouseDown: e => {
24206
- wasCheckedAtMousedownRef.current = e.currentTarget.checked;
24207
- },
24208
24217
  click: e => {
24209
- if (isRadio && wasCheckedAtMousedownRef.current) {
24210
- // When a radio is already checked and gets clicked, the browser does NOT
24211
- // fire an input event (state doesn't change), so asAction never runs.
24212
- // We still want uiAction + command to fire. We can tell whether the click
24213
- // is on an already-checked radio by looking at wasCheckedAtMousedownRef:
24214
- // if it was checked at mousedown, the input event won't come, so we do it here.
24218
+ // When a radio is already checked and gets clicked, the browser does NOT
24219
+ // fire an input event (state doesn't change), so syncUIStateWithDOM never
24220
+ // runs. We still want uiAction + command to fire. We can tell whether the
24221
+ // click is on an already-checked radio by comparing our own tracked
24222
+ // uiState (value when checked, undefined when not see toDomProps) to
24223
+ // this radio's value: it's still the PRE-click value here, since it's
24224
+ // only updated later by the "input" handler. Reading the DOM `.checked`
24225
+ // instead would not work: the browser applies the checked toggle before
24226
+ // dispatching "click", so `.checked` is already the POST-click value
24227
+ // whether or not this was already the checked radio — and worse, a click
24228
+ // that lands on the <label> (not the <input>) never fires "mousedown" on
24229
+ // the input at all, so a DOM-snapshot-at-mousedown approach misses it.
24230
+ if (isRadio && uiStateController.uiState !== undefined) {
24215
24231
  return {
24216
24232
  name: `click on checked radio`,
24217
24233
  allowed: () => triggerUIAction(e),
@@ -24800,7 +24816,7 @@ const useInteractiveProps = (props, {
24800
24816
  }
24801
24817
  }));
24802
24818
  }
24803
- }, [disabledResolved, readOnlyResolved]);
24819
+ }, [disabledResolved, readOnlyResolved, ref]);
24804
24820
  useLayoutEffect(() => {
24805
24821
  return () => {
24806
24822
  const element = ref.current;
@@ -25002,7 +25018,7 @@ const useInteractiveProps = (props, {
25002
25018
  // already aggregated the new state by now, so uiStateSignal is correct.
25003
25019
  const parentController = uiStateController.parentUIStateController;
25004
25020
  if (parentController && (parentController.controlType === "radio_group" || parentController.controlType === "checkbox_group")) {
25005
- const parentEl = parentController.elementRef.current;
25021
+ const parentEl = parentController.ref.current;
25006
25022
  if (parentEl) {
25007
25023
  const originalEvent = e.detail.eventChain[0];
25008
25024
  dispatchRequestAction(parentEl, {
@@ -31500,24 +31516,15 @@ const FieldAsLabel = props => {
31500
31516
  children
31501
31517
  } = props;
31502
31518
  props.paddingWithControl = resolveSpacingSize(props.paddingWithControl, "s");
31503
- const [messageProps, remainingProps] = extractMessageAndRemainingProps(props);
31504
- const messagePropsRef = useRef();
31505
- messagePropsRef.current = messageProps;
31506
31519
  return jsx(Label, {
31507
31520
  "navi-field": "",
31508
31521
  styleCSSVars: FieldCSSVars,
31509
31522
  flex: vertical ? "y" : undefined,
31510
31523
  alignX: vertical ? "start" : undefined,
31511
31524
  "data-vertical": vertical ? "" : undefined,
31512
- ...remainingProps,
31525
+ ...props,
31513
31526
  vertical: undefined,
31514
- children: jsx(MessagePropsRefContext.Provider, {
31515
- value: messagePropsRef,
31516
- children: jsx(ControlIdContext.Provider, {
31517
- value: props.htmlFor,
31518
- children: children
31519
- })
31520
- })
31527
+ children: children
31521
31528
  });
31522
31529
  };
31523
31530
  const FieldCSSVars = {
@@ -31557,8 +31564,7 @@ const FIELD_PSEUDO_CLASSES = [":hover", ":active", ":focus", ":focus-visible", "
31557
31564
  const Label = props => {
31558
31565
  import.meta.css = [css$A, "@jsenv/navi/src/control/field.jsx"];
31559
31566
  const {
31560
- children,
31561
- ...rest
31567
+ children
31562
31568
  } = props;
31563
31569
  const controlId = useContext(ControlIdContext);
31564
31570
  const [disabled, setDisabled] = useState(false);
@@ -31566,6 +31572,9 @@ const Label = props => {
31566
31572
  const [connected, setConnected] = useState(false);
31567
31573
  const defaultId = useId();
31568
31574
  const htmlFor = props.htmlFor || controlId || `label_auto_${defaultId}`;
31575
+ const [messageProps, remainingProps] = extractMessageAndRemainingProps(props);
31576
+ const messagePropsRef = useRef();
31577
+ messagePropsRef.current = messageProps;
31569
31578
  return jsx(Box, {
31570
31579
  as: "label",
31571
31580
  htmlFor: htmlFor,
@@ -31576,7 +31585,7 @@ const Label = props => {
31576
31585
  ":disabled": disabled,
31577
31586
  ":read-only": readOnly
31578
31587
  },
31579
- ...rest,
31588
+ ...remainingProps,
31580
31589
  onnavi_control_state: e => {
31581
31590
  setConnected(true);
31582
31591
  setDisabled(e.detail.disabled);
@@ -31587,9 +31596,12 @@ const Label = props => {
31587
31596
  setDisabled(false);
31588
31597
  setReadOnly(false);
31589
31598
  },
31590
- children: jsx(ControlIdContext.Provider, {
31591
- value: htmlFor,
31592
- children: children
31599
+ children: jsx(MessagePropsRefContext.Provider, {
31600
+ value: messagePropsRef,
31601
+ children: jsx(ControlIdContext.Provider, {
31602
+ value: htmlFor,
31603
+ children: children
31604
+ })
31593
31605
  })
31594
31606
  });
31595
31607
  };
@@ -40947,6 +40959,7 @@ const PickerButton = props => {
40947
40959
  ref,
40948
40960
  variant,
40949
40961
  icon,
40962
+ iconSize = "m",
40950
40963
  placeholder,
40951
40964
  ui,
40952
40965
  maxLines = 1
@@ -40980,6 +40993,7 @@ const PickerButton = props => {
40980
40993
  styleCSSVars: PickerStyleCSSVars,
40981
40994
  variant: undefined,
40982
40995
  icon: undefined,
40996
+ iconSize: undefined,
40983
40997
  ui: undefined,
40984
40998
  maxLines: undefined,
40985
40999
  dayLabel: undefined
@@ -41091,7 +41105,7 @@ const PickerButton = props => {
41091
41105
  }), variant === "headless" || ui === "default" ? null : jsx("span", {
41092
41106
  className: "navi_picker_right_slot",
41093
41107
  children: jsx(Icon, {
41094
- size: "m",
41108
+ size: iconSize,
41095
41109
  children: icon === undefined ? jsx(ChevronDownSvg, {}) : icon
41096
41110
  })
41097
41111
  }), jsx(ControlFacadeChildrenWrapper, {