@bento/focus-lock 0.0.2 → 0.0.3

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
@@ -18,19 +18,19 @@ The following properties are available to be used on the `FocusLock` component:
18
18
 
19
19
  | Prop | Type | Required | Description |
20
20
  |------|------|----------|------------|
21
- | `contain` | `boolean` | No | Whether to contain focus within the scope.
21
+ | `contain` | `boolean \| undefined` | No | Whether to contain focus within the scope.
22
22
  When true, focus will cycle between focusable elements within the scope. |
23
- | `restoreFocus` | `boolean` | No | Whether to restore focus to the previously focused element when the focus scope unmounts. |
24
- | `autoFocus` | `boolean` | No | Whether to automatically focus the first focusable element when the focus scope mounts. |
23
+ | `restoreFocus` | `boolean \| undefined` | No | Whether to restore focus to the previously focused element when the focus scope unmounts. |
24
+ | `autoFocus` | `boolean \| undefined` | No | Whether to automatically focus the first focusable element when the focus scope mounts. |
25
25
  | `children` | `ReactNode` | No | The content to render inside the focus lock.
26
26
  Can be a single element or multiple elements. |
27
- | `onFocusEnter` | `(e: FocusEvent<Element, Element>) => void` | No | Callback fired when focus enters the scope |
28
- | `onFocusLeave` | `(e: FocusEvent<Element, Element>) => void` | No | Callback fired when focus leaves the scope |
29
- | `className` | `string \| ((state: FocusLockState) => string)` | No | Render prop for className |
30
- | `style` | `((state: FocusLockState) => CSSProperties) \| CSSProperties` | No | Render prop for style |
31
- | `slot` | `string` | No | A named part of a component that can be customized. This is implemented by the consuming component.
27
+ | `onFocusEnter` | `((e: FocusEvent<Element, Element>) => void) \| undefined` | No | Callback fired when focus enters the scope |
28
+ | `onFocusLeave` | `((e: FocusEvent<Element, Element>) => void) \| undefined` | No | Callback fired when focus leaves the scope |
29
+ | `className` | `string \| ((state: FocusLockState) => string) \| undefined` | No | Render prop for className |
30
+ | `style` | `((state: FocusLockState) => CSSProperties) \| CSSProperties \| undefined` | No | Render prop for style |
31
+ | `slot` | `string \| undefined` | No | A named part of a component that can be customized. This is implemented by the consuming component.
32
32
  The exposed slot names of a component are available in the components documentation. |
33
- | `slots` | `Record<string, object \| Function>` | No | An object that contains the customizations for the slots.
33
+ | `slots` | `Record<string, object \| Function> \| undefined` | No | An object that contains the customizations for the slots.
34
34
  The main way you interact with the slot system as a consumer. |
35
35
 
36
36
  For all other properties specified on the `FocusLock` component, they will be
package/dist/index.cjs CHANGED
@@ -1,44 +1,131 @@
1
- 'use strict';
2
-
3
- var React = require('react');
4
- var useDataAttributes = require('@bento/use-data-attributes');
5
- var interactions = require('@react-aria/interactions');
6
- var slots = require('@bento/slots');
7
- var focus = require('@react-aria/focus');
8
- var useProps = require('@bento/use-props');
9
-
10
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
11
-
12
- var React__default = /*#__PURE__*/_interopDefault(React);
13
-
14
- // src/index.tsx
15
- var FocusLock = slots.withSlots("BentoFocusLock", function FocusLock2(args) {
16
- const { contain = false, restoreFocus = false, autoFocus = false, children, onFocusEnter, onFocusLeave } = args;
17
- const [hasFocus, setHasFocus] = React.useState(false);
18
- const { focusWithinProps } = interactions.useFocusWithin({
19
- onFocusWithinChange: function onFocusWithinChange(isFocusWithin) {
20
- setHasFocus(isFocusWithin);
21
- },
22
- onFocusWithin: onFocusEnter,
23
- onBlurWithin: onFocusLeave
24
- });
25
- const state = {
26
- hasFocus,
27
- isContained: contain
28
- };
29
- const { apply } = useProps.useProps(args, state);
30
- const data = useDataAttributes.useDataAttributes({
31
- "focus-contained": contain,
32
- "has-focus": hasFocus
33
- });
34
- if (!children) return null;
35
- const spread = apply({ contain, restoreFocus, autoFocus }, ["children", "onFocusEnter", "onFocusLeave"]);
36
- const kids = { ...focusWithinProps, ...data };
37
- return /* @__PURE__ */ React__default.default.createElement(focus.FocusScope, { ...spread }, React.Children.map(children, function applyDataAttributes(child) {
38
- return React.isValidElement(child) ? React.cloneElement(child, kids) : child;
39
- }));
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region \0rolldown/runtime.js
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 __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
+ key = keys[i];
12
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
13
+ get: ((k) => from[k]).bind(null, key),
14
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
+ });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
20
+ value: mod,
21
+ enumerable: true
22
+ }) : target, mod));
23
+ //#endregion
24
+ let react = require("react");
25
+ react = __toESM(react);
26
+ let _bento_use_data_attributes = require("@bento/use-data-attributes");
27
+ let _react_aria_interactions = require("@react-aria/interactions");
28
+ let _bento_slots = require("@bento/slots");
29
+ let _react_aria_focus = require("@react-aria/focus");
30
+ let _bento_use_props = require("@bento/use-props");
31
+ let react_jsx_runtime = require("react/jsx-runtime");
32
+ //#region src/index.tsx
33
+ /**
34
+ * FocusLock manages focus within a scope, preventing focus from escaping and optionally
35
+ * restoring focus when the scope is removed. Built on top of React ARIA's FocusScope.
36
+ *
37
+ * The FocusLock primitive provides essential focus management for modals, dialogs, drawers,
38
+ * select popovers, and other overlay components that need to trap focus within their boundaries.
39
+ *
40
+ * This component does not add any wrapper elements - it applies data attributes directly to
41
+ * its children, allowing for flexible composition with multiple elements or single elements.
42
+ *
43
+ * @component
44
+ * @param args - The properties {@link FocusLockProps} passed to the FocusLock component.
45
+ *
46
+ * @example
47
+ * ```tsx
48
+ * // Overlay with multiple children (backdrop + content)
49
+ * <FocusLock contain restoreFocus autoFocus>
50
+ * <div slot="backdrop" />
51
+ * <div slot="content">
52
+ * <h2>Modal Title</h2>
53
+ * <button>Close</button>
54
+ * </div>
55
+ * </FocusLock>
56
+ * ```
57
+ *
58
+ * @example
59
+ * ```tsx
60
+ * // Select popover with single child
61
+ * <FocusLock contain restoreFocus autoFocus>
62
+ * <div className="popover">
63
+ * <ListBox>
64
+ * <ListBoxItem>Option 1</ListBoxItem>
65
+ * <ListBoxItem>Option 2</ListBoxItem>
66
+ * </ListBox>
67
+ * </div>
68
+ * </FocusLock>
69
+ * ```
70
+ *
71
+ * @example
72
+ * ```tsx
73
+ * // With render props for dynamic styling
74
+ * <FocusLock
75
+ * contain
76
+ * className={({ hasFocus, isContained }) =>
77
+ * `modal ${isContained ? 'contained' : ''} ${hasFocus ? 'focused' : ''}`
78
+ * }
79
+ * style={({ hasFocus }) => ({
80
+ * opacity: hasFocus ? 1 : 0.8
81
+ * })}
82
+ * >
83
+ * <div>Content</div>
84
+ * </FocusLock>
85
+ * ```
86
+ *
87
+ * @public
88
+ */
89
+ const FocusLock = (0, _bento_slots.withSlots)("BentoFocusLock", function FocusLock(args) {
90
+ const { contain = false, restoreFocus = false, autoFocus = false, children, onFocusEnter, onFocusLeave } = args;
91
+ const [hasFocus, setHasFocus] = (0, react.useState)(false);
92
+ const { focusWithinProps } = (0, _react_aria_interactions.useFocusWithin)({
93
+ onFocusWithinChange: function onFocusWithinChange(isFocusWithin) {
94
+ setHasFocus(isFocusWithin);
95
+ },
96
+ onFocusWithin: onFocusEnter,
97
+ onBlurWithin: onFocusLeave
98
+ });
99
+ const { apply } = (0, _bento_use_props.useProps)(args, {
100
+ hasFocus,
101
+ isContained: contain
102
+ });
103
+ const data = (0, _bento_use_data_attributes.useDataAttributes)({
104
+ "focus-contained": contain,
105
+ "has-focus": hasFocus
106
+ });
107
+ if (!children) return null;
108
+ const spread = apply({
109
+ contain,
110
+ restoreFocus,
111
+ autoFocus
112
+ }, [
113
+ "children",
114
+ "onFocusEnter",
115
+ "onFocusLeave"
116
+ ]);
117
+ const kids = {
118
+ ...focusWithinProps,
119
+ ...data
120
+ };
121
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_react_aria_focus.FocusScope, {
122
+ ...spread,
123
+ children: react.default.Children.map(children, function applyDataAttributes(child) {
124
+ return (0, react.isValidElement)(child) ? (0, react.cloneElement)(child, kids) : child;
125
+ })
126
+ });
40
127
  });
41
-
128
+ //#endregion
42
129
  exports.FocusLock = FocusLock;
43
- //# sourceMappingURL=index.cjs.map
130
+
44
131
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.tsx"],"names":["withSlots","FocusLock","useState","useFocusWithin","useProps","useDataAttributes","React","FocusScope","Children","isValidElement","cloneElement"],"mappings":";;;;;;;;;;;;;;AAiIO,IAAM,SAAA,GAAYA,eAAA,CAAU,gBAAA,EAAkB,SAASC,WAAU,IAAA,EAAsB;AAC5F,EAAA,MAAM,EAAE,OAAA,GAAU,KAAA,EAAO,YAAA,GAAe,KAAA,EAAO,YAAY,KAAA,EAAO,QAAA,EAAU,YAAA,EAAc,YAAA,EAAa,GAAI,IAAA;AAC3G,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAIC,eAAS,KAAK,CAAA;AAG9C,EAAA,MAAM,EAAE,gBAAA,EAAiB,GAAIC,2BAAA,CAAe;AAAA,IAC1C,mBAAA,EAAqB,SAAS,mBAAA,CAAoB,aAAA,EAAe;AAC/D,MAAA,WAAA,CAAY,aAAa,CAAA;AAAA,IAC3B,CAAA;AAAA,IACA,aAAA,EAAe,YAAA;AAAA,IACf,YAAA,EAAc;AAAA,GACf,CAAA;AAGD,EAAA,MAAM,KAAA,GAAwB;AAAA,IAC5B,QAAA;AAAA,IACA,WAAA,EAAa;AAAA,GACf;AAGA,EAAA,MAAM,EAAE,KAAA,EAAM,GAAIC,iBAAA,CAAS,MAAM,KAAK,CAAA;AAGtC,EAAA,MAAM,OAAOC,mCAAA,CAAkB;AAAA,IAC7B,iBAAA,EAAmB,OAAA;AAAA,IACnB,WAAA,EAAa;AAAA,GACd,CAAA;AAED,EAAA,IAAI,CAAC,UAAU,OAAO,IAAA;AAKtB,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,EAAE,OAAA,EAAS,YAAA,EAAc,SAAA,EAAU,EAAG,CAAC,UAAA,EAAY,cAAA,EAAgB,cAAc,CAAC,CAAA;AAKvG,EAAA,MAAM,IAAA,GAAO,EAAE,GAAG,gBAAA,EAAkB,GAAG,IAAA,EAAK;AAE5C,EAAA,uBACEC,sBAAA,CAAA,aAAA,CAACC,oBAAY,GAAG,MAAA,EAAA,EACbC,eAAS,GAAA,CAAI,QAAA,EAAU,SAAS,mBAAA,CAAoB,KAAA,EAAO;AAC1D,IAAA,OAAOC,qBAAe,KAAK,CAAA,GAAIC,kBAAA,CAAa,KAAA,EAA6B,IAAI,CAAA,GAAI,KAAA;AAAA,EACnF,CAAC,CACH,CAAA;AAEJ,CAAC","file":"index.cjs","sourcesContent":["import React, { Children, cloneElement, isValidElement, type ReactNode, useState } from 'react';\nimport { useDataAttributes } from '@bento/use-data-attributes';\nimport { useFocusWithin } from '@react-aria/interactions';\nimport { withSlots, type Slots } from '@bento/slots';\nimport { FocusScope } from '@react-aria/focus';\nimport { useProps } from '@bento/use-props';\n\n/**\n * State object passed to render prop functions\n * @public\n */\nexport interface FocusLockState {\n /**\n * Whether focus is currently within the scope\n */\n hasFocus: boolean;\n\n /**\n * Whether focus is contained within the scope\n */\n isContained: boolean;\n}\n\nexport interface FocusLockProps extends Slots {\n /**\n * Whether to contain focus within the scope.\n * When true, focus will cycle between focusable elements within the scope.\n *\n * @default false\n */\n contain?: boolean;\n\n /**\n * Whether to restore focus to the previously focused element when the focus scope unmounts.\n *\n * @default false\n */\n restoreFocus?: boolean;\n\n /**\n * Whether to automatically focus the first focusable element when the focus scope mounts.\n *\n * @default false\n */\n autoFocus?: boolean;\n\n /**\n * The content to render inside the focus lock.\n * Can be a single element or multiple elements.\n */\n children?: ReactNode;\n\n /**\n * Callback fired when focus enters the scope\n */\n onFocusEnter?: (e: React.FocusEvent) => void;\n\n /**\n * Callback fired when focus leaves the scope\n */\n onFocusLeave?: (e: React.FocusEvent) => void;\n\n /**\n * Render prop for className\n */\n className?: ((state: FocusLockState) => string) | string;\n\n /**\n * Render prop for style\n */\n style?: ((state: FocusLockState) => React.CSSProperties) | React.CSSProperties;\n}\n\n/**\n * FocusLock manages focus within a scope, preventing focus from escaping and optionally\n * restoring focus when the scope is removed. Built on top of React ARIA's FocusScope.\n *\n * The FocusLock primitive provides essential focus management for modals, dialogs, drawers,\n * select popovers, and other overlay components that need to trap focus within their boundaries.\n *\n * This component does not add any wrapper elements - it applies data attributes directly to\n * its children, allowing for flexible composition with multiple elements or single elements.\n *\n * @component\n * @param args - The properties {@link FocusLockProps} passed to the FocusLock component.\n *\n * @example\n * ```tsx\n * // Overlay with multiple children (backdrop + content)\n * <FocusLock contain restoreFocus autoFocus>\n * <div slot=\"backdrop\" />\n * <div slot=\"content\">\n * <h2>Modal Title</h2>\n * <button>Close</button>\n * </div>\n * </FocusLock>\n * ```\n *\n * @example\n * ```tsx\n * // Select popover with single child\n * <FocusLock contain restoreFocus autoFocus>\n * <div className=\"popover\">\n * <ListBox>\n * <ListBoxItem>Option 1</ListBoxItem>\n * <ListBoxItem>Option 2</ListBoxItem>\n * </ListBox>\n * </div>\n * </FocusLock>\n * ```\n *\n * @example\n * ```tsx\n * // With render props for dynamic styling\n * <FocusLock\n * contain\n * className={({ hasFocus, isContained }) =>\n * `modal ${isContained ? 'contained' : ''} ${hasFocus ? 'focused' : ''}`\n * }\n * style={({ hasFocus }) => ({\n * opacity: hasFocus ? 1 : 0.8\n * })}\n * >\n * <div>Content</div>\n * </FocusLock>\n * ```\n *\n * @public\n */\nexport const FocusLock = withSlots('BentoFocusLock', function FocusLock(args: FocusLockProps) {\n const { contain = false, restoreFocus = false, autoFocus = false, children, onFocusEnter, onFocusLeave } = args;\n const [hasFocus, setHasFocus] = useState(false);\n\n // Track focus within the scope using React ARIA\n const { focusWithinProps } = useFocusWithin({\n onFocusWithinChange: function onFocusWithinChange(isFocusWithin) {\n setHasFocus(isFocusWithin);\n },\n onFocusWithin: onFocusEnter,\n onBlurWithin: onFocusLeave\n });\n\n // Create state object for render props\n const state: FocusLockState = {\n hasFocus,\n isContained: contain\n };\n\n // Pass state to useProps so render props can access it\n const { apply } = useProps(args, state);\n\n // Generate data attributes for focus lock state\n const data = useDataAttributes({\n 'focus-contained': contain,\n 'has-focus': hasFocus\n });\n\n if (!children) return null;\n\n //\n // Apply props to FocusScope for slot inheritance\n //\n const spread = apply({ contain, restoreFocus, autoFocus }, ['children', 'onFocusEnter', 'onFocusLeave']);\n\n //\n // Merge focus tracking props with data attributes to apply to children\n //\n const kids = { ...focusWithinProps, ...data };\n\n return (\n <FocusScope {...spread}>\n {Children.map(children, function applyDataAttributes(child) {\n return isValidElement(child) ? cloneElement(child as React.ReactElement, kids) : child;\n })}\n </FocusScope>\n );\n}) as (props: FocusLockProps) => React.ReactElement | null;\n"]}
1
+ {"version":3,"file":"index.cjs","names":["FocusScope","React"],"sources":["../src/index.tsx"],"sourcesContent":["import React, { cloneElement, isValidElement, type ReactNode, useState } from 'react';\nimport { useDataAttributes } from '@bento/use-data-attributes';\nimport { useFocusWithin } from '@react-aria/interactions';\nimport { withSlots, type Slots } from '@bento/slots';\nimport { FocusScope } from '@react-aria/focus';\nimport { useProps } from '@bento/use-props';\n\n/**\n * State object passed to render prop functions\n * @public\n */\nexport interface FocusLockState {\n /**\n * Whether focus is currently within the scope\n */\n hasFocus: boolean;\n\n /**\n * Whether focus is contained within the scope\n */\n isContained: boolean;\n}\n\nexport interface FocusLockProps extends Slots {\n /**\n * Whether to contain focus within the scope.\n * When true, focus will cycle between focusable elements within the scope.\n *\n * @default false\n */\n contain?: boolean;\n\n /**\n * Whether to restore focus to the previously focused element when the focus scope unmounts.\n *\n * @default false\n */\n restoreFocus?: boolean;\n\n /**\n * Whether to automatically focus the first focusable element when the focus scope mounts.\n *\n * @default false\n */\n autoFocus?: boolean;\n\n /**\n * The content to render inside the focus lock.\n * Can be a single element or multiple elements.\n */\n children?: ReactNode;\n\n /**\n * Callback fired when focus enters the scope\n */\n onFocusEnter?: (e: React.FocusEvent) => void;\n\n /**\n * Callback fired when focus leaves the scope\n */\n onFocusLeave?: (e: React.FocusEvent) => void;\n\n /**\n * Render prop for className\n */\n className?: ((state: FocusLockState) => string) | string;\n\n /**\n * Render prop for style\n */\n style?: ((state: FocusLockState) => React.CSSProperties) | React.CSSProperties;\n}\n\n/**\n * FocusLock manages focus within a scope, preventing focus from escaping and optionally\n * restoring focus when the scope is removed. Built on top of React ARIA's FocusScope.\n *\n * The FocusLock primitive provides essential focus management for modals, dialogs, drawers,\n * select popovers, and other overlay components that need to trap focus within their boundaries.\n *\n * This component does not add any wrapper elements - it applies data attributes directly to\n * its children, allowing for flexible composition with multiple elements or single elements.\n *\n * @component\n * @param args - The properties {@link FocusLockProps} passed to the FocusLock component.\n *\n * @example\n * ```tsx\n * // Overlay with multiple children (backdrop + content)\n * <FocusLock contain restoreFocus autoFocus>\n * <div slot=\"backdrop\" />\n * <div slot=\"content\">\n * <h2>Modal Title</h2>\n * <button>Close</button>\n * </div>\n * </FocusLock>\n * ```\n *\n * @example\n * ```tsx\n * // Select popover with single child\n * <FocusLock contain restoreFocus autoFocus>\n * <div className=\"popover\">\n * <ListBox>\n * <ListBoxItem>Option 1</ListBoxItem>\n * <ListBoxItem>Option 2</ListBoxItem>\n * </ListBox>\n * </div>\n * </FocusLock>\n * ```\n *\n * @example\n * ```tsx\n * // With render props for dynamic styling\n * <FocusLock\n * contain\n * className={({ hasFocus, isContained }) =>\n * `modal ${isContained ? 'contained' : ''} ${hasFocus ? 'focused' : ''}`\n * }\n * style={({ hasFocus }) => ({\n * opacity: hasFocus ? 1 : 0.8\n * })}\n * >\n * <div>Content</div>\n * </FocusLock>\n * ```\n *\n * @public\n */\nexport const FocusLock = withSlots('BentoFocusLock', function FocusLock(args: FocusLockProps) {\n const { contain = false, restoreFocus = false, autoFocus = false, children, onFocusEnter, onFocusLeave } = args;\n const [hasFocus, setHasFocus] = useState(false);\n\n // Track focus within the scope using React ARIA\n const { focusWithinProps } = useFocusWithin({\n onFocusWithinChange: function onFocusWithinChange(isFocusWithin) {\n setHasFocus(isFocusWithin);\n },\n onFocusWithin: onFocusEnter,\n onBlurWithin: onFocusLeave\n });\n\n // Create state object for render props\n const state: FocusLockState = {\n hasFocus,\n isContained: contain\n };\n\n // Pass state to useProps so render props can access it\n const { apply } = useProps(args, state);\n\n // Generate data attributes for focus lock state\n const data = useDataAttributes({\n 'focus-contained': contain,\n 'has-focus': hasFocus\n });\n\n if (!children) return null;\n\n //\n // Apply props to FocusScope for slot inheritance\n //\n const spread = apply({ contain, restoreFocus, autoFocus }, ['children', 'onFocusEnter', 'onFocusLeave']);\n\n //\n // Merge focus tracking props with data attributes to apply to children\n //\n const kids = { ...focusWithinProps, ...data };\n\n return (\n <FocusScope {...spread}>\n {React.Children.map(children, function applyDataAttributes(child) {\n return isValidElement(child) ? cloneElement(child as React.ReactElement, kids) : child;\n })}\n </FocusScope>\n );\n}) as (props: FocusLockProps) => React.ReactElement | null;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiIA,MAAa,aAAA,GAAA,aAAA,WAAsB,kBAAkB,SAAS,UAAU,MAAsB;CAC5F,MAAM,EAAE,UAAU,OAAO,eAAe,OAAO,YAAY,OAAO,UAAU,cAAc,iBAAiB;CAC3G,MAAM,CAAC,UAAU,gBAAA,GAAA,MAAA,UAAwB,KAAK;CAG9C,MAAM,EAAE,sBAAA,GAAA,yBAAA,gBAAoC;EAC1C,qBAAqB,SAAS,oBAAoB,eAAe;GAC/D,YAAY,aAAa;EAC3B;EACA,eAAe;EACf,cAAc;CAChB,CAAC;CASD,MAAM,EAAE,WAAA,GAAA,iBAAA,UAAmB,MAAM;EAL/B;EACA,aAAa;CAIsB,CAAC;CAGtC,MAAM,QAAA,GAAA,2BAAA,mBAAyB;EAC7B,mBAAmB;EACnB,aAAa;CACf,CAAC;CAED,IAAI,CAAC,UAAU,OAAO;CAKtB,MAAM,SAAS,MAAM;EAAE;EAAS;EAAc;CAAU,GAAG;EAAC;EAAY;EAAgB;CAAc,CAAC;CAKvG,MAAM,OAAO;EAAE,GAAG;EAAkB,GAAG;CAAK;CAE5C,OACE,iBAAA,GAAA,kBAAA,KAACA,kBAAAA,YAAD;EAAY,GAAI;YACbC,MAAAA,QAAM,SAAS,IAAI,UAAU,SAAS,oBAAoB,OAAO;GAChE,QAAA,GAAA,MAAA,gBAAsB,KAAK,KAAA,GAAA,MAAA,cAAiB,OAA6B,IAAI,IAAI;EACnF,CAAC;CACS,CAAA;AAEhB,CAAC"}
package/dist/index.d.cts CHANGED
@@ -1,61 +1,62 @@
1
- import React, { ReactNode } from 'react';
2
- import { Slots } from '@bento/slots';
1
+ import React, { ReactNode } from "react";
2
+ import { Slots } from "@bento/slots";
3
3
 
4
+ //#region src/index.d.ts
4
5
  /**
5
6
  * State object passed to render prop functions
6
7
  * @public
7
8
  */
8
9
  interface FocusLockState {
9
- /**
10
- * Whether focus is currently within the scope
11
- */
12
- hasFocus: boolean;
13
- /**
14
- * Whether focus is contained within the scope
15
- */
16
- isContained: boolean;
10
+ /**
11
+ * Whether focus is currently within the scope
12
+ */
13
+ hasFocus: boolean;
14
+ /**
15
+ * Whether focus is contained within the scope
16
+ */
17
+ isContained: boolean;
17
18
  }
18
19
  interface FocusLockProps extends Slots {
19
- /**
20
- * Whether to contain focus within the scope.
21
- * When true, focus will cycle between focusable elements within the scope.
22
- *
23
- * @default false
24
- */
25
- contain?: boolean;
26
- /**
27
- * Whether to restore focus to the previously focused element when the focus scope unmounts.
28
- *
29
- * @default false
30
- */
31
- restoreFocus?: boolean;
32
- /**
33
- * Whether to automatically focus the first focusable element when the focus scope mounts.
34
- *
35
- * @default false
36
- */
37
- autoFocus?: boolean;
38
- /**
39
- * The content to render inside the focus lock.
40
- * Can be a single element or multiple elements.
41
- */
42
- children?: ReactNode;
43
- /**
44
- * Callback fired when focus enters the scope
45
- */
46
- onFocusEnter?: (e: React.FocusEvent) => void;
47
- /**
48
- * Callback fired when focus leaves the scope
49
- */
50
- onFocusLeave?: (e: React.FocusEvent) => void;
51
- /**
52
- * Render prop for className
53
- */
54
- className?: ((state: FocusLockState) => string) | string;
55
- /**
56
- * Render prop for style
57
- */
58
- style?: ((state: FocusLockState) => React.CSSProperties) | React.CSSProperties;
20
+ /**
21
+ * Whether to contain focus within the scope.
22
+ * When true, focus will cycle between focusable elements within the scope.
23
+ *
24
+ * @default false
25
+ */
26
+ contain?: boolean;
27
+ /**
28
+ * Whether to restore focus to the previously focused element when the focus scope unmounts.
29
+ *
30
+ * @default false
31
+ */
32
+ restoreFocus?: boolean;
33
+ /**
34
+ * Whether to automatically focus the first focusable element when the focus scope mounts.
35
+ *
36
+ * @default false
37
+ */
38
+ autoFocus?: boolean;
39
+ /**
40
+ * The content to render inside the focus lock.
41
+ * Can be a single element or multiple elements.
42
+ */
43
+ children?: ReactNode;
44
+ /**
45
+ * Callback fired when focus enters the scope
46
+ */
47
+ onFocusEnter?: (e: React.FocusEvent) => void;
48
+ /**
49
+ * Callback fired when focus leaves the scope
50
+ */
51
+ onFocusLeave?: (e: React.FocusEvent) => void;
52
+ /**
53
+ * Render prop for className
54
+ */
55
+ className?: ((state: FocusLockState) => string) | string;
56
+ /**
57
+ * Render prop for style
58
+ */
59
+ style?: ((state: FocusLockState) => React.CSSProperties) | React.CSSProperties;
59
60
  }
60
61
  /**
61
62
  * FocusLock manages focus within a scope, preventing focus from escaping and optionally
@@ -113,6 +114,7 @@ interface FocusLockProps extends Slots {
113
114
  *
114
115
  * @public
115
116
  */
116
- declare const FocusLock: (props: FocusLockProps) => React.ReactElement | null;
117
-
118
- export { FocusLock, type FocusLockProps, type FocusLockState };
117
+ declare const FocusLock: (props: FocusLockProps) => React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | null;
118
+ //#endregion
119
+ export { FocusLock, FocusLockProps, FocusLockState };
120
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/index.tsx"],"mappings":";;;;;;AAWA;;UAAiB,cAAA;EAIf;AAKW;AAGb;EARE,QAAA;;;;EAKA,WAAW;AAAA;AAAA,UAGI,cAAA,SAAuB,KAAA;EA+CF;;;;;;EAxCpC,OAAA;EAOA;;;;;EAAA,YAAA;EAkByB;;;;;EAXzB,SAAA;EAqBA;;;;EAfA,QAAA,GAAW,SAAA;EAoBD;;;EAfV,YAAA,IAAgB,CAAA,EAAG,KAAA,CAAM,UAAA;EAewC;;AAAa;EAV9E,YAAA,IAAgB,CAAA,EAAG,KAAA,CAAM,UAAA;EAoH+B;;;EA/GxD,SAAA,KAAc,KAAA,EAAO,cAAA;EA+GU;;;EA1G/B,KAAA,KAAU,KAAA,EAAO,cAAA,KAAmB,KAAA,CAAM,aAAA,IAAiB,KAAA,CAAM,aAAA;AAAA;;;;;;AA0GlC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA/CpB,SAAA,GA+CN,KAAA,EAAK,cAAA,KAAqB,KAAA,CAAA,YAAA,mBAAA,KAAA,CAAA,qBAAA"}
@@ -1,61 +1,62 @@
1
- import React, { ReactNode } from 'react';
2
- import { Slots } from '@bento/slots';
1
+ import React, { ReactNode } from "react";
2
+ import { Slots } from "@bento/slots";
3
3
 
4
+ //#region src/index.d.ts
4
5
  /**
5
6
  * State object passed to render prop functions
6
7
  * @public
7
8
  */
8
9
  interface FocusLockState {
9
- /**
10
- * Whether focus is currently within the scope
11
- */
12
- hasFocus: boolean;
13
- /**
14
- * Whether focus is contained within the scope
15
- */
16
- isContained: boolean;
10
+ /**
11
+ * Whether focus is currently within the scope
12
+ */
13
+ hasFocus: boolean;
14
+ /**
15
+ * Whether focus is contained within the scope
16
+ */
17
+ isContained: boolean;
17
18
  }
18
19
  interface FocusLockProps extends Slots {
19
- /**
20
- * Whether to contain focus within the scope.
21
- * When true, focus will cycle between focusable elements within the scope.
22
- *
23
- * @default false
24
- */
25
- contain?: boolean;
26
- /**
27
- * Whether to restore focus to the previously focused element when the focus scope unmounts.
28
- *
29
- * @default false
30
- */
31
- restoreFocus?: boolean;
32
- /**
33
- * Whether to automatically focus the first focusable element when the focus scope mounts.
34
- *
35
- * @default false
36
- */
37
- autoFocus?: boolean;
38
- /**
39
- * The content to render inside the focus lock.
40
- * Can be a single element or multiple elements.
41
- */
42
- children?: ReactNode;
43
- /**
44
- * Callback fired when focus enters the scope
45
- */
46
- onFocusEnter?: (e: React.FocusEvent) => void;
47
- /**
48
- * Callback fired when focus leaves the scope
49
- */
50
- onFocusLeave?: (e: React.FocusEvent) => void;
51
- /**
52
- * Render prop for className
53
- */
54
- className?: ((state: FocusLockState) => string) | string;
55
- /**
56
- * Render prop for style
57
- */
58
- style?: ((state: FocusLockState) => React.CSSProperties) | React.CSSProperties;
20
+ /**
21
+ * Whether to contain focus within the scope.
22
+ * When true, focus will cycle between focusable elements within the scope.
23
+ *
24
+ * @default false
25
+ */
26
+ contain?: boolean;
27
+ /**
28
+ * Whether to restore focus to the previously focused element when the focus scope unmounts.
29
+ *
30
+ * @default false
31
+ */
32
+ restoreFocus?: boolean;
33
+ /**
34
+ * Whether to automatically focus the first focusable element when the focus scope mounts.
35
+ *
36
+ * @default false
37
+ */
38
+ autoFocus?: boolean;
39
+ /**
40
+ * The content to render inside the focus lock.
41
+ * Can be a single element or multiple elements.
42
+ */
43
+ children?: ReactNode;
44
+ /**
45
+ * Callback fired when focus enters the scope
46
+ */
47
+ onFocusEnter?: (e: React.FocusEvent) => void;
48
+ /**
49
+ * Callback fired when focus leaves the scope
50
+ */
51
+ onFocusLeave?: (e: React.FocusEvent) => void;
52
+ /**
53
+ * Render prop for className
54
+ */
55
+ className?: ((state: FocusLockState) => string) | string;
56
+ /**
57
+ * Render prop for style
58
+ */
59
+ style?: ((state: FocusLockState) => React.CSSProperties) | React.CSSProperties;
59
60
  }
60
61
  /**
61
62
  * FocusLock manages focus within a scope, preventing focus from escaping and optionally
@@ -113,6 +114,7 @@ interface FocusLockProps extends Slots {
113
114
  *
114
115
  * @public
115
116
  */
116
- declare const FocusLock: (props: FocusLockProps) => React.ReactElement | null;
117
-
118
- export { FocusLock, type FocusLockProps, type FocusLockState };
117
+ declare const FocusLock: (props: FocusLockProps) => React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | null;
118
+ //#endregion
119
+ export { FocusLock, FocusLockProps, FocusLockState };
120
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.tsx"],"mappings":";;;;;;AAWA;;UAAiB,cAAA;EAIf;AAKW;AAGb;EARE,QAAA;;;;EAKA,WAAW;AAAA;AAAA,UAGI,cAAA,SAAuB,KAAA;EA+CF;;;;;;EAxCpC,OAAA;EAOA;;;;;EAAA,YAAA;EAkByB;;;;;EAXzB,SAAA;EAqBA;;;;EAfA,QAAA,GAAW,SAAA;EAoBD;;;EAfV,YAAA,IAAgB,CAAA,EAAG,KAAA,CAAM,UAAA;EAewC;;AAAa;EAV9E,YAAA,IAAgB,CAAA,EAAG,KAAA,CAAM,UAAA;EAoH+B;;;EA/GxD,SAAA,KAAc,KAAA,EAAO,cAAA;EA+GU;;;EA1G/B,KAAA,KAAU,KAAA,EAAO,cAAA,KAAmB,KAAA,CAAM,aAAA,IAAiB,KAAA,CAAM,aAAA;AAAA;;;;;;AA0GlC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA/CpB,SAAA,GA+CN,KAAA,EAAK,cAAA,KAAqB,KAAA,CAAA,YAAA,mBAAA,KAAA,CAAA,qBAAA"}
package/dist/index.mjs ADDED
@@ -0,0 +1,107 @@
1
+ import React, { cloneElement, isValidElement, useState } from "react";
2
+ import { useDataAttributes } from "@bento/use-data-attributes";
3
+ import { useFocusWithin } from "@react-aria/interactions";
4
+ import { withSlots } from "@bento/slots";
5
+ import { FocusScope } from "@react-aria/focus";
6
+ import { useProps } from "@bento/use-props";
7
+ import { jsx } from "react/jsx-runtime";
8
+ //#region src/index.tsx
9
+ /**
10
+ * FocusLock manages focus within a scope, preventing focus from escaping and optionally
11
+ * restoring focus when the scope is removed. Built on top of React ARIA's FocusScope.
12
+ *
13
+ * The FocusLock primitive provides essential focus management for modals, dialogs, drawers,
14
+ * select popovers, and other overlay components that need to trap focus within their boundaries.
15
+ *
16
+ * This component does not add any wrapper elements - it applies data attributes directly to
17
+ * its children, allowing for flexible composition with multiple elements or single elements.
18
+ *
19
+ * @component
20
+ * @param args - The properties {@link FocusLockProps} passed to the FocusLock component.
21
+ *
22
+ * @example
23
+ * ```tsx
24
+ * // Overlay with multiple children (backdrop + content)
25
+ * <FocusLock contain restoreFocus autoFocus>
26
+ * <div slot="backdrop" />
27
+ * <div slot="content">
28
+ * <h2>Modal Title</h2>
29
+ * <button>Close</button>
30
+ * </div>
31
+ * </FocusLock>
32
+ * ```
33
+ *
34
+ * @example
35
+ * ```tsx
36
+ * // Select popover with single child
37
+ * <FocusLock contain restoreFocus autoFocus>
38
+ * <div className="popover">
39
+ * <ListBox>
40
+ * <ListBoxItem>Option 1</ListBoxItem>
41
+ * <ListBoxItem>Option 2</ListBoxItem>
42
+ * </ListBox>
43
+ * </div>
44
+ * </FocusLock>
45
+ * ```
46
+ *
47
+ * @example
48
+ * ```tsx
49
+ * // With render props for dynamic styling
50
+ * <FocusLock
51
+ * contain
52
+ * className={({ hasFocus, isContained }) =>
53
+ * `modal ${isContained ? 'contained' : ''} ${hasFocus ? 'focused' : ''}`
54
+ * }
55
+ * style={({ hasFocus }) => ({
56
+ * opacity: hasFocus ? 1 : 0.8
57
+ * })}
58
+ * >
59
+ * <div>Content</div>
60
+ * </FocusLock>
61
+ * ```
62
+ *
63
+ * @public
64
+ */
65
+ const FocusLock = withSlots("BentoFocusLock", function FocusLock(args) {
66
+ const { contain = false, restoreFocus = false, autoFocus = false, children, onFocusEnter, onFocusLeave } = args;
67
+ const [hasFocus, setHasFocus] = useState(false);
68
+ const { focusWithinProps } = useFocusWithin({
69
+ onFocusWithinChange: function onFocusWithinChange(isFocusWithin) {
70
+ setHasFocus(isFocusWithin);
71
+ },
72
+ onFocusWithin: onFocusEnter,
73
+ onBlurWithin: onFocusLeave
74
+ });
75
+ const { apply } = useProps(args, {
76
+ hasFocus,
77
+ isContained: contain
78
+ });
79
+ const data = useDataAttributes({
80
+ "focus-contained": contain,
81
+ "has-focus": hasFocus
82
+ });
83
+ if (!children) return null;
84
+ const spread = apply({
85
+ contain,
86
+ restoreFocus,
87
+ autoFocus
88
+ }, [
89
+ "children",
90
+ "onFocusEnter",
91
+ "onFocusLeave"
92
+ ]);
93
+ const kids = {
94
+ ...focusWithinProps,
95
+ ...data
96
+ };
97
+ return /* @__PURE__ */ jsx(FocusScope, {
98
+ ...spread,
99
+ children: React.Children.map(children, function applyDataAttributes(child) {
100
+ return isValidElement(child) ? cloneElement(child, kids) : child;
101
+ })
102
+ });
103
+ });
104
+ //#endregion
105
+ export { FocusLock };
106
+
107
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/index.tsx"],"sourcesContent":["import React, { cloneElement, isValidElement, type ReactNode, useState } from 'react';\nimport { useDataAttributes } from '@bento/use-data-attributes';\nimport { useFocusWithin } from '@react-aria/interactions';\nimport { withSlots, type Slots } from '@bento/slots';\nimport { FocusScope } from '@react-aria/focus';\nimport { useProps } from '@bento/use-props';\n\n/**\n * State object passed to render prop functions\n * @public\n */\nexport interface FocusLockState {\n /**\n * Whether focus is currently within the scope\n */\n hasFocus: boolean;\n\n /**\n * Whether focus is contained within the scope\n */\n isContained: boolean;\n}\n\nexport interface FocusLockProps extends Slots {\n /**\n * Whether to contain focus within the scope.\n * When true, focus will cycle between focusable elements within the scope.\n *\n * @default false\n */\n contain?: boolean;\n\n /**\n * Whether to restore focus to the previously focused element when the focus scope unmounts.\n *\n * @default false\n */\n restoreFocus?: boolean;\n\n /**\n * Whether to automatically focus the first focusable element when the focus scope mounts.\n *\n * @default false\n */\n autoFocus?: boolean;\n\n /**\n * The content to render inside the focus lock.\n * Can be a single element or multiple elements.\n */\n children?: ReactNode;\n\n /**\n * Callback fired when focus enters the scope\n */\n onFocusEnter?: (e: React.FocusEvent) => void;\n\n /**\n * Callback fired when focus leaves the scope\n */\n onFocusLeave?: (e: React.FocusEvent) => void;\n\n /**\n * Render prop for className\n */\n className?: ((state: FocusLockState) => string) | string;\n\n /**\n * Render prop for style\n */\n style?: ((state: FocusLockState) => React.CSSProperties) | React.CSSProperties;\n}\n\n/**\n * FocusLock manages focus within a scope, preventing focus from escaping and optionally\n * restoring focus when the scope is removed. Built on top of React ARIA's FocusScope.\n *\n * The FocusLock primitive provides essential focus management for modals, dialogs, drawers,\n * select popovers, and other overlay components that need to trap focus within their boundaries.\n *\n * This component does not add any wrapper elements - it applies data attributes directly to\n * its children, allowing for flexible composition with multiple elements or single elements.\n *\n * @component\n * @param args - The properties {@link FocusLockProps} passed to the FocusLock component.\n *\n * @example\n * ```tsx\n * // Overlay with multiple children (backdrop + content)\n * <FocusLock contain restoreFocus autoFocus>\n * <div slot=\"backdrop\" />\n * <div slot=\"content\">\n * <h2>Modal Title</h2>\n * <button>Close</button>\n * </div>\n * </FocusLock>\n * ```\n *\n * @example\n * ```tsx\n * // Select popover with single child\n * <FocusLock contain restoreFocus autoFocus>\n * <div className=\"popover\">\n * <ListBox>\n * <ListBoxItem>Option 1</ListBoxItem>\n * <ListBoxItem>Option 2</ListBoxItem>\n * </ListBox>\n * </div>\n * </FocusLock>\n * ```\n *\n * @example\n * ```tsx\n * // With render props for dynamic styling\n * <FocusLock\n * contain\n * className={({ hasFocus, isContained }) =>\n * `modal ${isContained ? 'contained' : ''} ${hasFocus ? 'focused' : ''}`\n * }\n * style={({ hasFocus }) => ({\n * opacity: hasFocus ? 1 : 0.8\n * })}\n * >\n * <div>Content</div>\n * </FocusLock>\n * ```\n *\n * @public\n */\nexport const FocusLock = withSlots('BentoFocusLock', function FocusLock(args: FocusLockProps) {\n const { contain = false, restoreFocus = false, autoFocus = false, children, onFocusEnter, onFocusLeave } = args;\n const [hasFocus, setHasFocus] = useState(false);\n\n // Track focus within the scope using React ARIA\n const { focusWithinProps } = useFocusWithin({\n onFocusWithinChange: function onFocusWithinChange(isFocusWithin) {\n setHasFocus(isFocusWithin);\n },\n onFocusWithin: onFocusEnter,\n onBlurWithin: onFocusLeave\n });\n\n // Create state object for render props\n const state: FocusLockState = {\n hasFocus,\n isContained: contain\n };\n\n // Pass state to useProps so render props can access it\n const { apply } = useProps(args, state);\n\n // Generate data attributes for focus lock state\n const data = useDataAttributes({\n 'focus-contained': contain,\n 'has-focus': hasFocus\n });\n\n if (!children) return null;\n\n //\n // Apply props to FocusScope for slot inheritance\n //\n const spread = apply({ contain, restoreFocus, autoFocus }, ['children', 'onFocusEnter', 'onFocusLeave']);\n\n //\n // Merge focus tracking props with data attributes to apply to children\n //\n const kids = { ...focusWithinProps, ...data };\n\n return (\n <FocusScope {...spread}>\n {React.Children.map(children, function applyDataAttributes(child) {\n return isValidElement(child) ? cloneElement(child as React.ReactElement, kids) : child;\n })}\n </FocusScope>\n );\n}) as (props: FocusLockProps) => React.ReactElement | null;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiIA,MAAa,YAAY,UAAU,kBAAkB,SAAS,UAAU,MAAsB;CAC5F,MAAM,EAAE,UAAU,OAAO,eAAe,OAAO,YAAY,OAAO,UAAU,cAAc,iBAAiB;CAC3G,MAAM,CAAC,UAAU,eAAe,SAAS,KAAK;CAG9C,MAAM,EAAE,qBAAqB,eAAe;EAC1C,qBAAqB,SAAS,oBAAoB,eAAe;GAC/D,YAAY,aAAa;EAC3B;EACA,eAAe;EACf,cAAc;CAChB,CAAC;CASD,MAAM,EAAE,UAAU,SAAS,MAAM;EAL/B;EACA,aAAa;CAIsB,CAAC;CAGtC,MAAM,OAAO,kBAAkB;EAC7B,mBAAmB;EACnB,aAAa;CACf,CAAC;CAED,IAAI,CAAC,UAAU,OAAO;CAKtB,MAAM,SAAS,MAAM;EAAE;EAAS;EAAc;CAAU,GAAG;EAAC;EAAY;EAAgB;CAAc,CAAC;CAKvG,MAAM,OAAO;EAAE,GAAG;EAAkB,GAAG;CAAK;CAE5C,OACE,oBAAC,YAAD;EAAY,GAAI;YACb,MAAM,SAAS,IAAI,UAAU,SAAS,oBAAoB,OAAO;GAChE,OAAO,eAAe,KAAK,IAAI,aAAa,OAA6B,IAAI,IAAI;EACnF,CAAC;CACS,CAAA;AAEhB,CAAC"}
package/package.json CHANGED
@@ -1,22 +1,21 @@
1
1
  {
2
2
  "name": "@bento/focus-lock",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "Focus lock primitive for managing focus within a scope",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
7
- "module": "./dist/index.js",
7
+ "module": "./dist/index.mjs",
8
8
  "scripts": {
9
- "build": "tsup-node",
10
- "lint": "biome lint && tsc",
11
- "posttest": "npm run lint",
12
- "prepublishOnly": "node ../../scripts/compile-readme.ts",
13
- "pretest": "npm run build",
9
+ "build": "tsdown",
10
+ "lint": "biome lint && tsgo --noEmit",
11
+ "prepublishOnly": "node ../../../scripts/compile-readme.ts",
14
12
  "test": "vitest --run",
15
- "test:watch": "vitest"
13
+ "test:watch": "vitest",
14
+ "typecheck": "tsgo --noEmit -p tsconfig.json"
16
15
  },
17
16
  "repository": {
18
17
  "type": "git",
19
- "url": "git+https://github.com/godaddy/bento.git"
18
+ "url": "git+https://github.com/godaddy/antares.git"
20
19
  },
21
20
  "keywords": [
22
21
  "accessibility",
@@ -33,9 +32,9 @@
33
32
  "author": "GoDaddy Operating Company, LLC",
34
33
  "license": "MIT",
35
34
  "bugs": {
36
- "url": "https://github.com/godaddy/bento/issues"
35
+ "url": "https://github.com/godaddy/antares/issues"
37
36
  },
38
- "homepage": "https://github.com/godaddy/bento#readme",
37
+ "homepage": "https://github.com/godaddy/antares#readme",
39
38
  "files": [
40
39
  "dist",
41
40
  "src",
@@ -44,9 +43,9 @@
44
43
  "dependencies": {
45
44
  "@bento/slots": "^0.3.0",
46
45
  "@bento/use-data-attributes": "^0.1.1",
47
- "@bento/use-props": "^0.2.1",
48
- "@react-aria/focus": "^3.18.6",
49
- "@react-aria/interactions": "^3.22.6"
46
+ "@bento/use-props": "^0.2.3",
47
+ "@react-aria/focus": "^3.22.0",
48
+ "@react-aria/interactions": "^3.28.0"
50
49
  },
51
50
  "devDependencies": {
52
51
  "@bento/button": "*",
@@ -54,7 +53,13 @@
54
53
  "@bento/heading": "*",
55
54
  "@bento/listbox": "*",
56
55
  "@bento/radio": "*",
57
- "@bento/text": "*"
56
+ "@bento/text": "*",
57
+ "@types/react": "^19.2.15",
58
+ "@types/react-dom": "^19.2.3",
59
+ "tsdown": "^0.22.1",
60
+ "typescript": "^6.0.3",
61
+ "vitest": "^4.1.7",
62
+ "vitest-browser-react": "^2.2.0"
58
63
  },
59
64
  "peerDependencies": {
60
65
  "react": "18.x || 19.x",
@@ -63,8 +68,8 @@
63
68
  "exports": {
64
69
  ".": {
65
70
  "import": {
66
- "types": "./dist/index.d.ts",
67
- "default": "./dist/index.js"
71
+ "types": "./dist/index.d.mts",
72
+ "default": "./dist/index.mjs"
68
73
  },
69
74
  "require": {
70
75
  "types": "./dist/index.d.cts",
package/src/index.tsx CHANGED
@@ -1,4 +1,4 @@
1
- import React, { Children, cloneElement, isValidElement, type ReactNode, useState } from 'react';
1
+ import React, { cloneElement, isValidElement, type ReactNode, useState } from 'react';
2
2
  import { useDataAttributes } from '@bento/use-data-attributes';
3
3
  import { useFocusWithin } from '@react-aria/interactions';
4
4
  import { withSlots, type Slots } from '@bento/slots';
@@ -169,7 +169,7 @@ export const FocusLock = withSlots('BentoFocusLock', function FocusLock(args: Fo
169
169
 
170
170
  return (
171
171
  <FocusScope {...spread}>
172
- {Children.map(children, function applyDataAttributes(child) {
172
+ {React.Children.map(children, function applyDataAttributes(child) {
173
173
  return isValidElement(child) ? cloneElement(child as React.ReactElement, kids) : child;
174
174
  })}
175
175
  </FocusScope>
package/dist/index.js DELETED
@@ -1,38 +0,0 @@
1
- import React, { useState, Children, cloneElement, isValidElement } from 'react';
2
- import { useDataAttributes } from '@bento/use-data-attributes';
3
- import { useFocusWithin } from '@react-aria/interactions';
4
- import { withSlots } from '@bento/slots';
5
- import { FocusScope } from '@react-aria/focus';
6
- import { useProps } from '@bento/use-props';
7
-
8
- // src/index.tsx
9
- var FocusLock = withSlots("BentoFocusLock", function FocusLock2(args) {
10
- const { contain = false, restoreFocus = false, autoFocus = false, children, onFocusEnter, onFocusLeave } = args;
11
- const [hasFocus, setHasFocus] = useState(false);
12
- const { focusWithinProps } = useFocusWithin({
13
- onFocusWithinChange: function onFocusWithinChange(isFocusWithin) {
14
- setHasFocus(isFocusWithin);
15
- },
16
- onFocusWithin: onFocusEnter,
17
- onBlurWithin: onFocusLeave
18
- });
19
- const state = {
20
- hasFocus,
21
- isContained: contain
22
- };
23
- const { apply } = useProps(args, state);
24
- const data = useDataAttributes({
25
- "focus-contained": contain,
26
- "has-focus": hasFocus
27
- });
28
- if (!children) return null;
29
- const spread = apply({ contain, restoreFocus, autoFocus }, ["children", "onFocusEnter", "onFocusLeave"]);
30
- const kids = { ...focusWithinProps, ...data };
31
- return /* @__PURE__ */ React.createElement(FocusScope, { ...spread }, Children.map(children, function applyDataAttributes(child) {
32
- return isValidElement(child) ? cloneElement(child, kids) : child;
33
- }));
34
- });
35
-
36
- export { FocusLock };
37
- //# sourceMappingURL=index.js.map
38
- //# sourceMappingURL=index.js.map
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/index.tsx"],"names":["FocusLock"],"mappings":";;;;;;;;AAiIO,IAAM,SAAA,GAAY,SAAA,CAAU,gBAAA,EAAkB,SAASA,WAAU,IAAA,EAAsB;AAC5F,EAAA,MAAM,EAAE,OAAA,GAAU,KAAA,EAAO,YAAA,GAAe,KAAA,EAAO,YAAY,KAAA,EAAO,QAAA,EAAU,YAAA,EAAc,YAAA,EAAa,GAAI,IAAA;AAC3G,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,SAAS,KAAK,CAAA;AAG9C,EAAA,MAAM,EAAE,gBAAA,EAAiB,GAAI,cAAA,CAAe;AAAA,IAC1C,mBAAA,EAAqB,SAAS,mBAAA,CAAoB,aAAA,EAAe;AAC/D,MAAA,WAAA,CAAY,aAAa,CAAA;AAAA,IAC3B,CAAA;AAAA,IACA,aAAA,EAAe,YAAA;AAAA,IACf,YAAA,EAAc;AAAA,GACf,CAAA;AAGD,EAAA,MAAM,KAAA,GAAwB;AAAA,IAC5B,QAAA;AAAA,IACA,WAAA,EAAa;AAAA,GACf;AAGA,EAAA,MAAM,EAAE,KAAA,EAAM,GAAI,QAAA,CAAS,MAAM,KAAK,CAAA;AAGtC,EAAA,MAAM,OAAO,iBAAA,CAAkB;AAAA,IAC7B,iBAAA,EAAmB,OAAA;AAAA,IACnB,WAAA,EAAa;AAAA,GACd,CAAA;AAED,EAAA,IAAI,CAAC,UAAU,OAAO,IAAA;AAKtB,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,EAAE,OAAA,EAAS,YAAA,EAAc,SAAA,EAAU,EAAG,CAAC,UAAA,EAAY,cAAA,EAAgB,cAAc,CAAC,CAAA;AAKvG,EAAA,MAAM,IAAA,GAAO,EAAE,GAAG,gBAAA,EAAkB,GAAG,IAAA,EAAK;AAE5C,EAAA,uBACE,KAAA,CAAA,aAAA,CAAC,cAAY,GAAG,MAAA,EAAA,EACb,SAAS,GAAA,CAAI,QAAA,EAAU,SAAS,mBAAA,CAAoB,KAAA,EAAO;AAC1D,IAAA,OAAO,eAAe,KAAK,CAAA,GAAI,YAAA,CAAa,KAAA,EAA6B,IAAI,CAAA,GAAI,KAAA;AAAA,EACnF,CAAC,CACH,CAAA;AAEJ,CAAC","file":"index.js","sourcesContent":["import React, { Children, cloneElement, isValidElement, type ReactNode, useState } from 'react';\nimport { useDataAttributes } from '@bento/use-data-attributes';\nimport { useFocusWithin } from '@react-aria/interactions';\nimport { withSlots, type Slots } from '@bento/slots';\nimport { FocusScope } from '@react-aria/focus';\nimport { useProps } from '@bento/use-props';\n\n/**\n * State object passed to render prop functions\n * @public\n */\nexport interface FocusLockState {\n /**\n * Whether focus is currently within the scope\n */\n hasFocus: boolean;\n\n /**\n * Whether focus is contained within the scope\n */\n isContained: boolean;\n}\n\nexport interface FocusLockProps extends Slots {\n /**\n * Whether to contain focus within the scope.\n * When true, focus will cycle between focusable elements within the scope.\n *\n * @default false\n */\n contain?: boolean;\n\n /**\n * Whether to restore focus to the previously focused element when the focus scope unmounts.\n *\n * @default false\n */\n restoreFocus?: boolean;\n\n /**\n * Whether to automatically focus the first focusable element when the focus scope mounts.\n *\n * @default false\n */\n autoFocus?: boolean;\n\n /**\n * The content to render inside the focus lock.\n * Can be a single element or multiple elements.\n */\n children?: ReactNode;\n\n /**\n * Callback fired when focus enters the scope\n */\n onFocusEnter?: (e: React.FocusEvent) => void;\n\n /**\n * Callback fired when focus leaves the scope\n */\n onFocusLeave?: (e: React.FocusEvent) => void;\n\n /**\n * Render prop for className\n */\n className?: ((state: FocusLockState) => string) | string;\n\n /**\n * Render prop for style\n */\n style?: ((state: FocusLockState) => React.CSSProperties) | React.CSSProperties;\n}\n\n/**\n * FocusLock manages focus within a scope, preventing focus from escaping and optionally\n * restoring focus when the scope is removed. Built on top of React ARIA's FocusScope.\n *\n * The FocusLock primitive provides essential focus management for modals, dialogs, drawers,\n * select popovers, and other overlay components that need to trap focus within their boundaries.\n *\n * This component does not add any wrapper elements - it applies data attributes directly to\n * its children, allowing for flexible composition with multiple elements or single elements.\n *\n * @component\n * @param args - The properties {@link FocusLockProps} passed to the FocusLock component.\n *\n * @example\n * ```tsx\n * // Overlay with multiple children (backdrop + content)\n * <FocusLock contain restoreFocus autoFocus>\n * <div slot=\"backdrop\" />\n * <div slot=\"content\">\n * <h2>Modal Title</h2>\n * <button>Close</button>\n * </div>\n * </FocusLock>\n * ```\n *\n * @example\n * ```tsx\n * // Select popover with single child\n * <FocusLock contain restoreFocus autoFocus>\n * <div className=\"popover\">\n * <ListBox>\n * <ListBoxItem>Option 1</ListBoxItem>\n * <ListBoxItem>Option 2</ListBoxItem>\n * </ListBox>\n * </div>\n * </FocusLock>\n * ```\n *\n * @example\n * ```tsx\n * // With render props for dynamic styling\n * <FocusLock\n * contain\n * className={({ hasFocus, isContained }) =>\n * `modal ${isContained ? 'contained' : ''} ${hasFocus ? 'focused' : ''}`\n * }\n * style={({ hasFocus }) => ({\n * opacity: hasFocus ? 1 : 0.8\n * })}\n * >\n * <div>Content</div>\n * </FocusLock>\n * ```\n *\n * @public\n */\nexport const FocusLock = withSlots('BentoFocusLock', function FocusLock(args: FocusLockProps) {\n const { contain = false, restoreFocus = false, autoFocus = false, children, onFocusEnter, onFocusLeave } = args;\n const [hasFocus, setHasFocus] = useState(false);\n\n // Track focus within the scope using React ARIA\n const { focusWithinProps } = useFocusWithin({\n onFocusWithinChange: function onFocusWithinChange(isFocusWithin) {\n setHasFocus(isFocusWithin);\n },\n onFocusWithin: onFocusEnter,\n onBlurWithin: onFocusLeave\n });\n\n // Create state object for render props\n const state: FocusLockState = {\n hasFocus,\n isContained: contain\n };\n\n // Pass state to useProps so render props can access it\n const { apply } = useProps(args, state);\n\n // Generate data attributes for focus lock state\n const data = useDataAttributes({\n 'focus-contained': contain,\n 'has-focus': hasFocus\n });\n\n if (!children) return null;\n\n //\n // Apply props to FocusScope for slot inheritance\n //\n const spread = apply({ contain, restoreFocus, autoFocus }, ['children', 'onFocusEnter', 'onFocusLeave']);\n\n //\n // Merge focus tracking props with data attributes to apply to children\n //\n const kids = { ...focusWithinProps, ...data };\n\n return (\n <FocusScope {...spread}>\n {Children.map(children, function applyDataAttributes(child) {\n return isValidElement(child) ? cloneElement(child as React.ReactElement, kids) : child;\n })}\n </FocusScope>\n );\n}) as (props: FocusLockProps) => React.ReactElement | null;\n"]}