-
Notifications
You must be signed in to change notification settings - Fork 1.3k
docs: add s2 non-component hook docs #9106
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
42e64d9
docs: add non-component hook docs
yihuiliao c76fdf6
add useClipboard
yihuiliao 5c2263d
add more hook docs
yihuiliao cd290e1
clean up interaction hooks
yihuiliao 00b4ae6
clean up dnd hooks
yihuiliao e1b9fb2
cleanup focus hooks
yihuiliao a6360cd
cleanup internationalized hooks
yihuiliao 898abd8
add page description
yihuiliao e810710
fix types
yihuiliao ebdd889
fix lint
yihuiliao 844f2a5
actually fix lint
yihuiliao 8fff5ce
remove console log
yihuiliao 9427e52
update css import
yihuiliao ce757a1
fix styles
yihuiliao 840dbe9
Merge branch 'main' into s2-hook-docs
snowystinger File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| "use client"; | ||
| import React from 'react'; | ||
| import {useDrag} from '@react-aria/dnd'; | ||
|
|
||
| export function Draggable() { | ||
| let {dragProps, isDragging} = useDrag({ | ||
| getItems() { | ||
| return [{ | ||
| 'text/plain': 'hello world', | ||
| 'my-app-custom-type': JSON.stringify({message: 'hello world'}) | ||
| }]; | ||
| } | ||
| }); | ||
|
|
||
| return ( | ||
| <div {...dragProps} role="button" tabIndex={0} className={`draggable ${isDragging ? 'dragging' : ''}`}> | ||
| Drag me | ||
| </div> | ||
| ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| "use client"; | ||
|
|
||
| import React, {JSX} from 'react'; | ||
| import type {TextDropItem} from '@react-aria/dnd'; | ||
| import {useDrop} from '@react-aria/dnd'; | ||
|
|
||
| interface DroppedItem { | ||
| message: string; | ||
| style?: 'bold' | 'italic'; | ||
| } | ||
|
|
||
| export function DropTarget() { | ||
| let [dropped, setDropped] = React.useState<DroppedItem[] | null>(null); | ||
| let ref = React.useRef(null); | ||
| let {dropProps, isDropTarget} = useDrop({ | ||
| ref, | ||
| async onDrop(e) { | ||
| let items = await Promise.all( | ||
| (e.items as TextDropItem[]) | ||
| .filter(item => item.kind === 'text' && (item.types.has('text/plain') || item.types.has('my-app-custom-type'))) | ||
| .map(async item => { | ||
| if (item.types.has('my-app-custom-type')) { | ||
| return JSON.parse(await item.getText('my-app-custom-type')); | ||
| } else { | ||
| return {message: await item.getText('text/plain')}; | ||
| } | ||
| }) | ||
| ); | ||
| setDropped(items); | ||
| } | ||
| }); | ||
|
|
||
| let message: JSX.Element[] = [<div>{`Drop here`}</div>]; | ||
| if (dropped) { | ||
| message = dropped.map((d, index) => { | ||
| let m = d.message; | ||
| if (d.style === 'bold') { | ||
| message = [<strong>{m}</strong>]; | ||
| } else if (d.style === 'italic') { | ||
| message = [<em>{m}</em>]; | ||
| } | ||
| return <div key={index}>{message}</div>; | ||
| }); | ||
| } | ||
|
|
||
| return ( | ||
| <div {...dropProps} role="button" tabIndex={0} ref={ref} className={`droppable ${isDropTarget ? 'target' : ''}`}> | ||
| {message} | ||
| </div> | ||
| ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| {/* Copyright 2025 Adobe. All rights reserved. | ||
| This file is licensed to you under the Apache License, Version 2.0 (the "License"); | ||
| you may not use this file except in compliance with the License. You may obtain a copy | ||
| of the License at http://www.apache.org/licenses/LICENSE-2.0 | ||
| Unless required by applicable law or agreed to in writing, software distributed under | ||
| the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS | ||
| OF ANY KIND, either express or implied. See the License for the specific language | ||
| governing permissions and limitations under the License. */} | ||
|
|
||
| import {Layout} from '../../src/Layout'; | ||
| export default Layout; | ||
| import {GroupedPropTable} from '../../src/PropTable'; | ||
| import {FunctionAPI} from '../../src/FunctionAPI'; | ||
| import docs from 'docs:@react-aria/focus'; | ||
|
|
||
| export const section = 'Focus'; | ||
|
|
||
| # FocusRing | ||
|
|
||
| <PageDescription>{docs.exports.FocusRing.description}</PageDescription> | ||
|
|
||
| ## Introduction | ||
|
|
||
| `FocusRing` is a utility component that can be used to apply a CSS class when an element has keyboard focus. | ||
| This helps keyboard users determine which element on a page or in an application has keyboard focus as they | ||
| navigate around. Focus rings are only visible when interacting with a keyboard so as not to distract mouse | ||
| and touch screen users. When we are unable to detect if the user is using a mouse or touch screen, such as | ||
| switching in from a different tab, we show the focus ring. | ||
|
|
||
| If CSS classes are not being used for styling, see [useFocusRing](useFocusRing.html) for a hooks version. | ||
|
|
||
| ## Props | ||
|
|
||
| <PropTable links={docs.links} component={docs.exports.FocusRing} /> | ||
|
|
||
| ## Example | ||
|
|
||
| This example shows how to use `<FocusRing>` to apply a CSS class when keyboard focus is on a button. | ||
|
|
||
| ```tsx render files={["packages/dev/s2-docs/pages/react-aria/FocusRingExample.css"]} | ||
| 'use client'; | ||
| import {FocusRing} from '@react-aria/focus'; | ||
| import './FocusRingExample.css'; | ||
|
|
||
| <FocusRing focusRingClass="focus-ring"> | ||
| <button className="button">Test</button> | ||
| </FocusRing> | ||
| ``` | ||
14 changes: 14 additions & 0 deletions
14
packages/dev/s2-docs/pages/react-aria/FocusRingExample.css
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| .button { | ||
| -webkit-appearance: none; | ||
| appearance: none; | ||
| background: green; | ||
| border: none; | ||
| color: white; | ||
| font-size: 14px; | ||
| padding: 4px 8px; | ||
| } | ||
|
|
||
| .button.focus-ring { | ||
| outline: 2px solid dodgerblue; | ||
| outline-offset: 2px; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,130 @@ | ||
| {/* Copyright 2025 Adobe. All rights reserved. | ||
| This file is licensed to you under the Apache License, Version 2.0 (the "License"); | ||
| you may not use this file except in compliance with the License. You may obtain a copy | ||
| of the License at http://www.apache.org/licenses/LICENSE-2.0 | ||
| Unless required by applicable law or agreed to in writing, software distributed under | ||
| the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS | ||
| OF ANY KIND, either express or implied. See the License for the specific language | ||
| governing permissions and limitations under the License. */} | ||
|
|
||
| import {Layout} from '../../src/Layout'; | ||
| export default Layout; | ||
| import {GroupedPropTable} from '../../src/PropTable'; | ||
| import {FunctionAPI} from '../../src/FunctionAPI'; | ||
| import docs from 'docs:@react-aria/focus'; | ||
|
|
||
| export const section = 'Focus'; | ||
|
|
||
| # FocusScope | ||
|
|
||
| <PageDescription>{docs.exports.FocusScope.description}</PageDescription> | ||
|
|
||
| ## Introduction | ||
|
|
||
| `FocusScope` is a utility component that can be used to manage focus for its descendants. | ||
| When the `contain` prop is set, focus is contained within the scope. This is useful when | ||
| implementing overlays like modal dialogs, which should not allow focus to escape them while open. | ||
| In addition, the `restoreFocus` prop can be used to restore focus back to the previously focused | ||
| element when the focus scope unmounts, for example, back to a button which opened a dialog. | ||
| A FocusScope can also optionally auto focus the first focusable element within it on mount | ||
| when the `autoFocus` prop is set. | ||
|
|
||
| The <TypeLink links={docs.links} type={docs.exports.useFocusManager} /> hook can also be used | ||
| in combination with a FocusScope to programmatically move focus within the scope. For example, | ||
| arrow key navigation could be implemented by handling keyboard events and using a focus manager | ||
| to move focus to the next and previous elements. | ||
|
|
||
| ## Props | ||
|
|
||
| <PropTable links={docs.links} component={docs.exports.FocusScope} /> | ||
|
|
||
| ## FocusManager Interface | ||
|
|
||
| To get a focus manager, call the <TypeLink links={docs.links} type={docs.exports.useFocusManager} /> hook | ||
| from a component within the FocusScope. A focus manager supports the following methods: | ||
|
|
||
| <ClassAPI links={docs.links} class={docs.exports.FocusManager} /> | ||
|
|
||
| ## Example | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should be above props, i suspect i'll have this comment for all the pages. I'm ok for it to be follow up as well |
||
|
|
||
| A basic example of a focus scope that contains focus within it is below. Clicking the "Open" | ||
| button mounts a FocusScope, which auto focuses the first input inside it. Once open, you can | ||
| press the <Keyboard>Tab</Keyboard> key to move within the scope, but focus is contained inside. Clicking the "Close" | ||
| button unmounts the focus scope, which restores focus back to the button. | ||
|
|
||
| {/* Not implemented yet */} | ||
| {/* For a full example of building a modal dialog, see [useDialog](useDialog.html). */} | ||
|
|
||
| ```tsx render | ||
| 'use client'; | ||
| import React from 'react'; | ||
| import {FocusScope} from '@react-aria/focus'; | ||
|
|
||
| function Example() { | ||
| let [isOpen, setOpen] = React.useState(false); | ||
| return ( | ||
| <> | ||
| <button onClick={() => setOpen(true)}>Open</button> | ||
| {isOpen && | ||
| <FocusScope contain restoreFocus autoFocus> | ||
| <label htmlFor="first-input">First Input</label> | ||
| <input id="first-input" /> | ||
| <label htmlFor="second-input">Second Input</label> | ||
| <input id="second-input" /> | ||
| <button onClick={() => setOpen(false)}>Close</button> | ||
| </FocusScope> | ||
| } | ||
| </> | ||
| ); | ||
| } | ||
| ``` | ||
|
|
||
| ## useFocusManager Example | ||
|
|
||
| This example shows how to use `useFocusManager` to programmatically move focus within a | ||
| `FocusScope`. It implements a basic toolbar component, which allows using the left and | ||
| right arrow keys to move focus to the previous and next buttons. The `wrap` option is | ||
| used to make focus wrap around when it reaches the first or last button. | ||
|
|
||
| ```tsx render | ||
| 'use client'; | ||
| import {FocusScope} from '@react-aria/focus'; | ||
| import {useFocusManager} from '@react-aria/focus'; | ||
|
|
||
| function Toolbar(props) { | ||
| return ( | ||
| <div role="toolbar"> | ||
| <FocusScope> | ||
| {props.children} | ||
| </FocusScope> | ||
| </div> | ||
| ); | ||
| } | ||
|
|
||
| function ToolbarButton(props) { | ||
| let focusManager = useFocusManager(); | ||
| let onKeyDown = (e) => { | ||
| switch (e.key) { | ||
| case 'ArrowRight': | ||
| focusManager.focusNext({wrap: true}); | ||
| break; | ||
| case 'ArrowLeft': | ||
| focusManager.focusPrevious({wrap: true}); | ||
| break; | ||
| } | ||
| }; | ||
|
|
||
| return ( | ||
| <button | ||
| onKeyDown={onKeyDown}> | ||
| {props.children} | ||
| </button> | ||
| ); | ||
| } | ||
|
|
||
| <Toolbar> | ||
| <ToolbarButton>Cut</ToolbarButton> | ||
| <ToolbarButton>Copy</ToolbarButton> | ||
| <ToolbarButton>Paste</ToolbarButton> | ||
| </Toolbar> | ||
| ``` | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| {/* Copyright 2025 Adobe. All rights reserved. | ||
| This file is licensed to you under the Apache License, Version 2.0 (the "License"); | ||
| you may not use this file except in compliance with the License. You may obtain a copy | ||
| of the License at http://www.apache.org/licenses/LICENSE-2.0 | ||
| Unless required by applicable law or agreed to in writing, software distributed under | ||
| the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS | ||
| OF ANY KIND, either express or implied. See the License for the specific language | ||
| governing permissions and limitations under the License. */} | ||
|
|
||
| import {Layout} from '../../src/Layout'; | ||
| export default Layout; | ||
| import {FunctionAPI} from '../../src/FunctionAPI'; | ||
| import docs from 'docs:@react-aria/i18n'; | ||
|
|
||
| export const section = 'Internationalization'; | ||
| export const description = 'Implementing collections in React Aria'; | ||
|
|
||
|
|
||
| # I18nProvider | ||
|
|
||
| ## Introduction | ||
|
|
||
| `I18nProvider` allows you to override the default locale as determined by the browser/system setting | ||
| with a locale defined by your application (e.g. application setting). This should be done by wrapping | ||
| your entire application in the provider, which will be cause all child elements to receive the new locale | ||
| information via [useLocale](useLocale.html). | ||
|
|
||
| ## Props | ||
|
|
||
| <PropTable component={docs.exports.I18nProvider} links={docs.links} /> | ||
|
|
||
| ## Example | ||
|
|
||
| ```tsx | ||
| import {I18nProvider} from '@react-aria/i18n'; | ||
|
|
||
| <I18nProvider locale="fr-FR"> | ||
| <YourApp /> | ||
| </I18nProvider> | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| 'use client'; | ||
| import React from 'react'; | ||
| import {UNSTABLE_ToastRegion as ToastRegion, UNSTABLE_Toast as Toast, UNSTABLE_ToastQueue as ToastQueue, UNSTABLE_ToastContent as ToastContent, Button, Text} from 'react-aria-components'; | ||
|
|
||
| // Define the type for your toast content. | ||
| interface MyToastContent { | ||
| title: string, | ||
| description?: string | ||
| } | ||
|
|
||
| export function MyToastRegion({queue}: {queue: ToastQueue<MyToastContent>}) { | ||
| return ( | ||
| <ToastRegion queue={queue}> | ||
| {({toast}) => ( | ||
| <Toast toast={toast}> | ||
| <ToastContent> | ||
| <Text slot="title">{toast.content.title}</Text> | ||
| <Text slot="description">{toast.content.description}</Text> | ||
| </ToastContent> | ||
| <Button slot="close">x</Button> | ||
| </Toast> | ||
| )} | ||
| </ToastRegion> | ||
| ); | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move example to be at the top? or at least above props