diff --git a/CHANGELOG.md b/CHANGELOG.md index c1649ec446..0d351b2347 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix incorrect `active` option in the Listbox/Combobox component ([#1264](https://github.com/tailwindlabs/headlessui/pull/1264)) - Properly merge incoming props ([#1265](https://github.com/tailwindlabs/headlessui/pull/1265)) - Fix incorrect closing while interacting with third party libraries in `Dialog` component ([#1268](https://github.com/tailwindlabs/headlessui/pull/1268)) +- Mimic browser select on focus when navigating via `Tab` ([#1272](https://github.com/tailwindlabs/headlessui/pull/1272)) ### Added @@ -65,6 +66,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix Tree-shaking support ([#1247](https://github.com/tailwindlabs/headlessui/pull/1247)) - Stop propagation on the Popover Button ([#1263](https://github.com/tailwindlabs/headlessui/pull/1263)) - Fix incorrect closing while interacting with third party libraries in `Dialog` component ([#1268](https://github.com/tailwindlabs/headlessui/pull/1268)) +- Mimic browser select on focus when navigating via `Tab` ([#1272](https://github.com/tailwindlabs/headlessui/pull/1272)) - Resolve `initialFocusRef` correctly ([#1276](https://github.com/tailwindlabs/headlessui/pull/1276)) ### Added diff --git a/packages/@headlessui-react/src/utils/focus-management.ts b/packages/@headlessui-react/src/utils/focus-management.ts index 8d0c717ac4..1e31069639 100644 --- a/packages/@headlessui-react/src/utils/focus-management.ts +++ b/packages/@headlessui-react/src/utils/focus-management.ts @@ -103,6 +103,14 @@ export function focusElement(element: HTMLElement | null) { element?.focus({ preventScroll: true }) } +// https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/select +let selectableSelector = ['textarea', 'input'].join(',') +function isSelectableElement( + element: Element | null +): element is HTMLInputElement | HTMLTextAreaElement { + return element?.matches?.(selectableSelector) ?? false +} + export function sortByDomNode( nodes: T[], resolveKey: (item: T) => HTMLElement | null = (i) => i as unknown as HTMLElement | null @@ -176,6 +184,18 @@ export function focusIn(container: HTMLElement | HTMLElement[], focus: Focus) { offset += direction } while (next !== ownerDocument.activeElement) + // By default if you to a text input or a textarea, the browser will + // select all the text once the focus is inside these DOM Nodes. However, + // since we are manually moving focus this behaviour is not happening. This + // code will make sure that the text gets selected as-if you did it manually. + // Note: We only do this when going forward / backward. Not for the + // Focus.First or Focus.Last actions. This is similar to the `autoFocus` + // behaviour on an input where the input will get focus but won't be + // selected. + if (focus & (Focus.Next | Focus.Previous) && isSelectableElement(next)) { + next.select() + } + // This is a little weird, but let me try and explain: There are a few scenario's // in chrome for example where a focused `` tag does not get the default focus // styles and sometimes they do. This highly depends on whether you started by diff --git a/packages/@headlessui-vue/src/utils/focus-management.ts b/packages/@headlessui-vue/src/utils/focus-management.ts index c7da796d94..a64e5f88ae 100644 --- a/packages/@headlessui-vue/src/utils/focus-management.ts +++ b/packages/@headlessui-vue/src/utils/focus-management.ts @@ -96,6 +96,14 @@ export function focusElement(element: HTMLElement | null) { element?.focus({ preventScroll: true }) } +// https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/select +let selectableSelector = ['textarea', 'input'].join(',') +function isSelectableElement( + element: Element | null +): element is HTMLInputElement | HTMLTextAreaElement { + return element?.matches?.(selectableSelector) ?? false +} + export function sortByDomNode( nodes: T[], resolveKey: (item: T) => HTMLElement | null = (i) => i as unknown as HTMLElement | null @@ -179,5 +187,17 @@ export function focusIn(container: HTMLElement | HTMLElement[], focus: Focus) { // also add this tabindex. if (!next.hasAttribute('tabindex')) next.setAttribute('tabindex', '0') + // By default if you to a text input or a textarea, the browser will + // select all the text once the focus is inside these DOM Nodes. However, + // since we are manually moving focus this behaviour is not happening. This + // code will make sure that the text gets selected as-if you did it manually. + // Note: We only do this when going forward / backward. Not for the + // Focus.First or Focus.Last actions. This is similar to the `autoFocus` + // behaviour on an input where the input will get focus but won't be + // selected. + if (focus & (Focus.Next | Focus.Previous) && isSelectableElement(next)) { + next.select() + } + return FocusResult.Success }