@jsenv/navi 0.29.376 → 0.29.378

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.
@@ -35096,10 +35096,18 @@ registerNaviCommand("--navi-define", (source, event) => {
35096
35096
  implementation: () => executeNaviDefine(source, event, target),
35097
35097
  };
35098
35098
  });
35099
+ // A send whose target is the popup itself: what is around the source IS the
35100
+ // popup's answer, and a picker runs its action with that answer as it closes
35101
+ // (see onRequestClose in picker_custom.jsx) — dispatching the action here too
35102
+ // would run it twice. `isSend` tells this close from a dismissal (the cross,
35103
+ // a press outside, Escape): the person pressed a choice, which a picker may
35104
+ // act on even when the choice is the one it already held.
35099
35105
  const executeNaviDefine = (source, event, target) => {
35100
- // The picker's onClose already dispatches the action with the final value.
35101
- // Dispatching again here would fire the action twice.
35102
- return triggerNaviCommand(target, "--navi-close", event);
35106
+ return dispatchCustomEvent(target, "navi_request_close", {
35107
+ event,
35108
+ source: resolveCommandProxySource(source),
35109
+ isSend: true,
35110
+ });
35103
35111
  };
35104
35112
 
35105
35113
  registerNaviCommand("--navi-scroll", (source, event) => {
@@ -45310,6 +45318,10 @@ time.navi_text {
45310
45318
  border-radius: inherit;
45311
45319
  }
45312
45320
 
45321
+ &[data-text-clamp] {
45322
+ min-width: 0;
45323
+ }
45324
+
45313
45325
  &[data-text-overflow] {
45314
45326
  text-overflow: ellipsis;
45315
45327
  overflow-wrap: normal;
@@ -45683,6 +45695,10 @@ const shouldInjectSpacingBetween = (left, right) => {
45683
45695
  * n lines. This is the only prop to use for that — `Box`'s `lineClamp` /
45684
45696
  * `overflowEllipsis` are raw CSS mappings meant for elements that are not a
45685
45697
  * `Text`, and `lineClamp={1}` is never the single-line truncation you want.
45698
+ * Either value states the `white-space` it needs — one line does not wrap,
45699
+ * n lines may — so it holds inside a single-line ancestor too (a `Picker`'s
45700
+ * value, a `Time`); an explicit `noWrap`/`pre`/`preLine` on this `Text`
45701
+ * still wins.
45686
45702
  * Truncation only happens if the element may become narrower than its
45687
45703
  * content: `maxLines` sets `min-width: 0` here, but each `Box` between this
45688
45704
  * one and the element that carries the width must set it too.
@@ -45745,6 +45761,25 @@ const shouldInjectSpacingBetween = (left, right) => {
45745
45761
  * internally for overlays such as the skeleton container.
45746
45762
  */
45747
45763
  const Text = props => {
45764
+ if (props.maxLines > 1) {
45765
+ props = {
45766
+ ...props,
45767
+ "data-text-clamp": ""
45768
+ };
45769
+ if (!saysWhiteSpace(props)) {
45770
+ // n lines means "you may wrap". white-space is inherited, so left unsaid
45771
+ // a single-line ancestor (a Picker's value, a Time) leaves the clamp one
45772
+ // line to cut, and the text runs past its box. Same tag rule as
45773
+ // TextOverflow: a paragraph keeps its line breaks.
45774
+ props = props.as === "p" ? {
45775
+ ...props,
45776
+ preLine: true
45777
+ } : {
45778
+ ...props,
45779
+ noWrap: false
45780
+ };
45781
+ }
45782
+ }
45748
45783
  if (props.loading || props.skeleton) {
45749
45784
  return jsx(TextSkeleton, {
45750
45785
  ...props
@@ -45956,6 +45991,14 @@ const TextOverflow = ({
45956
45991
  children: children
45957
45992
  });
45958
45993
  };
45994
+ const saysWhiteSpace = ({
45995
+ noWrap,
45996
+ pre,
45997
+ preWrap,
45998
+ preLine
45999
+ }) => {
46000
+ return noWrap !== undefined || pre !== undefined || preWrap !== undefined || preLine !== undefined;
46001
+ };
45959
46002
  const TextWithSelectRange = ({
45960
46003
  ref,
45961
46004
  selectRange,
@@ -59993,9 +60036,11 @@ const TimeRelative = ({
59993
60036
  });
59994
60037
  };
59995
60038
  const TimeText = props => {
60039
+ // A date stays on one line, unless the caller grants it several.
60040
+ const noWrap = props.maxLines > 1 ? undefined : true;
59996
60041
  return jsx(Text, {
59997
60042
  as: "time",
59998
- noWrap: true,
60043
+ noWrap: noWrap,
59999
60044
  ...props
60000
60045
  });
60001
60046
  };
@@ -66012,6 +66057,9 @@ const PickerCustom = props => {
66012
66057
  // closed on it. A confirm picker is the one saying something (see
66013
66058
  // picker_confirm.jsx): its press, deferred until the question is answered.
66014
66059
  onConfirm,
66060
+ // A choice pressed inside the popup runs `action` even when it is the one
66061
+ // the picker already held. See the JSDoc.
66062
+ canSendWhileUnchanged,
66015
66063
  // The popup's lifecycle, the same pair Dialog and Popover take. Taken on
66016
66064
  // the picker and chained into its own openController below: the picker
66017
66065
  // hands the popup that controller, and a controlled Dialog/Popover reads
@@ -66054,6 +66102,7 @@ const PickerCustom = props => {
66054
66102
  delete pickerProps.defaultOpen;
66055
66103
  delete pickerProps.escapeEffect;
66056
66104
  delete pickerProps.onConfirm;
66105
+ delete pickerProps.canSendWhileUnchanged;
66057
66106
  delete pickerProps.onOpen;
66058
66107
  delete pickerProps.onClose;
66059
66108
  delete pickerProps.openOn;
@@ -66155,8 +66204,17 @@ const PickerCustom = props => {
66155
66204
  // suggestion to accept, and confirming `undefined` cannot mean
66156
66205
  // anything. A picker on a defaultValue is untouched by this: it
66157
66206
  // shows something, so closing on it still confirms it.
66158
- // No action to run, but still allow the close.
66159
- return;
66207
+ //
66208
+ // Unless the close is a choice PRESSED inside the popup (a row's
66209
+ // `--navi-send`, a `--navi-confirm`) on a picker that asked to
66210
+ // send while unchanged: the person picked what was already picked,
66211
+ // and for that picker the same value said again means something.
66212
+ // A press outside, Escape, the cross say nothing and keep skipping.
66213
+ const said = requestCloseEvent.detail.isSend || confirmEventRef.current;
66214
+ if (!(canSendWhileUnchanged && said)) {
66215
+ // No action to run, but still allow the close.
66216
+ return;
66217
+ }
66160
66218
  }
66161
66219
  dispatchRequestAction(inputEl, {
66162
66220
  event: requestCloseEvent,
@@ -66350,7 +66408,8 @@ const PickerCustom = props => {
66350
66408
  intent: "read",
66351
66409
  allowed: () => {
66352
66410
  const closing = requestClose(e, {
66353
- isCancel: e.detail.isCancel
66411
+ isCancel: e.detail.isCancel,
66412
+ isSend: e.detail.isSend
66354
66413
  });
66355
66414
  if (!closing) {
66356
66415
  e.preventDefault();
@@ -76367,6 +76426,12 @@ const PickerFirstResolver = props => {
76367
76426
  * press, the way a click is the innermost target's. Being a gesture, the open goes through
76368
76427
  * the interaction gate as one: a `readOnly` picker refuses it and says so
76369
76428
  * where the finger is, whatever `openWhileReadOnly` says.
76429
+ * @param {number} [maxLines=1] How many lines the value may take before it is
76430
+ * cut with an ellipsis — `Text`'s `maxLines`, put on the value box, and
76431
+ * granted to a `BadgeList` in the `ui`, which caps its rows to it (see
76432
+ * max_lines_context.js). `variant="text"` and `variant="bare"` are not
76433
+ * clamped: their drawing is the caller's own, a `Text` in the `ui` cuts
76434
+ * itself with its own `maxLines`. See docs/typography.md.
76370
76435
  * @param {import("ignore:preact").ComponentChildren | "default" | import("ignore:preact").ComponentType} [ui] What the
76371
76436
  * trigger draws in place of the value's default rendering (a date, a list
76372
76437
  * joined by commas…) — and then all it draws: `placeholder` is not shown
@@ -76606,6 +76671,15 @@ const PickerFirstResolver = props => {
76606
76671
  * @param {(confirmEvent: CustomEvent) => void} [onConfirm] Called once the
76607
76672
  * popup has closed on a `--navi-confirm` said inside it. What a confirm
76608
76673
  * picker uses to run its press; a picker of any type may listen too.
76674
+ * @param {boolean} [canSendWhileUnchanged] Run `action` when a choice pressed
76675
+ * inside the popup — a row's `--navi-send`, a `--navi-confirm` — is the
76676
+ * value the picker already held. By default that close runs nothing: the
76677
+ * answer has not moved, so there is nothing new to say, same as `Form`.
76678
+ * Set this for a picker whose value is a setting that can be valid while
76679
+ * what it points to is not — a channel whose account came unlinked — so
76680
+ * picking it again is how the person asks for it to be put right, and the
76681
+ * action sees the same value and re-asks what there is to re-ask. A press
76682
+ * outside, Escape and the cross still send nothing: nothing was pressed.
76609
76683
  * @param {(openEvent: CustomEvent) => void} [onOpen] Called when the popup
76610
76684
  * opens, whatever asked for it (a press, a command, history bringing it
76611
76685
  * back). You most likely do NOT need this pair: "the user validated" is
@@ -78129,13 +78203,7 @@ const renderDayDefault = (day, {
78129
78203
  type: "date",
78130
78204
  format: format,
78131
78205
  dayLabel: true,
78132
- lang: lang
78133
- // A Time keeps its date on one line by default; here a day too long for
78134
- // the box is meant to wrap, so that is undone — except for maxLines={1},
78135
- // which IS one line and cuts it itself (see Text's own TextOverflow):
78136
- // saying "you may wrap" there would undo the truncation instead.
78137
- ,
78138
- noWrap: maxLines === 1 || maxLines === "1" ? undefined : false,
78206
+ lang: lang,
78139
78207
  maxLines: maxLines,
78140
78208
  children: day
78141
78209
  });