@jsenv/navi 0.16.56 → 0.16.58

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.
@@ -5,7 +5,7 @@ import { createIterableWeakSet, mergeOneStyle, stringifyStyle, createPubSub, mer
5
5
  import { prefixFirstAndIndentRemainingLines } from "@jsenv/humanize";
6
6
  import { effect, signal, computed, batch, useSignal } from "@preact/signals";
7
7
  import { createValidity } from "@jsenv/validity";
8
- import { createContext, render, isValidElement, toChildArray, createRef, cloneElement } from "preact";
8
+ import { createContext, toChildArray, render, isValidElement, createRef, cloneElement } from "preact";
9
9
  import { createPortal, forwardRef } from "preact/compat";
10
10
 
11
11
  const actionPrivatePropertiesWeakMap = new WeakMap();
@@ -6306,7 +6306,7 @@ const Box = props => {
6306
6306
  // then we'll track ":hover" state changes even for basic elements like <div>
6307
6307
  const pseudoClassesFromStyleSet = new Set();
6308
6308
  boxPseudoNamedStyles = {};
6309
- const assignStyle = (value, name, styleContext, boxStylesTarget, styleOrigin) => {
6309
+ const visitProp = (value, name, styleContext, boxStylesTarget, styleOrigin) => {
6310
6310
  const isPseudoElement = name.startsWith("::");
6311
6311
  const isPseudoClass = name.startsWith(":");
6312
6312
  if (isPseudoElement || isPseudoClass) {
@@ -6324,14 +6324,14 @@ const Box = props => {
6324
6324
  if (isPseudoElement) {
6325
6325
  const pseudoElementStyles = {};
6326
6326
  for (const key of pseudoStyleKeys) {
6327
- assignStyle(value[key], key, pseudoStyleContext, pseudoElementStyles, "pseudo_style");
6327
+ visitProp(value[key], key, pseudoStyleContext, pseudoElementStyles, "pseudo_style");
6328
6328
  }
6329
6329
  boxPseudoNamedStyles[name] = pseudoElementStyles;
6330
6330
  return;
6331
6331
  }
6332
6332
  const pseudoClassStyles = {};
6333
6333
  for (const key of pseudoStyleKeys) {
6334
- assignStyle(value[key], key, pseudoStyleContext, pseudoClassStyles, "pseudo_style");
6334
+ visitProp(value[key], key, pseudoStyleContext, pseudoClassStyles, "pseudo_style");
6335
6335
  boxPseudoNamedStyles[name] = pseudoClassStyles;
6336
6336
  }
6337
6337
  return;
@@ -6395,23 +6395,28 @@ const Box = props => {
6395
6395
  if (baseStyle) {
6396
6396
  for (const key of baseStyle) {
6397
6397
  const value = baseStyle[key];
6398
- assignStyle(value, key, styleContext, boxStyles, "baseStyle");
6398
+ visitProp(value, key, styleContext, boxStyles, "baseStyle");
6399
6399
  }
6400
6400
  }
6401
6401
  for (const propName of remainingPropKeySet) {
6402
6402
  const propValue = rest[propName];
6403
- assignStyle(propValue, propName, styleContext, boxStyles, "prop");
6403
+ const isDataAttribute = propName.startsWith("data-");
6404
+ if (isDataAttribute) {
6405
+ selfForwardedProps[propName] = propValue;
6406
+ continue;
6407
+ }
6408
+ visitProp(propValue, propName, styleContext, boxStyles, "prop");
6404
6409
  }
6405
6410
  if (typeof style === "string") {
6406
6411
  const styleObject = normalizeStyles(style, "css");
6407
6412
  for (const styleName of Object.keys(styleObject)) {
6408
6413
  const styleValue = styleObject[styleName];
6409
- assignStyle(styleValue, styleName, styleContext, boxStyles, "style");
6414
+ visitProp(styleValue, styleName, styleContext, boxStyles, "style");
6410
6415
  }
6411
6416
  } else if (style && typeof style === "object") {
6412
6417
  for (const styleName of Object.keys(style)) {
6413
6418
  const styleValue = style[styleName];
6414
- assignStyle(styleValue, styleName, styleContext, boxStyles, "style");
6419
+ visitProp(styleValue, styleName, styleContext, boxStyles, "style");
6415
6420
  }
6416
6421
  }
6417
6422
  const updateStyle = useCallback(state => {
@@ -6468,25 +6473,26 @@ const Box = props => {
6468
6473
  innerChildren = children;
6469
6474
  }
6470
6475
  if (separator) {
6471
- if (Array.isArray(innerChildren)) {
6472
- const childCount = innerChildren.length;
6473
- if (childCount > 1) {
6474
- const childrenWithSeparators = [];
6475
- let i = 0;
6476
- while (true) {
6477
- const child = innerChildren[i];
6478
- childrenWithSeparators.push(child);
6479
- i++;
6480
- if (i === childCount) {
6481
- break;
6482
- }
6483
- // Support function separators that receive separator index
6484
- const separatorElement = typeof separator === "function" ? separator(i - 1) // i-1 because i was incremented after pushing child
6485
- : separator;
6486
- childrenWithSeparators.push(separatorElement);
6476
+ // Flatten nested arrays (e.g., from .map()) to treat each element as individual child
6477
+ const flattenedChildren = toChildArray(innerChildren);
6478
+ if (flattenedChildren.length > 1) {
6479
+ const childrenWithSeparators = [];
6480
+ let i = 0;
6481
+ while (true) {
6482
+ const child = flattenedChildren[i];
6483
+ childrenWithSeparators.push(child);
6484
+ i++;
6485
+ if (i === flattenedChildren.length) {
6486
+ break;
6487
6487
  }
6488
- innerChildren = childrenWithSeparators;
6488
+ // Support function separators that receive separator index
6489
+ const separatorElement = typeof separator === "function" ? separator(i - 1) // i-1 because i was incremented after pushing child
6490
+ : separator;
6491
+ childrenWithSeparators.push(separatorElement);
6489
6492
  }
6493
+ innerChildren = childrenWithSeparators;
6494
+ } else {
6495
+ innerChildren = flattenedChildren;
6490
6496
  }
6491
6497
  }
6492
6498
  return jsx(TagName, {
@@ -16760,24 +16766,58 @@ const installCustomConstraintValidation = (
16760
16766
  if (keydownEvent.key !== "Enter") {
16761
16767
  return;
16762
16768
  }
16763
- if (element.hasAttribute("data-action")) {
16764
- if (wouldKeydownSubmitForm(keydownEvent)) {
16765
- keydownEvent.preventDefault();
16766
- }
16767
- dispatchActionRequestedCustomEvent(element, {
16768
- event: keydownEvent,
16769
- requester: element,
16770
- });
16769
+ const elementWithAction = closestElementWithAction(element);
16770
+ if (!elementWithAction) {
16771
16771
  return;
16772
16772
  }
16773
- const { form } = element;
16774
- if (!form) {
16775
- return;
16773
+
16774
+ const determineClosestFormSubmitTargetForEnterKeyEvent = () => {
16775
+ if (keydownEvent.defaultPrevented) {
16776
+ return null;
16777
+ }
16778
+ const keydownTarget = keydownEvent.target;
16779
+ const { form } = keydownTarget;
16780
+ if (!form) {
16781
+ return null;
16782
+ }
16783
+ if (keydownTarget.tagName === "BUTTON") {
16784
+ if (
16785
+ keydownTarget.type !== "submit" &&
16786
+ keydownTarget.type !== "image"
16787
+ ) {
16788
+ return null;
16789
+ }
16790
+ return keydownTarget;
16791
+ }
16792
+ if (keydownTarget.tagName === "INPUT") {
16793
+ if (
16794
+ ![
16795
+ "text",
16796
+ "email",
16797
+ "password",
16798
+ "search",
16799
+ "number",
16800
+ "url",
16801
+ "tel",
16802
+ ].includes(keydownTarget.type)
16803
+ ) {
16804
+ return null;
16805
+ }
16806
+ // when present, we use first button submitting the form as the requester
16807
+ // not the input, it aligns with browser behavior where
16808
+ // hitting Enter in a text input triggers the first submit button of the form, not the input itself
16809
+ return getFirstButtonSubmittingForm(keydownTarget) || keydownTarget;
16810
+ }
16811
+ return null;
16812
+ };
16813
+ const formSubmitTarget =
16814
+ determineClosestFormSubmitTargetForEnterKeyEvent();
16815
+ if (formSubmitTarget) {
16816
+ keydownEvent.preventDefault();
16776
16817
  }
16777
- keydownEvent.preventDefault();
16778
- dispatchActionRequestedCustomEvent(form, {
16818
+ dispatchActionRequestedCustomEvent(elementWithAction, {
16779
16819
  event: keydownEvent,
16780
- requester: getFirstButtonSubmittingForm(form) || element,
16820
+ requester: formSubmitTarget || element,
16781
16821
  });
16782
16822
  };
16783
16823
  element.addEventListener("keydown", onkeydown);
@@ -16794,29 +16834,44 @@ const installCustomConstraintValidation = (
16794
16834
  if (element.tagName !== "BUTTON") {
16795
16835
  return;
16796
16836
  }
16797
- if (element.hasAttribute("data-action")) {
16798
- if (wouldButtonClickSubmitForm(element, clickEvent)) {
16799
- clickEvent.preventDefault();
16800
- }
16801
- dispatchActionRequestedCustomEvent(element, {
16802
- event: clickEvent,
16803
- requester: element,
16804
- });
16805
- return;
16806
- }
16807
- const { form } = element;
16808
- if (!form) {
16809
- return;
16810
- }
16811
- if (element.type === "reset") {
16837
+ const button = element;
16838
+ const elementWithAction = closestElementWithAction(button);
16839
+ if (!elementWithAction) {
16812
16840
  return;
16813
16841
  }
16814
- if (wouldButtonClickSubmitForm(element, clickEvent)) {
16842
+ const determineClosestFormSubmitTargetForClickEvent = () => {
16843
+ if (clickEvent.defaultPrevented) {
16844
+ return null;
16845
+ }
16846
+ const clickTarget = clickEvent.target;
16847
+ const { form } = clickTarget;
16848
+ if (!form) {
16849
+ return null;
16850
+ }
16851
+ const wouldSubmitFormByType =
16852
+ button.type === "submit" || button.type === "image";
16853
+ if (wouldSubmitFormByType) {
16854
+ return button;
16855
+ }
16856
+ if (button.type) {
16857
+ // "reset", "button" or any other non submit type, it won't submit the form
16858
+ return null;
16859
+ }
16860
+ const firstButtonSubmittingForm = getFirstButtonSubmittingForm(form);
16861
+ if (button !== firstButtonSubmittingForm) {
16862
+ // an other button is explicitly submitting the form, this one would not submit it
16863
+ return null;
16864
+ }
16865
+ // this is the only button inside the form without type attribute, so it defaults to type="submit"
16866
+ return button;
16867
+ };
16868
+ const formSubmitTarget = determineClosestFormSubmitTargetForClickEvent();
16869
+ if (formSubmitTarget) {
16815
16870
  clickEvent.preventDefault();
16816
16871
  }
16817
- dispatchActionRequestedCustomEvent(form, {
16872
+ dispatchActionRequestedCustomEvent(elementWithAction, {
16818
16873
  event: clickEvent,
16819
- requester: element,
16874
+ requester: formSubmitTarget || button,
16820
16875
  });
16821
16876
  };
16822
16877
  element.addEventListener("click", onclick);
@@ -16832,13 +16887,14 @@ const installCustomConstraintValidation = (
16832
16887
  break request_on_input_change;
16833
16888
  }
16834
16889
  const stop = listenInputChange(element, (e) => {
16835
- if (element.hasAttribute("data-action")) {
16836
- dispatchActionRequestedCustomEvent(element, {
16837
- event: e,
16838
- requester: element,
16839
- });
16890
+ const elementWithAction = closestElementWithAction(element);
16891
+ if (!elementWithAction) {
16840
16892
  return;
16841
16893
  }
16894
+ dispatchActionRequestedCustomEvent(elementWithAction, {
16895
+ event: e,
16896
+ requester: element,
16897
+ });
16842
16898
  });
16843
16899
  addTeardown(() => {
16844
16900
  stop();
@@ -16936,6 +16992,31 @@ const installCustomConstraintValidation = (
16936
16992
  return validationInterface;
16937
16993
  };
16938
16994
 
16995
+ // When interacting with an element we want to find the closest element
16996
+ // eventually handling the action
16997
+ // 1. <button> itself has an action
16998
+ // 2. <button> is inside a <form> with an action
16999
+ // 3. <button> is inside a wrapper <div> with an action (data-action is not necessarly on the interactive element itself, it can be on a wrapper, we want to support that)
17000
+ // 4. <button> is inside a <fieldset> or any element that catches the action like a <form> would
17001
+ // In examples above <button> can also be <input> etc..
17002
+ const closestElementWithAction = (el) => {
17003
+ if (el.hasAttribute("data-action")) {
17004
+ return el;
17005
+ }
17006
+ const closestDataActionElement = el.closest("[data-action]");
17007
+ if (!closestDataActionElement) {
17008
+ return null;
17009
+ }
17010
+ const visualSelector = closestDataActionElement.getAttribute(
17011
+ "data-visual-selector",
17012
+ );
17013
+ if (!visualSelector) {
17014
+ return closestDataActionElement;
17015
+ }
17016
+ const visualElement = closestDataActionElement.querySelector(visualSelector);
17017
+ return visualElement;
17018
+ };
17019
+
16939
17020
  const pickConstraint = (a, b) => {
16940
17021
  const aPrio = getConstraintPriority(a);
16941
17022
  const bPrio = getConstraintPriority(b);
@@ -16954,60 +17035,14 @@ const getConstraintPriority = (constraint) => {
16954
17035
  return 1;
16955
17036
  };
16956
17037
 
16957
- const wouldButtonClickSubmitForm = (button, clickEvent) => {
16958
- if (clickEvent.defaultPrevented) {
16959
- return false;
16960
- }
16961
- const { form } = button;
16962
- if (!form) {
16963
- return false;
16964
- }
16965
- if (!button) {
16966
- return false;
16967
- }
16968
- const wouldSubmitFormByType =
16969
- button.type === "submit" || button.type === "image";
16970
- if (wouldSubmitFormByType) {
16971
- return true;
16972
- }
16973
- if (button.type) {
16974
- return false;
16975
- }
16976
- if (getFirstButtonSubmittingForm(form)) {
16977
- // an other button is explicitly submitting the form, this one would not submit it
16978
- return false;
16979
- }
16980
- // this is the only button inside the form without type attribute, so it defaults to type="submit"
16981
- return true;
16982
- };
16983
17038
  const getFirstButtonSubmittingForm = (form) => {
16984
17039
  return form.querySelector(
16985
17040
  `button[type="submit"], input[type="submit"], input[type="image"]`,
16986
17041
  );
16987
17042
  };
16988
17043
 
16989
- const wouldKeydownSubmitForm = (keydownEvent) => {
16990
- if (keydownEvent.defaultPrevented) {
16991
- return false;
16992
- }
16993
- const keydownTarget = keydownEvent.target;
16994
- const { form } = keydownTarget;
16995
- if (!form) {
16996
- return false;
16997
- }
16998
- if (keydownEvent.key !== "Enter") {
16999
- return false;
17000
- }
17001
- const isTextInput =
17002
- keydownTarget.tagName === "INPUT" || keydownTarget.tagName === "TEXTAREA";
17003
- if (!isTextInput) {
17004
- return false;
17005
- }
17006
- return true;
17007
- };
17008
-
17009
17044
  const dispatchActionRequestedCustomEvent = (
17010
- fieldOrForm,
17045
+ elementWithAction,
17011
17046
  { actionOrigin = "action_prop", event, requester },
17012
17047
  ) => {
17013
17048
  const actionRequestedCustomEvent = new CustomEvent("actionrequested", {
@@ -17018,7 +17053,7 @@ const dispatchActionRequestedCustomEvent = (
17018
17053
  requester,
17019
17054
  },
17020
17055
  });
17021
- fieldOrForm.dispatchEvent(actionRequestedCustomEvent);
17056
+ elementWithAction.dispatchEvent(actionRequestedCustomEvent);
17022
17057
  };
17023
17058
  // https://developer.mozilla.org/en-US/docs/Web/HTML/Guides/Constraint_validation
17024
17059
  const requestSubmit = HTMLFormElement.prototype.requestSubmit;
@@ -17759,7 +17794,6 @@ installImportMetaCss(import.meta);import.meta.css = /* css */`
17759
17794
  const Icon = ({
17760
17795
  href,
17761
17796
  children,
17762
- className,
17763
17797
  charWidth = 1,
17764
17798
  // 0 (zéro) is the real char width
17765
17799
  // but 2 zéros gives too big icons
@@ -17786,7 +17820,7 @@ const Icon = ({
17786
17820
  const hasExplicitWidth = width !== undefined;
17787
17821
  const hasExplicitHeight = height !== undefined;
17788
17822
  if (!hasExplicitWidth && !hasExplicitHeight) {
17789
- if (decorative === undefined) {
17823
+ if (decorative === undefined && !onClick) {
17790
17824
  decorative = true;
17791
17825
  }
17792
17826
  } else {
@@ -17821,7 +17855,7 @@ const Icon = ({
17821
17855
  return jsxs(Text, {
17822
17856
  ...props,
17823
17857
  ...ariaProps,
17824
- className: withPropsClassName("navi_icon", className),
17858
+ className: withPropsClassName("navi_icon", props.className),
17825
17859
  spacing: "pre",
17826
17860
  "data-icon-char": "",
17827
17861
  "data-has-width": hasExplicitWidth ? "" : undefined,
@@ -22450,6 +22484,15 @@ const InputRangeWithAction = props => {
22450
22484
  });
22451
22485
  };
22452
22486
 
22487
+ const SearchSvg = () => jsx("svg", {
22488
+ viewBox: "0 0 24 24",
22489
+ xmlns: "http://www.w3.org/2000/svg",
22490
+ children: jsx("path", {
22491
+ d: "M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z",
22492
+ fill: "currentColor"
22493
+ })
22494
+ });
22495
+
22453
22496
  installImportMetaCss(import.meta);import.meta.css = /* css */`
22454
22497
  @layer navi {
22455
22498
  .navi_input {
@@ -22513,29 +22556,73 @@ installImportMetaCss(import.meta);import.meta.css = /* css */`
22513
22556
  --x-background-color: var(--background-color);
22514
22557
  --x-color: var(--color);
22515
22558
  --x-placeholder-color: var(--placeholder-color);
22516
- }
22517
22559
 
22518
- .navi_input .navi_native_input {
22519
- box-sizing: border-box;
22520
- padding-top: var(--padding-top, var(--padding-y, var(--padding, 1px)));
22521
- padding-right: var(--padding-right, var(--padding-x, var(--padding, 2px)));
22522
- padding-bottom: var(
22523
- --padding-bottom,
22524
- var(--padding-y, var(--padding, 1px))
22525
- );
22526
- padding-left: var(--padding-left, var(--padding-x, var(--padding, 2px)));
22527
- color: var(--x-color);
22528
- background-color: var(--x-background-color);
22529
- border-width: var(--x-outer-width);
22530
- border-width: var(--x-outer-width);
22531
- border-style: solid;
22532
- border-color: transparent;
22533
- border-radius: var(--x-border-radius);
22534
- outline-width: var(--x-border-width);
22535
- outline-style: solid;
22536
- outline-color: var(--x-border-color);
22537
- outline-offset: calc(-1 * (var(--x-border-width)));
22560
+ .navi_native_input {
22561
+ box-sizing: border-box;
22562
+ padding-top: var(--padding-top, var(--padding-y, var(--padding, 1px)));
22563
+ padding-right: var(
22564
+ --padding-right,
22565
+ var(--padding-x, var(--padding, 2px))
22566
+ );
22567
+ padding-bottom: var(
22568
+ --padding-bottom,
22569
+ var(--padding-y, var(--padding, 1px))
22570
+ );
22571
+ padding-left: var(--padding-left, var(--padding-x, var(--padding, 2px)));
22572
+ color: var(--x-color);
22573
+ background-color: var(--x-background-color);
22574
+ border-width: var(--x-outer-width);
22575
+ border-width: var(--x-outer-width);
22576
+ border-style: solid;
22577
+ border-color: transparent;
22578
+ border-radius: var(--x-border-radius);
22579
+ outline-width: var(--x-border-width);
22580
+ outline-style: solid;
22581
+ outline-color: var(--x-border-color);
22582
+ outline-offset: calc(-1 * (var(--x-border-width)));
22583
+
22584
+ &[type="search"] {
22585
+ -webkit-appearance: textfield;
22586
+
22587
+ &::-webkit-search-cancel-button {
22588
+ display: none;
22589
+ }
22590
+ }
22591
+ }
22592
+
22593
+ .navi_start_icon_label {
22594
+ position: absolute;
22595
+ top: 0;
22596
+ bottom: 0;
22597
+ left: 0.25em;
22598
+ }
22599
+ .navi_end_icon_label {
22600
+ position: absolute;
22601
+ top: 0;
22602
+ right: 0.25em;
22603
+ bottom: 0;
22604
+ opacity: 0;
22605
+ pointer-events: none;
22606
+ }
22607
+ &[data-has-value] {
22608
+ .navi_end_icon_label {
22609
+ opacity: 1;
22610
+ pointer-events: auto;
22611
+ }
22612
+ }
22613
+
22614
+ &[data-start-icon] {
22615
+ .navi_native_input {
22616
+ padding-left: 20px;
22617
+ }
22618
+ }
22619
+ &[data-end-icon] {
22620
+ .navi_native_input {
22621
+ padding-right: 20px;
22622
+ }
22623
+ }
22538
22624
  }
22625
+
22539
22626
  .navi_input .navi_native_input::placeholder {
22540
22627
  color: var(--x-placeholder-color);
22541
22628
  }
@@ -22614,7 +22701,52 @@ const InputStyleCSSVars = {
22614
22701
  color: "--color-disabled"
22615
22702
  }
22616
22703
  };
22617
- const InputPseudoClasses = [":hover", ":active", ":focus", ":focus-visible", ":read-only", ":disabled", ":-navi-loading"];
22704
+ const InputPseudoClasses = [":hover", ":active", ":focus", ":focus-visible", ":read-only", ":disabled", ":-navi-loading", ":navi-has-value"];
22705
+ Object.assign(PSEUDO_CLASSES, {
22706
+ ":navi-has-value": {
22707
+ attribute: "data-has-value",
22708
+ setup: (el, callback) => {
22709
+ const onValueChange = () => {
22710
+ callback();
22711
+ };
22712
+
22713
+ // Standard user input (typing)
22714
+ el.addEventListener("input", onValueChange);
22715
+ // Autocomplete, programmatic changes, form restoration
22716
+ el.addEventListener("change", onValueChange);
22717
+ // Form reset - need to check the form
22718
+ const form = el.form;
22719
+ const onFormReset = () => {
22720
+ // Form reset happens asynchronously, check value after reset completes
22721
+ setTimeout(onValueChange, 0);
22722
+ };
22723
+ if (form) {
22724
+ form.addEventListener("reset", onFormReset);
22725
+ }
22726
+
22727
+ // Paste events (some browsers need special handling)
22728
+ el.addEventListener("paste", onValueChange);
22729
+ // Focus events to catch programmatic changes that don't fire other events
22730
+ // (like when value is set before user interaction)
22731
+ el.addEventListener("focus", onValueChange);
22732
+ return () => {
22733
+ el.removeEventListener("input", onValueChange);
22734
+ el.removeEventListener("change", onValueChange);
22735
+ el.removeEventListener("paste", onValueChange);
22736
+ el.removeEventListener("focus", onValueChange);
22737
+ if (form) {
22738
+ form.removeEventListener("reset", onFormReset);
22739
+ }
22740
+ };
22741
+ },
22742
+ test: el => {
22743
+ if (el.value === "") {
22744
+ return false;
22745
+ }
22746
+ return true;
22747
+ }
22748
+ }
22749
+ });
22618
22750
  const InputPseudoElements = ["::-navi-loader"];
22619
22751
  const InputTextualBasic = props => {
22620
22752
  const contextReadOnly = useContext(ReadOnlyContext);
@@ -22633,6 +22765,8 @@ const InputTextualBasic = props => {
22633
22765
  autoFocus,
22634
22766
  autoFocusVisible,
22635
22767
  autoSelect,
22768
+ icon,
22769
+ cancelButton = type === "search",
22636
22770
  ...rest
22637
22771
  } = props;
22638
22772
  const defaultRef = useRef();
@@ -22649,10 +22783,13 @@ const InputTextualBasic = props => {
22649
22783
  });
22650
22784
  const remainingProps = useConstraints(ref, rest);
22651
22785
  const innerOnInput = useStableCallback(onInput);
22786
+ const autoId = useId();
22787
+ const innerId = rest.id || autoId;
22652
22788
  const renderInput = inputProps => {
22653
22789
  return jsx(Box, {
22654
22790
  ...inputProps,
22655
22791
  as: "input",
22792
+ id: innerId,
22656
22793
  ref: ref,
22657
22794
  type: type,
22658
22795
  "data-value": uiState,
@@ -22683,7 +22820,15 @@ const InputTextualBasic = props => {
22683
22820
  baseClassName: "navi_native_input"
22684
22821
  });
22685
22822
  };
22686
- const renderInputMemoized = useCallback(renderInput, [type, uiState, innerValue, innerOnInput]);
22823
+ const renderInputMemoized = useCallback(renderInput, [type, uiState, innerValue, innerOnInput, innerId]);
22824
+ let innerIcon;
22825
+ if (icon === undefined) {
22826
+ if (type === "search") {
22827
+ innerIcon = jsx(SearchSvg, {});
22828
+ }
22829
+ } else {
22830
+ innerIcon = icon;
22831
+ }
22687
22832
  return jsxs(Box, {
22688
22833
  as: "span",
22689
22834
  box: true,
@@ -22699,13 +22844,37 @@ const InputTextualBasic = props => {
22699
22844
  pseudoClasses: InputPseudoClasses,
22700
22845
  pseudoElements: InputPseudoElements,
22701
22846
  hasChildFunction: true,
22847
+ "data-start-icon": innerIcon ? "" : undefined,
22848
+ "data-end-icon": cancelButton ? "" : undefined,
22702
22849
  ...remainingProps,
22703
22850
  ref: undefined,
22704
22851
  children: [jsx(LoaderBackground, {
22705
22852
  loading: innerLoading,
22706
22853
  color: "var(--loader-color)",
22707
22854
  inset: -1
22708
- }), renderInputMemoized]
22855
+ }), innerIcon && jsx(Icon, {
22856
+ as: "label",
22857
+ htmlFor: innerId,
22858
+ className: "navi_start_icon_label",
22859
+ alignY: "center",
22860
+ color: "rgba(28, 43, 52, 0.5)",
22861
+ children: innerIcon
22862
+ }), renderInputMemoized, cancelButton && jsx(Icon, {
22863
+ as: "label",
22864
+ htmlFor: innerId,
22865
+ className: "navi_end_icon_label",
22866
+ alignY: "center",
22867
+ color: "rgba(28, 43, 52, 0.5)",
22868
+ onMousedown: e => {
22869
+ e.preventDefault(); // keep focus on the button
22870
+ },
22871
+ onClick: () => {
22872
+ uiStateController.setUIState("", {
22873
+ trigger: "cancel_button"
22874
+ });
22875
+ },
22876
+ children: jsx(CloseSvg, {})
22877
+ })]
22709
22878
  });
22710
22879
  };
22711
22880
  const InputTextualWithAction = props => {
@@ -28495,15 +28664,6 @@ const HomeSvg = () => jsx("svg", {
28495
28664
  })
28496
28665
  });
28497
28666
 
28498
- const SearchSvg = () => jsx("svg", {
28499
- viewBox: "0 0 24 24",
28500
- xmlns: "http://www.w3.org/2000/svg",
28501
- children: jsx("path", {
28502
- d: "M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z",
28503
- fill: "currentColor"
28504
- })
28505
- });
28506
-
28507
28667
  const SettingsSvg = () => jsx("svg", {
28508
28668
  viewBox: "0 0 24 24",
28509
28669
  xmlns: "http://www.w3.org/2000/svg",