@huin-core/react-roving-focus 1.0.2 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,55 @@
1
+ import * as React from 'react';
2
+ import { Primitive } from '@huin-core/react-primitive';
3
+
4
+ type Scope<C = any> = {
5
+ [scopeName: string]: React.Context<C>[];
6
+ } | undefined;
7
+ type ScopeHook = (scope: Scope) => {
8
+ [__scopeProp: string]: Scope;
9
+ };
10
+ interface CreateScope {
11
+ scopeName: string;
12
+ (): ScopeHook;
13
+ }
14
+
15
+ declare const createRovingFocusGroupScope: CreateScope;
16
+ type Orientation = React.AriaAttributes['aria-orientation'];
17
+ type Direction = 'ltr' | 'rtl';
18
+ interface RovingFocusGroupOptions {
19
+ /**
20
+ * The orientation of the group.
21
+ * Mainly so arrow navigation is done accordingly (left & right vs. up & down)
22
+ */
23
+ orientation?: Orientation;
24
+ /**
25
+ * The direction of navigation between items.
26
+ */
27
+ dir?: Direction;
28
+ /**
29
+ * Whether keyboard navigation should loop around
30
+ * @defaultValue false
31
+ */
32
+ loop?: boolean;
33
+ }
34
+ interface RovingFocusGroupProps extends RovingFocusGroupImplProps {
35
+ }
36
+ declare const RovingFocusGroup: React.ForwardRefExoticComponent<RovingFocusGroupProps & React.RefAttributes<HTMLDivElement>>;
37
+ type PrimitiveDivProps = React.ComponentPropsWithoutRef<typeof Primitive.div>;
38
+ interface RovingFocusGroupImplProps extends Omit<PrimitiveDivProps, 'dir'>, RovingFocusGroupOptions {
39
+ currentTabStopId?: string | null;
40
+ defaultCurrentTabStopId?: string;
41
+ onCurrentTabStopIdChange?: (tabStopId: string | null) => void;
42
+ onEntryFocus?: (event: Event) => void;
43
+ preventScrollOnEntryFocus?: boolean;
44
+ }
45
+ type PrimitiveSpanProps = React.ComponentPropsWithoutRef<typeof Primitive.span>;
46
+ interface RovingFocusItemProps extends PrimitiveSpanProps {
47
+ tabStopId?: string;
48
+ focusable?: boolean;
49
+ active?: boolean;
50
+ }
51
+ declare const RovingFocusGroupItem: React.ForwardRefExoticComponent<RovingFocusItemProps & React.RefAttributes<HTMLSpanElement>>;
52
+ declare const Root: React.ForwardRefExoticComponent<RovingFocusGroupProps & React.RefAttributes<HTMLDivElement>>;
53
+ declare const Item: React.ForwardRefExoticComponent<RovingFocusItemProps & React.RefAttributes<HTMLSpanElement>>;
54
+
55
+ export { Item, Root, RovingFocusGroup, RovingFocusGroupItem, type RovingFocusGroupProps, type RovingFocusItemProps, createRovingFocusGroupScope };
@@ -0,0 +1,55 @@
1
+ import * as React from 'react';
2
+ import { Primitive } from '@huin-core/react-primitive';
3
+
4
+ type Scope<C = any> = {
5
+ [scopeName: string]: React.Context<C>[];
6
+ } | undefined;
7
+ type ScopeHook = (scope: Scope) => {
8
+ [__scopeProp: string]: Scope;
9
+ };
10
+ interface CreateScope {
11
+ scopeName: string;
12
+ (): ScopeHook;
13
+ }
14
+
15
+ declare const createRovingFocusGroupScope: CreateScope;
16
+ type Orientation = React.AriaAttributes['aria-orientation'];
17
+ type Direction = 'ltr' | 'rtl';
18
+ interface RovingFocusGroupOptions {
19
+ /**
20
+ * The orientation of the group.
21
+ * Mainly so arrow navigation is done accordingly (left & right vs. up & down)
22
+ */
23
+ orientation?: Orientation;
24
+ /**
25
+ * The direction of navigation between items.
26
+ */
27
+ dir?: Direction;
28
+ /**
29
+ * Whether keyboard navigation should loop around
30
+ * @defaultValue false
31
+ */
32
+ loop?: boolean;
33
+ }
34
+ interface RovingFocusGroupProps extends RovingFocusGroupImplProps {
35
+ }
36
+ declare const RovingFocusGroup: React.ForwardRefExoticComponent<RovingFocusGroupProps & React.RefAttributes<HTMLDivElement>>;
37
+ type PrimitiveDivProps = React.ComponentPropsWithoutRef<typeof Primitive.div>;
38
+ interface RovingFocusGroupImplProps extends Omit<PrimitiveDivProps, 'dir'>, RovingFocusGroupOptions {
39
+ currentTabStopId?: string | null;
40
+ defaultCurrentTabStopId?: string;
41
+ onCurrentTabStopIdChange?: (tabStopId: string | null) => void;
42
+ onEntryFocus?: (event: Event) => void;
43
+ preventScrollOnEntryFocus?: boolean;
44
+ }
45
+ type PrimitiveSpanProps = React.ComponentPropsWithoutRef<typeof Primitive.span>;
46
+ interface RovingFocusItemProps extends PrimitiveSpanProps {
47
+ tabStopId?: string;
48
+ focusable?: boolean;
49
+ active?: boolean;
50
+ }
51
+ declare const RovingFocusGroupItem: React.ForwardRefExoticComponent<RovingFocusItemProps & React.RefAttributes<HTMLSpanElement>>;
52
+ declare const Root: React.ForwardRefExoticComponent<RovingFocusGroupProps & React.RefAttributes<HTMLDivElement>>;
53
+ declare const Item: React.ForwardRefExoticComponent<RovingFocusItemProps & React.RefAttributes<HTMLSpanElement>>;
54
+
55
+ export { Item, Root, RovingFocusGroup, RovingFocusGroupItem, type RovingFocusGroupProps, type RovingFocusItemProps, createRovingFocusGroupScope };
package/dist/index.js ADDED
@@ -0,0 +1,260 @@
1
+ "use strict";
2
+ "use client";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+
31
+ // packages/react/roving-focus/src/index.ts
32
+ var src_exports = {};
33
+ __export(src_exports, {
34
+ Item: () => Item,
35
+ Root: () => Root,
36
+ RovingFocusGroup: () => RovingFocusGroup,
37
+ RovingFocusGroupItem: () => RovingFocusGroupItem,
38
+ createRovingFocusGroupScope: () => createRovingFocusGroupScope
39
+ });
40
+ module.exports = __toCommonJS(src_exports);
41
+
42
+ // packages/react/roving-focus/src/RovingFocusGroup.tsx
43
+ var React = __toESM(require("react"));
44
+ var import_primitive = require("@huin-core/primitive");
45
+ var import_react_collection = require("@huin-core/react-collection");
46
+ var import_react_compose_refs = require("@huin-core/react-compose-refs");
47
+ var import_react_context = require("@huin-core/react-context");
48
+ var import_react_id = require("@huin-core/react-id");
49
+ var import_react_primitive = require("@huin-core/react-primitive");
50
+ var import_react_use_callback_ref = require("@huin-core/react-use-callback-ref");
51
+ var import_react_use_controllable_state = require("@huin-core/react-use-controllable-state");
52
+ var import_react_direction = require("@huin-core/react-direction");
53
+ var import_jsx_runtime = require("react/jsx-runtime");
54
+ var ENTRY_FOCUS = "rovingFocusGroup.onEntryFocus";
55
+ var EVENT_OPTIONS = { bubbles: false, cancelable: true };
56
+ var GROUP_NAME = "RovingFocusGroup";
57
+ var [Collection, useCollection, createCollectionScope] = (0, import_react_collection.createCollection)(GROUP_NAME);
58
+ var [createRovingFocusGroupContext, createRovingFocusGroupScope] = (0, import_react_context.createContextScope)(
59
+ GROUP_NAME,
60
+ [createCollectionScope]
61
+ );
62
+ var [RovingFocusProvider, useRovingFocusContext] = createRovingFocusGroupContext(GROUP_NAME);
63
+ var RovingFocusGroup = React.forwardRef(
64
+ (props, forwardedRef) => {
65
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Collection.Provider, { scope: props.__scopeRovingFocusGroup, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Collection.Slot, { scope: props.__scopeRovingFocusGroup, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RovingFocusGroupImpl, { ...props, ref: forwardedRef }) }) });
66
+ }
67
+ );
68
+ RovingFocusGroup.displayName = GROUP_NAME;
69
+ var RovingFocusGroupImpl = React.forwardRef((props, forwardedRef) => {
70
+ const {
71
+ __scopeRovingFocusGroup,
72
+ orientation,
73
+ loop = false,
74
+ dir,
75
+ currentTabStopId: currentTabStopIdProp,
76
+ defaultCurrentTabStopId,
77
+ onCurrentTabStopIdChange,
78
+ onEntryFocus,
79
+ preventScrollOnEntryFocus = false,
80
+ ...groupProps
81
+ } = props;
82
+ const ref = React.useRef(null);
83
+ const composedRefs = (0, import_react_compose_refs.useComposedRefs)(forwardedRef, ref);
84
+ const direction = (0, import_react_direction.useDirection)(dir);
85
+ const [currentTabStopId = null, setCurrentTabStopId] = (0, import_react_use_controllable_state.useControllableState)({
86
+ prop: currentTabStopIdProp,
87
+ defaultProp: defaultCurrentTabStopId,
88
+ onChange: onCurrentTabStopIdChange
89
+ });
90
+ const [isTabbingBackOut, setIsTabbingBackOut] = React.useState(false);
91
+ const handleEntryFocus = (0, import_react_use_callback_ref.useCallbackRef)(onEntryFocus);
92
+ const getItems = useCollection(__scopeRovingFocusGroup);
93
+ const isClickFocusRef = React.useRef(false);
94
+ const [focusableItemsCount, setFocusableItemsCount] = React.useState(0);
95
+ React.useEffect(() => {
96
+ const node = ref.current;
97
+ if (node) {
98
+ node.addEventListener(ENTRY_FOCUS, handleEntryFocus);
99
+ return () => node.removeEventListener(ENTRY_FOCUS, handleEntryFocus);
100
+ }
101
+ }, [handleEntryFocus]);
102
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
103
+ RovingFocusProvider,
104
+ {
105
+ scope: __scopeRovingFocusGroup,
106
+ orientation,
107
+ dir: direction,
108
+ loop,
109
+ currentTabStopId,
110
+ onItemFocus: React.useCallback(
111
+ (tabStopId) => setCurrentTabStopId(tabStopId),
112
+ [setCurrentTabStopId]
113
+ ),
114
+ onItemShiftTab: React.useCallback(() => setIsTabbingBackOut(true), []),
115
+ onFocusableItemAdd: React.useCallback(
116
+ () => setFocusableItemsCount((prevCount) => prevCount + 1),
117
+ []
118
+ ),
119
+ onFocusableItemRemove: React.useCallback(
120
+ () => setFocusableItemsCount((prevCount) => prevCount - 1),
121
+ []
122
+ ),
123
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
124
+ import_react_primitive.Primitive.div,
125
+ {
126
+ tabIndex: isTabbingBackOut || focusableItemsCount === 0 ? -1 : 0,
127
+ "data-orientation": orientation,
128
+ ...groupProps,
129
+ ref: composedRefs,
130
+ style: { outline: "none", ...props.style },
131
+ onMouseDown: (0, import_primitive.composeEventHandlers)(props.onMouseDown, () => {
132
+ isClickFocusRef.current = true;
133
+ }),
134
+ onFocus: (0, import_primitive.composeEventHandlers)(props.onFocus, (event) => {
135
+ const isKeyboardFocus = !isClickFocusRef.current;
136
+ if (event.target === event.currentTarget && isKeyboardFocus && !isTabbingBackOut) {
137
+ const entryFocusEvent = new CustomEvent(ENTRY_FOCUS, EVENT_OPTIONS);
138
+ event.currentTarget.dispatchEvent(entryFocusEvent);
139
+ if (!entryFocusEvent.defaultPrevented) {
140
+ const items = getItems().filter((item) => item.focusable);
141
+ const activeItem = items.find((item) => item.active);
142
+ const currentItem = items.find((item) => item.id === currentTabStopId);
143
+ const candidateItems = [activeItem, currentItem, ...items].filter(
144
+ Boolean
145
+ );
146
+ const candidateNodes = candidateItems.map((item) => item.ref.current);
147
+ focusFirst(candidateNodes, preventScrollOnEntryFocus);
148
+ }
149
+ }
150
+ isClickFocusRef.current = false;
151
+ }),
152
+ onBlur: (0, import_primitive.composeEventHandlers)(props.onBlur, () => setIsTabbingBackOut(false))
153
+ }
154
+ )
155
+ }
156
+ );
157
+ });
158
+ var ITEM_NAME = "RovingFocusGroupItem";
159
+ var RovingFocusGroupItem = React.forwardRef(
160
+ (props, forwardedRef) => {
161
+ const {
162
+ __scopeRovingFocusGroup,
163
+ focusable = true,
164
+ active = false,
165
+ tabStopId,
166
+ ...itemProps
167
+ } = props;
168
+ const autoId = (0, import_react_id.useId)();
169
+ const id = tabStopId || autoId;
170
+ const context = useRovingFocusContext(ITEM_NAME, __scopeRovingFocusGroup);
171
+ const isCurrentTabStop = context.currentTabStopId === id;
172
+ const getItems = useCollection(__scopeRovingFocusGroup);
173
+ const { onFocusableItemAdd, onFocusableItemRemove } = context;
174
+ React.useEffect(() => {
175
+ if (focusable) {
176
+ onFocusableItemAdd();
177
+ return () => onFocusableItemRemove();
178
+ }
179
+ }, [focusable, onFocusableItemAdd, onFocusableItemRemove]);
180
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
181
+ Collection.ItemSlot,
182
+ {
183
+ scope: __scopeRovingFocusGroup,
184
+ id,
185
+ focusable,
186
+ active,
187
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
188
+ import_react_primitive.Primitive.span,
189
+ {
190
+ tabIndex: isCurrentTabStop ? 0 : -1,
191
+ "data-orientation": context.orientation,
192
+ ...itemProps,
193
+ ref: forwardedRef,
194
+ onMouseDown: (0, import_primitive.composeEventHandlers)(props.onMouseDown, (event) => {
195
+ if (!focusable) event.preventDefault();
196
+ else context.onItemFocus(id);
197
+ }),
198
+ onFocus: (0, import_primitive.composeEventHandlers)(props.onFocus, () => context.onItemFocus(id)),
199
+ onKeyDown: (0, import_primitive.composeEventHandlers)(props.onKeyDown, (event) => {
200
+ if (event.key === "Tab" && event.shiftKey) {
201
+ context.onItemShiftTab();
202
+ return;
203
+ }
204
+ if (event.target !== event.currentTarget) return;
205
+ const focusIntent = getFocusIntent(event, context.orientation, context.dir);
206
+ if (focusIntent !== void 0) {
207
+ if (event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) return;
208
+ event.preventDefault();
209
+ const items = getItems().filter((item) => item.focusable);
210
+ let candidateNodes = items.map((item) => item.ref.current);
211
+ if (focusIntent === "last") candidateNodes.reverse();
212
+ else if (focusIntent === "prev" || focusIntent === "next") {
213
+ if (focusIntent === "prev") candidateNodes.reverse();
214
+ const currentIndex = candidateNodes.indexOf(event.currentTarget);
215
+ candidateNodes = context.loop ? wrapArray(candidateNodes, currentIndex + 1) : candidateNodes.slice(currentIndex + 1);
216
+ }
217
+ setTimeout(() => focusFirst(candidateNodes));
218
+ }
219
+ })
220
+ }
221
+ )
222
+ }
223
+ );
224
+ }
225
+ );
226
+ RovingFocusGroupItem.displayName = ITEM_NAME;
227
+ var MAP_KEY_TO_FOCUS_INTENT = {
228
+ ArrowLeft: "prev",
229
+ ArrowUp: "prev",
230
+ ArrowRight: "next",
231
+ ArrowDown: "next",
232
+ PageUp: "first",
233
+ Home: "first",
234
+ PageDown: "last",
235
+ End: "last"
236
+ };
237
+ function getDirectionAwareKey(key, dir) {
238
+ if (dir !== "rtl") return key;
239
+ return key === "ArrowLeft" ? "ArrowRight" : key === "ArrowRight" ? "ArrowLeft" : key;
240
+ }
241
+ function getFocusIntent(event, orientation, dir) {
242
+ const key = getDirectionAwareKey(event.key, dir);
243
+ if (orientation === "vertical" && ["ArrowLeft", "ArrowRight"].includes(key)) return void 0;
244
+ if (orientation === "horizontal" && ["ArrowUp", "ArrowDown"].includes(key)) return void 0;
245
+ return MAP_KEY_TO_FOCUS_INTENT[key];
246
+ }
247
+ function focusFirst(candidates, preventScroll = false) {
248
+ const PREVIOUSLY_FOCUSED_ELEMENT = document.activeElement;
249
+ for (const candidate of candidates) {
250
+ if (candidate === PREVIOUSLY_FOCUSED_ELEMENT) return;
251
+ candidate.focus({ preventScroll });
252
+ if (document.activeElement !== PREVIOUSLY_FOCUSED_ELEMENT) return;
253
+ }
254
+ }
255
+ function wrapArray(array, startIndex) {
256
+ return array.map((_, index) => array[(startIndex + index) % array.length]);
257
+ }
258
+ var Root = RovingFocusGroup;
259
+ var Item = RovingFocusGroupItem;
260
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index.ts", "../src/RovingFocusGroup.tsx"],
4
+ "sourcesContent": ["'use client';\nexport {\n createRovingFocusGroupScope,\n //\n RovingFocusGroup,\n RovingFocusGroupItem,\n //\n Root,\n Item,\n} from './RovingFocusGroup';\nexport type { RovingFocusGroupProps, RovingFocusItemProps } from './RovingFocusGroup';\n", "import * as React from 'react';\nimport { composeEventHandlers } from '@huin-core/primitive';\nimport { createCollection } from '@huin-core/react-collection';\nimport { useComposedRefs } from '@huin-core/react-compose-refs';\nimport { createContextScope } from '@huin-core/react-context';\nimport { useId } from '@huin-core/react-id';\nimport { Primitive } from '@huin-core/react-primitive';\nimport { useCallbackRef } from '@huin-core/react-use-callback-ref';\nimport { useControllableState } from '@huin-core/react-use-controllable-state';\nimport { useDirection } from '@huin-core/react-direction';\n\nimport type { Scope } from '@huin-core/react-context';\n\nconst ENTRY_FOCUS = 'rovingFocusGroup.onEntryFocus';\nconst EVENT_OPTIONS = { bubbles: false, cancelable: true };\n\n/* -------------------------------------------------------------------------------------------------\n * RovingFocusGroup\n * -----------------------------------------------------------------------------------------------*/\n\nconst GROUP_NAME = 'RovingFocusGroup';\n\ntype ItemData = { id: string; focusable: boolean; active: boolean };\nconst [Collection, useCollection, createCollectionScope] = createCollection<\n HTMLSpanElement,\n ItemData\n>(GROUP_NAME);\n\ntype ScopedProps<P> = P & { __scopeRovingFocusGroup?: Scope };\nconst [createRovingFocusGroupContext, createRovingFocusGroupScope] = createContextScope(\n GROUP_NAME,\n [createCollectionScope]\n);\n\ntype Orientation = React.AriaAttributes['aria-orientation'];\ntype Direction = 'ltr' | 'rtl';\n\ninterface RovingFocusGroupOptions {\n /**\n * The orientation of the group.\n * Mainly so arrow navigation is done accordingly (left & right vs. up & down)\n */\n orientation?: Orientation;\n /**\n * The direction of navigation between items.\n */\n dir?: Direction;\n /**\n * Whether keyboard navigation should loop around\n * @defaultValue false\n */\n loop?: boolean;\n}\n\ntype RovingContextValue = RovingFocusGroupOptions & {\n currentTabStopId: string | null;\n onItemFocus(tabStopId: string): void;\n onItemShiftTab(): void;\n onFocusableItemAdd(): void;\n onFocusableItemRemove(): void;\n};\n\nconst [RovingFocusProvider, useRovingFocusContext] =\n createRovingFocusGroupContext<RovingContextValue>(GROUP_NAME);\n\ntype RovingFocusGroupElement = RovingFocusGroupImplElement;\ninterface RovingFocusGroupProps extends RovingFocusGroupImplProps {}\n\nconst RovingFocusGroup = React.forwardRef<RovingFocusGroupElement, RovingFocusGroupProps>(\n (props: ScopedProps<RovingFocusGroupProps>, forwardedRef) => {\n return (\n <Collection.Provider scope={props.__scopeRovingFocusGroup}>\n <Collection.Slot scope={props.__scopeRovingFocusGroup}>\n <RovingFocusGroupImpl {...props} ref={forwardedRef} />\n </Collection.Slot>\n </Collection.Provider>\n );\n }\n);\n\nRovingFocusGroup.displayName = GROUP_NAME;\n\n/* -----------------------------------------------------------------------------------------------*/\n\ntype RovingFocusGroupImplElement = React.ElementRef<typeof Primitive.div>;\ntype PrimitiveDivProps = React.ComponentPropsWithoutRef<typeof Primitive.div>;\ninterface RovingFocusGroupImplProps\n extends Omit<PrimitiveDivProps, 'dir'>,\n RovingFocusGroupOptions {\n currentTabStopId?: string | null;\n defaultCurrentTabStopId?: string;\n onCurrentTabStopIdChange?: (tabStopId: string | null) => void;\n onEntryFocus?: (event: Event) => void;\n preventScrollOnEntryFocus?: boolean;\n}\n\nconst RovingFocusGroupImpl = React.forwardRef<\n RovingFocusGroupImplElement,\n RovingFocusGroupImplProps\n>((props: ScopedProps<RovingFocusGroupImplProps>, forwardedRef) => {\n const {\n __scopeRovingFocusGroup,\n orientation,\n loop = false,\n dir,\n currentTabStopId: currentTabStopIdProp,\n defaultCurrentTabStopId,\n onCurrentTabStopIdChange,\n onEntryFocus,\n preventScrollOnEntryFocus = false,\n ...groupProps\n } = props;\n const ref = React.useRef<RovingFocusGroupImplElement>(null);\n const composedRefs = useComposedRefs(forwardedRef, ref);\n const direction = useDirection(dir);\n const [currentTabStopId = null, setCurrentTabStopId] = useControllableState({\n prop: currentTabStopIdProp,\n defaultProp: defaultCurrentTabStopId,\n onChange: onCurrentTabStopIdChange,\n });\n const [isTabbingBackOut, setIsTabbingBackOut] = React.useState(false);\n const handleEntryFocus = useCallbackRef(onEntryFocus);\n const getItems = useCollection(__scopeRovingFocusGroup);\n const isClickFocusRef = React.useRef(false);\n const [focusableItemsCount, setFocusableItemsCount] = React.useState(0);\n\n React.useEffect(() => {\n const node = ref.current;\n if (node) {\n node.addEventListener(ENTRY_FOCUS, handleEntryFocus);\n return () => node.removeEventListener(ENTRY_FOCUS, handleEntryFocus);\n }\n }, [handleEntryFocus]);\n\n return (\n <RovingFocusProvider\n scope={__scopeRovingFocusGroup}\n orientation={orientation}\n dir={direction}\n loop={loop}\n currentTabStopId={currentTabStopId}\n onItemFocus={React.useCallback(\n (tabStopId) => setCurrentTabStopId(tabStopId),\n [setCurrentTabStopId]\n )}\n onItemShiftTab={React.useCallback(() => setIsTabbingBackOut(true), [])}\n onFocusableItemAdd={React.useCallback(\n () => setFocusableItemsCount((prevCount) => prevCount + 1),\n []\n )}\n onFocusableItemRemove={React.useCallback(\n () => setFocusableItemsCount((prevCount) => prevCount - 1),\n []\n )}\n >\n <Primitive.div\n tabIndex={isTabbingBackOut || focusableItemsCount === 0 ? -1 : 0}\n data-orientation={orientation}\n {...groupProps}\n ref={composedRefs}\n style={{ outline: 'none', ...props.style }}\n onMouseDown={composeEventHandlers(props.onMouseDown, () => {\n isClickFocusRef.current = true;\n })}\n onFocus={composeEventHandlers(props.onFocus, (event) => {\n // We normally wouldn't need this check, because we already check\n // that the focus is on the current target and not bubbling to it.\n // We do this because Safari doesn't focus buttons when clicked, and\n // instead, the wrapper will get focused and not through a bubbling event.\n const isKeyboardFocus = !isClickFocusRef.current;\n\n if (event.target === event.currentTarget && isKeyboardFocus && !isTabbingBackOut) {\n const entryFocusEvent = new CustomEvent(ENTRY_FOCUS, EVENT_OPTIONS);\n event.currentTarget.dispatchEvent(entryFocusEvent);\n\n if (!entryFocusEvent.defaultPrevented) {\n const items = getItems().filter((item) => item.focusable);\n const activeItem = items.find((item) => item.active);\n const currentItem = items.find((item) => item.id === currentTabStopId);\n const candidateItems = [activeItem, currentItem, ...items].filter(\n Boolean\n ) as typeof items;\n const candidateNodes = candidateItems.map((item) => item.ref.current!);\n focusFirst(candidateNodes, preventScrollOnEntryFocus);\n }\n }\n\n isClickFocusRef.current = false;\n })}\n onBlur={composeEventHandlers(props.onBlur, () => setIsTabbingBackOut(false))}\n />\n </RovingFocusProvider>\n );\n});\n\n/* -------------------------------------------------------------------------------------------------\n * RovingFocusGroupItem\n * -----------------------------------------------------------------------------------------------*/\n\nconst ITEM_NAME = 'RovingFocusGroupItem';\n\ntype RovingFocusItemElement = React.ElementRef<typeof Primitive.span>;\ntype PrimitiveSpanProps = React.ComponentPropsWithoutRef<typeof Primitive.span>;\ninterface RovingFocusItemProps extends PrimitiveSpanProps {\n tabStopId?: string;\n focusable?: boolean;\n active?: boolean;\n}\n\nconst RovingFocusGroupItem = React.forwardRef<RovingFocusItemElement, RovingFocusItemProps>(\n (props: ScopedProps<RovingFocusItemProps>, forwardedRef) => {\n const {\n __scopeRovingFocusGroup,\n focusable = true,\n active = false,\n tabStopId,\n ...itemProps\n } = props;\n const autoId = useId();\n const id = tabStopId || autoId;\n const context = useRovingFocusContext(ITEM_NAME, __scopeRovingFocusGroup);\n const isCurrentTabStop = context.currentTabStopId === id;\n const getItems = useCollection(__scopeRovingFocusGroup);\n\n const { onFocusableItemAdd, onFocusableItemRemove } = context;\n\n React.useEffect(() => {\n if (focusable) {\n onFocusableItemAdd();\n return () => onFocusableItemRemove();\n }\n }, [focusable, onFocusableItemAdd, onFocusableItemRemove]);\n\n return (\n <Collection.ItemSlot\n scope={__scopeRovingFocusGroup}\n id={id}\n focusable={focusable}\n active={active}\n >\n <Primitive.span\n tabIndex={isCurrentTabStop ? 0 : -1}\n data-orientation={context.orientation}\n {...itemProps}\n ref={forwardedRef}\n onMouseDown={composeEventHandlers(props.onMouseDown, (event) => {\n // We prevent focusing non-focusable items on `mousedown`.\n // Even though the item has tabIndex={-1}, that only means take it out of the tab order.\n if (!focusable) event.preventDefault();\n // Safari doesn't focus a button when clicked so we run our logic on mousedown also\n else context.onItemFocus(id);\n })}\n onFocus={composeEventHandlers(props.onFocus, () => context.onItemFocus(id))}\n onKeyDown={composeEventHandlers(props.onKeyDown, (event) => {\n if (event.key === 'Tab' && event.shiftKey) {\n context.onItemShiftTab();\n return;\n }\n\n if (event.target !== event.currentTarget) return;\n\n const focusIntent = getFocusIntent(event, context.orientation, context.dir);\n\n if (focusIntent !== undefined) {\n if (event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) return;\n event.preventDefault();\n const items = getItems().filter((item) => item.focusable);\n let candidateNodes = items.map((item) => item.ref.current!);\n\n if (focusIntent === 'last') candidateNodes.reverse();\n else if (focusIntent === 'prev' || focusIntent === 'next') {\n if (focusIntent === 'prev') candidateNodes.reverse();\n const currentIndex = candidateNodes.indexOf(event.currentTarget);\n candidateNodes = context.loop\n ? wrapArray(candidateNodes, currentIndex + 1)\n : candidateNodes.slice(currentIndex + 1);\n }\n\n /**\n * Imperative focus during keydown is risky so we prevent React's batching updates\n * to avoid potential bugs. See: https://github.com/facebook/react/issues/20332\n */\n setTimeout(() => focusFirst(candidateNodes));\n }\n })}\n />\n </Collection.ItemSlot>\n );\n }\n);\n\nRovingFocusGroupItem.displayName = ITEM_NAME;\n\n/* -----------------------------------------------------------------------------------------------*/\n\n// prettier-ignore\nconst MAP_KEY_TO_FOCUS_INTENT: Record<string, FocusIntent> = {\n ArrowLeft: 'prev', ArrowUp: 'prev',\n ArrowRight: 'next', ArrowDown: 'next',\n PageUp: 'first', Home: 'first',\n PageDown: 'last', End: 'last',\n};\n\nfunction getDirectionAwareKey(key: string, dir?: Direction) {\n if (dir !== 'rtl') return key;\n return key === 'ArrowLeft' ? 'ArrowRight' : key === 'ArrowRight' ? 'ArrowLeft' : key;\n}\n\ntype FocusIntent = 'first' | 'last' | 'prev' | 'next';\n\nfunction getFocusIntent(event: React.KeyboardEvent, orientation?: Orientation, dir?: Direction) {\n const key = getDirectionAwareKey(event.key, dir);\n if (orientation === 'vertical' && ['ArrowLeft', 'ArrowRight'].includes(key)) return undefined;\n if (orientation === 'horizontal' && ['ArrowUp', 'ArrowDown'].includes(key)) return undefined;\n return MAP_KEY_TO_FOCUS_INTENT[key];\n}\n\nfunction focusFirst(candidates: HTMLElement[], preventScroll = false) {\n const PREVIOUSLY_FOCUSED_ELEMENT = document.activeElement;\n for (const candidate of candidates) {\n // if focus is already where we want to go, we don't want to keep going through the candidates\n if (candidate === PREVIOUSLY_FOCUSED_ELEMENT) return;\n candidate.focus({ preventScroll });\n if (document.activeElement !== PREVIOUSLY_FOCUSED_ELEMENT) return;\n }\n}\n\n/**\n * Wraps an array around itself at a given start index\n * Example: `wrapArray(['a', 'b', 'c', 'd'], 2) === ['c', 'd', 'a', 'b']`\n */\nfunction wrapArray<T>(array: T[], startIndex: number) {\n return array.map((_, index) => array[(startIndex + index) % array.length]);\n}\n\nconst Root = RovingFocusGroup;\nconst Item = RovingFocusGroupItem;\n\nexport {\n createRovingFocusGroupScope,\n //\n RovingFocusGroup,\n RovingFocusGroupItem,\n //\n Root,\n Item,\n};\nexport type { RovingFocusGroupProps, RovingFocusItemProps };\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
package/dist/index.mjs ADDED
@@ -0,0 +1,228 @@
1
+ "use client";
2
+
3
+ // packages/react/roving-focus/src/RovingFocusGroup.tsx
4
+ import * as React from "react";
5
+ import { composeEventHandlers } from "@huin-core/primitive";
6
+ import { createCollection } from "@huin-core/react-collection";
7
+ import { useComposedRefs } from "@huin-core/react-compose-refs";
8
+ import { createContextScope } from "@huin-core/react-context";
9
+ import { useId } from "@huin-core/react-id";
10
+ import { Primitive } from "@huin-core/react-primitive";
11
+ import { useCallbackRef } from "@huin-core/react-use-callback-ref";
12
+ import { useControllableState } from "@huin-core/react-use-controllable-state";
13
+ import { useDirection } from "@huin-core/react-direction";
14
+ import { jsx } from "react/jsx-runtime";
15
+ var ENTRY_FOCUS = "rovingFocusGroup.onEntryFocus";
16
+ var EVENT_OPTIONS = { bubbles: false, cancelable: true };
17
+ var GROUP_NAME = "RovingFocusGroup";
18
+ var [Collection, useCollection, createCollectionScope] = createCollection(GROUP_NAME);
19
+ var [createRovingFocusGroupContext, createRovingFocusGroupScope] = createContextScope(
20
+ GROUP_NAME,
21
+ [createCollectionScope]
22
+ );
23
+ var [RovingFocusProvider, useRovingFocusContext] = createRovingFocusGroupContext(GROUP_NAME);
24
+ var RovingFocusGroup = React.forwardRef(
25
+ (props, forwardedRef) => {
26
+ return /* @__PURE__ */ jsx(Collection.Provider, { scope: props.__scopeRovingFocusGroup, children: /* @__PURE__ */ jsx(Collection.Slot, { scope: props.__scopeRovingFocusGroup, children: /* @__PURE__ */ jsx(RovingFocusGroupImpl, { ...props, ref: forwardedRef }) }) });
27
+ }
28
+ );
29
+ RovingFocusGroup.displayName = GROUP_NAME;
30
+ var RovingFocusGroupImpl = React.forwardRef((props, forwardedRef) => {
31
+ const {
32
+ __scopeRovingFocusGroup,
33
+ orientation,
34
+ loop = false,
35
+ dir,
36
+ currentTabStopId: currentTabStopIdProp,
37
+ defaultCurrentTabStopId,
38
+ onCurrentTabStopIdChange,
39
+ onEntryFocus,
40
+ preventScrollOnEntryFocus = false,
41
+ ...groupProps
42
+ } = props;
43
+ const ref = React.useRef(null);
44
+ const composedRefs = useComposedRefs(forwardedRef, ref);
45
+ const direction = useDirection(dir);
46
+ const [currentTabStopId = null, setCurrentTabStopId] = useControllableState({
47
+ prop: currentTabStopIdProp,
48
+ defaultProp: defaultCurrentTabStopId,
49
+ onChange: onCurrentTabStopIdChange
50
+ });
51
+ const [isTabbingBackOut, setIsTabbingBackOut] = React.useState(false);
52
+ const handleEntryFocus = useCallbackRef(onEntryFocus);
53
+ const getItems = useCollection(__scopeRovingFocusGroup);
54
+ const isClickFocusRef = React.useRef(false);
55
+ const [focusableItemsCount, setFocusableItemsCount] = React.useState(0);
56
+ React.useEffect(() => {
57
+ const node = ref.current;
58
+ if (node) {
59
+ node.addEventListener(ENTRY_FOCUS, handleEntryFocus);
60
+ return () => node.removeEventListener(ENTRY_FOCUS, handleEntryFocus);
61
+ }
62
+ }, [handleEntryFocus]);
63
+ return /* @__PURE__ */ jsx(
64
+ RovingFocusProvider,
65
+ {
66
+ scope: __scopeRovingFocusGroup,
67
+ orientation,
68
+ dir: direction,
69
+ loop,
70
+ currentTabStopId,
71
+ onItemFocus: React.useCallback(
72
+ (tabStopId) => setCurrentTabStopId(tabStopId),
73
+ [setCurrentTabStopId]
74
+ ),
75
+ onItemShiftTab: React.useCallback(() => setIsTabbingBackOut(true), []),
76
+ onFocusableItemAdd: React.useCallback(
77
+ () => setFocusableItemsCount((prevCount) => prevCount + 1),
78
+ []
79
+ ),
80
+ onFocusableItemRemove: React.useCallback(
81
+ () => setFocusableItemsCount((prevCount) => prevCount - 1),
82
+ []
83
+ ),
84
+ children: /* @__PURE__ */ jsx(
85
+ Primitive.div,
86
+ {
87
+ tabIndex: isTabbingBackOut || focusableItemsCount === 0 ? -1 : 0,
88
+ "data-orientation": orientation,
89
+ ...groupProps,
90
+ ref: composedRefs,
91
+ style: { outline: "none", ...props.style },
92
+ onMouseDown: composeEventHandlers(props.onMouseDown, () => {
93
+ isClickFocusRef.current = true;
94
+ }),
95
+ onFocus: composeEventHandlers(props.onFocus, (event) => {
96
+ const isKeyboardFocus = !isClickFocusRef.current;
97
+ if (event.target === event.currentTarget && isKeyboardFocus && !isTabbingBackOut) {
98
+ const entryFocusEvent = new CustomEvent(ENTRY_FOCUS, EVENT_OPTIONS);
99
+ event.currentTarget.dispatchEvent(entryFocusEvent);
100
+ if (!entryFocusEvent.defaultPrevented) {
101
+ const items = getItems().filter((item) => item.focusable);
102
+ const activeItem = items.find((item) => item.active);
103
+ const currentItem = items.find((item) => item.id === currentTabStopId);
104
+ const candidateItems = [activeItem, currentItem, ...items].filter(
105
+ Boolean
106
+ );
107
+ const candidateNodes = candidateItems.map((item) => item.ref.current);
108
+ focusFirst(candidateNodes, preventScrollOnEntryFocus);
109
+ }
110
+ }
111
+ isClickFocusRef.current = false;
112
+ }),
113
+ onBlur: composeEventHandlers(props.onBlur, () => setIsTabbingBackOut(false))
114
+ }
115
+ )
116
+ }
117
+ );
118
+ });
119
+ var ITEM_NAME = "RovingFocusGroupItem";
120
+ var RovingFocusGroupItem = React.forwardRef(
121
+ (props, forwardedRef) => {
122
+ const {
123
+ __scopeRovingFocusGroup,
124
+ focusable = true,
125
+ active = false,
126
+ tabStopId,
127
+ ...itemProps
128
+ } = props;
129
+ const autoId = useId();
130
+ const id = tabStopId || autoId;
131
+ const context = useRovingFocusContext(ITEM_NAME, __scopeRovingFocusGroup);
132
+ const isCurrentTabStop = context.currentTabStopId === id;
133
+ const getItems = useCollection(__scopeRovingFocusGroup);
134
+ const { onFocusableItemAdd, onFocusableItemRemove } = context;
135
+ React.useEffect(() => {
136
+ if (focusable) {
137
+ onFocusableItemAdd();
138
+ return () => onFocusableItemRemove();
139
+ }
140
+ }, [focusable, onFocusableItemAdd, onFocusableItemRemove]);
141
+ return /* @__PURE__ */ jsx(
142
+ Collection.ItemSlot,
143
+ {
144
+ scope: __scopeRovingFocusGroup,
145
+ id,
146
+ focusable,
147
+ active,
148
+ children: /* @__PURE__ */ jsx(
149
+ Primitive.span,
150
+ {
151
+ tabIndex: isCurrentTabStop ? 0 : -1,
152
+ "data-orientation": context.orientation,
153
+ ...itemProps,
154
+ ref: forwardedRef,
155
+ onMouseDown: composeEventHandlers(props.onMouseDown, (event) => {
156
+ if (!focusable) event.preventDefault();
157
+ else context.onItemFocus(id);
158
+ }),
159
+ onFocus: composeEventHandlers(props.onFocus, () => context.onItemFocus(id)),
160
+ onKeyDown: composeEventHandlers(props.onKeyDown, (event) => {
161
+ if (event.key === "Tab" && event.shiftKey) {
162
+ context.onItemShiftTab();
163
+ return;
164
+ }
165
+ if (event.target !== event.currentTarget) return;
166
+ const focusIntent = getFocusIntent(event, context.orientation, context.dir);
167
+ if (focusIntent !== void 0) {
168
+ if (event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) return;
169
+ event.preventDefault();
170
+ const items = getItems().filter((item) => item.focusable);
171
+ let candidateNodes = items.map((item) => item.ref.current);
172
+ if (focusIntent === "last") candidateNodes.reverse();
173
+ else if (focusIntent === "prev" || focusIntent === "next") {
174
+ if (focusIntent === "prev") candidateNodes.reverse();
175
+ const currentIndex = candidateNodes.indexOf(event.currentTarget);
176
+ candidateNodes = context.loop ? wrapArray(candidateNodes, currentIndex + 1) : candidateNodes.slice(currentIndex + 1);
177
+ }
178
+ setTimeout(() => focusFirst(candidateNodes));
179
+ }
180
+ })
181
+ }
182
+ )
183
+ }
184
+ );
185
+ }
186
+ );
187
+ RovingFocusGroupItem.displayName = ITEM_NAME;
188
+ var MAP_KEY_TO_FOCUS_INTENT = {
189
+ ArrowLeft: "prev",
190
+ ArrowUp: "prev",
191
+ ArrowRight: "next",
192
+ ArrowDown: "next",
193
+ PageUp: "first",
194
+ Home: "first",
195
+ PageDown: "last",
196
+ End: "last"
197
+ };
198
+ function getDirectionAwareKey(key, dir) {
199
+ if (dir !== "rtl") return key;
200
+ return key === "ArrowLeft" ? "ArrowRight" : key === "ArrowRight" ? "ArrowLeft" : key;
201
+ }
202
+ function getFocusIntent(event, orientation, dir) {
203
+ const key = getDirectionAwareKey(event.key, dir);
204
+ if (orientation === "vertical" && ["ArrowLeft", "ArrowRight"].includes(key)) return void 0;
205
+ if (orientation === "horizontal" && ["ArrowUp", "ArrowDown"].includes(key)) return void 0;
206
+ return MAP_KEY_TO_FOCUS_INTENT[key];
207
+ }
208
+ function focusFirst(candidates, preventScroll = false) {
209
+ const PREVIOUSLY_FOCUSED_ELEMENT = document.activeElement;
210
+ for (const candidate of candidates) {
211
+ if (candidate === PREVIOUSLY_FOCUSED_ELEMENT) return;
212
+ candidate.focus({ preventScroll });
213
+ if (document.activeElement !== PREVIOUSLY_FOCUSED_ELEMENT) return;
214
+ }
215
+ }
216
+ function wrapArray(array, startIndex) {
217
+ return array.map((_, index) => array[(startIndex + index) % array.length]);
218
+ }
219
+ var Root = RovingFocusGroup;
220
+ var Item = RovingFocusGroupItem;
221
+ export {
222
+ Item,
223
+ Root,
224
+ RovingFocusGroup,
225
+ RovingFocusGroupItem,
226
+ createRovingFocusGroupScope
227
+ };
228
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/RovingFocusGroup.tsx"],
4
+ "sourcesContent": ["import * as React from 'react';\nimport { composeEventHandlers } from '@huin-core/primitive';\nimport { createCollection } from '@huin-core/react-collection';\nimport { useComposedRefs } from '@huin-core/react-compose-refs';\nimport { createContextScope } from '@huin-core/react-context';\nimport { useId } from '@huin-core/react-id';\nimport { Primitive } from '@huin-core/react-primitive';\nimport { useCallbackRef } from '@huin-core/react-use-callback-ref';\nimport { useControllableState } from '@huin-core/react-use-controllable-state';\nimport { useDirection } from '@huin-core/react-direction';\n\nimport type { Scope } from '@huin-core/react-context';\n\nconst ENTRY_FOCUS = 'rovingFocusGroup.onEntryFocus';\nconst EVENT_OPTIONS = { bubbles: false, cancelable: true };\n\n/* -------------------------------------------------------------------------------------------------\n * RovingFocusGroup\n * -----------------------------------------------------------------------------------------------*/\n\nconst GROUP_NAME = 'RovingFocusGroup';\n\ntype ItemData = { id: string; focusable: boolean; active: boolean };\nconst [Collection, useCollection, createCollectionScope] = createCollection<\n HTMLSpanElement,\n ItemData\n>(GROUP_NAME);\n\ntype ScopedProps<P> = P & { __scopeRovingFocusGroup?: Scope };\nconst [createRovingFocusGroupContext, createRovingFocusGroupScope] = createContextScope(\n GROUP_NAME,\n [createCollectionScope]\n);\n\ntype Orientation = React.AriaAttributes['aria-orientation'];\ntype Direction = 'ltr' | 'rtl';\n\ninterface RovingFocusGroupOptions {\n /**\n * The orientation of the group.\n * Mainly so arrow navigation is done accordingly (left & right vs. up & down)\n */\n orientation?: Orientation;\n /**\n * The direction of navigation between items.\n */\n dir?: Direction;\n /**\n * Whether keyboard navigation should loop around\n * @defaultValue false\n */\n loop?: boolean;\n}\n\ntype RovingContextValue = RovingFocusGroupOptions & {\n currentTabStopId: string | null;\n onItemFocus(tabStopId: string): void;\n onItemShiftTab(): void;\n onFocusableItemAdd(): void;\n onFocusableItemRemove(): void;\n};\n\nconst [RovingFocusProvider, useRovingFocusContext] =\n createRovingFocusGroupContext<RovingContextValue>(GROUP_NAME);\n\ntype RovingFocusGroupElement = RovingFocusGroupImplElement;\ninterface RovingFocusGroupProps extends RovingFocusGroupImplProps {}\n\nconst RovingFocusGroup = React.forwardRef<RovingFocusGroupElement, RovingFocusGroupProps>(\n (props: ScopedProps<RovingFocusGroupProps>, forwardedRef) => {\n return (\n <Collection.Provider scope={props.__scopeRovingFocusGroup}>\n <Collection.Slot scope={props.__scopeRovingFocusGroup}>\n <RovingFocusGroupImpl {...props} ref={forwardedRef} />\n </Collection.Slot>\n </Collection.Provider>\n );\n }\n);\n\nRovingFocusGroup.displayName = GROUP_NAME;\n\n/* -----------------------------------------------------------------------------------------------*/\n\ntype RovingFocusGroupImplElement = React.ElementRef<typeof Primitive.div>;\ntype PrimitiveDivProps = React.ComponentPropsWithoutRef<typeof Primitive.div>;\ninterface RovingFocusGroupImplProps\n extends Omit<PrimitiveDivProps, 'dir'>,\n RovingFocusGroupOptions {\n currentTabStopId?: string | null;\n defaultCurrentTabStopId?: string;\n onCurrentTabStopIdChange?: (tabStopId: string | null) => void;\n onEntryFocus?: (event: Event) => void;\n preventScrollOnEntryFocus?: boolean;\n}\n\nconst RovingFocusGroupImpl = React.forwardRef<\n RovingFocusGroupImplElement,\n RovingFocusGroupImplProps\n>((props: ScopedProps<RovingFocusGroupImplProps>, forwardedRef) => {\n const {\n __scopeRovingFocusGroup,\n orientation,\n loop = false,\n dir,\n currentTabStopId: currentTabStopIdProp,\n defaultCurrentTabStopId,\n onCurrentTabStopIdChange,\n onEntryFocus,\n preventScrollOnEntryFocus = false,\n ...groupProps\n } = props;\n const ref = React.useRef<RovingFocusGroupImplElement>(null);\n const composedRefs = useComposedRefs(forwardedRef, ref);\n const direction = useDirection(dir);\n const [currentTabStopId = null, setCurrentTabStopId] = useControllableState({\n prop: currentTabStopIdProp,\n defaultProp: defaultCurrentTabStopId,\n onChange: onCurrentTabStopIdChange,\n });\n const [isTabbingBackOut, setIsTabbingBackOut] = React.useState(false);\n const handleEntryFocus = useCallbackRef(onEntryFocus);\n const getItems = useCollection(__scopeRovingFocusGroup);\n const isClickFocusRef = React.useRef(false);\n const [focusableItemsCount, setFocusableItemsCount] = React.useState(0);\n\n React.useEffect(() => {\n const node = ref.current;\n if (node) {\n node.addEventListener(ENTRY_FOCUS, handleEntryFocus);\n return () => node.removeEventListener(ENTRY_FOCUS, handleEntryFocus);\n }\n }, [handleEntryFocus]);\n\n return (\n <RovingFocusProvider\n scope={__scopeRovingFocusGroup}\n orientation={orientation}\n dir={direction}\n loop={loop}\n currentTabStopId={currentTabStopId}\n onItemFocus={React.useCallback(\n (tabStopId) => setCurrentTabStopId(tabStopId),\n [setCurrentTabStopId]\n )}\n onItemShiftTab={React.useCallback(() => setIsTabbingBackOut(true), [])}\n onFocusableItemAdd={React.useCallback(\n () => setFocusableItemsCount((prevCount) => prevCount + 1),\n []\n )}\n onFocusableItemRemove={React.useCallback(\n () => setFocusableItemsCount((prevCount) => prevCount - 1),\n []\n )}\n >\n <Primitive.div\n tabIndex={isTabbingBackOut || focusableItemsCount === 0 ? -1 : 0}\n data-orientation={orientation}\n {...groupProps}\n ref={composedRefs}\n style={{ outline: 'none', ...props.style }}\n onMouseDown={composeEventHandlers(props.onMouseDown, () => {\n isClickFocusRef.current = true;\n })}\n onFocus={composeEventHandlers(props.onFocus, (event) => {\n // We normally wouldn't need this check, because we already check\n // that the focus is on the current target and not bubbling to it.\n // We do this because Safari doesn't focus buttons when clicked, and\n // instead, the wrapper will get focused and not through a bubbling event.\n const isKeyboardFocus = !isClickFocusRef.current;\n\n if (event.target === event.currentTarget && isKeyboardFocus && !isTabbingBackOut) {\n const entryFocusEvent = new CustomEvent(ENTRY_FOCUS, EVENT_OPTIONS);\n event.currentTarget.dispatchEvent(entryFocusEvent);\n\n if (!entryFocusEvent.defaultPrevented) {\n const items = getItems().filter((item) => item.focusable);\n const activeItem = items.find((item) => item.active);\n const currentItem = items.find((item) => item.id === currentTabStopId);\n const candidateItems = [activeItem, currentItem, ...items].filter(\n Boolean\n ) as typeof items;\n const candidateNodes = candidateItems.map((item) => item.ref.current!);\n focusFirst(candidateNodes, preventScrollOnEntryFocus);\n }\n }\n\n isClickFocusRef.current = false;\n })}\n onBlur={composeEventHandlers(props.onBlur, () => setIsTabbingBackOut(false))}\n />\n </RovingFocusProvider>\n );\n});\n\n/* -------------------------------------------------------------------------------------------------\n * RovingFocusGroupItem\n * -----------------------------------------------------------------------------------------------*/\n\nconst ITEM_NAME = 'RovingFocusGroupItem';\n\ntype RovingFocusItemElement = React.ElementRef<typeof Primitive.span>;\ntype PrimitiveSpanProps = React.ComponentPropsWithoutRef<typeof Primitive.span>;\ninterface RovingFocusItemProps extends PrimitiveSpanProps {\n tabStopId?: string;\n focusable?: boolean;\n active?: boolean;\n}\n\nconst RovingFocusGroupItem = React.forwardRef<RovingFocusItemElement, RovingFocusItemProps>(\n (props: ScopedProps<RovingFocusItemProps>, forwardedRef) => {\n const {\n __scopeRovingFocusGroup,\n focusable = true,\n active = false,\n tabStopId,\n ...itemProps\n } = props;\n const autoId = useId();\n const id = tabStopId || autoId;\n const context = useRovingFocusContext(ITEM_NAME, __scopeRovingFocusGroup);\n const isCurrentTabStop = context.currentTabStopId === id;\n const getItems = useCollection(__scopeRovingFocusGroup);\n\n const { onFocusableItemAdd, onFocusableItemRemove } = context;\n\n React.useEffect(() => {\n if (focusable) {\n onFocusableItemAdd();\n return () => onFocusableItemRemove();\n }\n }, [focusable, onFocusableItemAdd, onFocusableItemRemove]);\n\n return (\n <Collection.ItemSlot\n scope={__scopeRovingFocusGroup}\n id={id}\n focusable={focusable}\n active={active}\n >\n <Primitive.span\n tabIndex={isCurrentTabStop ? 0 : -1}\n data-orientation={context.orientation}\n {...itemProps}\n ref={forwardedRef}\n onMouseDown={composeEventHandlers(props.onMouseDown, (event) => {\n // We prevent focusing non-focusable items on `mousedown`.\n // Even though the item has tabIndex={-1}, that only means take it out of the tab order.\n if (!focusable) event.preventDefault();\n // Safari doesn't focus a button when clicked so we run our logic on mousedown also\n else context.onItemFocus(id);\n })}\n onFocus={composeEventHandlers(props.onFocus, () => context.onItemFocus(id))}\n onKeyDown={composeEventHandlers(props.onKeyDown, (event) => {\n if (event.key === 'Tab' && event.shiftKey) {\n context.onItemShiftTab();\n return;\n }\n\n if (event.target !== event.currentTarget) return;\n\n const focusIntent = getFocusIntent(event, context.orientation, context.dir);\n\n if (focusIntent !== undefined) {\n if (event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) return;\n event.preventDefault();\n const items = getItems().filter((item) => item.focusable);\n let candidateNodes = items.map((item) => item.ref.current!);\n\n if (focusIntent === 'last') candidateNodes.reverse();\n else if (focusIntent === 'prev' || focusIntent === 'next') {\n if (focusIntent === 'prev') candidateNodes.reverse();\n const currentIndex = candidateNodes.indexOf(event.currentTarget);\n candidateNodes = context.loop\n ? wrapArray(candidateNodes, currentIndex + 1)\n : candidateNodes.slice(currentIndex + 1);\n }\n\n /**\n * Imperative focus during keydown is risky so we prevent React's batching updates\n * to avoid potential bugs. See: https://github.com/facebook/react/issues/20332\n */\n setTimeout(() => focusFirst(candidateNodes));\n }\n })}\n />\n </Collection.ItemSlot>\n );\n }\n);\n\nRovingFocusGroupItem.displayName = ITEM_NAME;\n\n/* -----------------------------------------------------------------------------------------------*/\n\n// prettier-ignore\nconst MAP_KEY_TO_FOCUS_INTENT: Record<string, FocusIntent> = {\n ArrowLeft: 'prev', ArrowUp: 'prev',\n ArrowRight: 'next', ArrowDown: 'next',\n PageUp: 'first', Home: 'first',\n PageDown: 'last', End: 'last',\n};\n\nfunction getDirectionAwareKey(key: string, dir?: Direction) {\n if (dir !== 'rtl') return key;\n return key === 'ArrowLeft' ? 'ArrowRight' : key === 'ArrowRight' ? 'ArrowLeft' : key;\n}\n\ntype FocusIntent = 'first' | 'last' | 'prev' | 'next';\n\nfunction getFocusIntent(event: React.KeyboardEvent, orientation?: Orientation, dir?: Direction) {\n const key = getDirectionAwareKey(event.key, dir);\n if (orientation === 'vertical' && ['ArrowLeft', 'ArrowRight'].includes(key)) return undefined;\n if (orientation === 'horizontal' && ['ArrowUp', 'ArrowDown'].includes(key)) return undefined;\n return MAP_KEY_TO_FOCUS_INTENT[key];\n}\n\nfunction focusFirst(candidates: HTMLElement[], preventScroll = false) {\n const PREVIOUSLY_FOCUSED_ELEMENT = document.activeElement;\n for (const candidate of candidates) {\n // if focus is already where we want to go, we don't want to keep going through the candidates\n if (candidate === PREVIOUSLY_FOCUSED_ELEMENT) return;\n candidate.focus({ preventScroll });\n if (document.activeElement !== PREVIOUSLY_FOCUSED_ELEMENT) return;\n }\n}\n\n/**\n * Wraps an array around itself at a given start index\n * Example: `wrapArray(['a', 'b', 'c', 'd'], 2) === ['c', 'd', 'a', 'b']`\n */\nfunction wrapArray<T>(array: T[], startIndex: number) {\n return array.map((_, index) => array[(startIndex + index) % array.length]);\n}\n\nconst Root = RovingFocusGroup;\nconst Item = RovingFocusGroupItem;\n\nexport {\n createRovingFocusGroupScope,\n //\n RovingFocusGroup,\n RovingFocusGroupItem,\n //\n Root,\n Item,\n};\nexport type { RovingFocusGroupProps, RovingFocusItemProps };\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@huin-core/react-roving-focus",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": {
@@ -20,7 +20,8 @@
20
20
  "types": "./dist/index.d.ts",
21
21
  "files": [
22
22
  "dist",
23
- "README.md"
23
+ "README.md",
24
+ "package.json"
24
25
  ],
25
26
  "sideEffects": false,
26
27
  "scripts": {