@jsenv/navi 0.28.9 → 0.28.10

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.
@@ -6521,6 +6521,13 @@ const DIMENSION_PROPS = {
6521
6521
  const inHorizontalFlexFlow =
6522
6522
  parentBoxFlow === "flex-x" || parentBoxFlow === "inline-flex-x";
6523
6523
  if (inHorizontalFlexFlow) {
6524
+ if (value === "content") {
6525
+ // flex-basis stays auto: the item still takes the free space, but its
6526
+ // content size is what line-breaking sees — so in a flexWrap parent a
6527
+ // neighbor that no longer fits wraps to the next line. With basis 0%
6528
+ // (below) nothing ever wraps: every item claims a size of zero.
6529
+ return { flexGrow: 1 };
6530
+ }
6524
6531
  // Parent is flex-x: grow as flex item
6525
6532
  return { flexGrow: 1, flexBasis: "0%" };
6526
6533
  }
@@ -6540,6 +6547,11 @@ const DIMENSION_PROPS = {
6540
6547
  const inVerticalFlexFlow =
6541
6548
  parentBoxFlow === "flex-y" || parentBoxFlow === "inline-flex-y";
6542
6549
  if (inVerticalFlexFlow) {
6550
+ if (value === "content") {
6551
+ // Same as expandX="content": grow from the content size, so a
6552
+ // flexWrap parent can wrap.
6553
+ return { flexGrow: 1 };
6554
+ }
6543
6555
  // Parent is flex-y: grow as flex item
6544
6556
  return { flexGrow: 1, flexBasis: "0%" };
6545
6557
  }
@@ -17435,8 +17447,12 @@ const canContainCallout = element => {
17435
17447
  if (VOID_ELEMENT_TAG_NAMES.has(element.tagName)) {
17436
17448
  return false;
17437
17449
  }
17438
- if (element.tagName === "BUTTON") {
17439
- // callout itself contains a button, browser would not let that happen
17450
+ if (element.tagName === "BUTTON" || element.getAttribute("role") === "button") {
17451
+ // Never mount the callout inside a button, native or role="button":
17452
+ // - a <button> cannot contain the callout's own close button
17453
+ // - presses inside the callout would bubble into the button's own press
17454
+ // handlers: a preventDefault there makes the callout text unselectable,
17455
+ // and a handler that opens the callout re-opens it (visible blink)
17440
17456
  return false;
17441
17457
  }
17442
17458
  return true;
@@ -35070,7 +35086,7 @@ installImportMetaCssBuild(import.meta);/**
35070
35086
  * - variant="discrete" + backgroundColor: the color applies at rest and hover,
35071
35087
  * and the field goes transparent while focused.
35072
35088
  */
35073
- const css$D = /* css */`
35089
+ const inputCss = /* css */`
35074
35090
  @layer navi {
35075
35091
  .navi_input {
35076
35092
  --border-radius: var(--navi-control-border-radius);
@@ -35401,7 +35417,7 @@ const useInputTextualProps = props => {
35401
35417
  });
35402
35418
  };
35403
35419
  const InputTextualUI = props => {
35404
- import.meta.css = [css$D, "@jsenv/navi/src/control/input/input_textual.jsx"];
35420
+ import.meta.css = [inputCss, "@jsenv/navi/src/control/input/input_textual.jsx"];
35405
35421
  // Spacing props travel to CSS as a raw custom property value, so the size
35406
35422
  // keywords have to become lengths here — "s" reaching CSS untouched makes the
35407
35423
  // declaration invalid, silently, and the gap just goes away.
@@ -35520,6 +35536,9 @@ const RealInput = ({
35520
35536
  "navi-max-length": maxLength
35521
35537
  });
35522
35538
  };
35539
+
35540
+ // Shared with textarea.jsx: a textarea is styled as a .navi_input box, so the
35541
+ // two read the same style props and pseudo states.
35523
35542
  const InputStyleCSSVars = {
35524
35543
  "slotSpacing": "--slot-spacing",
35525
35544
  "outlineWidth": "--outline-width",
@@ -36058,7 +36077,7 @@ installImportMetaCssBuild(import.meta);/**
36058
36077
  * This means an editable thing MUST have a parent with position relative that wraps the content and the eventual editable input
36059
36078
  *
36060
36079
  */
36061
- const css$C = /* css */`
36080
+ const css$D = /* css */`
36062
36081
  .navi_editable_wrapper {
36063
36082
  --inset-top: 0px;
36064
36083
  --inset-right: 0px;
@@ -36107,7 +36126,7 @@ const useEditionController = () => {
36107
36126
  };
36108
36127
  };
36109
36128
  const Editable = props => {
36110
- import.meta.css = [css$C, "@jsenv/navi/src/control/edition/editable.jsx"];
36129
+ import.meta.css = [css$D, "@jsenv/navi/src/control/edition/editable.jsx"];
36111
36130
  let {
36112
36131
  children,
36113
36132
  action,
@@ -36521,7 +36540,7 @@ HTMLFormElement.prototype.requestSubmit = function (submitter) {
36521
36540
  // form.dispatchEvent(customEvent);
36522
36541
  // };
36523
36542
 
36524
- installImportMetaCssBuild(import.meta);const css$B = /* css */`
36543
+ installImportMetaCssBuild(import.meta);const css$C = /* css */`
36525
36544
  .navi_group {
36526
36545
  --group-border-width: 1px;
36527
36546
 
@@ -36617,7 +36636,7 @@ const Group = ({
36617
36636
  vertical = row,
36618
36637
  ...props
36619
36638
  }) => {
36620
- import.meta.css = [css$B, "@jsenv/navi/src/control/group.jsx"];
36639
+ import.meta.css = [css$C, "@jsenv/navi/src/control/group.jsx"];
36621
36640
  return jsx(Box, {
36622
36641
  baseClassName: "navi_group",
36623
36642
  "data-vertical": vertical ? "" : undefined,
@@ -37027,7 +37046,7 @@ installImportMetaCssBuild(import.meta);/**
37027
37046
  * pair. One popup holding slides of its own contents has no such problem — and
37028
37047
  * it is the same component in the document, in a dialog or in a popover.
37029
37048
  */
37030
- const css$A = /* css */`
37049
+ const css$B = /* css */`
37031
37050
  /* Every slide in the same grid cell: the box then measures itself on the
37032
37051
  LARGEST of them, in both directions, without anything being measured by
37033
37052
  hand — which is also why nothing here resizes as the slides change. Each
@@ -37325,7 +37344,7 @@ const SlideContainer = ({
37325
37344
  children,
37326
37345
  ...rest
37327
37346
  }) => {
37328
- import.meta.css = [css$A, "@jsenv/navi/src/layout/slide_container.jsx"];
37347
+ import.meta.css = [css$B, "@jsenv/navi/src/layout/slide_container.jsx"];
37329
37348
  const debugFocus = useDebugFocus();
37330
37349
  const trackRef = useRef();
37331
37350
  // The box itself: it is what takes the keyboard when what is on screen holds
@@ -39925,7 +39944,7 @@ installImportMetaCssBuild(import.meta);/**
39925
39944
  * reaches the real container.
39926
39945
  */
39927
39946
  let openLocalDialogCount = 0;
39928
- const css$z = /* css */`
39947
+ const css$A = /* css */`
39929
39948
  @layer navi {
39930
39949
  .navi_dialog {
39931
39950
  /* Min gap between the dialog and the edges of its container. Written
@@ -40332,7 +40351,7 @@ const css$z = /* css */`
40332
40351
  * @param {import("ignore:preact").ComponentChildren} props.children
40333
40352
  */
40334
40353
  const Dialog = props => {
40335
- import.meta.css = [css$z, "@jsenv/navi/src/layout/dialog.jsx"];
40354
+ import.meta.css = [css$A, "@jsenv/navi/src/layout/dialog.jsx"];
40336
40355
  if (props.openController) {
40337
40356
  return jsx(ControlledDialog, {
40338
40357
  ...props
@@ -41150,7 +41169,7 @@ installImportMetaCssBuild(import.meta);/**
41150
41169
  * and applied.
41151
41170
  */
41152
41171
  let openLocalPopoverCount = 0;
41153
- const css$y = /* css */`
41172
+ const css$z = /* css */`
41154
41173
  @layer navi {
41155
41174
  .navi_popover {
41156
41175
  /* soft: user-configurable preferred max-height. Kept as a *default*
@@ -41517,7 +41536,7 @@ const css$y = /* css */`
41517
41536
  * @param {import("ignore:preact").ComponentChildren} props.children
41518
41537
  */
41519
41538
  const Popover = props => {
41520
- import.meta.css = [css$y, "@jsenv/navi/src/layout/popover.jsx"];
41539
+ import.meta.css = [css$z, "@jsenv/navi/src/layout/popover.jsx"];
41521
41540
  if (props.openController) {
41522
41541
  return jsx(ControlledPopover, {
41523
41542
  ...props
@@ -42481,7 +42500,7 @@ installImportMetaCssBuild(import.meta);/**
42481
42500
  * pass through untouched via `...rest` to whichever of Popover/Dialog
42482
42501
  * actually renders.
42483
42502
  */
42484
- const css$x = /* css */`
42503
+ const css$y = /* css */`
42485
42504
  @layer navi {
42486
42505
  .navi_popup {
42487
42506
  --popup-border-radius: var(--navi-popup-border-radius);
@@ -42575,7 +42594,7 @@ const css$x = /* css */`
42575
42594
  * @param {import("ignore:preact").ComponentChildren} props.children
42576
42595
  */
42577
42596
  const Popup = props => {
42578
- import.meta.css = [css$x, "@jsenv/navi/src/layout/popup.jsx"];
42597
+ import.meta.css = [css$y, "@jsenv/navi/src/layout/popup.jsx"];
42579
42598
  const {
42580
42599
  mode: modeProp,
42581
42600
  maxWidth,
@@ -42718,7 +42737,7 @@ const commitSubtree = (controller, e) => {
42718
42737
  }
42719
42738
  };
42720
42739
 
42721
- installImportMetaCssBuild(import.meta);const css$w = /* css */`
42740
+ installImportMetaCssBuild(import.meta);const css$x = /* css */`
42722
42741
  .navi_picker {
42723
42742
  /* Sizing ceilings (maxmax), background, box-shadow, outline, padding,
42724
42743
  overflow... are already handled correctly by Popup/Popover/Dialog
@@ -42830,7 +42849,7 @@ installImportMetaCssBuild(import.meta);const css$w = /* css */`
42830
42849
  }
42831
42850
  `;
42832
42851
  const PickerCustomResolver = props => {
42833
- import.meta.css = [css$w, "@jsenv/navi/src/control/picker/picker_custom.jsx"];
42852
+ import.meta.css = [css$x, "@jsenv/navi/src/control/picker/picker_custom.jsx"];
42834
42853
  if (props.children === undefined) {
42835
42854
  return jsx(PickerNative, {
42836
42855
  ...props
@@ -43475,7 +43494,7 @@ const LoadingIndicator = ({
43475
43494
  });
43476
43495
  };
43477
43496
 
43478
- installImportMetaCssBuild(import.meta);const css$v = /* css */`
43497
+ installImportMetaCssBuild(import.meta);const css$w = /* css */`
43479
43498
  @layer navi {
43480
43499
  .navi_separator {
43481
43500
  --size: 1px;
@@ -43553,7 +43572,7 @@ const Separator = ({
43553
43572
  style,
43554
43573
  ...props
43555
43574
  }) => {
43556
- import.meta.css = [css$v, "@jsenv/navi/src/layout/separator.jsx"];
43575
+ import.meta.css = [css$w, "@jsenv/navi/src/layout/separator.jsx"];
43557
43576
  return jsx(Box, {
43558
43577
  as: vertical ? "span" : "hr",
43559
43578
  ...props,
@@ -44010,7 +44029,7 @@ const ListItemFooter = props => {
44010
44029
  });
44011
44030
  };
44012
44031
 
44013
- installImportMetaCssBuild(import.meta);const css$u = /* css */`
44032
+ installImportMetaCssBuild(import.meta);const css$v = /* css */`
44014
44033
  @layer navi {
44015
44034
  .navi_list_container[navi-selectable] {
44016
44035
  /* Focus outline */
@@ -44214,7 +44233,7 @@ const ListSelectableResolver = props => {
44214
44233
  };
44215
44234
  const ListSelectable = props => {
44216
44235
  const Next = useNextResolver();
44217
- import.meta.css = [css$u, "@jsenv/navi/src/control/list/list_selectable.jsx"];
44236
+ import.meta.css = [css$v, "@jsenv/navi/src/control/list/list_selectable.jsx"];
44218
44237
  // we allow ourselves to auto-generate a name
44219
44238
  const defaultName = useId();
44220
44239
  props.name = props.name || `listbox_${defaultName}`;
@@ -44756,7 +44775,7 @@ const SeparatorContext = createContext(null);
44756
44775
  // Set by <List itemTransition>: each row then gets a view-transition-name of
44757
44776
  // its own, so a change wrapped in a view transition animates row by row.
44758
44777
  const ItemTransitionContext = createContext(false);
44759
- const css$t = /* css */`
44778
+ const css$u = /* css */`
44760
44779
  @layer navi {
44761
44780
  .navi_list_container {
44762
44781
  --list-outline-width: 1px;
@@ -45244,7 +45263,7 @@ const css$t = /* css */`
45244
45263
  }
45245
45264
  `;
45246
45265
  const ListUI = props => {
45247
- import.meta.css = [css$t, "@jsenv/navi/src/control/list/list.jsx"];
45266
+ import.meta.css = [css$u, "@jsenv/navi/src/control/list/list.jsx"];
45248
45267
  const {
45249
45268
  ref,
45250
45269
  renderBudget: renderBudgetProp = RENDER_BUDGET_DEFAULT,
@@ -46809,7 +46828,7 @@ const PickerPresetResolver = props => {
46809
46828
  });
46810
46829
  };
46811
46830
 
46812
- installImportMetaCssBuild(import.meta);const css$s = /* css */`
46831
+ installImportMetaCssBuild(import.meta);const css$t = /* css */`
46813
46832
  @layer navi {
46814
46833
  }
46815
46834
  .navi_badge {
@@ -46888,7 +46907,7 @@ const Badge = ({
46888
46907
  className,
46889
46908
  ...props
46890
46909
  }) => {
46891
- import.meta.css = [css$s, "@jsenv/navi/src/text/badge.jsx"];
46910
+ import.meta.css = [css$t, "@jsenv/navi/src/text/badge.jsx"];
46892
46911
  const defaultRef = useRef();
46893
46912
  props.ref = props.ref || defaultRef;
46894
46913
  const {
@@ -46935,7 +46954,7 @@ const BadgeButton = props => {
46935
46954
  };
46936
46955
  Badge.Button = BadgeButton;
46937
46956
 
46938
- installImportMetaCssBuild(import.meta);const css$r = /* css */`
46957
+ installImportMetaCssBuild(import.meta);const css$s = /* css */`
46939
46958
  @layer navi {
46940
46959
  }
46941
46960
  .navi_badge_list {
@@ -46960,7 +46979,7 @@ const BadgeList = ({
46960
46979
  max,
46961
46980
  ...props
46962
46981
  }) => {
46963
- import.meta.css = [css$r, "@jsenv/navi/src/text/badge_list.jsx"];
46982
+ import.meta.css = [css$s, "@jsenv/navi/src/text/badge_list.jsx"];
46964
46983
  const measureRef = useRef();
46965
46984
  const visibleRef = useRef();
46966
46985
  useLayoutEffect(() => {
@@ -47035,7 +47054,7 @@ const BadgeList = ({
47035
47054
  });
47036
47055
  };
47037
47056
 
47038
- installImportMetaCssBuild(import.meta);const css$q = /* css */`
47057
+ installImportMetaCssBuild(import.meta);const css$r = /* css */`
47039
47058
  .navi_color {
47040
47059
  display: block;
47041
47060
  aspect-ratio: 1/1;
@@ -47066,7 +47085,7 @@ const Color = ({
47066
47085
  children,
47067
47086
  ...rest
47068
47087
  }) => {
47069
- import.meta.css = [css$q, "@jsenv/navi/src/text/color.jsx"];
47088
+ import.meta.css = [css$r, "@jsenv/navi/src/text/color.jsx"];
47070
47089
  const color = children || undefined;
47071
47090
  return jsx(Box, {
47072
47091
  as: "span",
@@ -47521,7 +47540,7 @@ const PickerFileUI = () => {
47521
47540
  return String(value);
47522
47541
  };
47523
47542
 
47524
- installImportMetaCssBuild(import.meta);const css$p = /* css */`
47543
+ installImportMetaCssBuild(import.meta);const css$q = /* css */`
47525
47544
  @layer navi {
47526
47545
  .navi_picker {
47527
47546
  --picker-border-radius: var(--navi-control-border-radius);
@@ -47847,7 +47866,7 @@ installImportMetaCssBuild(import.meta);const css$p = /* css */`
47847
47866
  }
47848
47867
  `;
47849
47868
  const PickerButton = props => {
47850
- import.meta.css = [css$p, "@jsenv/navi/src/control/picker/picker.jsx"];
47869
+ import.meta.css = [css$q, "@jsenv/navi/src/control/picker/picker.jsx"];
47851
47870
  if (typeof props.maxLines === "string") {
47852
47871
  props.maxLines = parseInt(props.maxLines);
47853
47872
  }
@@ -48305,7 +48324,7 @@ installImportMetaCssBuild(import.meta);/**
48305
48324
  * is what keeps the focus where the travel happens instead of moving it into a
48306
48325
  * slide that is about to leave.
48307
48326
  */
48308
- const css$o = /* css */`
48327
+ const css$p = /* css */`
48309
48328
  .navi_picker_spin {
48310
48329
  /* What the loading outline is drawn around. */
48311
48330
  position: relative;
@@ -48544,7 +48563,7 @@ const DaySpin = ({
48544
48563
  nextLabel,
48545
48564
  ...rest
48546
48565
  }) => {
48547
- import.meta.css = [css$o, "@jsenv/navi/src/control/picker/picker_spin.jsx"];
48566
+ import.meta.css = [css$p, "@jsenv/navi/src/control/picker/picker_spin.jsx"];
48548
48567
  const id = useId();
48549
48568
  const containerId = `${id}_days`;
48550
48569
  const pickerId = `${id}_picker`;
@@ -48939,7 +48958,7 @@ const addDays = (day, count) => {
48939
48958
  };
48940
48959
 
48941
48960
  installImportMetaCssBuild(import.meta);// TOFIX: select in data then reset, it reset to red/blue instead of red/blue/green
48942
- const css$n = /* css */`
48961
+ const css$o = /* css */`
48943
48962
  .navi_checkbox_group {
48944
48963
  border-style: solid;
48945
48964
 
@@ -48959,7 +48978,7 @@ const CheckboxGroup = props => {
48959
48978
  return checkboxGroup;
48960
48979
  };
48961
48980
  const CheckboxGroupInterface = props => {
48962
- import.meta.css = [css$n, "@jsenv/navi/src/control/input/checkbox_group.jsx"];
48981
+ import.meta.css = [css$o, "@jsenv/navi/src/control/input/checkbox_group.jsx"];
48963
48982
  const {
48964
48983
  ref
48965
48984
  } = props;
@@ -48990,6 +49009,197 @@ const CheckboxGroupInterface = props => {
48990
49009
  });
48991
49010
  };
48992
49011
 
49012
+ installImportMetaCssBuild(import.meta);/**
49013
+ * Multiline text control that grows with what is typed.
49014
+ *
49015
+ * Autosize is native: `field-sizing: content` lets the browser size the
49016
+ * textarea from its value — no hidden mirror textarea to measure against (the
49017
+ * technique libraries used before the property existed). `minRows`/`maxRows`
49018
+ * become min/max heights in `lh` units on top of it; past `maxRows` the
49019
+ * content scrolls.
49020
+ *
49021
+ * TextareaCharCount is the counter that goes with it, and the caller places
49022
+ * it: under the box, in a form footer, next to a label — fed with the same
49023
+ * value/signal as the textarea. The textarea draws no counter of its own.
49024
+ *
49025
+ * Styled as a `.navi_input` box (border, background, focus ring, readonly and
49026
+ * disabled fades, variants): one look for everything one types into. The
49027
+ * shared sheet is registered here too — a page may render a Textarea without
49028
+ * any Input.
49029
+ */
49030
+ const css$n = /* css */`
49031
+ .navi_input.navi_textarea {
49032
+ .navi_control_input {
49033
+ min-height: calc(var(--textarea-min-rows, 1.5) * 1lh);
49034
+ /* Above maxRows the box stops growing and the content scrolls. The
49035
+ 99999 fallback means "no cap" without needing a conditional rule. */
49036
+ max-height: calc(var(--textarea-max-rows, 99999) * 1lh);
49037
+ field-sizing: content;
49038
+ /* Explicit, never normal: minRows/maxRows are lengths in lh, and with
49039
+ line-height normal the lh unit resolves to a theoretical value that
49040
+ does not match the real rendered line — the box then jumps by a few
49041
+ pixels the moment the first character replaces the theory with a real
49042
+ line. One number for both keeps every row count exact. */
49043
+ line-height: 1.5;
49044
+ /* The control grows itself; resizable below hands the handle back. */
49045
+ resize: none;
49046
+ overflow: auto;
49047
+ }
49048
+ &[data-resizable] .navi_control_input {
49049
+ height: calc(var(--textarea-min-rows, 1.5) * 1lh);
49050
+ /* The two are exclusive: with field-sizing content the browser removes
49051
+ the resize handle (the size follows the content, there is nothing to
49052
+ drag). resizable means the hand takes over — fixed sizing, starting
49053
+ at minRows, and the drag writes its own inline height from there. */
49054
+ field-sizing: fixed;
49055
+ resize: vertical;
49056
+ }
49057
+ }
49058
+ .navi_textarea_char_count {
49059
+ color: color-mix(in srgb, currentColor 60%, transparent);
49060
+ font-size: 0.75em;
49061
+ user-select: none;
49062
+ }
49063
+ `;
49064
+
49065
+ /**
49066
+ * @type {import("ignore:preact").FunctionComponent<{
49067
+ * value?: string,
49068
+ * defaultValue?: string,
49069
+ * signal?: import("@preact/signals").Signal<string>,
49070
+ * name?: string,
49071
+ * minRows?: number,
49072
+ * maxRows?: number,
49073
+ * resizable?: boolean,
49074
+ * maxLength?: number,
49075
+ * width?: string,
49076
+ * [key: string]: any,
49077
+ * }>}
49078
+ * @param {number} [minRows=1.5] Height the empty control starts at, in lines.
49079
+ * The default shows half of a second line: enough to read "multiline" at a
49080
+ * glance without the height of a full extra row.
49081
+ * @param {number} [maxRows] Lines after which the control stops growing and
49082
+ * scrolls instead. Without it the control grows with its content.
49083
+ * @param {boolean} [resizable] Give the browser's vertical resize handle back.
49084
+ * A manual resize takes over from the automatic growth.
49085
+ * @param {number} [maxLength] The character limit, validated at submit. Pair
49086
+ * with `maxLengthGuard` to block typing past it, and render a
49087
+ * TextareaCharCount to show it.
49088
+ * @param {string} [width="35ch"] The control's width. Fixed on purpose: with
49089
+ * field-sizing the width would otherwise follow the longest line, and a box
49090
+ * that widens while one types is a box one chases.
49091
+ */
49092
+ const Textarea = ({
49093
+ // Destructured, never deleted off the props object: Preact reuses the same
49094
+ // props object when an internal state update re-renders the component, so a
49095
+ // delete would make these props vanish from the second render on (the box
49096
+ // then jumps back to the default minRows at the first keystroke).
49097
+ minRows = 1.5,
49098
+ maxRows,
49099
+ resizable,
49100
+ width = "35ch",
49101
+ ...props
49102
+ }) => {
49103
+ import.meta.css = [inputCss + css$n, "@jsenv/navi/src/control/input/textarea.jsx"];
49104
+ const defaultRef = useRef(null);
49105
+ props.ref = props.ref || defaultRef;
49106
+ const [rootProps, hostProps, childrenWrapperProps] = useControlProps(props, {
49107
+ controlType: "input"
49108
+ });
49109
+ const {
49110
+ basePseudoState,
49111
+ children
49112
+ } = hostProps;
49113
+ // Children go through ControlChildrenWrapper below; inside the <textarea>
49114
+ // element they would become its text content.
49115
+ delete hostProps.children;
49116
+ const loading = basePseudoState[":-navi-loading"];
49117
+ delete rootProps.width;
49118
+ hostProps.width = width;
49119
+ return jsxs(Box, {
49120
+ as: "span",
49121
+ inline: true,
49122
+ flex: true,
49123
+ baseClassName: "navi_input",
49124
+ className: "navi_textarea",
49125
+ ...rootProps,
49126
+ basePseudoState: basePseudoState,
49127
+ "data-resizable": resizable ? "" : undefined,
49128
+ styleCSSVars: InputStyleCSSVars,
49129
+ pseudoStateSelector: ".navi_control_input",
49130
+ pseudoClasses: InputPseudoClasses,
49131
+ pseudoElements: InputPseudoElements,
49132
+ "data-callout-anchor": ".navi_control_input",
49133
+ style: {
49134
+ "--textarea-min-rows": minRows,
49135
+ "--textarea-max-rows": maxRows,
49136
+ ...rootProps.style
49137
+ },
49138
+ children: [jsx(LoadingOutline, {
49139
+ loading: loading,
49140
+ color: "var(--loader-color)",
49141
+ inset: -1
49142
+ }), jsx(RealTextarea, {
49143
+ ...hostProps
49144
+ }), jsx(ControlChildrenWrapper, {
49145
+ ...childrenWrapperProps,
49146
+ children: children
49147
+ })]
49148
+ });
49149
+ };
49150
+
49151
+ /**
49152
+ * The counter that goes with a Textarea: "50/200" — how many characters are
49153
+ * typed over how many the limit allows, the way Material writes it (just the
49154
+ * count when there is no `maxLength`). Where it goes is the caller's call,
49155
+ * which is why it is a separate component rather than something the textarea
49156
+ * draws: put it under the box, in a form footer, next to a label, and feed it
49157
+ * the same value or signal as the textarea.
49158
+ *
49159
+ * @type {import("ignore:preact").FunctionComponent<{
49160
+ * value?: string,
49161
+ * signal?: import("@preact/signals").Signal<string>,
49162
+ * maxLength?: number,
49163
+ * [key: string]: any,
49164
+ * }>}
49165
+ * @param {string} [value] The text being counted. Say `signal` instead for a
49166
+ * two-way bound textarea: reading it here subscribes the count to it.
49167
+ * @param {number} [maxLength] The limit, shown after the count ("50/200").
49168
+ * Without it the count stands alone.
49169
+ */
49170
+ const TextareaCharCount = ({
49171
+ value,
49172
+ signal,
49173
+ maxLength,
49174
+ ...rest
49175
+ }) => {
49176
+ import.meta.css = [css$n, "@jsenv/navi/src/control/input/textarea.jsx"];
49177
+ const resolvedValue = signal ? signal.value : value;
49178
+ const length = typeof resolvedValue === "string" ? resolvedValue.length : 0;
49179
+ return jsx(Box, {
49180
+ as: "span",
49181
+ baseClassName: "navi_textarea_char_count",
49182
+ ...rest,
49183
+ children: maxLength === undefined ? length : `${length}/${maxLength}`
49184
+ });
49185
+ };
49186
+ const RealTextarea = ({
49187
+ maxLength,
49188
+ ...domProps
49189
+ }) => {
49190
+ const autoSelectReadOnlyProps = useAutoSelectReadOnly(domProps);
49191
+ return jsx(Box, {
49192
+ ...domProps,
49193
+ as: "textarea",
49194
+ baseClassName: "navi_control_input",
49195
+ ...autoSelectReadOnlyProps,
49196
+ // Native maxLength stays off, like RealInput in input_textual.jsx: the
49197
+ // maxLengthGuard handles live blocking, the constraint validates at
49198
+ // submit, and navi-max-length keeps the value readable from the DOM.
49199
+ "navi-max-length": maxLength
49200
+ });
49201
+ };
49202
+
48993
49203
  const Unit = ({
48994
49204
  unit,
48995
49205
  plural,
@@ -58411,5 +58621,5 @@ const UserSvg = () => jsx("svg", {
58411
58621
  })
58412
58622
  });
58413
58623
 
58414
- export { ActionRenderer, ActiveKeyboardShortcuts, Address, Badge, BadgeCount, BadgeList, Box, Button, ButtonCopyToClipboard, Caption, CardLayout, CheckSvg, CheckboxGroup, CloseSvg, Code, Col, Colgroup, Color, ConstructionSvg, ControlGroup, DaySpin, Details, Dialog, Editable, ErrorBoundary, ErrorBoundaryContext, ExclamationSvg, EyeClosedSvg, EyeSvg, Field, Form, Group, Head, HeartSvg, HomeSvg, Icon, Image, Input, InputDuration, Interpolate, Label, Link, LinkAnchorSvg, LinkBlankTargetSvg, LinkCurrentSvg, List, ListItem, ListItemGroup, Loading, LoadingDotsSvg, LoadingIndicator, LoadingIndicatorFluid, LoadingOutline, MessageBox, Meter, Nav, NaviDebug, Paragraph, Picker, Popover, Popup, Quantity, RadioGroup, Route, RowNumberCol, RowNumberTableCell, SVGMaskOverlay, SearchSvg, SelectableInput, SelectionContext, Separator, SettingsSvg, SidePanel, Slide, SlideContainer, StarSvg, SummaryMarker, Svg, Table, TableCell, Tbody, Text, TextBox, Thead, Time, Title, Tr, UITransition, Unit, UserSvg, ViewportLayout, Wheel, WheelGroup, WheelItem, actionRunEffect, anyMatchingRouteSignal, applySearch, arraySignalMembership, coarsePointerSignal, compareTwoJsValues, createAction, createAvailableConstraint, createRequestCanceller, createSearch, createSelectionKeyboardShortcuts, createSlot, enableDebugActions, enableDebugOnDocumentLoading, ensureDocumentStartViewTransition, filterTableSelection, formatDatetime, formatDay, formatDayRelative, formatMonth, formatNumber, formatTime, formatTimeRelative, getNowHours, getNowHoursRoundedToStep, interpolateText, isCellSelected, isColumnSelected, isRowSelected, isToday, languagesSignal, localStorageSignal, moveArrayItemByIndex, navBack, navForward, navIntegratedVia, navTo, naviI18n, openCallout, rawUrlPart, registerGlobalConstraint, reload, rerunActions, resource, route, routeAction, setBaseUrl, setPreferredLanguage, setSupportedLanguages, setupRoutes, stateSignal, stopLoad, stringifyTableSelectionValue, swapArrayItemByIndex, syncOwnedResourceToSignals, syncResourceToSignals, updateActions, useActionStatus, useArraySignalMembership, useAsyncData, useCalloutRequestClose, useCancelPrevious, useCellGridFromRows, useConstraintValidityState, useDependenciesDiff, useDisplayedLayoutEffect, useDocumentResource, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useKeyboardShortcuts, useNavState, useOrderedColumns, usePopupMode, useRouteStatus, useRunOnMount, useSearchText, useSelectableElement, useSelectionController, useSignalSync, useSlideValue, useStateArray, useTitleLevel, useUrlSearchParam, valueInLocalStorage, windowWidthSignal };
58624
+ export { ActionRenderer, ActiveKeyboardShortcuts, Address, Badge, BadgeCount, BadgeList, Box, Button, ButtonCopyToClipboard, Caption, CardLayout, CheckSvg, CheckboxGroup, CloseSvg, Code, Col, Colgroup, Color, ConstructionSvg, ControlGroup, DaySpin, Details, Dialog, Editable, ErrorBoundary, ErrorBoundaryContext, ExclamationSvg, EyeClosedSvg, EyeSvg, Field, Form, Group, Head, HeartSvg, HomeSvg, Icon, Image, Input, InputDuration, Interpolate, Label, Link, LinkAnchorSvg, LinkBlankTargetSvg, LinkCurrentSvg, List, ListItem, ListItemGroup, Loading, LoadingDotsSvg, LoadingIndicator, LoadingIndicatorFluid, LoadingOutline, MessageBox, Meter, Nav, NaviDebug, Paragraph, Picker, Popover, Popup, Quantity, RadioGroup, Route, RowNumberCol, RowNumberTableCell, SVGMaskOverlay, SearchSvg, SelectableInput, SelectionContext, Separator, SettingsSvg, SidePanel, Slide, SlideContainer, StarSvg, SummaryMarker, Svg, Table, TableCell, Tbody, Text, TextBox, Textarea, TextareaCharCount, Thead, Time, Title, Tr, UITransition, Unit, UserSvg, ViewportLayout, Wheel, WheelGroup, WheelItem, actionRunEffect, anyMatchingRouteSignal, applySearch, arraySignalMembership, coarsePointerSignal, compareTwoJsValues, createAction, createAvailableConstraint, createRequestCanceller, createSearch, createSelectionKeyboardShortcuts, createSlot, enableDebugActions, enableDebugOnDocumentLoading, ensureDocumentStartViewTransition, filterTableSelection, formatDatetime, formatDay, formatDayRelative, formatMonth, formatNumber, formatTime, formatTimeRelative, getNowHours, getNowHoursRoundedToStep, interpolateText, isCellSelected, isColumnSelected, isRowSelected, isToday, languagesSignal, localStorageSignal, moveArrayItemByIndex, navBack, navForward, navIntegratedVia, navTo, naviI18n, openCallout, rawUrlPart, registerGlobalConstraint, reload, rerunActions, resource, route, routeAction, setBaseUrl, setPreferredLanguage, setSupportedLanguages, setupRoutes, stateSignal, stopLoad, stringifyTableSelectionValue, swapArrayItemByIndex, syncOwnedResourceToSignals, syncResourceToSignals, updateActions, useActionStatus, useArraySignalMembership, useAsyncData, useCalloutRequestClose, useCancelPrevious, useCellGridFromRows, useConstraintValidityState, useDependenciesDiff, useDisplayedLayoutEffect, useDocumentResource, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useKeyboardShortcuts, useNavState, useOrderedColumns, usePopupMode, useRouteStatus, useRunOnMount, useSearchText, useSelectableElement, useSelectionController, useSignalSync, useSlideValue, useStateArray, useTitleLevel, useUrlSearchParam, valueInLocalStorage, windowWidthSignal };
58415
58625
  //# sourceMappingURL=jsenv_navi.js.map