├── .editorconfig
├── .gitattributes
├── .gitignore
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── example
├── app.json
├── babel.config.js
├── index.js
├── metro.config.js
├── package.json
├── patches
│ ├── @storybook+react-native+5.3.23.patch
│ └── core-js+3.8.3.patch
├── src
│ └── App.tsx
├── storybook
│ ├── addons.js
│ ├── index.js
│ ├── rn-addons.js
│ └── stories
│ │ ├── Button
│ │ ├── Button.stories.tsx
│ │ ├── Button.tsx
│ │ ├── ToggleButton.stories.tsx
│ │ └── ToggleButton.tsx
│ │ ├── Checkbox
│ │ ├── Checkbox.stories.tsx
│ │ ├── Checkbox.tsx
│ │ └── CheckboxGroup.tsx
│ │ ├── Combobox
│ │ ├── Combobox.stories.tsx
│ │ └── index.tsx
│ │ ├── Disclosure
│ │ ├── Disclosure.stories.tsx
│ │ ├── Disclosure.tsx
│ │ └── index.tsx
│ │ ├── Listbox
│ │ ├── Listbox.stories.tsx
│ │ └── index.tsx
│ │ ├── Menu
│ │ ├── Menu.stories.tsx
│ │ ├── Menu.tsx
│ │ └── index.tsx
│ │ ├── Overlays
│ │ ├── Overlays.stories.tsx
│ │ └── index.tsx
│ │ ├── Popover
│ │ ├── Popover.stories.tsx
│ │ └── index.tsx
│ │ ├── Radio
│ │ ├── Radio.stories.tsx
│ │ └── index.tsx
│ │ ├── Slider
│ │ ├── Slider.stories.tsx
│ │ └── index.tsx
│ │ ├── Switch
│ │ ├── Switch.stories.tsx
│ │ └── index.tsx
│ │ ├── Tabs
│ │ ├── Tabs.stories.tsx
│ │ └── index.tsx
│ │ ├── Tooltip
│ │ ├── Tooltip.stories.tsx
│ │ └── index.tsx
│ │ ├── Wrapper.tsx
│ │ ├── index.js
│ │ └── useOverlayPosition
│ │ ├── useOverlayPosition.stories.tsx
│ │ └── useOverlayPosition.tsx
├── tsconfig.json
├── webpack.config.js
└── yarn.lock
├── lerna.json
├── package.json
├── packages
├── button
│ ├── README.md
│ ├── babel.config.js
│ ├── package.json
│ ├── scripts
│ │ └── bootstrap.js
│ ├── src
│ │ ├── index.ts
│ │ ├── index.web.ts
│ │ ├── useButton.ts
│ │ ├── useToggleButton.ts
│ │ └── useToggleButton.web.ts
│ └── tsconfig.build.json
├── checkbox
│ ├── README.md
│ ├── babel.config.js
│ ├── package.json
│ ├── scripts
│ │ └── bootstrap.js
│ ├── src
│ │ ├── index.ts
│ │ ├── index.web.ts
│ │ ├── useCheckbox.ts
│ │ ├── useCheckboxGroup.ts
│ │ ├── useCheckboxGroup.web.ts
│ │ ├── useCheckboxGroupItem.ts
│ │ └── utils.ts
│ └── tsconfig.build.json
├── combobox
│ ├── README.md
│ ├── babel.config.js
│ ├── package.json
│ ├── scripts
│ │ └── bootstrap.js
│ ├── src
│ │ ├── index.ts
│ │ ├── index.web.ts
│ │ ├── useComboBox.ts
│ │ └── useComboBox.web.ts
│ └── tsconfig.build.json
├── disclosure
│ ├── README.md
│ ├── babel.config.js
│ ├── package.json
│ ├── scripts
│ │ └── bootstrap.js
│ ├── src
│ │ ├── index.ts
│ │ ├── index.web.ts
│ │ ├── useDisclosure.ts
│ │ ├── useDisclosure.web.ts
│ │ ├── useDisclosureButton.ts
│ │ ├── useDisclosureButton.web.ts
│ │ └── utils.ts
│ └── tsconfig.build.json
├── focus
│ ├── README.md
│ ├── babel.config.js
│ ├── package.json
│ ├── scripts
│ │ └── bootstrap.js
│ ├── src
│ │ ├── FocusScope.tsx
│ │ ├── FocusScope.web.tsx
│ │ ├── index.ts
│ │ ├── index.web.ts
│ │ ├── useFocusRing.ts
│ │ └── useFocusRing.web.ts
│ └── tsconfig.build.json
├── interactions
│ ├── README.md
│ ├── babel.config.js
│ ├── package.json
│ ├── scripts
│ │ └── bootstrap.js
│ ├── src
│ │ ├── index.ts
│ │ ├── index.web.ts
│ │ ├── useHover.ts
│ │ ├── useHover.web.ts
│ │ └── usePress.ts
│ └── tsconfig.build.json
├── listbox
│ ├── README.md
│ ├── babel.config.js
│ ├── package.json
│ ├── scripts
│ │ └── bootstrap.js
│ ├── src
│ │ ├── index.ts
│ │ ├── index.web.ts
│ │ ├── useListBox.ts
│ │ ├── useListBox.web.ts
│ │ ├── useOption.ts
│ │ ├── useOption.web.ts
│ │ └── utils.ts
│ └── tsconfig.build.json
├── menu
│ ├── README.md
│ ├── babel.config.js
│ ├── package.json
│ ├── scripts
│ │ └── bootstrap.js
│ ├── src
│ │ ├── index.ts
│ │ ├── index.web.ts
│ │ ├── useMenu.ts
│ │ ├── useMenu.web.ts
│ │ ├── useMenuItem.ts
│ │ ├── useMenuItem.web.ts
│ │ ├── useMenuSection.ts
│ │ ├── useMenuTrigger.ts
│ │ └── useMenuTrigger.web.ts
│ └── tsconfig.build.json
├── overlays
│ ├── README.md
│ ├── babel.config.js
│ ├── package.json
│ ├── scripts
│ │ └── bootstrap.js
│ ├── src
│ │ ├── Portal.tsx
│ │ ├── index.ts
│ │ ├── index.web.ts
│ │ ├── useOverlay.ts
│ │ ├── useOverlay.web.ts
│ │ ├── useOverlayPosition.ts
│ │ ├── useOverlayPosition.web.ts
│ │ ├── useOverlayTrigger.ts
│ │ ├── useOverlayTrigger.web.ts
│ │ ├── usePreventScroll.ts
│ │ ├── usePreventScroll.web.ts
│ │ └── web
│ │ │ └── overlays
│ │ │ ├── index.ts
│ │ │ └── src
│ │ │ ├── calculatePosition.ts
│ │ │ ├── index.ts
│ │ │ ├── useCloseOnScroll.ts
│ │ │ └── useOverlayPosition.ts
│ └── tsconfig.build.json
├── radio
│ ├── README.md
│ ├── babel.config.js
│ ├── package.json
│ ├── scripts
│ │ └── bootstrap.js
│ ├── src
│ │ ├── index.ts
│ │ ├── index.web.ts
│ │ ├── useRadio.ts
│ │ ├── useRadioGroup.ts
│ │ └── useRadioGroup.web.ts
│ └── tsconfig.build.json
├── react-native-aria
│ ├── README.md
│ ├── babel.config.js
│ ├── package.json
│ ├── scripts
│ │ └── bootstrap.js
│ ├── src
│ │ └── index.ts
│ └── tsconfig.build.json
├── separator
│ ├── README.md
│ ├── babel.config.js
│ ├── package.json
│ ├── scripts
│ │ └── bootstrap.js
│ ├── src
│ │ ├── index.ts
│ │ └── useSeparator.ts
│ └── tsconfig.build.json
├── slider
│ ├── README.md
│ ├── babel.config.js
│ ├── package.json
│ ├── scripts
│ │ └── bootstrap.js
│ ├── src
│ │ ├── index.ts
│ │ ├── textSelection.ts
│ │ ├── useMove.ts
│ │ ├── useMove.web.ts
│ │ ├── usePanResponder.ts
│ │ ├── useSlider.ts
│ │ ├── useSlider.web.ts
│ │ ├── useSliderThumb.ts
│ │ ├── useSliderThumb.web.ts
│ │ └── utils.ts
│ └── tsconfig.build.json
├── switch
│ ├── README.md
│ ├── babel.config.js
│ ├── package.json
│ ├── scripts
│ │ └── bootstrap.js
│ ├── src
│ │ ├── index.ts
│ │ ├── index.web.ts
│ │ ├── useSwitch.ts
│ │ └── useSwitch.web.ts
│ └── tsconfig.build.json
├── tabs
│ ├── README.md
│ ├── babel.config.js
│ ├── package.json
│ ├── scripts
│ │ └── bootstrap.js
│ ├── src
│ │ ├── TabsKeyboardDelegate.ts
│ │ ├── index.ts
│ │ ├── index.web.ts
│ │ ├── useTab.ts
│ │ ├── useTab.web.ts
│ │ ├── useTabs.ts
│ │ └── useTabs.web.ts
│ └── tsconfig.build.json
├── toggle
│ ├── README.md
│ ├── babel.config.js
│ ├── package.json
│ ├── scripts
│ │ └── bootstrap.js
│ ├── src
│ │ ├── index.ts
│ │ ├── index.web.ts
│ │ ├── useToggle.ts
│ │ └── useToggle.web.ts
│ └── tsconfig.build.json
├── tooltip
│ ├── README.md
│ ├── babel.config.js
│ ├── package.json
│ ├── scripts
│ │ └── bootstrap.js
│ ├── src
│ │ ├── index.ts
│ │ ├── index.web.ts
│ │ ├── useTooltip.web.ts
│ │ └── useTooltipTrigger.web.ts
│ └── tsconfig.build.json
├── tsconfig.json
└── utils
│ ├── README.md
│ ├── babel.config.js
│ ├── package.json
│ ├── scripts
│ └── bootstrap.js
│ ├── src
│ ├── __tests__
│ │ └── ariaToAccessibilityMap.test.ts
│ ├── ariaToAccessibilityMap.ts
│ └── index.ts
│ └── tsconfig.build.json
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | # EditorConfig helps developers define and maintain consistent
2 | # coding styles between different editors and IDEs
3 | # editorconfig.org
4 |
5 | root = true
6 |
7 | [*]
8 |
9 | indent_style = space
10 | indent_size = 2
11 |
12 | end_of_line = lf
13 | charset = utf-8
14 | trim_trailing_whitespace = true
15 | insert_final_newline = true
16 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.pbxproj -text
2 | # specific for windows script files
3 | *.bat text eol=crlf
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # XDE
6 | .expo/
7 |
8 | # VSCode
9 | .vscode/
10 | jsconfig.json
11 |
12 | # Xcode
13 | #
14 | build/
15 | *.pbxuser
16 | !default.pbxuser
17 | *.mode1v3
18 | !default.mode1v3
19 | *.mode2v3
20 | !default.mode2v3
21 | *.perspectivev3
22 | !default.perspectivev3
23 | xcuserdata
24 | *.xccheckout
25 | *.moved-aside
26 | DerivedData
27 | *.hmap
28 | *.ipa
29 | *.xcuserstate
30 | project.xcworkspace
31 |
32 | # Android/IJ
33 | #
34 | .idea
35 | .gradle
36 | local.properties
37 | android.iml
38 |
39 | # Cocoapods
40 | #
41 | example/ios/Pods
42 |
43 | # node.js
44 | #
45 | node_modules/
46 | npm-debug.log
47 | yarn-debug.log
48 | yarn-error.log
49 |
50 | # BUCK
51 | buck-out/
52 | \.buckd/
53 | android/app/libs
54 | android/keystores/debug.keystore
55 |
56 | # Expo
57 | .expo/*
58 |
59 | # generated by bob
60 | lib/
61 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 nishan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/example/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-bob-mono-example",
3 | "displayName": "BobMono Example",
4 | "expo": {
5 | "name": "react-native-bob-mono-example",
6 | "slug": "react-native-bob-mono-example",
7 | "description": "Example app for react-native-bob-mono",
8 | "privacy": "public",
9 | "version": "1.0.0",
10 | "platforms": [
11 | "ios",
12 | "android",
13 | "web"
14 | ],
15 | "ios": {
16 | "supportsTablet": true
17 | },
18 | "assetBundlePatterns": [
19 | "**/*"
20 | ]
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/example/babel.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const pak = require("../package.json");
3 |
4 | const packagesPath = path.join(__dirname, "..", "packages");
5 | const rootNodeModules = path.join(__dirname, "..", "node_modules");
6 |
7 | const alias = {
8 | "^@react-native-aria/(.+)": `${packagesPath}/\\1/src`,
9 | // Major Hack : Fix later, Resolve to root react to prevent invalid hook call error
10 | react: `${rootNodeModules}/react`,
11 | };
12 |
13 | module.exports = function (api) {
14 | api.cache(true);
15 |
16 | return {
17 | presets: ["babel-preset-expo"],
18 | plugins: [
19 | [
20 | "module-resolver",
21 | {
22 | alias: {
23 | // For development, we want to alias the library to the source
24 | ...alias,
25 | },
26 | },
27 | ],
28 | ],
29 | };
30 | };
31 |
--------------------------------------------------------------------------------
/example/index.js:
--------------------------------------------------------------------------------
1 | import { registerRootComponent } from 'expo';
2 |
3 | import App from './src/App';
4 |
5 | // registerRootComponent calls AppRegistry.registerComponent('main', () => App);
6 | // It also ensures that whether you load the app in the Expo client or in a native build,
7 | // the environment is set up appropriately
8 | registerRootComponent(App);
9 |
--------------------------------------------------------------------------------
/example/metro.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const blacklist = require("metro-config/src/defaults/blacklist");
3 | const escape = require("escape-string-regexp");
4 | const pak = require("../package.json");
5 |
6 | const root = path.resolve(__dirname, "..");
7 |
8 | const modules = Object.keys({
9 | ...pak.peerDependencies,
10 | });
11 |
12 | module.exports = {
13 | projectRoot: __dirname,
14 | watchFolders: [root],
15 |
16 | // We need to make sure that only one version is loaded for peerDependencies
17 | // So we blacklist them at the root, and alias them to the versions in example's node_modules
18 | resolver: {
19 | blacklistRE: blacklist(
20 | modules.map(
21 | (m) =>
22 | new RegExp(`^${escape(path.join(root, "node_modules", m))}\\/.*$`)
23 | )
24 | ),
25 |
26 | extraNodeModules: modules.reduce((acc, name) => {
27 | acc[name] = path.join(__dirname, "node_modules", name);
28 | return acc;
29 | }, {}),
30 | },
31 |
32 | transformer: {
33 | getTransformOptions: async () => ({
34 | transform: {
35 | experimentalImportSupport: false,
36 | inlineRequires: true,
37 | },
38 | }),
39 | },
40 | };
41 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-bob-mono-example",
3 | "description": "Example app for react-native-bob-mono",
4 | "version": "0.0.1",
5 | "private": true,
6 | "main": "index",
7 | "scripts": {
8 | "android": "expo start --android",
9 | "ios": "expo start --ios",
10 | "web": "expo start --web",
11 | "start": "expo start",
12 | "test": "jest",
13 | "storybook": "start-storybook -p 7007",
14 | "build-storybook": "build-storybook",
15 | "postinstall": "patch-package"
16 | },
17 | "dependencies": {
18 | "expo": "^40.0.0",
19 | "expo-splash-screen": "~0.8.1",
20 | "patch-package": "^6.2.2",
21 | "react-native": "0.63.4",
22 | "react-native-safe-area-context": "3.1.9",
23 | "react-native-unimodules": "~0.12.0",
24 | "react-native-web": "^0.15.6"
25 | },
26 | "devDependencies": {
27 | "@babel/core": "~7.12.10",
28 | "@babel/runtime": "^7.9.6",
29 | "@storybook/addon-actions": "^5.3",
30 | "@storybook/addon-knobs": "^5.3",
31 | "@storybook/addon-links": "^5.3",
32 | "@storybook/addon-ondevice-actions": "^5.3.23",
33 | "@storybook/addon-ondevice-knobs": "^5.3.23",
34 | "@storybook/react-native": "^5.3.23",
35 | "@storybook/react-native-server": "^5.3.23",
36 | "babel-loader": "^8.2.2",
37 | "babel-plugin-module-resolver": "^4.0.0",
38 | "babel-preset-expo": "8.3.0",
39 | "expo-cli": "^4.0.13"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/example/patches/@storybook+react-native+5.3.23.patch:
--------------------------------------------------------------------------------
1 | diff --git a/node_modules/@storybook/react-native/dist/preview/components/OnDeviceUI/index.js b/node_modules/@storybook/react-native/dist/preview/components/OnDeviceUI/index.js
2 | index bb73f6a..c94d1b2 100644
3 | --- a/node_modules/@storybook/react-native/dist/preview/components/OnDeviceUI/index.js
4 | +++ b/node_modules/@storybook/react-native/dist/preview/components/OnDeviceUI/index.js
5 | @@ -99,8 +99,7 @@ var OnDeviceUI = /** @class */ (function (_super) {
6 | animation_1.getPreviewPosition(this.animatedValue, previewWidth, previewHeight, slideBetweenAnimation),
7 | ];
8 | var previewStyles = [flex, animation_1.getPreviewScale(this.animatedValue, slideBetweenAnimation)];
9 | - return (react_1.default.createElement(react_native_1.SafeAreaView, { style: flex },
10 | - react_1.default.createElement(react_native_1.KeyboardAvoidingView, { enabled: !shouldDisableKeyboardAvoidingView || tabOpen !== constants_1.PREVIEW, behavior: IS_IOS ? 'padding' : null, keyboardVerticalOffset: keyboardAvoidingViewVerticalOffset, style: flex },
11 | + return (react_1.default.createElement(react_native_1.KeyboardAvoidingView, { enabled: !shouldDisableKeyboardAvoidingView || tabOpen !== constants_1.PREVIEW, behavior: IS_IOS ? 'padding' : null, keyboardVerticalOffset: keyboardAvoidingViewVerticalOffset, style: flex },
12 | react_1.default.createElement(absolute_positioned_keyboard_aware_view_1.default, { onLayout: this.onLayout, previewHeight: previewHeight, previewWidth: previewWidth },
13 | react_1.default.createElement(react_native_1.Animated.View, { style: previewWrapperStyles },
14 | react_1.default.createElement(react_native_1.Animated.View, { style: previewStyles },
15 | @@ -111,7 +110,7 @@ var OnDeviceUI = /** @class */ (function (_super) {
16 | react_1.default.createElement(StoryListView_1.default, { stories: stories })),
17 | react_1.default.createElement(panel_1.default, { style: animation_1.getAddonPanelPosition(this.animatedValue, previewWidth) },
18 | react_1.default.createElement(addons_2.default, null))),
19 | - react_1.default.createElement(navigation_1.default, { tabOpen: tabOpen, onChangeTab: this.handleToggleTab, initialUiVisible: !isUIHidden }))));
20 | + react_1.default.createElement(navigation_1.default, { tabOpen: tabOpen, onChangeTab: this.handleToggleTab, initialUiVisible: !isUIHidden })));
21 | };
22 | return OnDeviceUI;
23 | }(react_1.PureComponent));
24 |
--------------------------------------------------------------------------------
/example/src/App.tsx:
--------------------------------------------------------------------------------
1 | //@ts-ignore
2 | export { default } from "../storybook";
3 |
--------------------------------------------------------------------------------
/example/storybook/addons.js:
--------------------------------------------------------------------------------
1 | // import '@storybook/addon-actions/register';
2 | // import '@storybook/addon-links/register';
3 | // import '@storybook/addon-knobs/register';
4 |
--------------------------------------------------------------------------------
/example/storybook/index.js:
--------------------------------------------------------------------------------
1 | // if you use expo remove this line
2 | import { AppRegistry } from 'react-native';
3 |
4 | import { getStorybookUI, configure, addDecorator } from '@storybook/react-native';
5 | import { withKnobs } from '@storybook/addon-knobs';
6 |
7 | import './rn-addons';
8 |
9 | // enables knobs for all stories
10 | addDecorator(withKnobs);
11 |
12 | // import stories
13 | configure(() => {
14 | require('./stories');
15 | }, module);
16 |
17 | // Refer to https://github.com/storybookjs/storybook/tree/master/app/react-native#start-command-parameters
18 | // To find allowed options for getStorybookUI
19 | const StorybookUIRoot = getStorybookUI({});
20 |
21 | // If you are using React Native vanilla and after installation you don't see your app name here, write it manually.
22 | // If you use Expo you should remove this line.
23 | AppRegistry.registerComponent('%APP_NAME%', () => StorybookUIRoot);
24 |
25 | export default StorybookUIRoot;
26 |
--------------------------------------------------------------------------------
/example/storybook/rn-addons.js:
--------------------------------------------------------------------------------
1 | // import '@storybook/addon-ondevice-actions/register';
2 | // import '@storybook/addon-ondevice-knobs/register';
3 |
--------------------------------------------------------------------------------
/example/storybook/stories/Button/Button.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { storiesOf } from "@storybook/react-native";
3 | import { Button } from "./Button";
4 | import { Wrapper } from "../Wrapper";
5 |
6 | export const Example = () => {
7 | return (
8 |
9 |
10 |
11 | );
12 | };
13 |
14 | storiesOf("Button", module).add("Button", Example);
15 |
--------------------------------------------------------------------------------
/example/storybook/stories/Button/Button.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useHover } from "@react-native-aria/interactions";
3 | import { useButton } from "@react-native-aria/button";
4 | import { useFocusRing } from "@react-native-aria/focus";
5 | import { OverlayContainer, OverlayProvider } from "@react-native-aria/overlays";
6 | import { Pressable, Text, View } from "react-native";
7 | import { useRef } from "react";
8 |
9 | export function Button(props: any) {
10 | const ref = useRef(null);
11 | let { buttonProps, isPressed } = useButton(props);
12 |
13 | const { isHovered, hoverProps } = useHover({}, ref);
14 |
15 | const { focusProps, isFocusVisible } = useFocusRing();
16 |
17 | return (
18 |
19 |
29 |
34 | A simple button
35 |
36 |
37 |
38 |
39 |
40 | {isFocusVisible ? "focus visible" : "not focus visible"}
41 |
42 |
43 | {isHovered ? "Hovering" : "Not hovering"}
44 |
45 |
46 |
47 | );
48 | }
49 |
--------------------------------------------------------------------------------
/example/storybook/stories/Button/ToggleButton.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { storiesOf } from "@storybook/react-native";
3 | import { ToggleButton } from "./ToggleButton";
4 | import { Wrapper } from "../Wrapper";
5 |
6 | export const Example = () => {
7 | return (
8 |
9 | Toggle button
10 |
11 | );
12 | };
13 |
14 | storiesOf("Button", module).add("Toggle Button", Example);
15 |
--------------------------------------------------------------------------------
/example/storybook/stories/Button/ToggleButton.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useHover } from "@react-native-aria/interactions";
3 | import { useToggleButton } from "@react-native-aria/button";
4 | import { useFocusRing } from "@react-native-aria/focus";
5 | import { useToggleState } from "@react-stately/toggle";
6 | import { Pressable, Text, View } from "react-native";
7 | import { useRef } from "react";
8 |
9 | export function ToggleButton(props: any) {
10 | const ref = useRef(null);
11 | let state = useToggleState(props);
12 | let { buttonProps, isPressed } = useToggleButton(props, state, ref);
13 |
14 | const { isHovered, hoverProps } = useHover({}, ref);
15 |
16 | const { focusProps, isFocusVisible } = useFocusRing();
17 |
18 | return (
19 |
20 |
30 |
35 | A simple toggle button
36 |
37 |
38 |
39 |
40 |
41 | {isFocusVisible ? "focus visible" : "not focus visible"}
42 |
43 |
44 | {isHovered ? "Hovering" : "Not hovering"}
45 |
46 |
47 | {state.isSelected ? "Selected" : "Not Selected"}
48 |
49 |
50 |
51 | );
52 | }
53 |
--------------------------------------------------------------------------------
/example/storybook/stories/Checkbox/Checkbox.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { storiesOf } from "@storybook/react-native";
3 | import { CheckboxGroup } from "./CheckboxGroup";
4 | import { Checkbox } from "./Checkbox";
5 | import { Text } from "react-native";
6 | import { Wrapper } from "../Wrapper";
7 |
8 | const CheckboxExample = () => {
9 | const [state, setCheckbox] = React.useState([]);
10 |
11 | return (
12 | {
16 | setCheckbox(val);
17 | }}
18 | >
19 |
20 | Soccer
21 |
22 |
23 | Baseball
24 |
25 |
26 | Basketball
27 |
28 |
29 | );
30 | };
31 |
32 | export const Example = () => {
33 | return (
34 |
35 |
36 |
37 | );
38 | };
39 |
40 | storiesOf("Checkbox", module).add("Checkbox group", Example);
41 |
--------------------------------------------------------------------------------
/example/storybook/stories/Checkbox/Checkbox.tsx:
--------------------------------------------------------------------------------
1 | import React, { useContext, useRef } from 'react';
2 | import { Platform, Pressable, View } from 'react-native';
3 | import { useCheckbox, useCheckboxGroupItem } from '@react-native-aria/checkbox';
4 | import { useFocusRing } from '@react-native-aria/focus';
5 | import { VisuallyHidden } from '@react-aria/visually-hidden';
6 | import { useToggleState } from '@react-stately/toggle';
7 | import { MaterialCommunityIcons } from '@expo/vector-icons';
8 | import { CheckboxGroupContext } from './CheckboxGroup';
9 |
10 | export function Checkbox(props: any) {
11 | let groupState = useContext(CheckboxGroupContext);
12 | let inputRef = useRef(null);
13 |
14 | let { isFocusVisible, focusProps } = useFocusRing();
15 |
16 | let { inputProps } = groupState
17 | ? // eslint-disable-next-line react-hooks/rules-of-hooks
18 | useCheckboxGroupItem(
19 | {
20 | ...props,
21 | // Only pass isRequired and validationState to react-aria if they came from
22 | // the props for this individual checkbox, and not from the group via context.
23 | isRequired: props.isRequired,
24 | validationState: props.validationState,
25 | },
26 | groupState,
27 | inputRef
28 | )
29 | : // eslint-disable-next-line react-hooks/rules-of-hooks
30 | useCheckbox(props, useToggleState(props), inputRef);
31 |
32 | let icon: any = props.isIndeterminate
33 | ? 'checkbox-intermediate'
34 | : inputProps.checked
35 | ? 'checkbox-marked'
36 | : 'checkbox-blank-outline';
37 |
38 | const iconColor = props.isDisabled ? '#d1d1d1' : '#000';
39 |
40 | return (
41 |
42 | {Platform.OS === 'web' ? (
43 |
53 | ) : (
54 |
55 |
56 |
57 | {props.children}
58 |
59 |
60 | )}
61 |
62 | );
63 | }
64 |
--------------------------------------------------------------------------------
/example/storybook/stories/Checkbox/CheckboxGroup.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useCheckboxGroupState } from "@react-stately/checkbox";
3 | import { useCheckboxGroup } from "@react-native-aria/checkbox";
4 | import { Text, View } from "react-native";
5 |
6 | export let CheckboxGroupContext = React.createContext(null);
7 |
8 | export function CheckboxGroup(props: any) {
9 | let { children, label } = props;
10 | let state = useCheckboxGroupState(props);
11 | let { groupProps, labelProps } = useCheckboxGroup(props, state);
12 |
13 | return (
14 |
15 | {label && {label}}
16 |
17 | {children}
18 |
19 |
20 | );
21 | }
22 |
--------------------------------------------------------------------------------
/example/storybook/stories/Combobox/Combobox.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { storiesOf } from "@storybook/react-native";
3 | import { Wrapper } from "../Wrapper";
4 | import { ComboBox } from "./index";
5 | import { Item } from "@react-stately/collections";
6 |
7 | export const Example = () => {
8 | return (
9 |
10 |
11 | - Red Panda
12 | - Cat
13 | - Dog
14 | - Aardvark
15 | - Kangaroo
16 | - Snake
17 |
18 |
19 | );
20 | };
21 |
22 | storiesOf("Combobox", module).add("Basic", () => );
23 |
--------------------------------------------------------------------------------
/example/storybook/stories/Disclosure/Disclosure.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { storiesOf } from "@storybook/react-native";
3 | import { View } from "react-native";
4 | import { Disclosure } from "./index";
5 | import { Wrapper } from "../Wrapper";
6 |
7 | const DisclosureExample = () => {
8 | return ;
9 | };
10 |
11 | const Example = () => {
12 | return (
13 |
14 |
15 |
16 |
17 |
18 | );
19 | };
20 |
21 | storiesOf("Disclosure", module).add("Disclosure", Example);
22 |
--------------------------------------------------------------------------------
/example/storybook/stories/Disclosure/Disclosure.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useToggleState } from "@react-stately/toggle";
3 | import {
4 | useDisclosureButton,
5 | useDisclosure,
6 | } from "@react-native-aria/disclosure";
7 | import { Pressable, Text, View } from "react-native";
8 |
9 | type Props = {
10 | isOpen?: boolean;
11 | onChange?: (val: boolean) => void;
12 | onToggle?: () => void;
13 | defaultOpen?: boolean;
14 | isDisabled?: boolean;
15 | };
16 |
17 | export const Disclosure = (props: Props) => {
18 | const state = useToggleState({
19 | isSelected: props.isOpen,
20 | isDisabled: props.isDisabled,
21 | onChange: (val) => {
22 | props.onChange && props.onChange(val);
23 | props.onToggle && props.onToggle();
24 | },
25 | });
26 |
27 | const { buttonProps } = useDisclosureButton({}, state);
28 | const { disclosureProps } = useDisclosure({}, state);
29 |
30 | return (
31 |
32 |
33 | Press me to toggle below content
34 |
35 | {state.isSelected && (
36 |
37 | Hello. My name is disclosed
38 |
39 | )}
40 |
41 | );
42 | };
43 |
--------------------------------------------------------------------------------
/example/storybook/stories/Disclosure/index.tsx:
--------------------------------------------------------------------------------
1 | export * from "./Disclosure";
2 |
--------------------------------------------------------------------------------
/example/storybook/stories/Listbox/Listbox.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { storiesOf } from "@storybook/react-native";
3 | import { Wrapper } from "../Wrapper";
4 | import { ListBox } from "./index";
5 | import { Item } from "@react-stately/collections";
6 |
7 | export const Example = () => {
8 | return (
9 |
10 |
16 | - One
17 | - Two
18 | - Three
19 |
20 |
21 | );
22 | };
23 |
24 | storiesOf("Listbox", module).add("Basic", Example);
25 |
--------------------------------------------------------------------------------
/example/storybook/stories/Listbox/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useListBox, useOption } from "@react-native-aria/listbox";
3 | import { useListState } from "@react-stately/list";
4 | import { SpectrumListBoxProps } from "@react-types/listbox";
5 | import { useFocusRing } from "@react-native-aria/focus";
6 | import { mergeProps } from "@react-aria/utils";
7 | import { Pressable, Text, View } from "react-native";
8 |
9 | type IListBoxProps = SpectrumListBoxProps & {
10 | children: any;
11 | label?: string;
12 | };
13 |
14 | export function ListBox(props: IListBoxProps) {
15 | // Create state based on the incoming props
16 | let state = useListState(props);
17 |
18 | // Get props for the listbox element
19 | let ref = React.useRef();
20 | let { listBoxProps, labelProps } = useListBox(props, state, ref);
21 |
22 | return (
23 | <>
24 | {props.label}
25 |
35 | {[...state.collection].map((item) => (
36 |
37 | ))}
38 |
39 | >
40 | );
41 | }
42 |
43 | function Option({ item, state }) {
44 | // Get props for the option element
45 | let ref = React.useRef();
46 | let isDisabled = state.disabledKeys.has(item.key);
47 | let isSelected = state.selectionManager.isSelected(item.key);
48 | let { optionProps } = useOption(
49 | {
50 | key: item.key,
51 | isDisabled,
52 | isSelected,
53 | },
54 | state,
55 | ref
56 | );
57 |
58 | // Determine whether we should show a keyboard
59 | // focus ring for accessibility
60 | let { isFocusVisible, focusProps } = useFocusRing();
61 |
62 | return (
63 |
74 | {item.rendered}
75 |
76 | );
77 | }
78 |
--------------------------------------------------------------------------------
/example/storybook/stories/Menu/Menu.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { storiesOf } from "@storybook/react-native";
3 | import { MenuButton } from "./index";
4 | import { Item, Section } from "@react-stately/collections";
5 | import { View } from "react-native";
6 | import { Wrapper } from "../Wrapper";
7 |
8 | const MenuExample = () => {
9 | return (
10 |
11 |
18 |
19 | - Copy
20 | - Cut
21 | - Paste
22 |
23 |
24 | - Copy
25 | - Cut
26 | - Paste
27 |
28 |
29 |
30 | );
31 | };
32 |
33 | const Example = () => {
34 | return (
35 |
36 |
37 |
38 | );
39 | };
40 |
41 | storiesOf("Menu", module).add("Menu", Example);
42 |
--------------------------------------------------------------------------------
/example/storybook/stories/Menu/index.tsx:
--------------------------------------------------------------------------------
1 | export * from './Menu';
2 |
--------------------------------------------------------------------------------
/example/storybook/stories/Overlays/Overlays.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { storiesOf } from "@storybook/react-native";
3 | import { OverlayContainerExample } from "./index";
4 | import { Wrapper } from "../Wrapper";
5 |
6 | const MenuExample = () => {
7 | return (
8 |
9 |
10 |
11 | );
12 | };
13 |
14 | storiesOf("Overlay", module).add("Overlay", MenuExample);
15 |
--------------------------------------------------------------------------------
/example/storybook/stories/Overlays/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import {
3 | Pressable,
4 | View,
5 | Text,
6 | StyleSheet,
7 | TouchableWithoutFeedback,
8 | Button,
9 | } from "react-native";
10 | import {
11 | OverlayContainer,
12 | useOverlayPosition,
13 | } from "@react-native-aria/overlays";
14 |
15 | function CloseButton(props: any) {
16 | return (
17 |
22 |
23 |
24 | );
25 | }
26 |
27 | const OverlayContent = ({ targetRef }) => {
28 | let overlayRef = React.useRef(null);
29 | const { overlayProps } = useOverlayPosition({
30 | placement: "bottom",
31 | targetRef,
32 | overlayRef,
33 | });
34 |
35 | return (
36 |
43 | This content will be mounted in OverlayProvider
44 |
45 | );
46 | };
47 |
48 | export function OverlayContainerExample(props: any) {
49 | const [visible, setVisible] = React.useState(false);
50 |
51 | let ref = React.useRef(null);
52 |
53 | return (
54 |
55 | setVisible(!visible)}
59 | style={{
60 | backgroundColor: "#F3F4F6",
61 | maxWidth: 100,
62 | padding: 10,
63 | justifyContent: "center",
64 | alignItems: "center",
65 | }}
66 | >
67 | Press me
68 |
69 | {visible && (
70 |
71 | setVisible(!visible)} />
72 |
73 |
74 | )}
75 |
76 | );
77 | }
78 |
--------------------------------------------------------------------------------
/example/storybook/stories/Popover/Popover.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { storiesOf } from "@storybook/react-native";
3 | import { PopoverExample } from "./index";
4 | import { Wrapper } from "../Wrapper";
5 |
6 | export const Example = () => {
7 | return (
8 |
9 |
10 |
11 | );
12 | };
13 |
14 | storiesOf("Popover", module).add("Popover", Example);
15 |
--------------------------------------------------------------------------------
/example/storybook/stories/Popover/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import {
3 | Pressable,
4 | View,
5 | Text,
6 | StyleSheet,
7 | TouchableWithoutFeedback,
8 | Button,
9 | } from "react-native";
10 | import {
11 | OverlayContainer,
12 | OverlayProvider,
13 | useOverlayPosition,
14 | } from "@react-native-aria/overlays";
15 |
16 | function CloseButton(props: any) {
17 | return (
18 |
23 |
24 |
25 | );
26 | }
27 |
28 | const PopoverContent = ({ targetRef }) => {
29 | let overlayRef = React.useRef(null);
30 | const { overlayProps } = useOverlayPosition({
31 | placement: "top",
32 | targetRef,
33 | overlayRef,
34 | });
35 |
36 | return (
37 |
44 |
52 |
53 | Popover Title
54 |
55 |
56 |
57 | Lorem Ipsum is simply dummy text of the printing and typesetting
58 | industry. Lorem Ipsum has been the industry's standard dummy text
59 | ever since the 1500s, when an unknown printer took a galley of type
60 | and scrambled it to make a type specimen book.
61 |
62 |
63 |
70 |
71 |
72 |
73 |
74 |
75 | );
76 | };
77 |
78 | export function PopoverExample(props: any) {
79 | const [visible, setVisible] = React.useState(false);
80 |
81 | let ref = React.useRef(null);
82 |
83 | return (
84 |
85 | setVisible(!visible)}
89 | style={{
90 | backgroundColor: "#F3F4F6",
91 | maxWidth: 100,
92 | padding: 10,
93 | justifyContent: "center",
94 | alignItems: "center",
95 | }}
96 | >
97 | Press me
98 |
99 | {visible && (
100 |
101 | setVisible(false)}>
102 |
103 |
104 | )}
105 |
106 | );
107 | }
108 |
--------------------------------------------------------------------------------
/example/storybook/stories/Radio/Radio.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { storiesOf } from "@storybook/react-native";
3 | import { RadioGroup, Radio } from "./index";
4 | import { Wrapper } from "../Wrapper";
5 |
6 | const RadioExample = () => {
7 | return (
8 |
9 | Dogs
10 | Cats
11 |
12 | );
13 | };
14 |
15 | export const Example = () => {
16 | return (
17 |
18 |
19 |
20 | );
21 | };
22 |
23 | storiesOf("Radio", module).add("Radio group", Example);
24 |
--------------------------------------------------------------------------------
/example/storybook/stories/Radio/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useRadioGroupState } from "@react-stately/radio";
3 | import { useRadio, useRadioGroup } from "@react-native-aria/radio";
4 | import { Platform, Pressable, Text, View } from "react-native";
5 | import { VisuallyHidden } from "@react-aria/visually-hidden";
6 | import { MaterialCommunityIcons } from "@expo/vector-icons";
7 | import { useFocusRing } from "@react-native-aria/focus";
8 |
9 | let RadioContext = React.createContext({});
10 |
11 | export function RadioGroup(props: any) {
12 | let { children, label } = props;
13 | let state = useRadioGroupState(props);
14 | let { radioGroupProps, labelProps } = useRadioGroup(props, state);
15 |
16 | return (
17 |
18 | {label}
19 |
26 | {children}
27 |
28 |
29 | );
30 | }
31 |
32 | export function Radio(props: any) {
33 | let { state, isReadOnly, isDisabled } = React.useContext(RadioContext);
34 | const inputRef = React.useRef(null);
35 | let { inputProps } = useRadio(
36 | { isReadOnly, isDisabled, ...props },
37 | state,
38 | inputRef
39 | );
40 | let { isFocusVisible, focusProps } = useFocusRing();
41 |
42 | let isSelected = state.selectedValue === props.value;
43 | const icon = isSelected ? "radiobox-marked" : "radiobox-blank";
44 |
45 | return (
46 | <>
47 | {Platform.OS === "web" ? (
48 |
60 | ) : (
61 |
62 |
63 |
64 |
65 |
66 | {props.children}
67 |
68 | {isSelected ? "selected" : "not selected"}
69 |
70 | )}
71 | >
72 | );
73 | }
74 |
--------------------------------------------------------------------------------
/example/storybook/stories/Slider/Slider.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { storiesOf } from "@storybook/react-native";
3 | import { Slider } from "./index";
4 | import { Wrapper } from "../Wrapper";
5 |
6 | const SliderExample = () => {
7 | return (
8 |
9 | );
10 | };
11 |
12 | export const Example = () => {
13 | return (
14 |
15 |
16 |
17 | );
18 | };
19 |
20 | storiesOf("Slider", module).add("basic", Example);
21 |
--------------------------------------------------------------------------------
/example/storybook/stories/Slider/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { View, Text, Pressable, Platform } from "react-native";
3 | import { useSlider, useSliderThumb } from "@react-native-aria/slider";
4 | import { useSliderState } from "@react-stately/slider";
5 | import { useFocusRing } from "@react-native-aria/focus";
6 | import { VisuallyHidden } from "@react-aria/visually-hidden";
7 | import { mergeProps } from "@react-aria/utils";
8 |
9 | const useLayout = () => {
10 | const [layout, setLayout] = React.useState({});
11 | return {
12 | onLayout: (e) => {
13 | setLayout(e.nativeEvent.layout);
14 | },
15 | layout,
16 | };
17 | };
18 |
19 | export function Slider(props) {
20 | let trackRef = React.useRef(null);
21 | const { onLayout, layout } = useLayout();
22 | let state = useSliderState({
23 | ...props,
24 | numberFormatter: { format: (e) => e },
25 | });
26 | let { groupProps, trackProps, labelProps, outputProps } = useSlider(
27 | props,
28 | state,
29 | layout
30 | );
31 |
32 | return (
33 |
40 | {/* Create a flex container for the label and output element. */}
41 |
42 | {props.label && {props.label}}
43 | {Platform.OS === "web" && (
44 |
50 | )}
51 |
52 | {/* The track element holds the visible track line and the thumb. */}
53 |
54 |
63 |
72 |
73 |
74 |
75 |
76 | );
77 | }
78 |
79 | function Thumb(props) {
80 | let { state, trackLayout, index } = props;
81 | let inputRef = React.useRef(null);
82 | const { onLayout, layout } = useLayout();
83 | let { thumbProps, inputProps } = useSliderThumb(
84 | {
85 | index,
86 | trackLayout,
87 | inputRef,
88 | },
89 | state
90 | );
91 |
92 | let { focusProps, isFocusVisible } = useFocusRing();
93 |
94 | return (
95 |
104 |
117 | {Platform.OS === "web" && (
118 |
119 |
120 |
121 | )}
122 |
123 |
124 | );
125 | }
126 |
--------------------------------------------------------------------------------
/example/storybook/stories/Switch/Switch.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { storiesOf } from "@storybook/react-native";
3 | import { ControlledSwitch } from "./index";
4 | import { Wrapper } from "../Wrapper";
5 |
6 | export const Example = () => {
7 | return (
8 |
9 |
10 |
11 | );
12 | };
13 |
14 | storiesOf("Switch", module).add("Switch", Example);
15 |
--------------------------------------------------------------------------------
/example/storybook/stories/Tabs/Tabs.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { storiesOf } from "@storybook/react-native";
3 | import { Wrapper } from "../Wrapper";
4 | import { TabsExample } from "./index";
5 | import { Item } from "@react-stately/collections";
6 | import { Text } from "react-native";
7 |
8 | export const Example = () => {
9 | return (
10 |
11 |
12 | -
13 | Tab 1 Content
14 |
15 | -
16 | Tab 2 Content
17 |
18 | -
19 | Tab 3 Content
20 |
21 |
22 |
23 | );
24 | };
25 |
26 | storiesOf("Tabs", module).add("Basic", Example);
27 |
--------------------------------------------------------------------------------
/example/storybook/stories/Tabs/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { MutableRefObject } from "react";
2 | import { Pressable, StyleSheet, Text } from "react-native";
3 | import { useTabsState } from "@react-stately/tabs";
4 | import { useTabs, useTab } from "@react-native-aria/tabs";
5 | import { SpectrumTabsProps } from "@react-types/tabs";
6 | import { Orientation, DOMProps, Node } from "@react-types/shared";
7 | import { SingleSelectListState } from "@react-stately/list";
8 | import { View } from "react-native";
9 |
10 |
11 | export function TabsExample(props: SpectrumTabsProps) {
12 | let state = useTabsState(props);
13 | let tablistRef = React.useRef();
14 | let { tabListProps, tabPanelProps } = useTabs(props, state, tablistRef);
15 |
16 | return (
17 | <>
18 |
24 |
25 | {state.selectedItem && state.selectedItem.props.children}
26 |
27 | >
28 | );
29 | }
30 |
31 | interface TabListProps {
32 | isQuiet?: boolean;
33 | density?: "compact" | "regular";
34 | isDisabled?: boolean;
35 | orientation?: Orientation;
36 | state: SingleSelectListState;
37 | selectedTab: HTMLElement;
38 | className?: string;
39 | }
40 |
41 | const TabList = React.forwardRef(function (
42 | props: TabListProps,
43 | ref: any
44 | ) {
45 | let {
46 | isQuiet,
47 | density,
48 | state,
49 | isDisabled,
50 | orientation,
51 | selectedTab,
52 | className,
53 | ...otherProps
54 | } = props;
55 |
56 | return (
57 |
58 | {[...state.collection].map((item) => (
59 |
66 | ))}
67 |
68 | );
69 | });
70 |
71 | interface TabProps extends DOMProps {
72 | item: Node;
73 | state: SingleSelectListState;
74 | isDisabled?: boolean;
75 | orientation?: Orientation;
76 | }
77 |
78 | export function Tab(props: TabProps) {
79 | let { item, state, isDisabled: propsDisabled } = props;
80 | let { key, rendered } = item;
81 | let isDisabled = propsDisabled || state.disabledKeys.has(key);
82 | let ref = React.useRef();
83 | let { tabProps } = useTab({ item, isDisabled }, state, ref);
84 | let isSelected = state.selectedKey === key;
85 | const style = styles({ ...props, isSelected }).tab;
86 | const textStyle = styles({ ...props, isSelected }).tabText;
87 |
88 |
89 | return (
90 |
91 | {typeof rendered === "string" ? {rendered} : rendered}
92 |
93 | );
94 | }
95 |
96 | const styles = (props: any) => {
97 | return StyleSheet.create({
98 | tab: {
99 | marginLeft: 10,
100 | },
101 | tabText: {
102 | color: props.isSelected ? "#1E40AF" : "#1F2937",
103 | borderBottomColor: "#1E40AF",
104 | borderBottomWidth: props.isSelected ? 2 : 0,
105 | padding: 10,
106 | },
107 | tabButtons: {
108 | flexDirection: props.orientation === "vertical" ? "column" : "row",
109 | },
110 | });
111 | };
112 |
--------------------------------------------------------------------------------
/example/storybook/stories/Tooltip/Tooltip.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { Platform } from "react-native";
3 | import { storiesOf } from "@storybook/react-native";
4 | import { TooltipExample } from "./index";
5 | import { Wrapper } from "../Wrapper";
6 |
7 | export const Example = () => {
8 | if (Platform.OS === "web") {
9 | return (
10 |
11 |
12 |
13 | );
14 | }
15 |
16 | return null;
17 | };
18 |
19 | storiesOf("Tooltip", module).add("Tooltip", Example);
20 |
--------------------------------------------------------------------------------
/example/storybook/stories/Tooltip/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { View, Pressable, Text } from "react-native";
3 | import { useTooltipTriggerState } from "@react-stately/tooltip";
4 | import { mergeProps } from "@react-aria/utils";
5 | import { useTooltip, useTooltipTrigger } from "@react-native-aria/tooltip";
6 |
7 | function Tooltip({ state, ...props }) {
8 | let { tooltipProps } = useTooltip(props, state);
9 |
10 | return (
11 |
22 | {props.children}
23 |
24 | );
25 | }
26 |
27 | export function TooltipExample(props) {
28 | let state = useTooltipTriggerState(props);
29 | let ref = React.useRef();
30 |
31 | // Get props for the trigger and its tooltip
32 | let { triggerProps, tooltipProps } = useTooltipTrigger(props, state, ref);
33 |
34 | return (
35 |
36 |
41 | I have a tooltip
42 |
43 | {state.isOpen && (
44 |
45 | And the tooltip tells you more information.
46 |
47 | )}
48 |
49 | );
50 | }
51 |
--------------------------------------------------------------------------------
/example/storybook/stories/Wrapper.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { SafeAreaView } from "react-native";
3 | import { OverlayProvider } from "@react-native-aria/overlays";
4 | import { SSRProvider } from "@react-native-aria/utils";
5 |
6 | export function Wrapper({ children }) {
7 | return (
8 |
9 |
10 | {children}
11 |
12 |
13 | );
14 | }
15 |
--------------------------------------------------------------------------------
/example/storybook/stories/index.js:
--------------------------------------------------------------------------------
1 | import "./Slider/Slider.stories";
2 | import "./Combobox/Combobox.stories";
3 | import "./Listbox/Listbox.stories";
4 | import "./Tabs/Tabs.stories";
5 | import "./Menu/Menu.stories";
6 | // import "./Button/Button.stories";
7 | // import "./Button/ToggleButton.stories";
8 | // import "./Checkbox/Checkbox.stories";
9 | // import "./Radio/Radio.stories";
10 | // import "./Switch/Switch.stories";
11 | // import "./useOverlayPosition/useOverlayPosition.stories";
12 | // import "./Tooltip/Tooltip.stories";
13 | // import "./Overlays/Overlays.stories";
14 | // import "./Disclosure/Disclosure.stories";
15 |
--------------------------------------------------------------------------------
/example/storybook/stories/useOverlayPosition/useOverlayPosition.stories.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { storiesOf } from "@storybook/react-native";
3 | import { TriggerWrapper } from "./useOverlayPosition";
4 | import { Wrapper } from "../Wrapper";
5 |
6 | const Example = () => {
7 | return (
8 |
9 |
10 |
11 | );
12 | };
13 |
14 | storiesOf("useOverlayPosition", module).add("useOverlayPosition", Example);
15 |
--------------------------------------------------------------------------------
/example/storybook/stories/useOverlayPosition/useOverlayPosition.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import {
3 | OverlayContainer,
4 | useOverlayPosition,
5 | } from "@react-native-aria/overlays";
6 | import { useButton } from "@react-native-aria/button";
7 | import {
8 | View,
9 | Text,
10 | Pressable,
11 | TouchableWithoutFeedback,
12 | StyleSheet,
13 | findNodeHandle,
14 | ScrollView,
15 | Platform,
16 | SafeAreaView,
17 | } from "react-native";
18 | import { useToggleState } from "@react-stately/toggle";
19 |
20 | // Button to close overlay on outside click
21 | function CloseButton(props) {
22 | return (
23 |
28 |
29 |
30 | );
31 | }
32 |
33 | const positions = [
34 | "top",
35 | "left",
36 | "right",
37 | "bottom",
38 | "top left",
39 | "top right",
40 | "left top",
41 | "left bottom",
42 | "bottom right, bottom left",
43 | "right top",
44 | "right bottom",
45 | ];
46 |
47 | export function TriggerWrapper() {
48 | const [placement, setPlacement] = React.useState(-1);
49 | React.useEffect(() => {
50 | const id = setInterval(() => {
51 | setPlacement((prev) => (prev + 1) % positions.length);
52 | }, 2000);
53 | return () => clearInterval(id);
54 | }, []);
55 |
56 | return ;
57 | }
58 |
59 | const OverlayView = ({ targetRef, placement }) => {
60 | let overlayRef = React.useRef();
61 |
62 | const { overlayProps } = useOverlayPosition({
63 | placement: "top",
64 | targetRef,
65 | overlayRef,
66 | offset: 10,
67 | });
68 |
69 | return (
70 | {
79 | if (Platform.OS === "web") {
80 | overlayRef.current = findNodeHandle(node);
81 | } else {
82 | overlayRef.current = node;
83 | }
84 | }}
85 | >
86 |
92 | Hello world
93 |
94 |
95 | );
96 | };
97 |
98 | export default function Trigger({ placement }: any) {
99 | let ref = React.useRef();
100 | const toggleState = useToggleState();
101 |
102 | let { buttonProps } = useButton({ onPress: toggleState.toggle }, ref);
103 |
104 | return (
105 |
112 |
118 |
126 | Trigger
127 |
128 |
129 | {toggleState.isSelected && (
130 |
131 |
132 |
133 |
134 | )}
135 |
136 | );
137 | }
138 |
--------------------------------------------------------------------------------
/example/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "jsx": "react-native",
4 | "target": "esnext",
5 | "lib": [
6 | "esnext"
7 | ],
8 | "allowJs": true,
9 | "skipLibCheck": true,
10 | "noEmit": true,
11 | "allowSyntheticDefaultImports": true,
12 | "resolveJsonModule": true,
13 | "esModuleInterop": true,
14 | "moduleResolution": "node",
15 | "paths": {
16 | "@react-native-aria/*": ["../packages/*/src/index"]
17 | },
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/example/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require("path");
2 | const createExpoWebpackConfigAsync = require("@expo/webpack-config");
3 | const { resolver } = require("./metro.config");
4 |
5 | const root = path.resolve(__dirname, "..");
6 | const node_modules = path.join(__dirname, "node_modules");
7 |
8 | module.exports = async function (env, argv) {
9 | const config = await createExpoWebpackConfigAsync(env, argv);
10 |
11 | config.module.rules.push({
12 | test: /\.(js|jsx|ts|tsx)$/,
13 | include: path.resolve(root, "packages"),
14 | use: "babel-loader",
15 | });
16 |
17 | // We need to make sure that only one version is loaded for peerDependencies
18 | // So we alias them to the versions in example's node_modules
19 | Object.assign(config.resolve.alias, {
20 | ...resolver.extraNodeModules,
21 | "react-native-web": path.join(node_modules, "react-native-web"),
22 | // Major Hack : Fix later, Resolve to root react to prevent invalid hook call error
23 | react: path.join(root, "node_modules", "react"),
24 | });
25 |
26 | return config;
27 | };
28 |
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "lerna": "3.0.0",
3 | "version": "independent",
4 | "useWorkspaces": true,
5 | "command": {
6 | "publish": {
7 | "allowBranch": [
8 | "main",
9 | "dev"
10 | ]
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-bob-mono",
3 | "version": "0.1.0",
4 | "description": "mono repo setup with bob",
5 | "private": true,
6 | "workspaces": [
7 | "packages/*"
8 | ],
9 | "devDependencies": {
10 | "@commitlint/config-conventional": "^11.0.0",
11 | "@react-native-community/eslint-config": "^2.0.0",
12 | "@release-it/conventional-changelog": "^2.0.0",
13 | "@types/jest": "^26.0.0",
14 | "@types/react": "^16.9.19",
15 | "@types/react-native": "0.63.4",
16 | "commitlint": "^11.0.0",
17 | "eslint": "^7.2.0",
18 | "eslint-config-prettier": "^7.0.0",
19 | "eslint-plugin-prettier": "^3.1.3",
20 | "husky": "^4.2.5",
21 | "jest": "^26.0.1",
22 | "pod-install": "^0.1.0",
23 | "prettier": "^2.0.5",
24 | "react": "16.13.1",
25 | "react-dom": "16.13.1",
26 | "react-native": "0.63.4",
27 | "react-native-builder-bob": "^0.17.1",
28 | "typescript": "^4.1.3"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/packages/button/README.md:
--------------------------------------------------------------------------------
1 | ## Installation
2 |
3 | ```js
4 | yarn add @react-native-aria/button
5 | ```
6 |
--------------------------------------------------------------------------------
/packages/button/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['module:metro-react-native-babel-preset'],
3 | };
4 |
--------------------------------------------------------------------------------
/packages/button/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@react-native-aria/button",
3 | "version": "0.2.4",
4 | "description": "mono repo setup with bob",
5 | "main": "lib/commonjs/index",
6 | "module": "lib/module/index",
7 | "typings": "lib/typescript/index.d.ts",
8 | "react-native": "src/index",
9 | "source": "src/index",
10 | "files": [
11 | "src",
12 | "lib",
13 | "android",
14 | "ios",
15 | "cpp",
16 | "react-native-bob-mono.podspec",
17 | "!lib/typescript/example",
18 | "!android/build",
19 | "!ios/build",
20 | "!**/__tests__",
21 | "!**/__fixtures__",
22 | "!**/__mocks__"
23 | ],
24 | "scripts": {
25 | "test": "jest",
26 | "typescript": "tsc --noEmit",
27 | "lint": "eslint \"**/*.{js,ts,tsx}\"",
28 | "prepare": "bob build",
29 | "release": "release-it",
30 | "example": "yarn --cwd example",
31 | "pods": "cd example && pod-install --quiet",
32 | "bootstrap": "yarn example && yarn && yarn pods"
33 | },
34 | "keywords": [
35 | "react-native",
36 | "ios",
37 | "android"
38 | ],
39 | "repository": "https://github.com/intergalacticspacehighway/react-native-bob-mono",
40 | "author": "nishan (https://github.com/intergalacticspacehighway)",
41 | "license": "MIT",
42 | "bugs": {
43 | "url": "https://github.com/intergalacticspacehighway/react-native-bob-mono/issues"
44 | },
45 | "homepage": "https://github.com/intergalacticspacehighway/react-native-bob-mono#readme",
46 | "publishConfig": {
47 | "registry": "https://registry.npmjs.org/"
48 | },
49 | "dependencies": {
50 | "@react-aria/utils": "^3.6.0",
51 | "@react-native-aria/interactions": "^0.2.3",
52 | "@react-stately/toggle": "^3.2.1",
53 | "@react-types/checkbox": "^3.2.1"
54 | },
55 | "peerDependencies": {
56 | "react": "*",
57 | "react-native": "*"
58 | },
59 | "jest": {
60 | "preset": "react-native",
61 | "modulePathIgnorePatterns": [
62 | "/example/node_modules",
63 | "/lib/"
64 | ]
65 | },
66 | "husky": {
67 | "hooks": {
68 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
69 | "pre-commit": "yarn lint && yarn typescript"
70 | }
71 | },
72 | "commitlint": {
73 | "extends": [
74 | "@commitlint/config-conventional"
75 | ]
76 | },
77 | "release-it": {
78 | "git": {
79 | "commitMessage": "chore: release ${version}",
80 | "tagName": "v${version}"
81 | },
82 | "npm": {
83 | "publish": true
84 | },
85 | "github": {
86 | "release": true
87 | },
88 | "plugins": {
89 | "@release-it/conventional-changelog": {
90 | "preset": "angular"
91 | }
92 | }
93 | },
94 | "eslintConfig": {
95 | "root": true,
96 | "extends": [
97 | "@react-native-community",
98 | "prettier"
99 | ],
100 | "rules": {
101 | "prettier/prettier": [
102 | "error",
103 | {
104 | "quoteProps": "consistent",
105 | "singleQuote": true,
106 | "tabWidth": 2,
107 | "trailingComma": "es5",
108 | "useTabs": false
109 | }
110 | ]
111 | }
112 | },
113 | "eslintIgnore": [
114 | "node_modules/",
115 | "lib/"
116 | ],
117 | "prettier": {
118 | "quoteProps": "consistent",
119 | "singleQuote": true,
120 | "tabWidth": 2,
121 | "trailingComma": "es5",
122 | "useTabs": false
123 | },
124 | "react-native-builder-bob": {
125 | "source": "src",
126 | "output": "lib",
127 | "targets": [
128 | "commonjs",
129 | "module",
130 | [
131 | "typescript",
132 | {
133 | "project": "tsconfig.build.json"
134 | }
135 | ]
136 | ]
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/packages/button/scripts/bootstrap.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const child_process = require('child_process');
3 |
4 | const root = path.resolve(__dirname, '..');
5 | const args = process.argv.slice(2);
6 | const options = {
7 | cwd: process.cwd(),
8 | env: process.env,
9 | stdio: 'inherit',
10 | encoding: 'utf-8',
11 | };
12 |
13 | let result;
14 |
15 | if (process.cwd() !== root || args.length) {
16 | // We're not in the root of the project, or additional arguments were passed
17 | // In this case, forward the command to `yarn`
18 | result = child_process.spawnSync('yarn', args, options);
19 | } else {
20 | // If `yarn` is run without arguments, perform bootstrap
21 | result = child_process.spawnSync('yarn', ['bootstrap'], options);
22 | }
23 |
24 | process.exitCode = result.status;
25 |
--------------------------------------------------------------------------------
/packages/button/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useButton';
2 | export * from './useToggleButton';
3 |
--------------------------------------------------------------------------------
/packages/button/src/index.web.ts:
--------------------------------------------------------------------------------
1 | export * from "./useButton";
2 | export * from "./useToggleButton.web";
3 |
--------------------------------------------------------------------------------
/packages/button/src/useButton.ts:
--------------------------------------------------------------------------------
1 | import { ReactNode } from 'react';
2 | import { PressEvents, usePress } from '@react-native-aria/interactions';
3 | import { AccessibilityProps, PressableProps } from 'react-native';
4 | import { mergeProps } from '@react-aria/utils';
5 |
6 | interface ButtonProps extends PressEvents {
7 | /** Whether the button is disabled. */
8 | isDisabled?: boolean;
9 | /** The content to display in the button. */
10 | children?: ReactNode;
11 | }
12 |
13 | export interface RNAriaButtonProps extends AccessibilityProps, ButtonProps {}
14 |
15 | export interface ButtonAria {
16 | /** Props for the button element. */
17 | buttonProps: PressableProps;
18 | /** Whether the button is currently pressed. */
19 | isPressed: boolean;
20 | }
21 |
22 | export function useButton(props: RNAriaButtonProps): ButtonAria {
23 | let {
24 | isDisabled,
25 | onPress,
26 | onPressStart,
27 | onPressEnd,
28 | onPressChange,
29 | ...rest
30 | } = props;
31 |
32 | let { pressProps, isPressed } = usePress({
33 | onPressStart,
34 | onPressEnd,
35 | onPressChange,
36 | onPress,
37 | isDisabled,
38 | });
39 |
40 | const mergedProps = mergeProps(pressProps, rest, {
41 | accessibilityState: {
42 | disabled: isDisabled,
43 | },
44 | accessibilityRole: 'button',
45 | disabled: isDisabled,
46 | });
47 |
48 | return {
49 | isPressed,
50 | buttonProps: mergedProps,
51 | };
52 | }
53 |
--------------------------------------------------------------------------------
/packages/button/src/useToggleButton.ts:
--------------------------------------------------------------------------------
1 | import { ButtonAria, useButton } from './useButton';
2 | import type { ToggleState } from '@react-stately/toggle';
3 | import { chain, mergeProps } from '@react-aria/utils';
4 | import type { PressEvents } from '@react-native-aria/interactions';
5 | import type { PressableProps } from 'react-native';
6 |
7 | export type AriaButtonProps = PressableProps &
8 | PressEvents & {
9 | isDisabled: boolean;
10 | };
11 |
12 | export interface AriaToggleButtonProps extends AriaButtonProps {
13 | /** Whether the element should be selected (controlled). */
14 | isSelected?: boolean;
15 | /** Whether the element should be selected (uncontrolled). */
16 | defaultSelected?: boolean;
17 | /** Handler that is called when the element's selection state changes. */
18 | onChange?: (isSelected: boolean) => void;
19 | }
20 |
21 | export function useToggleButton(
22 | props: AriaToggleButtonProps,
23 | state: ToggleState
24 | ): ButtonAria {
25 | const { isSelected } = state;
26 | const { isPressed, buttonProps } = useButton({
27 | ...props,
28 | onPress: chain(state.toggle, props.onPress),
29 | });
30 |
31 | return {
32 | isPressed,
33 | buttonProps: mergeProps(buttonProps, {
34 | accessibilityState: {
35 | selected: isSelected,
36 | },
37 | }),
38 | };
39 | }
40 |
--------------------------------------------------------------------------------
/packages/button/src/useToggleButton.web.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Adobe. All rights reserved.
3 | * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License. You may obtain a copy
5 | * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software distributed under
8 | * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 | * OF ANY KIND, either express or implied. See the License for the specific language
10 | * governing permissions and limitations under the License.
11 | */
12 |
13 | import { ElementType } from 'react';
14 | import { AriaToggleButtonProps } from '@react-types/button';
15 | import { useButton } from './useButton';
16 | import { chain } from '@react-aria/utils';
17 | import { mergeProps } from '@react-aria/utils';
18 | import { ToggleState } from '@react-stately/toggle';
19 |
20 | /**
21 | * Provides the behavior and accessibility implementation for a toggle button component.
22 | * ToggleButtons allow users to toggle a selection on or off, for example switching between two states or modes.
23 | */
24 | export function useToggleButton(
25 | props: AriaToggleButtonProps,
26 | state: ToggleState
27 | ): any {
28 | /* eslint-enable no-redeclare */
29 | const { isSelected } = state;
30 | const { isPressed, buttonProps } = useButton({
31 | ...props,
32 | onPress: chain(state.toggle, props.onPress),
33 | });
34 |
35 | return {
36 | isPressed,
37 | buttonProps: mergeProps(buttonProps, {
38 | // For v0.14+
39 | 'aria-pressed': isSelected,
40 |
41 | // For v0.15+
42 | 'accessibilityPressed': isSelected,
43 | }),
44 | };
45 | }
46 |
--------------------------------------------------------------------------------
/packages/button/tsconfig.build.json:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "extends": "../tsconfig",
4 | }
5 |
--------------------------------------------------------------------------------
/packages/checkbox/README.md:
--------------------------------------------------------------------------------
1 | ## Installation
2 |
3 | ```js
4 | yarn add @react-native-aria/checkbox
5 | ```
6 |
7 | ## Usage
8 |
9 | ```js
10 | import {
11 | useCheckbox,
12 | useCheckboxGroup,
13 | useCheckboxGroupItem,
14 | } from '@react-native-aria/checkbox';
15 | ```
16 |
--------------------------------------------------------------------------------
/packages/checkbox/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['module:metro-react-native-babel-preset'],
3 | };
4 |
--------------------------------------------------------------------------------
/packages/checkbox/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@react-native-aria/checkbox",
3 | "version": "0.2.3",
4 | "description": "mono repo setup with bob",
5 | "main": "lib/commonjs/index",
6 | "module": "lib/module/index",
7 | "typings": "lib/typescript/index.d.ts",
8 | "react-native": "src/index",
9 | "source": "src/index",
10 | "files": [
11 | "src",
12 | "lib",
13 | "android",
14 | "ios",
15 | "cpp",
16 | "react-native-bob-mono.podspec",
17 | "!lib/typescript/example",
18 | "!android/build",
19 | "!ios/build",
20 | "!**/__tests__",
21 | "!**/__fixtures__",
22 | "!**/__mocks__"
23 | ],
24 | "scripts": {
25 | "test": "jest",
26 | "typescript": "tsc --noEmit",
27 | "lint": "eslint \"**/*.{js,ts,tsx}\"",
28 | "prepare": "bob build",
29 | "release": "release-it",
30 | "example": "yarn --cwd example",
31 | "pods": "cd example && pod-install --quiet",
32 | "bootstrap": "yarn example && yarn && yarn pods"
33 | },
34 | "keywords": [
35 | "react-native",
36 | "ios",
37 | "android"
38 | ],
39 | "repository": "https://github.com/intergalacticspacehighway/react-native-bob-mono",
40 | "author": "nishan (https://github.com/intergalacticspacehighway)",
41 | "license": "MIT",
42 | "bugs": {
43 | "url": "https://github.com/intergalacticspacehighway/react-native-bob-mono/issues"
44 | },
45 | "homepage": "https://github.com/intergalacticspacehighway/react-native-bob-mono#readme",
46 | "publishConfig": {
47 | "registry": "https://registry.npmjs.org/"
48 | },
49 | "dependencies": {
50 | "@react-aria/checkbox": "^3.2.1",
51 | "@react-aria/utils": "^3.6.0",
52 | "@react-native-aria/toggle": "^0.2.3",
53 | "@react-native-aria/utils": "^0.2.6",
54 | "@react-stately/toggle": "^3.2.1"
55 | },
56 | "peerDependencies": {
57 | "react": "*",
58 | "react-native": "*"
59 | },
60 | "jest": {
61 | "preset": "react-native",
62 | "modulePathIgnorePatterns": [
63 | "/example/node_modules",
64 | "/lib/"
65 | ]
66 | },
67 | "husky": {
68 | "hooks": {
69 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
70 | "pre-commit": "yarn lint && yarn typescript"
71 | }
72 | },
73 | "commitlint": {
74 | "extends": [
75 | "@commitlint/config-conventional"
76 | ]
77 | },
78 | "release-it": {
79 | "git": {
80 | "commitMessage": "chore: release ${version}",
81 | "tagName": "v${version}"
82 | },
83 | "npm": {
84 | "publish": true
85 | },
86 | "github": {
87 | "release": true
88 | },
89 | "plugins": {
90 | "@release-it/conventional-changelog": {
91 | "preset": "angular"
92 | }
93 | }
94 | },
95 | "eslintConfig": {
96 | "root": true,
97 | "extends": [
98 | "@react-native-community",
99 | "prettier"
100 | ],
101 | "rules": {
102 | "prettier/prettier": [
103 | "error",
104 | {
105 | "quoteProps": "consistent",
106 | "singleQuote": true,
107 | "tabWidth": 2,
108 | "trailingComma": "es5",
109 | "useTabs": false
110 | }
111 | ]
112 | }
113 | },
114 | "eslintIgnore": [
115 | "node_modules/",
116 | "lib/"
117 | ],
118 | "prettier": {
119 | "quoteProps": "consistent",
120 | "singleQuote": true,
121 | "tabWidth": 2,
122 | "trailingComma": "es5",
123 | "useTabs": false
124 | },
125 | "react-native-builder-bob": {
126 | "source": "src",
127 | "output": "lib",
128 | "targets": [
129 | "commonjs",
130 | "module",
131 | [
132 | "typescript",
133 | {
134 | "project": "tsconfig.build.json"
135 | }
136 | ]
137 | ]
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/packages/checkbox/scripts/bootstrap.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const child_process = require('child_process');
3 |
4 | const root = path.resolve(__dirname, '..');
5 | const args = process.argv.slice(2);
6 | const options = {
7 | cwd: process.cwd(),
8 | env: process.env,
9 | stdio: 'inherit',
10 | encoding: 'utf-8',
11 | };
12 |
13 | let result;
14 |
15 | if (process.cwd() !== root || args.length) {
16 | // We're not in the root of the project, or additional arguments were passed
17 | // In this case, forward the command to `yarn`
18 | result = child_process.spawnSync('yarn', args, options);
19 | } else {
20 | // If `yarn` is run without arguments, perform bootstrap
21 | result = child_process.spawnSync('yarn', ['bootstrap'], options);
22 | }
23 |
24 | process.exitCode = result.status;
25 |
--------------------------------------------------------------------------------
/packages/checkbox/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useCheckbox';
2 | export * from './useCheckboxGroup';
3 | export * from './useCheckboxGroupItem';
4 |
--------------------------------------------------------------------------------
/packages/checkbox/src/index.web.ts:
--------------------------------------------------------------------------------
1 | export { useCheckbox } from "@react-aria/checkbox";
2 | export { useCheckboxGroup } from "./useCheckboxGroup.web";
3 | export { useCheckboxGroupItem } from "@react-aria/checkbox";
4 |
--------------------------------------------------------------------------------
/packages/checkbox/src/useCheckbox.ts:
--------------------------------------------------------------------------------
1 | import type { RefObject } from 'react';
2 | import type { ToggleState } from '@react-stately/toggle';
3 | import { mergeProps } from '@react-aria/utils';
4 | import { useToggle } from '@react-native-aria/toggle';
5 | import { AriaCheckboxProps } from '@react-types/checkbox';
6 |
7 | export interface CheckboxAria {
8 | /** Props for the input or Pressable/Touchable element. */
9 | inputProps: any;
10 | }
11 |
12 | /**
13 | * Provides the behavior and accessibility implementation for a checkbox component.
14 | * Checkboxes allow users to select multiple items from a list of individual items, or
15 | * to mark one individual item as selected.
16 | * @param props - Props for the checkbox.
17 | * @param state - State for the checkbox, as returned by `useToggleState`.
18 | * @param inputRef - A ref for the HTML input element.
19 | */
20 | export function useCheckbox(
21 | props: AriaCheckboxProps,
22 | state: ToggleState,
23 | inputRef: RefObject
24 | ): CheckboxAria {
25 | let { inputProps } = useToggle(props, state, inputRef);
26 | let { isSelected } = state;
27 |
28 | let { isIndeterminate } = props;
29 |
30 | return {
31 | inputProps: mergeProps(inputProps, {
32 | checked: isSelected,
33 | accessibilityRole: 'checkbox',
34 | accessibilityState: {
35 | checked: isIndeterminate ? 'mixed' : isSelected,
36 | disabled: props.isDisabled,
37 | },
38 | }),
39 | };
40 | }
41 |
--------------------------------------------------------------------------------
/packages/checkbox/src/useCheckboxGroup.ts:
--------------------------------------------------------------------------------
1 | import type { CheckboxGroupState } from '@react-stately/checkbox';
2 | import { mergeProps, filterDOMProps } from '@react-aria/utils';
3 | import { getLabel } from '@react-native-aria/utils';
4 | import { AriaCheckboxGroupProps } from '@react-types/checkbox';
5 |
6 | interface CheckboxGroupAria {
7 | /** Props for the checkbox group wrapper element. */
8 | groupProps: any;
9 | /** Props for the checkbox group's visible label (if any). */
10 | labelProps: any;
11 | }
12 |
13 | /**
14 | * Provides the behavior and accessibility implementation for a checkbox group component.
15 | * Checkbox groups allow users to select multiple items from a list of options.
16 | * @param props - Props for the checkbox group.
17 | * @param state - State for the checkbox group, as returned by `useCheckboxGroupState`.
18 | */
19 | export function useCheckboxGroup(
20 | props: AriaCheckboxGroupProps,
21 | _state: CheckboxGroupState
22 | ): CheckboxGroupAria {
23 | let { isDisabled } = props;
24 |
25 | let domProps = filterDOMProps(props, { labelable: true });
26 |
27 | return {
28 | groupProps: mergeProps(domProps, {
29 | accessibilityState: {
30 | disabled: isDisabled,
31 | },
32 | accessibilityLabel: getLabel(props),
33 | }),
34 | labelProps: {},
35 | };
36 | }
37 |
--------------------------------------------------------------------------------
/packages/checkbox/src/useCheckboxGroup.web.ts:
--------------------------------------------------------------------------------
1 | import { useCheckboxGroup as useCheckboxGroupWeb } from '@react-aria/checkbox';
2 | import { AriaCheckboxGroupProps } from '@react-types/checkbox';
3 | import { CheckboxGroupState } from '@react-stately/checkbox';
4 | import { mapDomPropsToRN } from '@react-native-aria/utils';
5 |
6 | interface CheckboxGroupAria {
7 | /** Props for the checkbox group wrapper element. */
8 | groupProps: any;
9 | /** Props for the checkbox group's visible label (if any). */
10 | labelProps: any;
11 | }
12 |
13 | /**
14 | * Provides the behavior and accessibility implementation for a checkbox group component.
15 | * Checkbox groups allow users to select multiple items from a list of options.
16 | * @param props - Props for the checkbox group.
17 | * @param state - State for the checkbox group, as returned by `useCheckboxGroupState`.
18 | */
19 | export function useCheckboxGroup(
20 | props: AriaCheckboxGroupProps,
21 | state: CheckboxGroupState
22 | ): CheckboxGroupAria {
23 | const params = useCheckboxGroupWeb(props, state);
24 | return {
25 | labelProps: {
26 | ...params.labelProps,
27 | ...mapDomPropsToRN(params.labelProps),
28 | },
29 | groupProps: {
30 | ...params.groupProps,
31 | ...mapDomPropsToRN(params.groupProps),
32 | },
33 | };
34 | }
35 |
--------------------------------------------------------------------------------
/packages/checkbox/src/useCheckboxGroupItem.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Adobe. All rights reserved.
3 | * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License. You may obtain a copy
5 | * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software distributed under
8 | * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 | * OF ANY KIND, either express or implied. See the License for the specific language
10 | * governing permissions and limitations under the License.
11 | */
12 |
13 | import type { AriaCheckboxGroupItemProps } from '@react-types/checkbox';
14 | import { CheckboxAria, useCheckbox } from './useCheckbox';
15 | import type { CheckboxGroupState } from '@react-stately/checkbox';
16 | import type { RefObject } from 'react';
17 | import { useToggleState } from '@react-stately/toggle';
18 |
19 | /**
20 | * Provides the behavior and accessibility implementation for a checkbox component contained within a checkbox group.
21 | * Checkbox groups allow users to select multiple items from a list of options.
22 | * @param props - Props for the checkbox.
23 | * @param state - State for the checkbox, as returned by `useCheckboxGroupState`.
24 | * @param inputRef - A ref for the HTML input element.
25 | */
26 | export function useCheckboxGroupItem(
27 | props: AriaCheckboxGroupItemProps,
28 | state: CheckboxGroupState,
29 | inputRef: RefObject
30 | ): CheckboxAria {
31 | const toggleState = useToggleState({
32 | isReadOnly: props.isReadOnly || state.isReadOnly,
33 | //@ts-ignore
34 | isSelected: state.isSelected(props.value),
35 | onChange(isSelected) {
36 | if (isSelected) {
37 | //@ts-ignore
38 | state.addValue(props.value);
39 | } else {
40 | //@ts-ignore
41 | state.removeValue(props.value);
42 | }
43 |
44 | if (props.onChange) {
45 | props.onChange(isSelected);
46 | }
47 | },
48 | });
49 |
50 | let { inputProps } = useCheckbox(
51 | {
52 | ...props,
53 | isReadOnly: props.isReadOnly || state.isReadOnly,
54 | isDisabled: props.isDisabled || state.isDisabled,
55 | },
56 | toggleState,
57 | inputRef
58 | );
59 |
60 | return { inputProps };
61 | }
62 |
--------------------------------------------------------------------------------
/packages/checkbox/src/utils.ts:
--------------------------------------------------------------------------------
1 | import { CheckboxGroupState } from "@react-stately/checkbox";
2 |
3 | export const checkboxGroupNames = new WeakMap();
4 |
--------------------------------------------------------------------------------
/packages/checkbox/tsconfig.build.json:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "extends": "../tsconfig",
4 | }
5 |
--------------------------------------------------------------------------------
/packages/combobox/README.md:
--------------------------------------------------------------------------------
1 | ## Installation
2 |
3 | ```js
4 | yarn add @react-native-aria/utils
5 | ```
6 |
--------------------------------------------------------------------------------
/packages/combobox/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['module:metro-react-native-babel-preset'],
3 | };
4 |
--------------------------------------------------------------------------------
/packages/combobox/scripts/bootstrap.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const child_process = require('child_process');
3 |
4 | const root = path.resolve(__dirname, '..');
5 | const args = process.argv.slice(2);
6 | const options = {
7 | cwd: process.cwd(),
8 | env: process.env,
9 | stdio: 'inherit',
10 | encoding: 'utf-8',
11 | };
12 |
13 | let result;
14 |
15 | if (process.cwd() !== root || args.length) {
16 | // We're not in the root of the project, or additional arguments were passed
17 | // In this case, forward the command to `yarn`
18 | result = child_process.spawnSync('yarn', args, options);
19 | } else {
20 | // If `yarn` is run without arguments, perform bootstrap
21 | result = child_process.spawnSync('yarn', ['bootstrap'], options);
22 | }
23 |
24 | process.exitCode = result.status;
25 |
--------------------------------------------------------------------------------
/packages/combobox/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useComboBox';
2 |
--------------------------------------------------------------------------------
/packages/combobox/src/index.web.ts:
--------------------------------------------------------------------------------
1 | export * from './useComboBox.web';
2 |
--------------------------------------------------------------------------------
/packages/combobox/src/useComboBox.ts:
--------------------------------------------------------------------------------
1 | import { ComboBoxProps } from '@react-types/combobox';
2 | import { ComboBoxState } from '@react-stately/combobox';
3 | import { RefObject } from 'react';
4 | // @ts-ignore
5 | import { KeyboardDelegate, PressEvent } from '@react-types/shared';
6 | import { TextInput, View, Pressable, Touchable } from 'react-native';
7 |
8 | interface AriaComboBoxProps extends ComboBoxProps {
9 | /** The ref for the input element. */
10 | inputRef: RefObject;
11 | /** The ref for the list box popover. */
12 | popoverRef: RefObject;
13 | /** The ref for the list box. */
14 | listBoxRef: RefObject;
15 | /** The ref for the list box popup trigger button. */
16 | buttonRef: RefObject;
17 | /** An optional keyboard delegate implementation, to override the default. */
18 | keyboardDelegate?: KeyboardDelegate;
19 | }
20 |
21 | interface ComboBoxAria {
22 | /** Props for the combo box menu trigger button. */
23 | buttonProps: any;
24 | /** Props for the combo box input element. */
25 | inputProps: any;
26 | /** Props for the combo box menu. */
27 | listBoxProps: any;
28 | /** Props for the combo box label element. */
29 | labelProps: any;
30 | }
31 |
32 | /**
33 | * Provides the behavior and accessibility implementation for a combo box component.
34 | * A combo box combines a text input with a listbox, allowing users to filter a list of options to items matching a query.
35 | * @param props - Props for the combo box.
36 | * @param state - State for the select, as returned by `useComboBoxState`.
37 | */
38 | export function useComboBox(
39 | props: AriaComboBoxProps,
40 | state: ComboBoxState
41 | ): ComboBoxAria {
42 | let { inputRef } = props;
43 |
44 | // Press handlers for the ComboBox button
45 | let onPress = () => {
46 | // Focus the input field in case it isn't focused yet
47 | inputRef.current?.focus();
48 | state.toggle();
49 | };
50 |
51 | const onChangeText = state.setInputValue;
52 |
53 | return {
54 | labelProps: {},
55 | buttonProps: {
56 | onPress,
57 | },
58 | inputProps: {
59 | onChangeText,
60 | value: state.inputValue,
61 | onFocus: () => {
62 | state.setFocused(true);
63 | },
64 | onBlur: () => {
65 | state.setFocused(false);
66 | },
67 | },
68 | listBoxProps: {},
69 | };
70 | }
71 |
--------------------------------------------------------------------------------
/packages/combobox/src/useComboBox.web.ts:
--------------------------------------------------------------------------------
1 | import { ComboBoxProps } from '@react-types/combobox';
2 | import { ComboBoxState } from '@react-stately/combobox';
3 | import { RefObject } from 'react';
4 | import { KeyboardDelegate } from '@react-types/shared';
5 | import { TextInput, View, Pressable, Touchable } from 'react-native';
6 | import { useComboBox as useComboBoxWeb } from '@react-aria/combobox';
7 | import { mapDomPropsToRN } from '@react-native-aria/utils';
8 | import { TextInputProps } from 'react-native';
9 |
10 | interface AriaComboBoxProps extends ComboBoxProps {
11 | /** The ref for the input element. */
12 | inputRef: RefObject;
13 | /** The ref for the list box popover. */
14 | popoverRef: RefObject;
15 | /** The ref for the list box. */
16 | listBoxRef: RefObject;
17 | /** The ref for the list box popup trigger button. */
18 | buttonRef: RefObject;
19 | /** An optional keyboard delegate implementation, to override the default. */
20 | keyboardDelegate?: KeyboardDelegate;
21 | }
22 |
23 | interface ComboBoxAria {
24 | /** Props for the combo box menu trigger button. */
25 | buttonProps: any;
26 | /** Props for the combo box input element. */
27 | inputProps: TextInputProps;
28 | /** Props for the combo box menu. */
29 | listBoxProps: any;
30 | /** Props for the combo box label element. */
31 | labelProps: any;
32 | }
33 |
34 | /**
35 | * Provides the behavior and accessibility implementation for a combo box component.
36 | * A combo box combines a text input with a listbox, allowing users to filter a list of options to items matching a query.
37 | * @param props - Props for the combo box.
38 | * @param state - State for the select, as returned by `useComboBoxState`.
39 | */
40 | export function useComboBox(
41 | props: AriaComboBoxProps,
42 | state: ComboBoxState
43 | ): ComboBoxAria {
44 | // @ts-ignore
45 | const params = useComboBoxWeb(props, state);
46 |
47 | const onKeyPress = params.inputProps.onKeyDown;
48 | params.inputProps.onKeyDown = undefined;
49 |
50 | // RN Web supports onKeyPress. It's same as onKeyDown
51 | // https://necolas.github.io/react-native-web/docs/text-input/
52 | params.inputProps.onKeyPress = onKeyPress;
53 |
54 | // @ts-ignore
55 | params.inputProps.blurOnSubmit = false;
56 | params.inputProps.onKeyDown = undefined;
57 |
58 | return {
59 | inputProps: mapDomPropsToRN(params.inputProps),
60 | buttonProps: mapDomPropsToRN(params.buttonProps),
61 | labelProps: mapDomPropsToRN(params.labelProps),
62 | listBoxProps: mapDomPropsToRN(params.listBoxProps),
63 | };
64 | }
65 |
--------------------------------------------------------------------------------
/packages/combobox/tsconfig.build.json:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "extends": "../tsconfig",
4 | }
5 |
--------------------------------------------------------------------------------
/packages/disclosure/README.md:
--------------------------------------------------------------------------------
1 | ## Installation
2 |
3 | ```js
4 | yarn add @react-native-aria/disclosure
5 | ```
6 |
--------------------------------------------------------------------------------
/packages/disclosure/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['module:metro-react-native-babel-preset'],
3 | };
4 |
--------------------------------------------------------------------------------
/packages/disclosure/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@react-native-aria/disclosure",
3 | "version": "0.2.5",
4 | "description": "mono repo setup with bob",
5 | "main": "lib/commonjs/index",
6 | "module": "lib/module/index",
7 | "types": "lib/typescript/index.d.ts",
8 | "typings": "lib/typescript/index.d.ts",
9 | "react-native": "src/index",
10 | "source": "src/index",
11 | "files": [
12 | "src",
13 | "lib",
14 | "android",
15 | "ios",
16 | "cpp",
17 | "react-native-bob-mono.podspec",
18 | "!lib/typescript/example",
19 | "!android/build",
20 | "!ios/build",
21 | "!**/__tests__",
22 | "!**/__fixtures__",
23 | "!**/__mocks__"
24 | ],
25 | "scripts": {
26 | "test": "jest",
27 | "typescript": "tsc --noEmit",
28 | "lint": "eslint \"**/*.{js,ts,tsx}\"",
29 | "prepare": "bob build",
30 | "release": "release-it",
31 | "example": "yarn --cwd example",
32 | "pods": "cd example && pod-install --quiet",
33 | "bootstrap": "yarn example && yarn && yarn pods"
34 | },
35 | "keywords": [
36 | "react-native",
37 | "ios",
38 | "android"
39 | ],
40 | "repository": "https://github.com/intergalacticspacehighway/react-native-bob-mono",
41 | "author": "nishan (https://github.com/intergalacticspacehighway)",
42 | "license": "MIT",
43 | "bugs": {
44 | "url": "https://github.com/intergalacticspacehighway/react-native-bob-mono/issues"
45 | },
46 | "homepage": "https://github.com/intergalacticspacehighway/react-native-bob-mono#readme",
47 | "publishConfig": {
48 | "registry": "https://registry.npmjs.org/"
49 | },
50 | "peerDependencies": {
51 | "@react-stately/toggle": "*",
52 | "react": "*",
53 | "react-native": "*"
54 | },
55 | "dependencies": {
56 | "@react-aria/utils": "^3.6.0",
57 | "@react-native-aria/utils": "^0.2.6"
58 | },
59 | "jest": {
60 | "preset": "react-native",
61 | "modulePathIgnorePatterns": [
62 | "/example/node_modules",
63 | "/lib/"
64 | ]
65 | },
66 | "husky": {
67 | "hooks": {
68 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
69 | "pre-commit": "yarn lint && yarn typescript"
70 | }
71 | },
72 | "commitlint": {
73 | "extends": [
74 | "@commitlint/config-conventional"
75 | ]
76 | },
77 | "release-it": {
78 | "git": {
79 | "commitMessage": "chore: release ${version}",
80 | "tagName": "v${version}"
81 | },
82 | "npm": {
83 | "publish": true
84 | },
85 | "github": {
86 | "release": true
87 | },
88 | "plugins": {
89 | "@release-it/conventional-changelog": {
90 | "preset": "angular"
91 | }
92 | }
93 | },
94 | "eslintConfig": {
95 | "root": true,
96 | "extends": [
97 | "@react-native-community",
98 | "prettier"
99 | ],
100 | "rules": {
101 | "prettier/prettier": [
102 | "error",
103 | {
104 | "quoteProps": "consistent",
105 | "singleQuote": true,
106 | "tabWidth": 2,
107 | "trailingComma": "es5",
108 | "useTabs": false
109 | }
110 | ]
111 | }
112 | },
113 | "eslintIgnore": [
114 | "node_modules/",
115 | "lib/"
116 | ],
117 | "prettier": {
118 | "quoteProps": "consistent",
119 | "singleQuote": true,
120 | "tabWidth": 2,
121 | "trailingComma": "es5",
122 | "useTabs": false
123 | },
124 | "react-native-builder-bob": {
125 | "source": "src",
126 | "output": "lib",
127 | "targets": [
128 | "commonjs",
129 | "module",
130 | [
131 | "typescript",
132 | {
133 | "project": "tsconfig.build.json"
134 | }
135 | ]
136 | ]
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/packages/disclosure/scripts/bootstrap.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const child_process = require('child_process');
3 |
4 | const root = path.resolve(__dirname, '..');
5 | const args = process.argv.slice(2);
6 | const options = {
7 | cwd: process.cwd(),
8 | env: process.env,
9 | stdio: 'inherit',
10 | encoding: 'utf-8',
11 | };
12 |
13 | let result;
14 |
15 | if (process.cwd() !== root || args.length) {
16 | // We're not in the root of the project, or additional arguments were passed
17 | // In this case, forward the command to `yarn`
18 | result = child_process.spawnSync('yarn', args, options);
19 | } else {
20 | // If `yarn` is run without arguments, perform bootstrap
21 | result = child_process.spawnSync('yarn', ['bootstrap'], options);
22 | }
23 |
24 | process.exitCode = result.status;
25 |
--------------------------------------------------------------------------------
/packages/disclosure/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useDisclosure';
2 | export * from './useDisclosureButton';
3 |
--------------------------------------------------------------------------------
/packages/disclosure/src/index.web.ts:
--------------------------------------------------------------------------------
1 | export * from './useDisclosure.web';
2 | export * from './useDisclosureButton.web';
3 |
--------------------------------------------------------------------------------
/packages/disclosure/src/useDisclosure.ts:
--------------------------------------------------------------------------------
1 | import { ToggleState } from '@react-stately/toggle';
2 | import { ViewProps } from 'react-native';
3 |
4 | // Polyfill
5 | export function useDisclosure(_props: ViewProps, _state: ToggleState) {
6 | return {
7 | disclosureProps: _props,
8 | };
9 | }
10 |
--------------------------------------------------------------------------------
/packages/disclosure/src/useDisclosure.web.ts:
--------------------------------------------------------------------------------
1 | import { ToggleState } from '@react-stately/toggle';
2 | import { mergeProps } from '@react-aria/utils';
3 | import { ViewProps } from 'react-native';
4 | import { disclosureIds } from './utils';
5 |
6 | export function useDisclosure(props: ViewProps, state: ToggleState) {
7 | const id = disclosureIds.get(state);
8 |
9 | return {
10 | disclosureProps: mergeProps(props, {
11 | nativeID: id,
12 | }),
13 | };
14 | }
15 |
--------------------------------------------------------------------------------
/packages/disclosure/src/useDisclosureButton.ts:
--------------------------------------------------------------------------------
1 | import { ToggleState } from '@react-stately/toggle';
2 | import { mergeProps } from '@react-aria/utils';
3 | import { PressableProps } from 'react-native';
4 |
5 | export function useDisclosureButton(
6 | props: Partial,
7 | state: ToggleState
8 | ) {
9 | const onPress = state.toggle;
10 |
11 | const accessibilityState = props.accessibilityState || {};
12 |
13 | accessibilityState.expanded = state.isSelected;
14 |
15 | return {
16 | buttonProps: mergeProps(props, {
17 | onPress,
18 | accessibilityState,
19 | accessibilityRole: 'button',
20 | }),
21 | };
22 | }
23 |
--------------------------------------------------------------------------------
/packages/disclosure/src/useDisclosureButton.web.ts:
--------------------------------------------------------------------------------
1 | import { ToggleState } from '@react-stately/toggle';
2 | import { useId, mergeProps } from '@react-aria/utils';
3 | import { mapDomPropsToRN } from '@react-native-aria/utils';
4 | import { PressableProps } from 'react-native';
5 | import { disclosureIds } from './utils';
6 |
7 | export function useDisclosureButton(props: PressableProps, state: ToggleState) {
8 | const id = useId();
9 |
10 | disclosureIds.set(state, id);
11 |
12 | const onPress = state.toggle;
13 |
14 | const ariaProps = mapDomPropsToRN({
15 | 'aria-expanded': state.isSelected,
16 | 'aria-controls': state.isSelected ? id : undefined,
17 | });
18 |
19 | return {
20 | buttonProps: mergeProps(props, {
21 | onPress,
22 | ...ariaProps,
23 | accessibilityRole: 'button',
24 | }),
25 | };
26 | }
27 |
--------------------------------------------------------------------------------
/packages/disclosure/src/utils.ts:
--------------------------------------------------------------------------------
1 | import { ToggleState } from '@react-stately/toggle';
2 |
3 | export const disclosureIds = new WeakMap();
4 |
--------------------------------------------------------------------------------
/packages/disclosure/tsconfig.build.json:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "extends": "../tsconfig",
4 | }
5 |
--------------------------------------------------------------------------------
/packages/focus/README.md:
--------------------------------------------------------------------------------
1 | ## Installation
2 |
3 | ```js
4 | yarn add @react-native-aria/focus
5 | ```
6 |
--------------------------------------------------------------------------------
/packages/focus/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['module:metro-react-native-babel-preset'],
3 | };
4 |
--------------------------------------------------------------------------------
/packages/focus/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@react-native-aria/focus",
3 | "version": "0.2.7",
4 | "description": "mono repo setup with bob",
5 | "main": "lib/commonjs/index",
6 | "module": "lib/module/index",
7 | "typings": "lib/typescript/index.d.ts",
8 | "react-native": "src/index",
9 | "source": "src/index",
10 | "files": [
11 | "src",
12 | "lib",
13 | "android",
14 | "ios",
15 | "cpp",
16 | "react-native-bob-mono.podspec",
17 | "!lib/typescript/example",
18 | "!android/build",
19 | "!ios/build",
20 | "!**/__tests__",
21 | "!**/__fixtures__",
22 | "!**/__mocks__"
23 | ],
24 | "scripts": {
25 | "test": "jest",
26 | "typescript": "tsc --noEmit",
27 | "lint": "eslint \"**/*.{js,ts,tsx}\"",
28 | "prepare": "bob build",
29 | "release": "release-it",
30 | "example": "yarn --cwd example",
31 | "pods": "cd example && pod-install --quiet",
32 | "bootstrap": "yarn example && yarn && yarn pods"
33 | },
34 | "dependencies": {
35 | "@react-aria/focus": "^3.2.3"
36 | },
37 | "keywords": [
38 | "react-native",
39 | "ios",
40 | "android"
41 | ],
42 | "repository": "https://github.com/intergalacticspacehighway/react-native-bob-mono",
43 | "author": "nishan (https://github.com/intergalacticspacehighway)",
44 | "license": "MIT",
45 | "bugs": {
46 | "url": "https://github.com/intergalacticspacehighway/react-native-bob-mono/issues"
47 | },
48 | "homepage": "https://github.com/intergalacticspacehighway/react-native-bob-mono#readme",
49 | "publishConfig": {
50 | "registry": "https://registry.npmjs.org/"
51 | },
52 | "peerDependencies": {
53 | "react": "*",
54 | "react-native": "*"
55 | },
56 | "jest": {
57 | "preset": "react-native",
58 | "modulePathIgnorePatterns": [
59 | "/example/node_modules",
60 | "/lib/"
61 | ]
62 | },
63 | "husky": {
64 | "hooks": {
65 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
66 | "pre-commit": "yarn lint && yarn typescript"
67 | }
68 | },
69 | "commitlint": {
70 | "extends": [
71 | "@commitlint/config-conventional"
72 | ]
73 | },
74 | "release-it": {
75 | "git": {
76 | "commitMessage": "chore: release ${version}",
77 | "tagName": "v${version}"
78 | },
79 | "npm": {
80 | "publish": true
81 | },
82 | "github": {
83 | "release": true
84 | },
85 | "plugins": {
86 | "@release-it/conventional-changelog": {
87 | "preset": "angular"
88 | }
89 | }
90 | },
91 | "eslintConfig": {
92 | "root": true,
93 | "extends": [
94 | "@react-native-community",
95 | "prettier"
96 | ],
97 | "rules": {
98 | "prettier/prettier": [
99 | "error",
100 | {
101 | "quoteProps": "consistent",
102 | "singleQuote": true,
103 | "tabWidth": 2,
104 | "trailingComma": "es5",
105 | "useTabs": false
106 | }
107 | ]
108 | }
109 | },
110 | "eslintIgnore": [
111 | "node_modules/",
112 | "lib/"
113 | ],
114 | "prettier": {
115 | "quoteProps": "consistent",
116 | "singleQuote": true,
117 | "tabWidth": 2,
118 | "trailingComma": "es5",
119 | "useTabs": false
120 | },
121 | "react-native-builder-bob": {
122 | "source": "src",
123 | "output": "lib",
124 | "targets": [
125 | "commonjs",
126 | "module",
127 | [
128 | "typescript",
129 | {
130 | "project": "tsconfig.build.json"
131 | }
132 | ]
133 | ]
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/packages/focus/scripts/bootstrap.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const child_process = require('child_process');
3 |
4 | const root = path.resolve(__dirname, '..');
5 | const args = process.argv.slice(2);
6 | const options = {
7 | cwd: process.cwd(),
8 | env: process.env,
9 | stdio: 'inherit',
10 | encoding: 'utf-8',
11 | };
12 |
13 | let result;
14 |
15 | if (process.cwd() !== root || args.length) {
16 | // We're not in the root of the project, or additional arguments were passed
17 | // In this case, forward the command to `yarn`
18 | result = child_process.spawnSync('yarn', args, options);
19 | } else {
20 | // If `yarn` is run without arguments, perform bootstrap
21 | result = child_process.spawnSync('yarn', ['bootstrap'], options);
22 | }
23 |
24 | process.exitCode = result.status;
25 |
--------------------------------------------------------------------------------
/packages/focus/src/FocusScope.tsx:
--------------------------------------------------------------------------------
1 | import React, { ReactNode } from 'react';
2 |
3 | export interface FocusScopeProps {
4 | /** The contents of the focus scope. */
5 | children: ReactNode;
6 |
7 | /**
8 | * Whether to contain focus inside the scope, so users cannot
9 | * move focus outside, for example in a modal dialog.
10 | */
11 | contain?: boolean;
12 |
13 | /**
14 | * Whether to restore focus back to the element that was focused
15 | * when the focus scope mounted, after the focus scope unmounts.
16 | */
17 | restoreFocus?: boolean;
18 |
19 | /** Whether to auto focus the first focusable element in the focus scope on mount. */
20 | autoFocus?: boolean;
21 | }
22 |
23 | /**
24 | * A FocusScope manages focus for its descendants. It supports containing focus inside
25 | * the scope, restoring focus to the previously focused element on unmount, and auto
26 | * focusing children on mount. It also acts as a container for a programmatic focus
27 | * management interface that can be used to move focus forward and back in response
28 | * to user events.
29 | */
30 | export function FocusScope(props: FocusScopeProps) {
31 | return {props.children};
32 | }
33 |
34 | // Noop - Implement this for mac and windows
35 | export const useFocusManager = () => {};
36 |
--------------------------------------------------------------------------------
/packages/focus/src/FocusScope.web.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | FocusScope as AriaFocusScope,
4 | useFocusManager,
5 | } from '@react-aria/focus';
6 | import type { FocusScopeProps } from './FocusScope';
7 | const FocusScope = ({ children, contain, ...props }: FocusScopeProps) => {
8 | /* Todo: stoping mounted and unMounted everytime contain is change */
9 | // if (contain === false) return <>>;
10 |
11 | return (
12 |
13 | {children}
14 |
15 | );
16 | };
17 |
18 | export { FocusScope, useFocusManager };
19 |
--------------------------------------------------------------------------------
/packages/focus/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useFocusRing';
2 | export { FocusScope, useFocusManager } from './FocusScope';
3 |
--------------------------------------------------------------------------------
/packages/focus/src/index.web.ts:
--------------------------------------------------------------------------------
1 | export { useFocusRing } from './useFocusRing.web';
2 | export { FocusScope } from './FocusScope.web';
3 | export { useFocusManager } from './FocusScope.web';
4 |
--------------------------------------------------------------------------------
/packages/focus/src/useFocusRing.ts:
--------------------------------------------------------------------------------
1 | export const useFocusRing = () => {
2 | return {
3 | focusProps: {},
4 | isFocusVisible: false,
5 | };
6 | };
7 |
--------------------------------------------------------------------------------
/packages/focus/src/useFocusRing.web.ts:
--------------------------------------------------------------------------------
1 | export { useFocusRing } from '@react-aria/focus';
2 |
--------------------------------------------------------------------------------
/packages/focus/tsconfig.build.json:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "extends": "../tsconfig",
4 | }
5 |
--------------------------------------------------------------------------------
/packages/interactions/README.md:
--------------------------------------------------------------------------------
1 | ## Installation
2 |
3 | ```js
4 | yarn add @react-native-aria/interactions
5 | ```
6 |
--------------------------------------------------------------------------------
/packages/interactions/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['module:metro-react-native-babel-preset'],
3 | };
4 |
--------------------------------------------------------------------------------
/packages/interactions/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@react-native-aria/interactions",
3 | "version": "0.2.8",
4 | "description": "mono repo setup with bob",
5 | "main": "lib/commonjs/index",
6 | "module": "lib/module/index",
7 | "typings": "lib/typescript/index.d.ts",
8 | "react-native": "src/index",
9 | "source": "src/index",
10 | "files": [
11 | "src",
12 | "lib",
13 | "android",
14 | "ios",
15 | "cpp",
16 | "react-native-bob-mono.podspec",
17 | "!lib/typescript/example",
18 | "!android/build",
19 | "!ios/build",
20 | "!**/__tests__",
21 | "!**/__fixtures__",
22 | "!**/__mocks__"
23 | ],
24 | "scripts": {
25 | "test": "jest",
26 | "typescript": "tsc --noEmit",
27 | "lint": "eslint \"**/*.{js,ts,tsx}\"",
28 | "prepare": "bob build",
29 | "release": "release-it",
30 | "example": "yarn --cwd example",
31 | "pods": "cd example && pod-install --quiet",
32 | "bootstrap": "yarn example && yarn && yarn pods"
33 | },
34 | "keywords": [
35 | "react-native",
36 | "ios",
37 | "android"
38 | ],
39 | "repository": "https://github.com/intergalacticspacehighway/react-native-bob-mono",
40 | "author": "nishan (https://github.com/intergalacticspacehighway)",
41 | "license": "MIT",
42 | "bugs": {
43 | "url": "https://github.com/intergalacticspacehighway/react-native-bob-mono/issues"
44 | },
45 | "homepage": "https://github.com/intergalacticspacehighway/react-native-bob-mono#readme",
46 | "publishConfig": {
47 | "registry": "https://registry.npmjs.org/"
48 | },
49 | "dependencies": {
50 | "@react-aria/interactions": "^3.3.2",
51 | "@react-aria/utils": "^3.6.0",
52 | "@react-native-aria/utils": "^0.2.6"
53 | },
54 | "peerDependencies": {
55 | "react": "*",
56 | "react-native": "*"
57 | },
58 | "jest": {
59 | "preset": "react-native",
60 | "modulePathIgnorePatterns": [
61 | "/example/node_modules",
62 | "/lib/"
63 | ]
64 | },
65 | "husky": {
66 | "hooks": {
67 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
68 | "pre-commit": "yarn lint && yarn typescript"
69 | }
70 | },
71 | "commitlint": {
72 | "extends": [
73 | "@commitlint/config-conventional"
74 | ]
75 | },
76 | "release-it": {
77 | "git": {
78 | "commitMessage": "chore: release ${version}",
79 | "tagName": "v${version}"
80 | },
81 | "npm": {
82 | "publish": true
83 | },
84 | "github": {
85 | "release": true
86 | },
87 | "plugins": {
88 | "@release-it/conventional-changelog": {
89 | "preset": "angular"
90 | }
91 | }
92 | },
93 | "eslintConfig": {
94 | "root": true,
95 | "extends": [
96 | "@react-native-community",
97 | "prettier"
98 | ],
99 | "rules": {
100 | "prettier/prettier": [
101 | "error",
102 | {
103 | "quoteProps": "consistent",
104 | "singleQuote": true,
105 | "tabWidth": 2,
106 | "trailingComma": "es5",
107 | "useTabs": false
108 | }
109 | ]
110 | }
111 | },
112 | "eslintIgnore": [
113 | "node_modules/",
114 | "lib/"
115 | ],
116 | "prettier": {
117 | "quoteProps": "consistent",
118 | "singleQuote": true,
119 | "tabWidth": 2,
120 | "trailingComma": "es5",
121 | "useTabs": false
122 | },
123 | "react-native-builder-bob": {
124 | "source": "src",
125 | "output": "lib",
126 | "targets": [
127 | "commonjs",
128 | "module",
129 | [
130 | "typescript",
131 | {
132 | "project": "tsconfig.build.json"
133 | }
134 | ]
135 | ]
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/packages/interactions/scripts/bootstrap.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const child_process = require('child_process');
3 |
4 | const root = path.resolve(__dirname, '..');
5 | const args = process.argv.slice(2);
6 | const options = {
7 | cwd: process.cwd(),
8 | env: process.env,
9 | stdio: 'inherit',
10 | encoding: 'utf-8',
11 | };
12 |
13 | let result;
14 |
15 | if (process.cwd() !== root || args.length) {
16 | // We're not in the root of the project, or additional arguments were passed
17 | // In this case, forward the command to `yarn`
18 | result = child_process.spawnSync('yarn', args, options);
19 | } else {
20 | // If `yarn` is run without arguments, perform bootstrap
21 | result = child_process.spawnSync('yarn', ['bootstrap'], options);
22 | }
23 |
24 | process.exitCode = result.status;
25 |
--------------------------------------------------------------------------------
/packages/interactions/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from './useHover';
2 | export * from './usePress';
3 |
--------------------------------------------------------------------------------
/packages/interactions/src/index.web.ts:
--------------------------------------------------------------------------------
1 | export { useHover } from "./useHover.web";
2 | export { usePress } from "./usePress";
3 |
--------------------------------------------------------------------------------
/packages/interactions/src/useHover.ts:
--------------------------------------------------------------------------------
1 | import { HoverProps } from '@react-aria/interactions';
2 |
3 | export const useHover = (_props: HoverProps, _ref: any) => {
4 | let params = {
5 | hoverProps: {},
6 | isHovered: false,
7 | };
8 |
9 | return params;
10 | };
11 |
--------------------------------------------------------------------------------
/packages/interactions/src/useHover.web.ts:
--------------------------------------------------------------------------------
1 | import { useHover as useHoverWeb, HoverProps } from '@react-aria/interactions';
2 | import { useEffect } from 'react';
3 | import { attachEventHandlersOnRef } from '@react-native-aria/utils';
4 |
5 | export const useHover = (props: HoverProps, ref: any) => {
6 | let params = useHoverWeb(props);
7 | useEffect(() => {
8 | attachEventHandlersOnRef(params.hoverProps, ref);
9 | }, []);
10 |
11 | return params;
12 | };
13 |
--------------------------------------------------------------------------------
/packages/interactions/src/usePress.ts:
--------------------------------------------------------------------------------
1 | import React, { RefObject } from 'react';
2 | import { mergeProps } from '@react-aria/utils';
3 |
4 | export interface PressEvents {
5 | /** Handler that is called when the press is released over the target. */
6 | onPress?: (e: any) => void;
7 | /** Handler that is called when a press interaction starts. */
8 | onPressStart?: (e: any) => void;
9 | /**
10 | * Handler that is called when a press interaction ends, either
11 | * over the target or when the pointer leaves the target.
12 | */
13 | onPressEnd?: (e: any) => void;
14 | /** Handler that is called when the press state changes. */
15 | onPressChange?: (isPressed: boolean) => void;
16 | /**
17 | * Handler that is called when a press is released over the target, regardless of
18 | * whether it started on the target or not.
19 | */
20 | onPressUp?: (e: any) => void;
21 | }
22 |
23 | export interface PressProps extends PressEvents {
24 | /** Whether the target is in a controlled press state (e.g. an overlay it triggers is open). */
25 | isPressed?: boolean;
26 | /** Whether the press events should be disabled. */
27 | isDisabled?: boolean;
28 | /** Whether the target should not receive focus on press. */
29 | preventFocusOnPress?: boolean;
30 | }
31 |
32 | export interface PressHookProps extends PressProps {
33 | /** A ref to the target element. */
34 | ref?: RefObject;
35 | }
36 |
37 | export type PressResult = {
38 | /** Whether the target is currently pressed. */
39 | isPressed: boolean;
40 | /** Props to spread on the target element. */
41 | pressProps: any;
42 | };
43 |
44 | export function usePress({
45 | isDisabled,
46 | onPress,
47 | onPressStart,
48 | onPressEnd,
49 | onPressUp, // No onPressUp on RN.
50 | onPressChange,
51 | isPressed: isPressedProp,
52 | ...restProps
53 | }: PressHookProps): PressResult {
54 | let [isPressed, setPressed] = React.useState(false);
55 |
56 | let pressProps = {
57 | onPress: (e: any) => {
58 | if (isDisabled) return;
59 | onPress && onPress(e);
60 | },
61 | onPressIn: (e: any) => {
62 | if (isDisabled) return;
63 | onPressStart && onPressStart(e);
64 | setPressed(true);
65 | onPressChange && onPressChange(true);
66 | },
67 | onPressOut: (e: any) => {
68 | if (isDisabled) return;
69 | onPressEnd && onPressEnd(e);
70 | setPressed(false);
71 | onPressChange && onPressChange(false);
72 | onPressUp && onPressUp(e);
73 | },
74 | };
75 |
76 | pressProps = mergeProps(pressProps, restProps);
77 |
78 | return {
79 | isPressed: isPressedProp || isPressed,
80 | pressProps,
81 | };
82 | }
83 |
--------------------------------------------------------------------------------
/packages/interactions/tsconfig.build.json:
--------------------------------------------------------------------------------
1 |
2 | {
3 | "extends": "../tsconfig",
4 | }
5 |
--------------------------------------------------------------------------------
/packages/listbox/README.md:
--------------------------------------------------------------------------------
1 | ## Installation
2 |
3 | ```js
4 | yarn add @react-native-aria/utils
5 | ```
6 |
--------------------------------------------------------------------------------
/packages/listbox/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['module:metro-react-native-babel-preset'],
3 | };
4 |
--------------------------------------------------------------------------------
/packages/listbox/scripts/bootstrap.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const child_process = require('child_process');
3 |
4 | const root = path.resolve(__dirname, '..');
5 | const args = process.argv.slice(2);
6 | const options = {
7 | cwd: process.cwd(),
8 | env: process.env,
9 | stdio: 'inherit',
10 | encoding: 'utf-8',
11 | };
12 |
13 | let result;
14 |
15 | if (process.cwd() !== root || args.length) {
16 | // We're not in the root of the project, or additional arguments were passed
17 | // In this case, forward the command to `yarn`
18 | result = child_process.spawnSync('yarn', args, options);
19 | } else {
20 | // If `yarn` is run without arguments, perform bootstrap
21 | result = child_process.spawnSync('yarn', ['bootstrap'], options);
22 | }
23 |
24 | process.exitCode = result.status;
25 |
--------------------------------------------------------------------------------
/packages/listbox/src/index.ts:
--------------------------------------------------------------------------------
1 | export { useOption } from './useOption';
2 | export { useListBox } from './useListBox';
3 |
--------------------------------------------------------------------------------
/packages/listbox/src/index.web.ts:
--------------------------------------------------------------------------------
1 | export { useOption } from './useOption.web';
2 | export { useListBox } from './useListBox.web';
3 |
--------------------------------------------------------------------------------
/packages/listbox/src/useListBox.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Adobe. All rights reserved.
3 | * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License. You may obtain a copy
5 | * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software distributed under
8 | * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 | * OF ANY KIND, either express or implied. See the License for the specific language
10 | * governing permissions and limitations under the License.
11 | */
12 |
13 | import { AriaListBoxProps } from '@react-types/listbox';
14 | import { ReactNode, RefObject } from 'react';
15 | import { KeyboardDelegate } from '@react-types/shared';
16 | import { ListState } from '@react-stately/list';
17 |
18 | interface ListBoxAria {
19 | /** Props for the listbox element. */
20 | listBoxProps: any;
21 | /** Props for the listbox's visual label element (if any). */
22 | labelProps: any;
23 | }
24 |
25 | interface AriaListBoxOptions extends Omit, 'children'> {
26 | /** Whether the listbox uses virtual scrolling. */
27 | isVirtualized?: boolean;
28 |
29 | /**
30 | * An optional keyboard delegate implementation for type to select,
31 | * to override the default.
32 | */
33 | keyboardDelegate?: KeyboardDelegate;
34 |
35 | /**
36 | * An optional visual label for the listbox.
37 | */
38 | label?: ReactNode;
39 | }
40 |
41 | /**
42 | * Provides the behavior and accessibility implementation for a listbox component.
43 | * A listbox displays a list of options and allows a user to select one or more of them.
44 | * @param props - Props for the listbox.
45 | * @param state - State for the listbox, as returned by `useListState`.
46 | */
47 | export function useListBox(
48 | _props: AriaListBoxOptions,
49 | _state: ListState,
50 | _ref: RefObject
51 | ): ListBoxAria {
52 | return {
53 | labelProps: {},
54 | listBoxProps: {},
55 | };
56 | }
57 |
--------------------------------------------------------------------------------
/packages/listbox/src/useListBox.web.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2020 Adobe. All rights reserved.
3 | * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 | * you may not use this file except in compliance with the License. You may obtain a copy
5 | * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 | *
7 | * Unless required by applicable law or agreed to in writing, software distributed under
8 | * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 | * OF ANY KIND, either express or implied. See the License for the specific language
10 | * governing permissions and limitations under the License.
11 | */
12 |
13 | import { AriaListBoxProps } from '@react-types/listbox';
14 | import { filterDOMProps, mergeProps } from '@react-aria/utils';
15 | import { HTMLAttributes, ReactNode, RefObject } from 'react';
16 | import { KeyboardDelegate } from '@react-types/shared';
17 | import { listIds } from './utils';
18 | import { ListState } from '@react-stately/list';
19 | import { useId } from '@react-aria/utils';
20 | import { useLabel } from '@react-aria/label';
21 | import { useSelectableList } from '@react-aria/selection';
22 | import { mapDomPropsToRN, useMapDomPropsToRN } from '@react-native-aria/utils';
23 |
24 | interface ListBoxAria {
25 | /** Props for the listbox element. */
26 | listBoxProps: HTMLAttributes;
27 | /** Props for the listbox's visual label element (if any). */
28 | labelProps: HTMLAttributes;
29 | }
30 |
31 | interface AriaListBoxOptions extends Omit, 'children'> {
32 | /** Whether the listbox uses virtual scrolling. */
33 | isVirtualized?: boolean;
34 |
35 | /**
36 | * An optional keyboard delegate implementation for type to select,
37 | * to override the default.
38 | */
39 | keyboardDelegate?: KeyboardDelegate;
40 |
41 | /**
42 | * An optional visual label for the listbox.
43 | */
44 | label?: ReactNode;
45 | }
46 |
47 | /**
48 | * Provides the behavior and accessibility implementation for a listbox component.
49 | * A listbox displays a list of options and allows a user to select one or more of them.
50 | * @param props - Props for the listbox.
51 | * @param state - State for the listbox, as returned by `useListState`.
52 | */
53 | export function useListBox(
54 | props: AriaListBoxOptions,
55 | state: ListState,
56 | ref: RefObject
57 | ): ListBoxAria {
58 | let domProps = filterDOMProps(props, { labelable: true });
59 | let { listProps } = useSelectableList({
60 | ...props,
61 | ref,
62 | selectionManager: state.selectionManager,
63 | collection: state.collection,
64 | disabledKeys: state.disabledKeys,
65 | });
66 |
67 | let id = useId(props.id);
68 | listIds.set(state, id);
69 |
70 | let { labelProps: _labelProps, fieldProps } = useLabel({
71 | ...props,
72 | id,
73 | // listbox is not an HTML input element so it
74 | // shouldn't be labeled by a