@acusti/dropdown 1.1.0 → 1.2.0

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/README.md CHANGED
@@ -325,7 +325,8 @@ type Props = {
325
325
  * multi-select. Defaults to !hasItems: a menu closes on submit, while a
326
326
  * hasItems={false} dialog stays open. In a hasItems={false} dropdown, a
327
327
  * submit is a form in the body being submitted, or Enter in a
328
- * single-line input there that no form owns.
328
+ * single-line input that appears in the body with no form or in the
329
+ * trigger.
329
330
  */
330
331
  keepOpenOnSubmit?: boolean;
331
332
  /**
@@ -726,12 +727,17 @@ while it’s open, as when it opened on hover. Escape closes the dropdown
726
727
  unless focus is in a text field (a text input, a textarea, or an editable
727
728
  element). On the trigger, Space and Enter open the dropdown and, unless
728
729
  focus is in a text field or a checkbox there, close it again, as a click
729
- there does.
730
+ there does. Once it is open, Enter in a textarea or an editable element
731
+ used as the trigger adds a line break there. Enter in a single-line input
732
+ used as the trigger is a submit, whether or not a form owns it: the
733
+ dropdown takes the key, so a form around the trigger isn’t submitted from
734
+ that input while the dropdown is open.
730
735
 
731
736
  A `hasItems={false}` dropdown stays open until it is dismissed. Pass
732
737
  `keepOpenOnSubmit={false}` to close it when its content is submitted. A
733
738
  submit is a form in the body being submitted, by Enter in one of its inputs
734
- or by its submit button, or Enter in a single-line input that no form owns:
739
+ or by its submit button, or Enter in a single-line input, either one in the
740
+ body that no form owns or one used as the trigger:
735
741
 
736
742
  ```tsx
737
743
  <Dropdown hasItems={false} keepOpenOnSubmit={false}>
@@ -753,9 +759,9 @@ empty. To keep it open after a submit that your own checks reject, call
753
759
  check that finishes later, such as a server round trip or a form library
754
760
  that validates asynchronously, can’t keep it open.
755
761
 
756
- Enter in an input that no form owns counts the way a form would count it:
757
- not when a handler has prevented the key, as a combobox does to pick an
758
- option, and not when it commits an IME composition.
762
+ Enter in such an input, in the body or the trigger, counts the way a form
763
+ would count it: not when a handler has prevented the key, as a combobox
764
+ does to pick an option, and not when it commits an IME composition.
759
765
 
760
766
  When a submit closes the dropdown while focus is in its body, focus returns
761
767
  to the trigger, as it does on Escape.
@@ -1330,6 +1336,13 @@ highlight into it.
1330
1336
  input, or one inside a custom trigger), where they move the caret instead
1331
1337
  - **Type characters**: Jump to the best-matching item in the current level
1332
1338
 
1339
+ While text is being composed with an IME, in a searchable dropdown’s input
1340
+ or a text field in a custom trigger, the dropdown ignores every keydown
1341
+ that the browser reports as part of the composition. With a Japanese IME,
1342
+ the Enter that commits the text doesn’t submit (the next Enter is an
1343
+ ordinary one), and the arrow keys and Escape don’t move the highlight or
1344
+ close the dropdown. Other IMEs can report their keys differently.
1345
+
1333
1346
  For accessibility, the component focuses on semantic HTML structure and
1334
1347
  keyboard navigation. It works best when you use appropriate HTML elements
1335
1348
  in your dropdown content (like `<ul>` and `<li>` for lists, `<button>`
@@ -101,7 +101,8 @@ export type Props = {
101
101
  * multi-select. Defaults to !hasItems: a menu closes on submit, while a
102
102
  * hasItems={false} dialog stays open. In a hasItems={false} dropdown, a
103
103
  * submit is a form in the body being submitted, or Enter in a
104
- * single-line input there that no form owns.
104
+ * single-line input that appears in the body with no form or in the
105
+ * trigger.
105
106
  */
106
107
  keepOpenOnSubmit?: boolean;
107
108
  /**
package/dist/Dropdown.js CHANGED
@@ -4,14 +4,12 @@ import clsx from "clsx";
4
4
  import { Children, Fragment, cloneElement, createContext, isValidElement, useContext, useEffect, useEffectEvent, useId, useLayoutEffect, useReducer, useRef, useState } from "react";
5
5
  import { getBestMatch } from "@acusti/matchmaking";
6
6
  import { jsx, jsxs } from "react/jsx-runtime";
7
- //#region src/context.ts
7
+ // src/context.ts
8
8
  var DropdownContext = createContext(null);
9
9
  var MenubarContext = createContext(null);
10
- //#endregion
11
- //#region src/Dropdown.css?inline
10
+ // src/Dropdown.css?inline
12
11
  var Dropdown_default = ":root{--uktdd-font-family:system-ui, Segoe UI, Roboto, Oxygen-Sans, Ubuntu, Cantarell, Helvetica Neue, Helvetica, Arial, sans-serif;--uktdd-color-scheme:inherit;--uktdd-body-bg-color:Canvas;--uktdd-body-bg-color-hover:Highlight;--uktdd-body-color-hover:HighlightText;--uktdd-body-bg-color-selected:color-mix(in oklab, currentColor 6%, transparent);--uktdd-body-box-shadow:0 8px 18px #00000040;--uktdd-body-border-color:color-mix(in oklab, currentColor 15%, transparent);--uktdd-body-buffer:10px;--uktdd-body-max-height:calc(100dvh - var(--uktdd-body-buffer));--uktdd-body-max-width:calc(100dvw - var(--uktdd-body-buffer));--uktdd-body-min-height:30px;--uktdd-body-pad-block-start:.5em;--uktdd-body-pad-block-end:.5em;--uktdd-body-pad-inline-start:.6875em;--uktdd-body-pad-inline-end:.6875em;--uktdd-body-min-width:min(50px, 100%);--uktdd-body-position-area:block-end span-inline-end;--uktdd-body-position-try-fallback-1:--uktdd-top-start;--uktdd-body-position-try-fallback-2:--uktdd-bottom-end;--uktdd-body-fill-fallbacks:--uktdd-fill-bottom, --uktdd-fill-top;--uktdd-body-gap:0px;--uktdd-body-color:CanvasText;--uktdd-body-color-path:currentColor;--uktdd-label-gap:.625em;--uktdd-submenu-position-area:inline-end span-block-end;--uktdd-submenu-position-try-fallback:--uktdd-submenu-inline-start;--uktdd-submenu-fill-fallbacks:--uktdd-submenu-fill, --uktdd-submenu-fill flip-inline;--uktdd-submenu-gap:0px;--uktdd-menubar-trigger-bg-color-active:color-mix(in oklab, currentColor 12%, transparent)}.uktdropdown,.uktdropdown-trigger{font-family:var(--uktdd-font-family)}.uktdropdown{anchor-scope:--uktdd-anchor;width:max-content}.uktdropdown.is-disabled{pointer-events:none}.uktdropdown>*{cursor:default}.uktdropdown>:first-child{anchor-name:--uktdd-anchor}.uktdropdown-label{align-items:center;gap:var(--uktdd-label-gap);display:flex}.uktdropdown-body{box-sizing:border-box;position-anchor:--uktdd-anchor;position-area:var(--uktdd-body-position-area);position-try-fallbacks:var(--uktdd-body-position-try-fallback-1), var(--uktdd-body-position-try-fallback-2), var(--uktdd-body-fill-fallbacks);min-block-size:min(var(--uktdd-body-min-height), var(--uktdd-body-max-height));max-block-size:var(--uktdd-body-max-height);min-inline-size:min(var(--uktdd-body-min-width), var(--uktdd-body-max-width));max-inline-size:var(--uktdd-body-max-width);inline-size:max-content;color:var(--uktdd-body-color);color-scheme:var(--uktdd-color-scheme);background-color:var(--uktdd-body-bg-color);box-shadow:var(--uktdd-body-box-shadow);flex-direction:column;display:flex;position:fixed;overflow:hidden}.uktdropdown-body.has-items{-webkit-user-select:none;user-select:none}.uktdropdown-body [aria-selected=true]{background-color:var(--uktdd-body-bg-color-selected)}@media (forced-colors:active){.uktdropdown-body [aria-selected=true]{outline-offset:-2px;outline:2px solid}}.uktdropdown-body [data-ukt-active]{background-color:var(--uktdd-body-bg-color-hover);color:var(--uktdd-body-color-hover)}:is(.uktdropdown-body [data-ukt-active]:has([data-ukt-submenu] [data-ukt-active]),.uktdropdown-body [data-ukt-active]:has([data-ukt-submenu]:hover)){background-color:var(--uktdd-body-bg-color-path,color-mix(in oklab, var(--uktdd-body-color) 12%, var(--uktdd-body-bg-color)));color:var(--uktdd-body-color-path)}.uktdropdown-body [data-ukt-item][aria-disabled=true]{opacity:.5;pointer-events:none}.uktdropdown-body [data-ukt-item]{anchor-scope:--uktdd-submenu-anchor}.uktdropdown-body :is([data-ukt-item],[data-ukt-value])[aria-haspopup=menu]{padding-inline-end:2em;position:relative}.uktdropdown-body :is([data-ukt-item],[data-ukt-value])[aria-haspopup=menu]:after{content:\"\";border-block-start:.14em solid;border-inline-end:.14em solid;block-size:.3em;inline-size:.3em;position:absolute;inset-block-start:50%;inset-inline-end:.9em;translate:0 -50%;rotate:45deg}.uktdropdown-body :is([data-ukt-item],[data-ukt-value])[aria-haspopup=menu]:is(:lang(ae),:lang(ar),:lang(arc),:lang(bcc),:lang(bqi),:lang(ckb),:lang(dv),:lang(fa),:lang(glk),:lang(he),:lang(ku),:lang(mzn),:lang(nqo),:lang(pnb),:lang(ps),:lang(sd),:lang(ug),:lang(ur),:lang(yi)):after{rotate:-45deg}.uktdropdown-body [data-ukt-item][aria-expanded=true]{anchor-name:--uktdd-submenu-anchor}.uktdropdown-body [data-ukt-submenu]:not([popover]){display:none}.uktdropdown-body [data-ukt-submenu]{box-sizing:border-box;border:1px solid var(--uktdd-body-border-color);block-size:auto;color:var(--uktdd-body-color);position-anchor:--uktdd-submenu-anchor;position-area:var(--uktdd-submenu-position-area);position-try-fallbacks:var(--uktdd-submenu-position-try-fallback), var(--uktdd-submenu-fill-fallbacks);z-index:2;margin-block:0;margin-inline:var(--uktdd-submenu-gap);--uktdd-body-gap:0px;padding-block:var(--uktdd-body-pad-block-start) var(--uktdd-body-pad-block-end);padding-inline:var(--uktdd-body-pad-inline-start) var(--uktdd-body-pad-inline-end);inline-size:max-content;max-inline-size:var(--uktdd-body-max-width);min-block-size:min(var(--uktdd-body-min-height), var(--uktdd-body-max-height));max-block-size:var(--uktdd-body-max-height);overscroll-behavior:contain;background-color:var(--uktdd-body-bg-color);box-shadow:var(--uktdd-body-box-shadow);list-style:none;position:fixed;inset:auto;overflow:auto}div.uktdropdown-body{block-size:auto;margin-block:var(--uktdd-body-gap);border:1px solid var(--uktdd-body-border-color);margin-inline:0;padding:0;inset:auto}.uktmenubar{width:max-content;font-family:var(--uktdd-font-family);align-items:stretch;display:flex}.uktmenubar .uktdropdown.is-open .uktdropdown-trigger{box-shadow:inset 0 0 0 100vmax var(--uktdd-menubar-trigger-bg-color-active)}.uktdropdown-content{box-sizing:border-box;overscroll-behavior:contain;min-block-size:0;padding-block:var(--uktdd-body-pad-block-start) var(--uktdd-body-pad-block-end);padding-inline:var(--uktdd-body-pad-inline-start) var(--uktdd-body-pad-inline-end);overflow:auto}@position-try --uktdd-noop{}@position-try --uktdd-top-start{position-area:block-start span-inline-end}@position-try --uktdd-bottom-start{position-area:block-end span-inline-end}@position-try --uktdd-bottom-end{position-area:block-end span-inline-start}@position-try --uktdd-top-end{position-area:block-start span-inline-start}@position-try --uktdd-submenu-inline-start{position-area:inline-start span-block-end}@position-try --uktdd-fill-bottom{position-area:block-end;block-size:calc(100% - var(--uktdd-body-gap) * 2);min-block-size:min(var(--uktdd-body-min-height), var(--uktdd-body-max-height), calc((100dvb - anchor-size(block)) / 2 - var(--uktdd-body-gap) * 2));justify-self:anchor-center}@position-try --uktdd-fill-top{position-area:block-start;block-size:calc(100% - var(--uktdd-body-gap) * 2);min-block-size:min(var(--uktdd-body-min-height), var(--uktdd-body-max-height), calc((100dvb - anchor-size(block)) / 2 - var(--uktdd-body-gap) * 2));justify-self:anchor-center}@position-try --uktdd-fill{block-size:calc(100% - var(--uktdd-body-gap) * 2)}@position-try --uktdd-submenu-fill{position-area:inline-end;block-size:calc(100% - var(--uktdd-body-gap) * 2);align-self:anchor-center}@position-try --uktdd-fill-cover{position-area:span-all span-inline-end;block-size:calc(100% - var(--uktdd-body-gap) * 2);align-self:anchor-center}";
13
- //#endregion
14
- //#region src/helpers.ts
12
+ // src/helpers.ts
15
13
  var BODY_SELECTOR = ".uktdropdown-body";
16
14
  var ITEM_SELECTOR = "[data-ukt-item], [data-ukt-value]";
17
15
  var SUBMENU_SELECTOR = "[data-ukt-submenu]";
@@ -28,7 +26,9 @@ var isFormlessTextInput = (element) => {
28
26
  const { form, type } = element;
29
27
  return form == null && !NON_TEXT_INPUT_TYPES.includes(type);
30
28
  };
31
- var isSubmitKey = (event) => event.key === "Enter" && !event.defaultPrevented && !event.isComposing && event.keyCode !== 229;
29
+ var isMultilineTextField = (element) => element.tagName === "TEXTAREA" || element.isContentEditable;
30
+ var isIMEKeyEvent = (event) => event.isComposing || event.keyCode === 229;
31
+ var isSubmitKey = (event) => event.key === "Enter" && !event.defaultPrevented;
32
32
  var getLevelRoot = (element) => element.closest(SUBMENU_SELECTOR);
33
33
  var getSubmenuOfItem = (item) => {
34
34
  const submenu = item.querySelector(SUBMENU_SELECTOR);
@@ -317,8 +317,7 @@ var isPointInTriangle = (p, a, b, c) => {
317
317
  const d3 = cross(p, c, a);
318
318
  return !((d1 < 0 || d2 < 0 || d3 < 0) && (d1 > 0 || d2 > 0 || d3 > 0));
319
319
  };
320
- //#endregion
321
- //#region src/Menubar.tsx
320
+ // src/Menubar.tsx
322
321
  var compareDocumentOrder = (a, b) => {
323
322
  if (a.element === b.element) return 0;
324
323
  const position = a.element.compareDocumentPosition(b.element);
@@ -519,8 +518,7 @@ function _temp2(member_3) {
519
518
  function _temp(count) {
520
519
  return count + 1;
521
520
  }
522
- //#endregion
523
- //#region src/Dropdown.tsx
521
+ // src/Dropdown.tsx
524
522
  var CHILDREN_ERROR = "Dropdown requires either 1 child (the dropdown body) or 2 children: the dropdown trigger and the dropdown body.";
525
523
  var CLICKABLE_SELECTOR = "button, a[href], input[type=\"button\"], input[type=\"submit\"]";
526
524
  var TEXT_INPUT_SELECTOR = `input${NON_TEXT_INPUT_TYPES.map((type) => `:not([type=${type}])`).join("")},textarea`;
@@ -1259,6 +1257,7 @@ function RootDropdown(t0) {
1259
1257
  const { altKey, ctrlKey, key: key_0, metaKey } = event_7;
1260
1258
  const eventTarget_2 = event_7.target;
1261
1259
  if (!dropdownElement) return;
1260
+ if (isIMEKeyEvent(event_7)) return;
1262
1261
  const onEventHandled = () => {
1263
1262
  event_7.stopPropagation();
1264
1263
  event_7.preventDefault();
@@ -1311,7 +1310,7 @@ function RootDropdown(t0) {
1311
1310
  if (!event_7.repeat) closeDropdown();
1312
1311
  return;
1313
1312
  }
1314
- if (key_0 === " ") return;
1313
+ if (isMultilineTextField(eventTarget_2) || !isSubmitKey(event_7)) return;
1315
1314
  } else if (isTargetUsingKeyEvents || !eventTarget_2.contains(dropdownElement)) return;
1316
1315
  }
1317
1316
  if (key_0 === "Enter" || key_0 === " " && !inputElementRef.current) {
@@ -1885,7 +1884,6 @@ function SubmenuDropdown(props) {
1885
1884
  } else t5 = $[20];
1886
1885
  return t5;
1887
1886
  }
1888
- //#endregion
1889
1887
  export { Menubar, Dropdown as default };
1890
1888
 
1891
1889
  //# sourceMappingURL=Dropdown.js.map