diff --git a/Makefile b/Makefile index 51aad416af4..e415c20a618 100644 --- a/Makefile +++ b/Makefile @@ -205,7 +205,6 @@ build-s2-docs: check-starter-css # Build old docs pages, which get inter-mixed with the new pages # TODO: We probably don't need to build this on every PR yarn parcel build 'packages/@adobe/react-spectrum/docs/**/*.mdx' 'packages/dev/docs/pages/{react-spectrum,releases}/**/*.mdx' --dist-dir dist/s2-docs/s2/$(PUBLIC_URL) --public-url $(PUBLIC_URL) - yarn parcel build 'packages/{react-aria,react-stately}/docs/**/*.mdx' --dist-dir dist/s2-docs/react-aria/$(PUBLIC_URL) --public-url $(PUBLIC_URL) build-starters: $(MAKE) starter-zip diff --git a/package.json b/package.json index 3aa0a953fa7..699a56203c8 100644 --- a/package.json +++ b/package.json @@ -40,8 +40,8 @@ "start:s2": "NODE_ENV=storybook storybook dev -p 6006 --ci -c '.storybook-s2'", "build:storybook-s2": "NODE_ENV=storybook storybook build -c .storybook-s2 -o dist/$(git rev-parse HEAD)/storybook-s2", "build:s2-storybook-docs": "NODE_ENV=storybook storybook build -c .storybook-s2 --docs", - "start:docs": "DOCS_ENV=dev parcel 'packages/{@adobe/react-spectrum,react-aria,react-stately}/docs/**/*.mdx' 'packages/dev/docs/pages/{react-spectrum,releases}/**/*.mdx'", - "build:docs": "DOCS_ENV=staging parcel build 'packages/{@adobe/react-spectrum,react-aria,react-stately}/docs/**/*.mdx' 'packages/dev/docs/pages/{react-spectrum,releases}/**/*.mdx'", + "start:docs": "DOCS_ENV=dev parcel 'packages/@adobe/react-spectrum/docs/**/*.mdx' 'packages/dev/docs/pages/{react-spectrum,releases}/**/*.mdx'", + "build:docs": "DOCS_ENV=staging parcel build 'packages/@adobe/react-spectrum/docs/**/*.mdx' 'packages/dev/docs/pages/{react-spectrum,releases}/**/*.mdx'", "start:s2-docs": "yarn workspace @react-spectrum/s2-docs start", "build:s2-docs": "yarn workspace @react-spectrum/s2-docs build", "check:s2-docs-build": "node packages/dev/s2-docs/scripts/validateS2DocsBuild.mjs", diff --git a/packages/@adobe/react-spectrum/docs/dnd/dnd.mdx b/packages/@adobe/react-spectrum/docs/dnd/dnd.mdx index 36116d4d957..e641362e390 100644 --- a/packages/@adobe/react-spectrum/docs/dnd/dnd.mdx +++ b/packages/@adobe/react-spectrum/docs/dnd/dnd.mdx @@ -15,12 +15,12 @@ import docs from 'docs:@react-spectrum/dnd'; import dndDocs from 'docs:@react-types/shared/src/dnd.d.ts'; import {FunctionAPI, PageDescription, TypeContext, InterfaceType} from '@react-spectrum/docs'; import {Keyboard} from '@react-spectrum/text'; -import DragPreview from '/packages/react-aria/docs/dnd/DragPreview.svg'; -import BetweenDropPosition from '/packages/react-aria/docs/dnd/BetweenDropPosition.svg'; -import OnDropPosition from '/packages/react-aria/docs/dnd/OnDropPosition.svg'; -import RootDropPosition from '/packages/react-aria/docs/dnd/RootDropPosition.svg'; -import DragAffordance from '/packages/react-aria/docs/dnd/DragAffordance.svg'; -import Anatomy from '/packages/react-aria/docs/dnd/Anatomy.svg'; +import DragPreview from '/packages/dev/s2-docs/pages/react-aria/DndDragPreview.svg'; +import BetweenDropPosition from '/packages/dev/s2-docs/pages/react-aria/DndBetweenDropPosition.svg'; +import OnDropPosition from '/packages/dev/s2-docs/pages/react-aria/DndOnDropPosition.svg'; +import RootDropPosition from '/packages/dev/s2-docs/pages/react-aria/DndRootDropPosition.svg'; +import DragAffordance from '/packages/dev/s2-docs/pages/react-aria/DndDragAffordance.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/DndAnatomy.svg'; --- category: Concepts diff --git a/packages/@react-spectrum/ai/exports/index.ts b/packages/@react-spectrum/ai/exports/index.ts index cacb63c5b52..418a959fcb6 100644 --- a/packages/@react-spectrum/ai/exports/index.ts +++ b/packages/@react-spectrum/ai/exports/index.ts @@ -3,7 +3,7 @@ export {MessageFeedback} from '../src/MessageFeedback'; export {MessageSource, SourceList, SourceListItem} from '../src/MessageSource'; export {MessageSuggestion, MessageSuggestionList} from '../src/MessageSuggestion'; export { - AutoLinkingTokenFieldValue, + PromptFieldValue, PromptField, PromptFieldSubmitButton, PromptTokenField, @@ -47,7 +47,10 @@ export type { PromptTokenFieldPopoverProps, PromptFieldToolbarProps, InsertMenuItemProps, - PromptFieldVoiceButtonProps + PromptFieldVoiceButtonProps, + InsertTokenMenuItemProps, + InsertTextMenuItemProps, + CommandMenuItemProps } from '../src/PromptField'; export type {MessageFeedbackProps} from '../src/MessageFeedback'; export type {MessageSourceProps, SourceListProps, SourceListItemProps} from '../src/MessageSource'; diff --git a/packages/@react-spectrum/ai/package.json b/packages/@react-spectrum/ai/package.json index fb09f74a77d..4d32a79c534 100644 --- a/packages/@react-spectrum/ai/package.json +++ b/packages/@react-spectrum/ai/package.json @@ -47,7 +47,6 @@ "dependencies": { "@internationalized/date": "^3.12.2", "@internationalized/number": "^3.6.7", - "@react-aria/utils": "^3.8.0", "@react-spectrum/s2": "1.5.1", "@react-types/shared": "^3.36.0", "react-aria": "3.50.0", diff --git a/packages/@react-spectrum/ai/src/AttachmentList.tsx b/packages/@react-spectrum/ai/src/AttachmentList.tsx index 9ccb19862ce..c67d6c9945e 100644 --- a/packages/@react-spectrum/ai/src/AttachmentList.tsx +++ b/packages/@react-spectrum/ai/src/AttachmentList.tsx @@ -181,7 +181,10 @@ export const AttachmentList = (forwardRef as forwardRefType)(function Attachment export interface AttachmentProps extends - Omit, + Omit< + CardProps, + 'styles' | 'UNSAFE_className' | 'UNSAFE_style' | 'allowsArrowNavigation' | 'focusMode' + >, AriaLabelingProps, Pick { /** The children of the Attachment. */ diff --git a/packages/@react-spectrum/ai/src/PromptField.tsx b/packages/@react-spectrum/ai/src/PromptField.tsx index 5f63de843c8..913df348abf 100644 --- a/packages/@react-spectrum/ai/src/PromptField.tsx +++ b/packages/@react-spectrum/ai/src/PromptField.tsx @@ -56,25 +56,26 @@ import Microphone from '@react-spectrum/s2/icons/Microphone'; import {PixelLoader} from './loader/react'; import Plus from '@react-spectrum/s2/icons/Add'; import {Popover, PopoverProps} from '@react-spectrum/s2/Popover'; +import {PromptFieldContainer} from './PromptFieldContainer'; +import {PromptFocusContext} from './Chat'; +import Send from '@react-spectrum/s2/icons/ArrowUpSend'; +import {setTokenFieldSelection} from 'react-aria/useTokenField'; +import Stop from '@react-spectrum/s2/icons/StopProcessing'; +import {ToggleButton} from '@react-spectrum/s2/ToggleButton'; import { - positionToDOMRange, Token, TokenField, + tokenFieldPositionToDOMRange, TokenInput, TokenProps } from 'react-aria-components/TokenField'; -import {PromptFieldContainer} from './PromptFieldContainer'; -import {PromptFocusContext} from './Chat'; -import Send from '@react-spectrum/s2/icons/ArrowUpSend'; -import {setSelection} from 'react-aria/useTokenField'; -import Stop from '@react-spectrum/s2/icons/StopProcessing'; -import {ToggleButton} from '@react-spectrum/s2/ToggleButton'; import {Tooltip, TooltipTrigger} from '@react-spectrum/s2/Tooltip'; import {useControlledState} from 'react-stately/useControlledState'; import {useDOMRef} from './useDOMRef'; import {useEffectEvent} from 'react-aria/private/utils/useEffectEvent'; import {useFocusWithin} from 'react-aria/useFocusWithin'; import {useKeyboard} from 'react-aria/useKeyboard'; +import {useLocale} from 'react-aria/I18nProvider'; import {useLocalizedStringFormatter} from 'react-aria/useLocalizedStringFormatter'; import {useVoiceInput, VoiceInputErrorCode} from './useVoiceInput'; @@ -146,7 +147,7 @@ function tokenizeURLs(text: string): TokenFieldSegment[] { return segments; } -export class AutoLinkingTokenFieldValue extends TokenFieldValue { +export class PromptFieldValue extends TokenFieldValue { tokenize(text: string): TokenFieldSegment[] { return tokenizeURLs(text); } @@ -155,7 +156,7 @@ export class AutoLinkingTokenFieldValue extends TokenFieldValue { const PromptFieldContext = createContext({ attachments: [], setAttachments: () => {}, - prompt: new AutoLinkingTokenFieldValue([]), + prompt: new PromptFieldValue([]), setPrompt: () => {}, inputRef: createRef(), isGenerating: false, @@ -199,7 +200,7 @@ export const PromptField = forwardRef(function PromptField( let stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-spectrum/ai'); let [prompt, setPrompt] = useControlledState( props.value, - props.defaultValue ?? new AutoLinkingTokenFieldValue([]), + props.defaultValue ?? new PromptFieldValue([]), props.onChange ); let [attachments, setAttachments] = useControlledState( @@ -247,7 +248,7 @@ export const PromptField = forwardRef(function PromptField( props.onSubmit?.(prompt, attachments); if (!isPromptControlled) { - setPrompt(new AutoLinkingTokenFieldValue([])); + setPrompt(new PromptFieldValue([])); } if (!isAttachmentsControlled) { setAttachments([]); @@ -479,7 +480,7 @@ export function PromptTokenField(props: PromptTokenFieldProps) { ); } -export interface PromptTokenFieldPopoverProps extends PopoverProps { +export interface PromptTokenFieldPopoverProps extends Omit { filterAnchor?: Position | null; items?: React.ReactNode[] | null | Promise; isFocused?: boolean; @@ -507,7 +508,7 @@ function PromptTokenFieldPopover(props: PromptTokenFieldPopoverProps) { hideArrow placement="bottom start" getTargetRect={target => { - return positionToDOMRange(target, filterAnchor!).getBoundingClientRect(); + return tokenFieldPositionToDOMRange(target, filterAnchor!).getBoundingClientRect(); }}> {menuItems} @@ -614,7 +615,9 @@ export interface PromptFieldVoiceButtonProps { } export function PromptFieldVoiceButton(props: PromptFieldVoiceButtonProps) { - let {lang, isDisabled: isDisabledProp, onError} = props; + let {lang: langProp, isDisabled: isDisabledProp, onError} = props; + let {locale} = useLocale(); + let lang = langProp ?? locale; let {prompt, setPrompt, inputRef, setListening} = useContext(PromptFieldContext); let isDisabled = isDisabledProp; let stringFormatter = useLocalizedStringFormatter(intlMessages, '@react-spectrum/ai'); @@ -640,7 +643,7 @@ export function PromptFieldVoiceButton(props: PromptFieldVoiceButtonProps) { // to be inaccurate let finalPrompt = buildVoicePrompt(basePromptRef.current, transcript); inputRef.current.focus(); - setSelection(inputRef.current, finalPrompt.caretPosition, finalPrompt.caretPosition); + setTokenFieldSelection(inputRef.current, finalPrompt.caretPosition, finalPrompt.caretPosition); setPrompt(finalPrompt); }); @@ -696,15 +699,11 @@ export function PromptFieldVoiceButton(props: PromptFieldVoiceButtonProps) { ); } -function buildVoicePrompt(base: TokenFieldValue, voiceText: string): AutoLinkingTokenFieldValue { +function buildVoicePrompt(base: TokenFieldValue, voiceText: string): PromptFieldValue { if (!voiceText) { - return base as AutoLinkingTokenFieldValue; + return base as PromptFieldValue; } - return base.replaceRange( - base.caretPosition, - base.caretPosition, - voiceText - ) as AutoLinkingTokenFieldValue; + return base.replaceRange(base.caretPosition, base.caretPosition, voiceText) as PromptFieldValue; } export interface InsertMenuItemProps { @@ -788,7 +787,7 @@ function useInsertPromptSegment(buildSegments: (item: any) => TokenFieldSegment[ inputRef.current.focus(); // we need to update the position manually since TokenField's update caret logic only happens if the field is focused // but this insert can happen from the + menu aka the field isn't focused until this gets called which is too late - setSelection(inputRef.current, position, position); + setTokenFieldSelection(inputRef.current, position, position); // the above focus and setCursor call can cause the internally tracked caret position to be reset incorrectly // seemingly due to TokenField's isProgrammaticSelectionChange being flipped to false by setCursor and thus reset to 0 by the .focus // fix this by resetting to proper position below @@ -800,27 +799,84 @@ function useInsertPromptSegment(buildSegments: (item: any) => TokenFieldSegment[ }; } -export function InsertTokenMenuItem(props: MenuItemProps) { +export interface InsertTokenMenuItemProps extends Omit< + MenuItemProps, + | 'UNSAFE_className' + | 'UNSAFE_style' + | 'download' + | 'href' + | 'hrefLang' + | 'ping' + | 'referrerPolicy' + | 'rel' + | 'routerOptions' + | 'target' +> {} + +export function InsertTokenMenuItem(props: InsertTokenMenuItemProps) { let insert = useInsertPromptSegment(item => [ {type: 'token', text: 'command' in item ? item.command : item.title, value: item}, {type: 'text', text: ' '} ]); - return insert(props.value)} />; + return ( + { + insert(props.value); + props.onAction?.(); + }} + /> + ); } -export function InsertTextMenuItem(props: MenuItemProps) { +export interface InsertTextMenuItemProps extends Omit< + MenuItemProps, + | 'UNSAFE_className' + | 'UNSAFE_style' + | 'download' + | 'href' + | 'hrefLang' + | 'ping' + | 'referrerPolicy' + | 'rel' + | 'routerOptions' + | 'target' +> {} + +export function InsertTextMenuItem(props: InsertTextMenuItemProps) { let insert = useInsertPromptSegment(item => [ {type: 'text', text: `${'command' in item ? item.command : item.title} `} ]); - return insert(props.value)} />; + return ( + { + insert(props.value); + props.onAction?.(); + }} + /> + ); } +export interface CommandMenuItemProps extends Omit< + MenuItemProps, + | 'UNSAFE_className' + | 'UNSAFE_style' + | 'download' + | 'href' + | 'hrefLang' + | 'ping' + | 'referrerPolicy' + | 'rel' + | 'routerOptions' + | 'target' +> {} // specifically for menu items that only trigger a callback in the autocomplete menu // since they dont end up inserting a token or text, we need to clear the partial text that the user used // to filter the menu -export function CommandMenuItem(props: MenuItemProps) { +export function CommandMenuItem(props: CommandMenuItemProps) { let insert = useInsertPromptSegment(() => []); return ( ([]); - let [promptValue, setPromptValue] = useState(new AutoLinkingTokenFieldValue([])); + let [promptValue, setPromptValue] = useState(new PromptFieldValue([])); let followUpMessage = useRef(null); function handleSend(prompt: TokenFieldValue) { @@ -561,7 +561,7 @@ export function VirtualizedStreamingChat() { value={promptValue} onChange={setPromptValue} onSubmit={prompt => { - setPromptValue(new AutoLinkingTokenFieldValue([])); + setPromptValue(new PromptFieldValue([])); handleSend(prompt); }} isGenerating={isGenerating} @@ -584,14 +584,14 @@ export function VirtualizedStreamingChat() { e.preventDefault(); if (promptValue.segments.length > 0) { action('onSteer')(promptValue.toString()); - setPromptValue(new AutoLinkingTokenFieldValue([])); + setPromptValue(new PromptFieldValue([])); } } else if (e.key === 'Enter' && e.altKey) { e.preventDefault(); if (promptValue.segments.length > 0) { action('onFollowUp')(promptValue.toString()); followUpMessage.current = promptValue; - setPromptValue(new AutoLinkingTokenFieldValue([])); + setPromptValue(new PromptFieldValue([])); } } else if (e.key === 'Escape') { e.preventDefault(); diff --git a/packages/@react-spectrum/ai/stories/PromptField.stories.tsx b/packages/@react-spectrum/ai/stories/PromptField.stories.tsx index 21356e72fcb..132b78c9d59 100644 --- a/packages/@react-spectrum/ai/stories/PromptField.stories.tsx +++ b/packages/@react-spectrum/ai/stories/PromptField.stories.tsx @@ -13,7 +13,6 @@ import {action} from 'storybook/actions'; import { AttachFileMenuItem, - AutoLinkingTokenFieldValue, CommandMenuItem, InsertMenuButton, InsertTextMenuItem, @@ -23,6 +22,7 @@ import { PromptFieldAttachmentList, PromptFieldSubmitButton, PromptFieldToolbar, + PromptFieldValue, PromptFieldVoiceButton, PromptToken, PromptTokenField @@ -245,7 +245,7 @@ interface UploadState { progress?: number; } -let prompt3Base = new AutoLinkingTokenFieldValue([ +let prompt3Base = new PromptFieldValue([ {type: 'text', text: 'Summarize the '}, {type: 'token', text: 'Welcome Flow', value: {type: 'journey', title: 'Welcome Flow'}} ]); @@ -255,12 +255,12 @@ let prompt3End = { }; let prompts = [ - new AutoLinkingTokenFieldValue([ + new PromptFieldValue([ {type: 'text', text: 'Analyze '}, {type: 'token', text: 'New Customers', value: {type: 'audience', title: 'New Customers'}}, {type: 'text', text: ' and suggest targeting strategies'} ]), - new AutoLinkingTokenFieldValue([ + new PromptFieldValue([ {type: 'text', text: 'Write a brief for '}, { type: 'token', @@ -273,7 +273,7 @@ let prompts = [ function EverythingRender(args) { let {placeholder, ...otherArgs} = args; - let [value, setValue] = useState(() => new AutoLinkingTokenFieldValue([])); + let [value, setValue] = useState(() => new PromptFieldValue([])); let [attachments, setAttachments] = useState([]); let [attachmentState, setAttachmentState] = useState>(new Map()); let historyRef = useRef([]); @@ -330,7 +330,7 @@ function EverythingRender(args) { if (nextIndex >= history.length) { historyIndexRef.current = -1; isHistoryNavigating.current = true; - setValue(new AutoLinkingTokenFieldValue([])); + setValue(new PromptFieldValue([])); } else { historyIndexRef.current = nextIndex; isHistoryNavigating.current = true; @@ -367,7 +367,7 @@ function EverythingRender(args) { action('onSubmit')(prompt.toString()); historyRef.current = [...historyRef.current, prompt]; historyIndexRef.current = -1; - setValue(new AutoLinkingTokenFieldValue([])); + setValue(new PromptFieldValue([])); setAttachments([]); setAttachmentState(new Map()); }} @@ -416,7 +416,7 @@ function EverythingRender(args) { renderCompletions={filterValue => renderCompletions(filterValue, { onClear: () => { - setValue(new AutoLinkingTokenFieldValue([])); + setValue(new PromptFieldValue([])); setAttachments([]); }, onCompact: action('onCompact') @@ -446,7 +446,7 @@ function EverythingRender(args) { { - setValue(new AutoLinkingTokenFieldValue([])); + setValue(new PromptFieldValue([])); setAttachments([]); }}> {item.command} diff --git a/packages/@react-spectrum/s2/src/SideNav.tsx b/packages/@react-spectrum/s2/src/SideNav.tsx index 5188835159d..c681b7cf98f 100644 --- a/packages/@react-spectrum/s2/src/SideNav.tsx +++ b/packages/@react-spectrum/s2/src/SideNav.tsx @@ -92,7 +92,7 @@ export interface SideNavProps >, UnsafeStyles { /** The route that is currently selected. */ - selectedRoute: string; + selectedRoute?: string | null; /** Spectrum-defined styles, returned by the `style()` macro. */ styles?: StylesPropWithHeight; } @@ -153,7 +153,7 @@ const tree = style({ interface InternalSideNavContextValue { /** The route that is currently selected. */ - selectedRoute?: string; + selectedRoute?: string | null; /** The last route the focused key was synced to; dedupes the focus sync across items. */ syncedRouteRef?: RefObject; } diff --git a/packages/@react-spectrum/s2/test/SideNav.test.tsx b/packages/@react-spectrum/s2/test/SideNav.test.tsx index 1e95cb8199c..01b032b1efa 100644 --- a/packages/@react-spectrum/s2/test/SideNav.test.tsx +++ b/packages/@react-spectrum/s2/test/SideNav.test.tsx @@ -237,6 +237,15 @@ describe('SideNav', () => { expect(queryByRole('link', {name: 'Projects 1'})).toBeNull(); }); + it('marks nothing if the selectedRoute is null', () => { + let {getAllByRole} = render(); + + let links = getAllByRole('link'); + for (let link of links) { + expect(link).not.toHaveAttribute('aria-current'); + } + }); + it('marks the link matching selectedRoute with aria-current="page"', () => { let {getByRole, rerender} = render(); diff --git a/packages/dev/s2-docs/pages/react-aria/Breadcrumbs.mdx b/packages/dev/s2-docs/pages/react-aria/Breadcrumbs.mdx index fa40a76d886..2b60dd90cba 100644 --- a/packages/dev/s2-docs/pages/react-aria/Breadcrumbs.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Breadcrumbs.mdx @@ -3,7 +3,7 @@ export default Layout; import docs from 'docs:react-aria-components'; import '../../tailwind/tailwind.css'; -import Anatomy from '/packages/react-aria/docs/breadcrumbs/anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/BreadcrumbsAnatomy.svg'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; export const tags = ['navigation']; diff --git a/packages/dev/s2-docs/pages/react-aria/Breadcrumbs/useBreadcrumbs.mdx b/packages/dev/s2-docs/pages/react-aria/Breadcrumbs/useBreadcrumbs.mdx index 5c8c8ea796d..00af36b88a0 100644 --- a/packages/dev/s2-docs/pages/react-aria/Breadcrumbs/useBreadcrumbs.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Breadcrumbs/useBreadcrumbs.mdx @@ -12,7 +12,7 @@ export default Layout; import {FunctionAPIGroup} from '../../../src/FunctionAPI'; import {InterfaceType} from '../../../src/types'; import docs from 'docs:@react-aria/breadcrumbs'; -import Anatomy from '/packages/react-aria/docs/breadcrumbs/anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/BreadcrumbsAnatomy.svg'; export const section = 'Hooks'; export const description = 'Provides the behavior and accessibility implementation for a breadcrumbs component.'; diff --git a/packages/react-aria/docs/breadcrumbs/anatomy.svg b/packages/dev/s2-docs/pages/react-aria/BreadcrumbsAnatomy.svg similarity index 100% rename from packages/react-aria/docs/breadcrumbs/anatomy.svg rename to packages/dev/s2-docs/pages/react-aria/BreadcrumbsAnatomy.svg diff --git a/packages/dev/s2-docs/pages/react-aria/Calendar.mdx b/packages/dev/s2-docs/pages/react-aria/Calendar.mdx index 95f0cad2472..cd73c8e7adf 100644 --- a/packages/dev/s2-docs/pages/react-aria/Calendar.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Calendar.mdx @@ -6,7 +6,7 @@ import vanillaDocs from 'docs:vanilla-starter/Calendar'; import {Calendar as VanillaCalendar} from 'vanilla-starter/Calendar'; import {Calendar as TailwindCalendar} from 'tailwind-starter/Calendar'; import '../../tailwind/tailwind.css'; -import Anatomy from '/packages/react-aria/docs/calendar/calendar-anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/CalendarAnatomy.svg'; export const tags = ['date']; export const relatedPages = [{'title': 'useCalendar', 'url': './Calendar/useCalendar'}]; diff --git a/packages/dev/s2-docs/pages/react-aria/Calendar/useCalendar.mdx b/packages/dev/s2-docs/pages/react-aria/Calendar/useCalendar.mdx index 94b961aee15..27b5e992aa0 100644 --- a/packages/dev/s2-docs/pages/react-aria/Calendar/useCalendar.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Calendar/useCalendar.mdx @@ -13,7 +13,7 @@ import {FunctionAPIGroup} from '../../../src/FunctionAPI'; import {InterfaceType} from '../../../src/types'; import docs from 'docs:@react-aria/calendar'; import statelyDocs from 'docs:@react-stately/calendar'; -import Anatomy from '/packages/react-aria/docs/calendar/calendar-anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/CalendarAnatomy.svg'; export const section = 'Hooks'; export const description = 'Provides the behavior and accessibility implementation for a calendar component.'; diff --git a/packages/react-aria/docs/calendar/calendar-anatomy.svg b/packages/dev/s2-docs/pages/react-aria/CalendarAnatomy.svg similarity index 100% rename from packages/react-aria/docs/calendar/calendar-anatomy.svg rename to packages/dev/s2-docs/pages/react-aria/CalendarAnatomy.svg diff --git a/packages/dev/s2-docs/pages/react-aria/Checkbox.mdx b/packages/dev/s2-docs/pages/react-aria/Checkbox.mdx index db4bc876876..c8afd936c8b 100644 --- a/packages/dev/s2-docs/pages/react-aria/Checkbox.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Checkbox.mdx @@ -6,7 +6,7 @@ import vanillaDocs from 'docs:vanilla-starter/Checkbox'; import {Checkbox as VanillaCheckbox} from 'vanilla-starter/Checkbox'; import {Checkbox as TailwindCheckbox} from 'tailwind-starter/Checkbox'; import '../../tailwind/tailwind.css'; -import Anatomy from '/packages/react-aria/docs/checkbox/checkbox-anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/CheckboxAnatomy.svg'; export const tags = ['input']; export const relatedPages = [{'title': 'useCheckbox', 'url': './Checkbox/useCheckbox'}]; diff --git a/packages/dev/s2-docs/pages/react-aria/Checkbox/useCheckbox.mdx b/packages/dev/s2-docs/pages/react-aria/Checkbox/useCheckbox.mdx index 74a298eecd8..a478ae91986 100644 --- a/packages/dev/s2-docs/pages/react-aria/Checkbox/useCheckbox.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Checkbox/useCheckbox.mdx @@ -13,7 +13,7 @@ import {FunctionAPIGroup} from '../../../src/FunctionAPI'; import {InterfaceType} from '../../../src/types'; import docs from 'docs:@react-aria/checkbox'; import statelyDocs from 'docs:@react-stately/toggle'; -import Anatomy from '/packages/react-aria/docs/checkbox/checkbox-anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/CheckboxAnatomy.svg'; export const section = 'Hooks'; export const description = 'Provides the behavior and accessibility implementation for a checkbox.'; diff --git a/packages/react-aria/docs/checkbox/checkbox-anatomy.svg b/packages/dev/s2-docs/pages/react-aria/CheckboxAnatomy.svg similarity index 100% rename from packages/react-aria/docs/checkbox/checkbox-anatomy.svg rename to packages/dev/s2-docs/pages/react-aria/CheckboxAnatomy.svg diff --git a/packages/dev/s2-docs/pages/react-aria/CheckboxGroup.mdx b/packages/dev/s2-docs/pages/react-aria/CheckboxGroup.mdx index edf194d79fb..3c84272973b 100644 --- a/packages/dev/s2-docs/pages/react-aria/CheckboxGroup.mdx +++ b/packages/dev/s2-docs/pages/react-aria/CheckboxGroup.mdx @@ -4,7 +4,7 @@ export default Layout; import docs from 'docs:react-aria-components'; import vanillaDocs from 'docs:vanilla-starter/CheckboxGroup'; import '../../tailwind/tailwind.css'; -import Anatomy from '/packages/react-aria/docs/checkbox/checkboxgroup-anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/CheckboxGroupAnatomy.svg'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2' export const tags = ['input']; diff --git a/packages/dev/s2-docs/pages/react-aria/CheckboxGroup/useCheckboxGroup.mdx b/packages/dev/s2-docs/pages/react-aria/CheckboxGroup/useCheckboxGroup.mdx index 704aa90efc5..d5d7bb948ad 100644 --- a/packages/dev/s2-docs/pages/react-aria/CheckboxGroup/useCheckboxGroup.mdx +++ b/packages/dev/s2-docs/pages/react-aria/CheckboxGroup/useCheckboxGroup.mdx @@ -13,7 +13,7 @@ import {FunctionAPIGroup} from '../../../src/FunctionAPI'; import {InterfaceType} from '../../../src/types'; import docs from 'docs:@react-aria/checkbox'; import statelyDocs from 'docs:@react-stately/checkbox'; -import Anatomy from '/packages/react-aria/docs/checkbox/checkboxgroup-anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/CheckboxGroupAnatomy.svg'; export const section = 'Hooks'; export const description = 'Provides the behavior and accessibility implementation for a checkbox group component.'; diff --git a/packages/react-aria/docs/checkbox/checkboxgroup-anatomy.svg b/packages/dev/s2-docs/pages/react-aria/CheckboxGroupAnatomy.svg similarity index 100% rename from packages/react-aria/docs/checkbox/checkboxgroup-anatomy.svg rename to packages/dev/s2-docs/pages/react-aria/CheckboxGroupAnatomy.svg diff --git a/packages/dev/s2-docs/pages/react-aria/ColorArea.mdx b/packages/dev/s2-docs/pages/react-aria/ColorArea.mdx index cd962371c4c..13befb871ad 100644 --- a/packages/dev/s2-docs/pages/react-aria/ColorArea.mdx +++ b/packages/dev/s2-docs/pages/react-aria/ColorArea.mdx @@ -5,7 +5,7 @@ import docs from 'docs:react-aria-components'; import {ColorArea as VanillaColorArea} from 'vanilla-starter/ColorArea'; import {ColorArea as TailwindColorArea} from 'tailwind-starter/ColorArea'; import '../../tailwind/tailwind.css'; -import Anatomy from '/packages/react-aria/docs/color/ColorAreaAnatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/ColorAreaAnatomy.svg'; export const tags = []; export const relatedPages = [{'title': 'useColorArea', 'url': './ColorArea/useColorArea'}]; diff --git a/packages/dev/s2-docs/pages/react-aria/ColorArea/useColorArea.mdx b/packages/dev/s2-docs/pages/react-aria/ColorArea/useColorArea.mdx index 96947100ad0..7e717fd79a1 100644 --- a/packages/dev/s2-docs/pages/react-aria/ColorArea/useColorArea.mdx +++ b/packages/dev/s2-docs/pages/react-aria/ColorArea/useColorArea.mdx @@ -13,7 +13,7 @@ import {FunctionAPIGroup} from '../../../src/FunctionAPI'; import {InterfaceType} from '../../../src/types'; import docs from 'docs:@react-aria/color'; import statelyDocs from 'docs:@react-stately/color'; -import Anatomy from '/packages/react-aria/docs/color/ColorAreaAnatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/ColorAreaAnatomy.svg'; export const section = 'Hooks'; export const description = 'Provides the behavior and accessibility implementation for a color area.'; diff --git a/packages/react-aria/docs/color/ColorAreaAnatomy.svg b/packages/dev/s2-docs/pages/react-aria/ColorAreaAnatomy.svg similarity index 100% rename from packages/react-aria/docs/color/ColorAreaAnatomy.svg rename to packages/dev/s2-docs/pages/react-aria/ColorAreaAnatomy.svg diff --git a/packages/dev/s2-docs/pages/react-aria/ColorField.mdx b/packages/dev/s2-docs/pages/react-aria/ColorField.mdx index bff24a940a6..2ff529d6441 100644 --- a/packages/dev/s2-docs/pages/react-aria/ColorField.mdx +++ b/packages/dev/s2-docs/pages/react-aria/ColorField.mdx @@ -6,7 +6,7 @@ import {ColorField as TailwindColorField} from 'tailwind-starter/ColorField'; import '../../tailwind/tailwind.css'; import vanillaDocs from 'docs:vanilla-starter/ColorField'; import docs from 'docs:react-aria-components'; -import Anatomy from '/packages/react-aria/docs/color/ColorFieldAnatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/ColorFieldAnatomy.svg'; export const tags = ['input']; export const relatedPages = [{'title': 'useColorField', 'url': './ColorField/useColorField'}]; diff --git a/packages/dev/s2-docs/pages/react-aria/ColorField/useColorField.mdx b/packages/dev/s2-docs/pages/react-aria/ColorField/useColorField.mdx index e185477c966..9792bbb804d 100644 --- a/packages/dev/s2-docs/pages/react-aria/ColorField/useColorField.mdx +++ b/packages/dev/s2-docs/pages/react-aria/ColorField/useColorField.mdx @@ -13,7 +13,7 @@ import {FunctionAPIGroup} from '../../../src/FunctionAPI'; import {InterfaceType} from '../../../src/types'; import docs from 'docs:@react-aria/color'; import statelyDocs from 'docs:@react-stately/color'; -import Anatomy from '/packages/react-aria/docs/color/ColorFieldAnatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/ColorFieldAnatomy.svg'; export const section = 'Hooks'; export const description = 'Provides the behavior and accessibility implementation for a color field.'; diff --git a/packages/react-aria/docs/color/ColorFieldAnatomy.svg b/packages/dev/s2-docs/pages/react-aria/ColorFieldAnatomy.svg similarity index 100% rename from packages/react-aria/docs/color/ColorFieldAnatomy.svg rename to packages/dev/s2-docs/pages/react-aria/ColorFieldAnatomy.svg diff --git a/packages/dev/s2-docs/pages/react-aria/ColorSlider.mdx b/packages/dev/s2-docs/pages/react-aria/ColorSlider.mdx index d32f25f9ea1..f90b1c3b1ed 100644 --- a/packages/dev/s2-docs/pages/react-aria/ColorSlider.mdx +++ b/packages/dev/s2-docs/pages/react-aria/ColorSlider.mdx @@ -6,7 +6,7 @@ import vanillaDocs from 'docs:vanilla-starter/ColorSlider'; import {ColorSlider as VanillaColorSlider} from 'vanilla-starter/ColorSlider'; import {ColorSlider as TailwindColorSlider} from 'tailwind-starter/ColorSlider'; import '../../tailwind/tailwind.css'; -import Anatomy from '/packages/react-aria/docs/color/ColorSliderAnatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/ColorSliderAnatomy.svg'; export const tags = ['input']; export const relatedPages = [{'title': 'useColorSlider', 'url': './ColorSlider/useColorSlider'}]; diff --git a/packages/dev/s2-docs/pages/react-aria/ColorSlider/useColorSlider.mdx b/packages/dev/s2-docs/pages/react-aria/ColorSlider/useColorSlider.mdx index 035e9b378da..9fec54148d3 100644 --- a/packages/dev/s2-docs/pages/react-aria/ColorSlider/useColorSlider.mdx +++ b/packages/dev/s2-docs/pages/react-aria/ColorSlider/useColorSlider.mdx @@ -13,7 +13,7 @@ import {FunctionAPIGroup} from '../../../src/FunctionAPI'; import {InterfaceType} from '../../../src/types'; import docs from 'docs:@react-aria/color'; import statelyDocs from 'docs:@react-stately/color'; -import Anatomy from '/packages/react-aria/docs/color/ColorSliderAnatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/ColorSliderAnatomy.svg'; export const section = 'Hooks'; export const description = 'Provides the behavior and accessibility implementation for a color slider.'; diff --git a/packages/react-aria/docs/color/ColorSliderAnatomy.svg b/packages/dev/s2-docs/pages/react-aria/ColorSliderAnatomy.svg similarity index 100% rename from packages/react-aria/docs/color/ColorSliderAnatomy.svg rename to packages/dev/s2-docs/pages/react-aria/ColorSliderAnatomy.svg diff --git a/packages/dev/s2-docs/pages/react-aria/ColorWheel.mdx b/packages/dev/s2-docs/pages/react-aria/ColorWheel.mdx index f29c9da1eae..9a88d7fb4ad 100644 --- a/packages/dev/s2-docs/pages/react-aria/ColorWheel.mdx +++ b/packages/dev/s2-docs/pages/react-aria/ColorWheel.mdx @@ -5,7 +5,7 @@ import docs from 'docs:react-aria-components'; import {ColorWheel as VanillaColorWheel} from 'vanilla-starter/ColorWheel'; import {ColorWheel as TailwindColorWheel} from 'tailwind-starter/ColorWheel'; import '../../tailwind/tailwind.css'; -import Anatomy from '/packages/react-aria/docs/color/ColorWheelAnatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/ColorWheelAnatomy.svg'; export const tags = ['input']; export const relatedPages = [{'title': 'useColorWheel', 'url': './ColorWheel/useColorWheel'}]; diff --git a/packages/dev/s2-docs/pages/react-aria/ColorWheel/useColorWheel.mdx b/packages/dev/s2-docs/pages/react-aria/ColorWheel/useColorWheel.mdx index 9e01653a817..4966b2fdcbe 100644 --- a/packages/dev/s2-docs/pages/react-aria/ColorWheel/useColorWheel.mdx +++ b/packages/dev/s2-docs/pages/react-aria/ColorWheel/useColorWheel.mdx @@ -13,7 +13,7 @@ import {FunctionAPIGroup} from '../../../src/FunctionAPI'; import {InterfaceType} from '../../../src/types'; import docs from 'docs:@react-aria/color'; import statelyDocs from 'docs:@react-stately/color'; -import Anatomy from '/packages/react-aria/docs/color/ColorWheelAnatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/ColorWheelAnatomy.svg'; export const section = 'Hooks'; export const description = 'Provides the behavior and accessibility implementation for a color wheel.'; diff --git a/packages/react-aria/docs/color/ColorWheelAnatomy.svg b/packages/dev/s2-docs/pages/react-aria/ColorWheelAnatomy.svg similarity index 100% rename from packages/react-aria/docs/color/ColorWheelAnatomy.svg rename to packages/dev/s2-docs/pages/react-aria/ColorWheelAnatomy.svg diff --git a/packages/dev/s2-docs/pages/react-aria/DateField.mdx b/packages/dev/s2-docs/pages/react-aria/DateField.mdx index de59f5a1513..b2e944de20c 100644 --- a/packages/dev/s2-docs/pages/react-aria/DateField.mdx +++ b/packages/dev/s2-docs/pages/react-aria/DateField.mdx @@ -6,7 +6,7 @@ import vanillaDocs from 'docs:vanilla-starter/DateField'; import {DateField as VanillaDateField} from 'vanilla-starter/DateField'; import {DateField as TailwindDateField} from 'tailwind-starter/DateField'; import '../../tailwind/tailwind.css'; -import Anatomy from '/packages/react-aria/docs/datepicker/datefield-anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/DateFieldAnatomy.svg'; export const tags = ['calendar', 'input']; export const relatedPages = [{'title': 'useDateField', 'url': './DateField/useDateField'}]; diff --git a/packages/dev/s2-docs/pages/react-aria/DateField/useDateField.mdx b/packages/dev/s2-docs/pages/react-aria/DateField/useDateField.mdx index 7d84e5c76f6..7d770a0f34e 100644 --- a/packages/dev/s2-docs/pages/react-aria/DateField/useDateField.mdx +++ b/packages/dev/s2-docs/pages/react-aria/DateField/useDateField.mdx @@ -13,7 +13,7 @@ import {FunctionAPIGroup} from '../../../src/FunctionAPI'; import {InterfaceType} from '../../../src/types'; import docs from 'docs:@react-aria/datepicker'; import statelyDocs from 'docs:@react-stately/datepicker'; -import Anatomy from '/packages/react-aria/docs/datepicker/datefield-anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/DateFieldAnatomy.svg'; export const section = 'Hooks'; export const description = 'Provides the behavior and accessibility implementation for a date field component.'; diff --git a/packages/react-aria/docs/datepicker/datefield-anatomy.svg b/packages/dev/s2-docs/pages/react-aria/DateFieldAnatomy.svg similarity index 100% rename from packages/react-aria/docs/datepicker/datefield-anatomy.svg rename to packages/dev/s2-docs/pages/react-aria/DateFieldAnatomy.svg diff --git a/packages/dev/s2-docs/pages/react-aria/DatePicker.mdx b/packages/dev/s2-docs/pages/react-aria/DatePicker.mdx index a097c9408f8..f0dd8eecb92 100644 --- a/packages/dev/s2-docs/pages/react-aria/DatePicker.mdx +++ b/packages/dev/s2-docs/pages/react-aria/DatePicker.mdx @@ -6,7 +6,7 @@ import vanillaDocs from 'docs:vanilla-starter/DatePicker'; import {DatePicker as VanillaDatePicker} from 'vanilla-starter/DatePicker'; import {DatePicker as TailwindDatePicker} from 'tailwind-starter/DatePicker'; import '../../tailwind/tailwind.css'; -import Anatomy from '/packages/react-aria/docs/datepicker/datepicker-anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/DatePickerAnatomy.svg'; export const tags = ['calendar', 'input']; export const relatedPages = [{'title': 'useDatePicker', 'url': './DatePicker/useDatePicker'}]; diff --git a/packages/dev/s2-docs/pages/react-aria/DatePicker/useDatePicker.mdx b/packages/dev/s2-docs/pages/react-aria/DatePicker/useDatePicker.mdx index b7859eb5e38..ce9b0be36f4 100644 --- a/packages/dev/s2-docs/pages/react-aria/DatePicker/useDatePicker.mdx +++ b/packages/dev/s2-docs/pages/react-aria/DatePicker/useDatePicker.mdx @@ -13,7 +13,7 @@ import {FunctionAPIGroup} from '../../../src/FunctionAPI'; import {InterfaceType} from '../../../src/types'; import docs from 'docs:@react-aria/datepicker'; import statelyDocs from 'docs:@react-stately/datepicker'; -import Anatomy from '/packages/react-aria/docs/datepicker/datepicker-anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/DatePickerAnatomy.svg'; export const section = 'Hooks'; export const description = 'Provides the behavior and accessibility implementation for a date picker component.'; diff --git a/packages/react-aria/docs/datepicker/datepicker-anatomy.svg b/packages/dev/s2-docs/pages/react-aria/DatePickerAnatomy.svg similarity index 100% rename from packages/react-aria/docs/datepicker/datepicker-anatomy.svg rename to packages/dev/s2-docs/pages/react-aria/DatePickerAnatomy.svg diff --git a/packages/dev/s2-docs/pages/react-aria/DateRangePicker.mdx b/packages/dev/s2-docs/pages/react-aria/DateRangePicker.mdx index c47b5b8912d..176825ba868 100644 --- a/packages/dev/s2-docs/pages/react-aria/DateRangePicker.mdx +++ b/packages/dev/s2-docs/pages/react-aria/DateRangePicker.mdx @@ -6,7 +6,7 @@ import vanillaDocs from 'docs:vanilla-starter/DateRangePicker'; import {DateRangePicker as VanillaDateRangePicker} from 'vanilla-starter/DateRangePicker'; import {DateRangePicker as TailwindDateRangePicker} from 'tailwind-starter/DateRangePicker'; import '../../tailwind/tailwind.css'; -import Anatomy from '/packages/react-aria/docs/datepicker/daterangepicker-anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/DateRangePickerAnatomy2.svg'; export const tags = ['calendar', 'input']; export const relatedPages = [{'title': 'useDateRangePicker', 'url': './DateRangePicker/useDateRangePicker'}]; diff --git a/packages/dev/s2-docs/pages/react-aria/DateRangePicker/useDateRangePicker.mdx b/packages/dev/s2-docs/pages/react-aria/DateRangePicker/useDateRangePicker.mdx index 9ab33e7f417..905bcf244fc 100644 --- a/packages/dev/s2-docs/pages/react-aria/DateRangePicker/useDateRangePicker.mdx +++ b/packages/dev/s2-docs/pages/react-aria/DateRangePicker/useDateRangePicker.mdx @@ -13,7 +13,7 @@ import {FunctionAPIGroup} from '../../../src/FunctionAPI'; import {InterfaceType} from '../../../src/types'; import docs from 'docs:@react-aria/datepicker'; import statelyDocs from 'docs:@react-stately/datepicker'; -import Anatomy from '/packages/react-aria/docs/datepicker/daterangepicker-anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/DateRangePickerAnatomy2.svg'; export const section = 'Hooks'; export const description = 'Provides the behavior and accessibility implementation for a date range picker component.'; diff --git a/packages/react-aria/docs/datepicker/daterangepicker-anatomy.svg b/packages/dev/s2-docs/pages/react-aria/DateRangePickerAnatomy2.svg similarity index 100% rename from packages/react-aria/docs/datepicker/daterangepicker-anatomy.svg rename to packages/dev/s2-docs/pages/react-aria/DateRangePickerAnatomy2.svg diff --git a/packages/react-aria/docs/dnd/Anatomy.svg b/packages/dev/s2-docs/pages/react-aria/DndAnatomy.svg similarity index 100% rename from packages/react-aria/docs/dnd/Anatomy.svg rename to packages/dev/s2-docs/pages/react-aria/DndAnatomy.svg diff --git a/packages/react-aria/docs/dnd/BetweenDropPosition.svg b/packages/dev/s2-docs/pages/react-aria/DndBetweenDropPosition.svg similarity index 100% rename from packages/react-aria/docs/dnd/BetweenDropPosition.svg rename to packages/dev/s2-docs/pages/react-aria/DndBetweenDropPosition.svg diff --git a/packages/react-aria/docs/dnd/DragAffordance.svg b/packages/dev/s2-docs/pages/react-aria/DndDragAffordance.svg similarity index 100% rename from packages/react-aria/docs/dnd/DragAffordance.svg rename to packages/dev/s2-docs/pages/react-aria/DndDragAffordance.svg diff --git a/packages/react-aria/docs/dnd/DragPreview.svg b/packages/dev/s2-docs/pages/react-aria/DndDragPreview.svg similarity index 100% rename from packages/react-aria/docs/dnd/DragPreview.svg rename to packages/dev/s2-docs/pages/react-aria/DndDragPreview.svg diff --git a/packages/react-aria/docs/dnd/DropOperation.svg b/packages/dev/s2-docs/pages/react-aria/DndDropOperation.svg similarity index 100% rename from packages/react-aria/docs/dnd/DropOperation.svg rename to packages/dev/s2-docs/pages/react-aria/DndDropOperation.svg diff --git a/packages/react-aria/docs/dnd/OnDropPosition.svg b/packages/dev/s2-docs/pages/react-aria/DndOnDropPosition.svg similarity index 100% rename from packages/react-aria/docs/dnd/OnDropPosition.svg rename to packages/dev/s2-docs/pages/react-aria/DndOnDropPosition.svg diff --git a/packages/react-aria/docs/dnd/RootDropPosition.svg b/packages/dev/s2-docs/pages/react-aria/DndRootDropPosition.svg similarity index 100% rename from packages/react-aria/docs/dnd/RootDropPosition.svg rename to packages/dev/s2-docs/pages/react-aria/DndRootDropPosition.svg diff --git a/packages/dev/s2-docs/pages/react-aria/Meter.mdx b/packages/dev/s2-docs/pages/react-aria/Meter.mdx index b8fdcab3e7c..483c0916f31 100644 --- a/packages/dev/s2-docs/pages/react-aria/Meter.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Meter.mdx @@ -6,7 +6,7 @@ import {Meter as VanillaMeter} from 'vanilla-starter/Meter'; import {ProgressCircle as VanillaProgressCircle} from 'vanilla-starter/ProgressCircle'; import {Meter as TailwindMeter} from 'tailwind-starter/Meter'; import '../../tailwind/tailwind.css'; -import Anatomy from '/packages/react-aria/docs/meter/anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/MeterAnatomy2.svg'; export const tags = ['gauge', 'progress', 'level']; export const relatedPages = [{'title': 'useMeter', 'url': './Meter/useMeter'}]; diff --git a/packages/dev/s2-docs/pages/react-aria/Meter/useMeter.mdx b/packages/dev/s2-docs/pages/react-aria/Meter/useMeter.mdx index b5fee0bcba8..2dd25a35117 100644 --- a/packages/dev/s2-docs/pages/react-aria/Meter/useMeter.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Meter/useMeter.mdx @@ -12,7 +12,7 @@ export default Layout; import {FunctionAPI} from '../../../src/FunctionAPI'; import {InterfaceType} from '../../../src/types'; import docs from 'docs:@react-aria/meter'; -import Anatomy from '/packages/react-aria/docs/meter/anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/MeterAnatomy2.svg'; export const section = 'Hooks'; export const description = 'Provides the accessibility implementation for a meter component.'; diff --git a/packages/react-aria/docs/meter/anatomy.svg b/packages/dev/s2-docs/pages/react-aria/MeterAnatomy2.svg similarity index 100% rename from packages/react-aria/docs/meter/anatomy.svg rename to packages/dev/s2-docs/pages/react-aria/MeterAnatomy2.svg diff --git a/packages/dev/s2-docs/pages/react-aria/Modal.mdx b/packages/dev/s2-docs/pages/react-aria/Modal.mdx index 0d8d2460696..61bb957eb6f 100644 --- a/packages/dev/s2-docs/pages/react-aria/Modal.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Modal.mdx @@ -4,7 +4,7 @@ export default Layout; import docs from 'docs:react-aria-components'; import vanillaDocs from 'docs:vanilla-starter/Modal'; import '../../tailwind/tailwind.css'; -import Anatomy from '/packages/react-aria/docs/overlays/modal-anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/ModalAnatomy.svg'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2' export const tags = ['dialog', 'popup', 'overlay']; diff --git a/packages/dev/s2-docs/pages/react-aria/Modal/useModalOverlay.mdx b/packages/dev/s2-docs/pages/react-aria/Modal/useModalOverlay.mdx index 82c3ab44dc9..f5862ff0464 100644 --- a/packages/dev/s2-docs/pages/react-aria/Modal/useModalOverlay.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Modal/useModalOverlay.mdx @@ -14,7 +14,7 @@ import {InterfaceType} from '../../../src/types'; import docs from 'docs:@react-aria/overlays'; import dialogDocs from 'docs:@react-aria/dialog'; import statelyDocs from 'docs:@react-stately/overlays'; -import Anatomy from '/packages/react-aria/docs/overlays/modal-anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/ModalAnatomy.svg'; import 'hooks-starter/theme.css'; export const section = 'Hooks'; diff --git a/packages/react-aria/docs/overlays/modal-anatomy.svg b/packages/dev/s2-docs/pages/react-aria/ModalAnatomy.svg similarity index 100% rename from packages/react-aria/docs/overlays/modal-anatomy.svg rename to packages/dev/s2-docs/pages/react-aria/ModalAnatomy.svg diff --git a/packages/dev/s2-docs/pages/react-aria/NumberField.mdx b/packages/dev/s2-docs/pages/react-aria/NumberField.mdx index d69f7efd87c..a65e2600ebd 100644 --- a/packages/dev/s2-docs/pages/react-aria/NumberField.mdx +++ b/packages/dev/s2-docs/pages/react-aria/NumberField.mdx @@ -6,7 +6,7 @@ import vanillaDocs from 'docs:vanilla-starter/NumberField'; import {NumberField as VanillaNumberField} from 'vanilla-starter/NumberField'; import {NumberField as TailwindNumberField} from 'tailwind-starter/NumberField'; import '../../tailwind/tailwind.css'; -import Anatomy from '/packages/react-aria/docs/numberfield/anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/NumberFieldAnatomy.svg'; export const tags = ['input']; export const relatedPages = [{'title': 'useNumberField', 'url': './NumberField/useNumberField'}]; diff --git a/packages/dev/s2-docs/pages/react-aria/NumberField/useNumberField.mdx b/packages/dev/s2-docs/pages/react-aria/NumberField/useNumberField.mdx index 6491ad1f7bc..4f3a81fe9a2 100644 --- a/packages/dev/s2-docs/pages/react-aria/NumberField/useNumberField.mdx +++ b/packages/dev/s2-docs/pages/react-aria/NumberField/useNumberField.mdx @@ -13,7 +13,7 @@ import {FunctionAPIGroup} from '../../../src/FunctionAPI'; import {InterfaceType} from '../../../src/types'; import docs from 'docs:@react-aria/numberfield'; import statelyDocs from 'docs:@react-stately/numberfield'; -import Anatomy from '/packages/react-aria/docs/numberfield/anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/NumberFieldAnatomy.svg'; export const section = 'Hooks'; export const description = 'Provides the behavior and accessibility implementation for a number field component.'; diff --git a/packages/react-aria/docs/numberfield/anatomy.svg b/packages/dev/s2-docs/pages/react-aria/NumberFieldAnatomy.svg similarity index 100% rename from packages/react-aria/docs/numberfield/anatomy.svg rename to packages/dev/s2-docs/pages/react-aria/NumberFieldAnatomy.svg diff --git a/packages/dev/s2-docs/pages/react-aria/Popover.mdx b/packages/dev/s2-docs/pages/react-aria/Popover.mdx index 7c57f16766f..baa40cbd137 100644 --- a/packages/dev/s2-docs/pages/react-aria/Popover.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Popover.mdx @@ -4,7 +4,7 @@ export default Layout; import docs from 'docs:react-aria-components'; import vanillaDocs from 'docs:vanilla-starter/Popover'; import '../../tailwind/tailwind.css'; -import Anatomy from '/packages/react-aria/docs/overlays/popover-anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/PopoverAnatomy2.svg'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2' export const tags = ['popup', 'overlay']; diff --git a/packages/dev/s2-docs/pages/react-aria/Popover/usePopover.mdx b/packages/dev/s2-docs/pages/react-aria/Popover/usePopover.mdx index 4a030e1e0e0..4a74a5579ed 100644 --- a/packages/dev/s2-docs/pages/react-aria/Popover/usePopover.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Popover/usePopover.mdx @@ -13,7 +13,7 @@ import {FunctionAPIGroup} from '../../../src/FunctionAPI'; import {InterfaceType} from '../../../src/types'; import docs from 'docs:@react-aria/overlays'; import statelyDocs from 'docs:@react-stately/overlays'; -import Anatomy from '/packages/react-aria/docs/overlays/popover-anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/PopoverAnatomy2.svg'; export const section = 'Hooks'; export const description = 'Provides the behavior and accessibility implementation for a popover overlay.'; diff --git a/packages/react-aria/docs/overlays/popover-anatomy.svg b/packages/dev/s2-docs/pages/react-aria/PopoverAnatomy2.svg similarity index 100% rename from packages/react-aria/docs/overlays/popover-anatomy.svg rename to packages/dev/s2-docs/pages/react-aria/PopoverAnatomy2.svg diff --git a/packages/dev/s2-docs/pages/react-aria/PreviewTrigger.mdx b/packages/dev/s2-docs/pages/react-aria/PreviewTrigger.mdx index cd4dc973a34..fe39cdb3472 100644 --- a/packages/dev/s2-docs/pages/react-aria/PreviewTrigger.mdx +++ b/packages/dev/s2-docs/pages/react-aria/PreviewTrigger.mdx @@ -4,7 +4,7 @@ export default Layout; import docs from 'docs:react-aria-components'; import vanillaDocs from 'docs:vanilla-starter/Popover'; import '../../tailwind/tailwind.css'; -import Anatomy from '/packages/react-aria/docs/overlays/preview-trigger-anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/PreviewTriggerAnatomy.svg'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2' export const tags = ['popup', 'overlay', 'preview', 'link', 'hover', 'tooltip']; @@ -17,7 +17,7 @@ export const description = 'Displays a non-modal preview of content on hover, fo ```tsx render type="vanilla" docs={vanillaDocs.exports.Popover} links={vanillaDocs.links} props={['placement', 'offset', 'crossOffset', 'shouldFlip']} "use client"; - import {PreviewTrigger} from 'react-aria-components'; + import {PreviewTrigger} from 'react-aria-components/PreviewTrigger'; import {Popover} from 'vanilla-starter/Popover'; import {Link} from 'vanilla-starter/Link'; import {Button} from 'vanilla-starter/Button'; @@ -68,7 +68,7 @@ export const description = 'Displays a non-modal preview of content on hover, fo ```tsx render type="tailwind" docs={vanillaDocs.exports.Popover} links={vanillaDocs.links} props={['placement', 'offset', 'crossOffset', 'shouldFlip']} "use client"; - import {PreviewTrigger} from 'react-aria-components'; + import {PreviewTrigger} from 'react-aria-components/PreviewTrigger'; import {Popover} from 'tailwind-starter/Popover'; import {Link} from 'tailwind-starter/Link'; import {Button} from 'tailwind-starter/Button'; @@ -127,7 +127,7 @@ When a preview is open, pressing Tab moves focus into the p ```tsx render type="vanilla" docs={docs.exports.PreviewTrigger} links={docs.links} props={['delay', 'closeDelay', 'isDisabled']} "use client"; -import {PreviewTrigger} from 'react-aria-components'; +import {PreviewTrigger} from 'react-aria-components/PreviewTrigger'; import {Popover} from 'vanilla-starter/Popover'; import {Link} from 'vanilla-starter/Link'; import {Button} from 'vanilla-starter/Button'; @@ -181,7 +181,7 @@ function Example(props) { ```tsx render "use client" -import {PreviewTrigger} from 'react-aria-components'; +import {PreviewTrigger} from 'react-aria-components/PreviewTrigger'; import {Focusable} from 'react-aria-components/Tooltip'; import {Popover} from 'vanilla-starter/Popover'; diff --git a/packages/react-aria/docs/overlays/preview-trigger-anatomy.svg b/packages/dev/s2-docs/pages/react-aria/PreviewTriggerAnatomy.svg similarity index 100% rename from packages/react-aria/docs/overlays/preview-trigger-anatomy.svg rename to packages/dev/s2-docs/pages/react-aria/PreviewTriggerAnatomy.svg diff --git a/packages/dev/s2-docs/pages/react-aria/ProgressBar.mdx b/packages/dev/s2-docs/pages/react-aria/ProgressBar.mdx index daadc6f354c..97bd9904cc4 100644 --- a/packages/dev/s2-docs/pages/react-aria/ProgressBar.mdx +++ b/packages/dev/s2-docs/pages/react-aria/ProgressBar.mdx @@ -8,7 +8,7 @@ import {ProgressBar as VanillaProgressBar} from 'vanilla-starter/ProgressBar'; import {ProgressCircle as VanillaProgressCircle} from 'vanilla-starter/ProgressCircle'; import {ProgressBar as TailwindProgressBar} from 'tailwind-starter/ProgressBar'; import '../../tailwind/tailwind.css'; -import Anatomy from '/packages/react-aria/docs/progress/anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/ProgressBarAnatomy.svg'; export const tags = ['loading', 'progress']; export const relatedPages = [{'title': 'useProgressBar', 'url': './ProgressBar/useProgressBar'}]; diff --git a/packages/dev/s2-docs/pages/react-aria/ProgressBar/useProgressBar.mdx b/packages/dev/s2-docs/pages/react-aria/ProgressBar/useProgressBar.mdx index 08d118cfd84..04daf74f90d 100644 --- a/packages/dev/s2-docs/pages/react-aria/ProgressBar/useProgressBar.mdx +++ b/packages/dev/s2-docs/pages/react-aria/ProgressBar/useProgressBar.mdx @@ -12,7 +12,7 @@ export default Layout; import {FunctionAPI} from '../../../src/FunctionAPI'; import {InterfaceType} from '../../../src/types'; import docs from 'docs:@react-aria/progress'; -import Anatomy from '/packages/react-aria/docs/progress/anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/ProgressBarAnatomy.svg'; export const section = 'Hooks'; export const description = 'Provides the accessibility implementation for a progress bar component.'; diff --git a/packages/react-aria/docs/progress/anatomy.svg b/packages/dev/s2-docs/pages/react-aria/ProgressBarAnatomy.svg similarity index 100% rename from packages/react-aria/docs/progress/anatomy.svg rename to packages/dev/s2-docs/pages/react-aria/ProgressBarAnatomy.svg diff --git a/packages/dev/s2-docs/pages/react-aria/RadioGroup.mdx b/packages/dev/s2-docs/pages/react-aria/RadioGroup.mdx index 1b7613fa0aa..ec2a617cbb4 100644 --- a/packages/dev/s2-docs/pages/react-aria/RadioGroup.mdx +++ b/packages/dev/s2-docs/pages/react-aria/RadioGroup.mdx @@ -4,7 +4,7 @@ export default Layout; import docs from 'docs:react-aria-components'; import vanillaDocs from 'docs:vanilla-starter/RadioGroup'; import '../../tailwind/tailwind.css'; -import Anatomy from '/packages/react-aria/docs/radio/anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/RadioGroupAnatomy.svg'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2' export const tags = ['input']; diff --git a/packages/dev/s2-docs/pages/react-aria/RadioGroup/useRadioGroup.mdx b/packages/dev/s2-docs/pages/react-aria/RadioGroup/useRadioGroup.mdx index e09d27232f7..3040104051b 100644 --- a/packages/dev/s2-docs/pages/react-aria/RadioGroup/useRadioGroup.mdx +++ b/packages/dev/s2-docs/pages/react-aria/RadioGroup/useRadioGroup.mdx @@ -13,7 +13,7 @@ import {FunctionAPIGroup} from '../../../src/FunctionAPI'; import {InterfaceType} from '../../../src/types'; import docs from 'docs:@react-aria/radio'; import statelyDocs from 'docs:@react-stately/radio'; -import Anatomy from '/packages/react-aria/docs/radio/anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/RadioGroupAnatomy.svg'; export const section = 'Hooks'; export const description = 'Provides the behavior and accessibility implementation for a radio group component.'; diff --git a/packages/react-aria/docs/radio/anatomy.svg b/packages/dev/s2-docs/pages/react-aria/RadioGroupAnatomy.svg similarity index 100% rename from packages/react-aria/docs/radio/anatomy.svg rename to packages/dev/s2-docs/pages/react-aria/RadioGroupAnatomy.svg diff --git a/packages/dev/s2-docs/pages/react-aria/RangeCalendar.mdx b/packages/dev/s2-docs/pages/react-aria/RangeCalendar.mdx index 5cad7f60098..07bcca98b56 100644 --- a/packages/dev/s2-docs/pages/react-aria/RangeCalendar.mdx +++ b/packages/dev/s2-docs/pages/react-aria/RangeCalendar.mdx @@ -6,7 +6,7 @@ import vanillaDocs from 'docs:vanilla-starter/RangeCalendar'; import {RangeCalendar as VanillaRangeCalendar} from 'vanilla-starter/RangeCalendar'; import {RangeCalendar as TailwindRangeCalendar} from 'tailwind-starter/RangeCalendar'; import '../../tailwind/tailwind.css'; -import Anatomy from '/packages/react-aria/docs/calendar/rangecalendar-anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/RangeCalendarAnatomy2.svg'; export const tags = ['calendar']; export const relatedPages = [{'title': 'useRangeCalendar', 'url': './RangeCalendar/useRangeCalendar'}]; diff --git a/packages/dev/s2-docs/pages/react-aria/RangeCalendar/useRangeCalendar.mdx b/packages/dev/s2-docs/pages/react-aria/RangeCalendar/useRangeCalendar.mdx index d1fd3ad0d8f..eafa94652ad 100644 --- a/packages/dev/s2-docs/pages/react-aria/RangeCalendar/useRangeCalendar.mdx +++ b/packages/dev/s2-docs/pages/react-aria/RangeCalendar/useRangeCalendar.mdx @@ -13,7 +13,7 @@ import {FunctionAPIGroup} from '../../../src/FunctionAPI'; import {InterfaceType} from '../../../src/types'; import docs from 'docs:@react-aria/calendar'; import statelyDocs from 'docs:@react-stately/calendar'; -import Anatomy from '/packages/react-aria/docs/calendar/rangecalendar-anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/RangeCalendarAnatomy2.svg'; export const section = 'Hooks'; export const description = 'Provides the behavior and accessibility implementation for a range calendar component.'; diff --git a/packages/react-aria/docs/calendar/rangecalendar-anatomy.svg b/packages/dev/s2-docs/pages/react-aria/RangeCalendarAnatomy2.svg similarity index 100% rename from packages/react-aria/docs/calendar/rangecalendar-anatomy.svg rename to packages/dev/s2-docs/pages/react-aria/RangeCalendarAnatomy2.svg diff --git a/packages/dev/s2-docs/pages/react-aria/SearchField.mdx b/packages/dev/s2-docs/pages/react-aria/SearchField.mdx index 0e171695acf..cdcbe510334 100644 --- a/packages/dev/s2-docs/pages/react-aria/SearchField.mdx +++ b/packages/dev/s2-docs/pages/react-aria/SearchField.mdx @@ -6,7 +6,7 @@ import vanillaDocs from 'docs:vanilla-starter/SearchField'; import {SearchField as VanillaSearchField} from 'vanilla-starter/SearchField'; import {SearchField as TailwindSearchField} from 'tailwind-starter/SearchField'; import '../../tailwind/tailwind.css'; -import Anatomy from '/packages/react-aria/docs/searchfield/anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/SearchFieldAnatomy.svg'; export const tags = ['input']; export const relatedPages = [{'title': 'useSearchField', 'url': './SearchField/useSearchField'}]; diff --git a/packages/dev/s2-docs/pages/react-aria/SearchField/useSearchField.mdx b/packages/dev/s2-docs/pages/react-aria/SearchField/useSearchField.mdx index 13c159de867..6b300745e50 100644 --- a/packages/dev/s2-docs/pages/react-aria/SearchField/useSearchField.mdx +++ b/packages/dev/s2-docs/pages/react-aria/SearchField/useSearchField.mdx @@ -13,7 +13,7 @@ import {FunctionAPIGroup} from '../../../src/FunctionAPI'; import {InterfaceType} from '../../../src/types'; import docs from 'docs:@react-aria/searchfield'; import statelyDocs from 'docs:@react-stately/searchfield'; -import Anatomy from '/packages/react-aria/docs/searchfield/anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/SearchFieldAnatomy.svg'; export const section = 'Hooks'; export const description = 'Provides the behavior and accessibility implementation for a search field.'; diff --git a/packages/react-aria/docs/searchfield/anatomy.svg b/packages/dev/s2-docs/pages/react-aria/SearchFieldAnatomy.svg similarity index 100% rename from packages/react-aria/docs/searchfield/anatomy.svg rename to packages/dev/s2-docs/pages/react-aria/SearchFieldAnatomy.svg diff --git a/packages/dev/s2-docs/pages/react-aria/Slider.mdx b/packages/dev/s2-docs/pages/react-aria/Slider.mdx index 58dc0f1028c..893ff592436 100644 --- a/packages/dev/s2-docs/pages/react-aria/Slider.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Slider.mdx @@ -7,7 +7,7 @@ import {Slider as VanillaSlider} from 'vanilla-starter/Slider'; import {Slider as TailwindSlider} from 'tailwind-starter/Slider'; import tailwindDocs from 'docs:tailwind-starter/Slider'; import '../../tailwind/tailwind.css'; -import Anatomy from '/packages/react-aria/docs/slider/anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/SliderAnatomy.svg'; export const tags = ['range input', 'track', 'scrubber']; export const relatedPages = [{'title': 'useSlider', 'url': './Slider/useSlider'}]; diff --git a/packages/dev/s2-docs/pages/react-aria/Slider/useSlider.mdx b/packages/dev/s2-docs/pages/react-aria/Slider/useSlider.mdx index 81226828e88..1428b331093 100644 --- a/packages/dev/s2-docs/pages/react-aria/Slider/useSlider.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Slider/useSlider.mdx @@ -13,7 +13,7 @@ import {FunctionAPIGroup} from '../../../src/FunctionAPI'; import {InterfaceType} from '../../../src/types'; import docs from 'docs:@react-aria/slider'; import statelyDocs from 'docs:@react-stately/slider'; -import Anatomy from '/packages/react-aria/docs/slider/anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/SliderAnatomy.svg'; export const section = 'Hooks'; export const description = 'Provides the behavior and accessibility implementation for a slider component.'; diff --git a/packages/react-aria/docs/slider/anatomy.svg b/packages/dev/s2-docs/pages/react-aria/SliderAnatomy.svg similarity index 100% rename from packages/react-aria/docs/slider/anatomy.svg rename to packages/dev/s2-docs/pages/react-aria/SliderAnatomy.svg diff --git a/packages/dev/s2-docs/pages/react-aria/Switch.mdx b/packages/dev/s2-docs/pages/react-aria/Switch.mdx index 5582ec487f3..f4d28c8a464 100644 --- a/packages/dev/s2-docs/pages/react-aria/Switch.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Switch.mdx @@ -6,7 +6,7 @@ import vanillaDocs from 'docs:vanilla-starter/Switch'; import {Switch as VanillaSwitch} from 'vanilla-starter/Switch'; import {Switch as TailwindSwitch} from 'tailwind-starter/Switch'; import '../../tailwind/tailwind.css'; -import Anatomy from '/packages/react-aria/docs/switch/anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/SwitchAnatomy.svg'; export const tags = ['toggle', 'input']; export const relatedPages = [{'title': 'useSwitch', 'url': './Switch/useSwitch'}]; diff --git a/packages/dev/s2-docs/pages/react-aria/Switch/useSwitch.mdx b/packages/dev/s2-docs/pages/react-aria/Switch/useSwitch.mdx index 1dec4eccced..359aff39ece 100644 --- a/packages/dev/s2-docs/pages/react-aria/Switch/useSwitch.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Switch/useSwitch.mdx @@ -13,7 +13,7 @@ import {FunctionAPIGroup} from '../../../src/FunctionAPI'; import {InterfaceType} from '../../../src/types'; import docs from 'docs:@react-aria/switch'; import statelyDocs from 'docs:@react-stately/toggle'; -import Anatomy from '/packages/react-aria/docs/switch/anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/SwitchAnatomy.svg'; export const section = 'Hooks'; export const description = 'Provides the behavior and accessibility implementation for a switch.'; diff --git a/packages/react-aria/docs/switch/anatomy.svg b/packages/dev/s2-docs/pages/react-aria/SwitchAnatomy.svg similarity index 100% rename from packages/react-aria/docs/switch/anatomy.svg rename to packages/dev/s2-docs/pages/react-aria/SwitchAnatomy.svg diff --git a/packages/dev/s2-docs/pages/react-aria/Tabs.mdx b/packages/dev/s2-docs/pages/react-aria/Tabs.mdx index 7f172e634c1..ad1e3ea5b5a 100644 --- a/packages/dev/s2-docs/pages/react-aria/Tabs.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Tabs.mdx @@ -5,7 +5,7 @@ import docs from 'docs:react-aria-components'; import {Tabs as VanillaTabs, TabsItem} from 'vanilla-starter/Tabs'; import vanillaDocs from 'docs:vanilla-starter/Tabs'; import '../../tailwind/tailwind.css'; -import Anatomy from '/packages/react-aria/docs/tabs/anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/TabsAnatomy.svg'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2' export const tags = ['navigation']; diff --git a/packages/dev/s2-docs/pages/react-aria/Tabs/useTabList.mdx b/packages/dev/s2-docs/pages/react-aria/Tabs/useTabList.mdx index 49d4c9b39c5..997e2005448 100644 --- a/packages/dev/s2-docs/pages/react-aria/Tabs/useTabList.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Tabs/useTabList.mdx @@ -13,7 +13,7 @@ import {FunctionAPIGroup} from '../../../src/FunctionAPI'; import {InterfaceType} from '../../../src/types'; import docs from 'docs:@react-aria/tabs'; import statelyDocs from 'docs:@react-stately/tabs'; -import Anatomy from '/packages/react-aria/docs/tabs/anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/TabsAnatomy.svg'; export const section = 'Hooks'; export const description = 'Provides the behavior and accessibility implementation for a tabs component.'; diff --git a/packages/react-aria/docs/tabs/anatomy.svg b/packages/dev/s2-docs/pages/react-aria/TabsAnatomy.svg similarity index 100% rename from packages/react-aria/docs/tabs/anatomy.svg rename to packages/dev/s2-docs/pages/react-aria/TabsAnatomy.svg diff --git a/packages/dev/s2-docs/pages/react-aria/TagGroup.mdx b/packages/dev/s2-docs/pages/react-aria/TagGroup.mdx index cfbb8abbb12..6a670ef953e 100644 --- a/packages/dev/s2-docs/pages/react-aria/TagGroup.mdx +++ b/packages/dev/s2-docs/pages/react-aria/TagGroup.mdx @@ -5,7 +5,7 @@ import docs from 'docs:react-aria-components'; import {TagGroup as VanillaTagGroup, Tag} from 'vanilla-starter/TagGroup'; import vanillaDocs from 'docs:vanilla-starter/TagGroup'; import '../../tailwind/tailwind.css'; -import Anatomy from '/packages/react-aria/docs/tag/anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/TagGroupAnatomy2.svg'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; export const tags = ['chips', 'pills']; diff --git a/packages/dev/s2-docs/pages/react-aria/TagGroup/useTagGroup.mdx b/packages/dev/s2-docs/pages/react-aria/TagGroup/useTagGroup.mdx index f5e240f6569..4aec397266a 100644 --- a/packages/dev/s2-docs/pages/react-aria/TagGroup/useTagGroup.mdx +++ b/packages/dev/s2-docs/pages/react-aria/TagGroup/useTagGroup.mdx @@ -13,7 +13,7 @@ import {FunctionAPIGroup} from '../../../src/FunctionAPI'; import {InterfaceType} from '../../../src/types'; import docs from 'docs:@react-aria/tag'; import statelyDocs from 'docs:@react-stately/list'; -import Anatomy from '/packages/react-aria/docs/tag/anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/TagGroupAnatomy2.svg'; export const section = 'Hooks'; export const description = 'Provides the behavior and accessibility implementation for a tag group component.'; diff --git a/packages/react-aria/docs/tag/anatomy.svg b/packages/dev/s2-docs/pages/react-aria/TagGroupAnatomy2.svg similarity index 100% rename from packages/react-aria/docs/tag/anatomy.svg rename to packages/dev/s2-docs/pages/react-aria/TagGroupAnatomy2.svg diff --git a/packages/dev/s2-docs/pages/react-aria/TextField.mdx b/packages/dev/s2-docs/pages/react-aria/TextField.mdx index 3cf9c3f4627..a76f2d374c8 100644 --- a/packages/dev/s2-docs/pages/react-aria/TextField.mdx +++ b/packages/dev/s2-docs/pages/react-aria/TextField.mdx @@ -6,7 +6,7 @@ import vanillaDocs from 'docs:vanilla-starter/TextField'; import {TextField as VanillaTextField} from 'vanilla-starter/TextField'; import {TextField as TailwindTextField} from 'tailwind-starter/TextField'; import '../../tailwind/tailwind.css'; -import Anatomy from '/packages/react-aria/docs/textfield/anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/TextFieldAnatomy.svg'; export const tags = ['input']; export const relatedPages = [{'title': 'useTextField', 'url': './TextField/useTextField'}]; diff --git a/packages/dev/s2-docs/pages/react-aria/TextField/useTextField.mdx b/packages/dev/s2-docs/pages/react-aria/TextField/useTextField.mdx index e172145a92f..c1d6dde37bc 100644 --- a/packages/dev/s2-docs/pages/react-aria/TextField/useTextField.mdx +++ b/packages/dev/s2-docs/pages/react-aria/TextField/useTextField.mdx @@ -12,7 +12,7 @@ export default Layout; import {FunctionAPI} from '../../../src/FunctionAPI'; import {InterfaceType} from '../../../src/types'; import docs from 'docs:@react-aria/textfield'; -import Anatomy from '/packages/react-aria/docs/textfield/anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/TextFieldAnatomy.svg'; export const section = 'Hooks'; export const description = 'Provides the behavior and accessibility implementation for a text field.'; diff --git a/packages/react-aria/docs/textfield/anatomy.svg b/packages/dev/s2-docs/pages/react-aria/TextFieldAnatomy.svg similarity index 100% rename from packages/react-aria/docs/textfield/anatomy.svg rename to packages/dev/s2-docs/pages/react-aria/TextFieldAnatomy.svg diff --git a/packages/dev/s2-docs/pages/react-aria/TimeField.mdx b/packages/dev/s2-docs/pages/react-aria/TimeField.mdx index 142248ca43b..df6ecaa7620 100644 --- a/packages/dev/s2-docs/pages/react-aria/TimeField.mdx +++ b/packages/dev/s2-docs/pages/react-aria/TimeField.mdx @@ -6,7 +6,7 @@ import vanillaDocs from 'docs:vanilla-starter/TimeField'; import {TimeField as VanillaTimeField} from 'vanilla-starter/TimeField'; import {TimeField as TailwindTimeField} from 'tailwind-starter/TimeField'; import '../../tailwind/tailwind.css'; -import Anatomy from '/packages/react-aria/docs/datepicker/timefield-anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/TimeFieldAnatomy.svg'; export const tags = ['date', 'input']; export const relatedPages = [{'title': 'useTimeField', 'url': './TimeField/useTimeField'}]; diff --git a/packages/dev/s2-docs/pages/react-aria/TimeField/useTimeField.mdx b/packages/dev/s2-docs/pages/react-aria/TimeField/useTimeField.mdx index b4e1200653f..4448a884a21 100644 --- a/packages/dev/s2-docs/pages/react-aria/TimeField/useTimeField.mdx +++ b/packages/dev/s2-docs/pages/react-aria/TimeField/useTimeField.mdx @@ -13,7 +13,7 @@ import {FunctionAPIGroup} from '../../../src/FunctionAPI'; import {InterfaceType} from '../../../src/types'; import docs from 'docs:@react-aria/datepicker'; import statelyDocs from 'docs:@react-stately/datepicker'; -import Anatomy from '/packages/react-aria/docs/datepicker/timefield-anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/TimeFieldAnatomy.svg'; export const section = 'Hooks'; export const description = 'Provides the behavior and accessibility implementation for a time field component.'; diff --git a/packages/react-aria/docs/datepicker/timefield-anatomy.svg b/packages/dev/s2-docs/pages/react-aria/TimeFieldAnatomy.svg similarity index 100% rename from packages/react-aria/docs/datepicker/timefield-anatomy.svg rename to packages/dev/s2-docs/pages/react-aria/TimeFieldAnatomy.svg diff --git a/packages/dev/s2-docs/pages/react-aria/Toast.mdx b/packages/dev/s2-docs/pages/react-aria/Toast.mdx index 223de5ec2c3..cd203171a6e 100644 --- a/packages/dev/s2-docs/pages/react-aria/Toast.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Toast.mdx @@ -4,7 +4,7 @@ export default Layout; import docs from 'docs:react-aria-components'; import toastDocs from 'docs:./ExampleToast'; import '../../tailwind/tailwind.css'; -import Anatomy from '/packages/react-aria/docs/toast/toast-anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/ToastAnatomy.svg'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2'; export const tags = ['notifications']; diff --git a/packages/dev/s2-docs/pages/react-aria/Toast/useToast.mdx b/packages/dev/s2-docs/pages/react-aria/Toast/useToast.mdx index b86e7c0b126..07f068e3a91 100644 --- a/packages/dev/s2-docs/pages/react-aria/Toast/useToast.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Toast/useToast.mdx @@ -13,7 +13,7 @@ import {FunctionAPIGroup} from '../../../src/FunctionAPI'; import {InterfaceType} from '../../../src/types'; import docs from 'docs:@react-aria/toast'; import statelyDocs from 'docs:@react-stately/toast'; -import Anatomy from '/packages/react-aria/docs/toast/toast-anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/ToastAnatomy.svg'; export const section = 'Hooks'; export const description = 'Provides the behavior and accessibility implementation for a toast notification.'; diff --git a/packages/react-aria/docs/toast/toast-anatomy.svg b/packages/dev/s2-docs/pages/react-aria/ToastAnatomy.svg similarity index 100% rename from packages/react-aria/docs/toast/toast-anatomy.svg rename to packages/dev/s2-docs/pages/react-aria/ToastAnatomy.svg diff --git a/packages/dev/s2-docs/pages/react-aria/ToggleButtonGroup.mdx b/packages/dev/s2-docs/pages/react-aria/ToggleButtonGroup.mdx index e42eef96d1f..543cccd9c9b 100644 --- a/packages/dev/s2-docs/pages/react-aria/ToggleButtonGroup.mdx +++ b/packages/dev/s2-docs/pages/react-aria/ToggleButtonGroup.mdx @@ -3,7 +3,7 @@ export default Layout; import docs from 'docs:react-aria-components'; import '../../tailwind/tailwind.css'; -import Anatomy from '/packages/react-aria/docs/button/ToggleButtonGroupAnatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/ToggleButtonGroupAnatomy2.svg'; export const tags = ['toggle', 'btn']; export const relatedPages = [{'title': 'useToggleButtonGroup', 'url': './ToggleButtonGroup/useToggleButtonGroup'}]; diff --git a/packages/dev/s2-docs/pages/react-aria/ToggleButtonGroup/useToggleButtonGroup.mdx b/packages/dev/s2-docs/pages/react-aria/ToggleButtonGroup/useToggleButtonGroup.mdx index 8e4a2aa99d8..9a4012b1f06 100644 --- a/packages/dev/s2-docs/pages/react-aria/ToggleButtonGroup/useToggleButtonGroup.mdx +++ b/packages/dev/s2-docs/pages/react-aria/ToggleButtonGroup/useToggleButtonGroup.mdx @@ -13,7 +13,7 @@ import {FunctionAPIGroup} from '../../../src/FunctionAPI'; import {InterfaceType} from '../../../src/types'; import docs from 'docs:@react-aria/button'; import statelyDocs from 'docs:@react-stately/toggle'; -import Anatomy from '/packages/react-aria/docs/button/ToggleButtonGroupAnatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/ToggleButtonGroupAnatomy2.svg'; export const section = 'Hooks'; export const description = 'Provides the behavior and accessibility implementation for a group of toggle buttons.'; diff --git a/packages/react-aria/docs/button/ToggleButtonGroupAnatomy.svg b/packages/dev/s2-docs/pages/react-aria/ToggleButtonGroupAnatomy2.svg similarity index 100% rename from packages/react-aria/docs/button/ToggleButtonGroupAnatomy.svg rename to packages/dev/s2-docs/pages/react-aria/ToggleButtonGroupAnatomy2.svg diff --git a/packages/dev/s2-docs/pages/react-aria/TokenField.mdx b/packages/dev/s2-docs/pages/react-aria/TokenField.mdx index aa0869e7c29..4374a8b9846 100644 --- a/packages/dev/s2-docs/pages/react-aria/TokenField.mdx +++ b/packages/dev/s2-docs/pages/react-aria/TokenField.mdx @@ -99,14 +99,14 @@ import {TokenizingFieldValue} from './TokenizingFieldValue'; ## Autocomplete -Combine `TokenField` with [Autocomplete](Autocomplete) to provide inline completions such as @mentions and slash commands. Use `TokenFieldValue.findText` to locate the anchor character, and `positionToDOMRange` to position a [Popover](Popover) relative to the filter text. +Combine `TokenField` with [Autocomplete](Autocomplete) to provide inline completions such as @mentions and slash commands. Use `TokenFieldValue.findText` to locate the anchor character, and `tokenFieldPositionToDOMRange` to position a [Popover](Popover) relative to the filter text. ```tsx render "use client"; import {Autocomplete} from 'react-aria-components/Autocomplete'; import {Text} from 'react-aria-components/Text'; import {Token, TokenField} from 'vanilla-starter/TokenField'; -import {positionToDOMRange} from 'react-aria/useTokenField'; +import {tokenFieldPositionToDOMRange} from 'react-aria/useTokenField'; import {Direction, TokenFieldValue} from 'react-aria-components/TokenField'; import {Menu, MenuItem} from 'vanilla-starter/Menu'; import {Popover} from 'vanilla-starter/Popover'; @@ -195,7 +195,7 @@ function Example() { placement="bottom start" trigger="MenuTrigger" getTargetRect={target => { - return positionToDOMRange(target, filterAnchor!).getBoundingClientRect(); + return tokenFieldPositionToDOMRange(target, filterAnchor!).getBoundingClientRect(); }}> {item => ( diff --git a/packages/dev/s2-docs/pages/react-aria/Toolbar.mdx b/packages/dev/s2-docs/pages/react-aria/Toolbar.mdx index 1ec9689020b..28632e52cc6 100644 --- a/packages/dev/s2-docs/pages/react-aria/Toolbar.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Toolbar.mdx @@ -3,7 +3,7 @@ export default Layout; import docs from 'docs:react-aria-components'; import '../../tailwind/tailwind.css'; -import Anatomy from '/packages/react-aria/docs/toolbar/toolbar-anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/ToolbarAnatomy.svg'; export const tags = ['group']; export const relatedPages = [{'title': 'useToolbar', 'url': './Toolbar/useToolbar'}]; diff --git a/packages/dev/s2-docs/pages/react-aria/Toolbar/useToolbar.mdx b/packages/dev/s2-docs/pages/react-aria/Toolbar/useToolbar.mdx index 18e56e6fd72..f667152c63f 100644 --- a/packages/dev/s2-docs/pages/react-aria/Toolbar/useToolbar.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Toolbar/useToolbar.mdx @@ -12,7 +12,7 @@ export default Layout; import {FunctionAPI} from '../../../src/FunctionAPI'; import {InterfaceType} from '../../../src/types'; import docs from 'docs:@react-aria/toolbar'; -import Anatomy from '/packages/react-aria/docs/toolbar/toolbar-anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/ToolbarAnatomy.svg'; export const section = 'Hooks'; export const description = 'Provides the behavior and accessibility implementation for a toolbar component.'; diff --git a/packages/react-aria/docs/toolbar/toolbar-anatomy.svg b/packages/dev/s2-docs/pages/react-aria/ToolbarAnatomy.svg similarity index 100% rename from packages/react-aria/docs/toolbar/toolbar-anatomy.svg rename to packages/dev/s2-docs/pages/react-aria/ToolbarAnatomy.svg diff --git a/packages/dev/s2-docs/pages/react-aria/Tooltip.mdx b/packages/dev/s2-docs/pages/react-aria/Tooltip.mdx index d02d85148eb..19d7e989670 100644 --- a/packages/dev/s2-docs/pages/react-aria/Tooltip.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Tooltip.mdx @@ -4,7 +4,7 @@ export default Layout; import docs from 'docs:react-aria-components'; import vanillaDocs from 'docs:vanilla-starter/Tooltip'; import '../../tailwind/tailwind.css'; -import Anatomy from '/packages/react-aria/docs/tooltip/anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/TooltipAnatomy.svg'; import {InlineAlert, Heading, Content} from '@react-spectrum/s2' export const tags = ['hint', 'popup', 'info']; @@ -103,7 +103,7 @@ function Example(props) { Accessibility - Tooltips are not shown on touch screen interactions. Ensure that your UI is usable without tooltips, or use an alternative component such as a [Popover](Popover) to show information in an adjacent element. + Tooltips are not shown on touch screen interactions. Ensure that your UI is usable without tooltips, or use an alternative component such as a [PreviewTrigger](PreviewTrigger) or a [Popover](Popover) to show information in an adjacent element. ## Custom trigger diff --git a/packages/dev/s2-docs/pages/react-aria/Tooltip/useTooltipTrigger.mdx b/packages/dev/s2-docs/pages/react-aria/Tooltip/useTooltipTrigger.mdx index 368364728dd..fa27778a800 100644 --- a/packages/dev/s2-docs/pages/react-aria/Tooltip/useTooltipTrigger.mdx +++ b/packages/dev/s2-docs/pages/react-aria/Tooltip/useTooltipTrigger.mdx @@ -13,7 +13,7 @@ import {FunctionAPIGroup} from '../../../src/FunctionAPI'; import {InterfaceType} from '../../../src/types'; import docs from 'docs:@react-aria/tooltip'; import statelyDocs from 'docs:@react-stately/tooltip'; -import Anatomy from '/packages/react-aria/docs/tooltip/anatomy.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/TooltipAnatomy.svg'; export const section = 'Hooks'; export const description = 'Provides the behavior and accessibility implementation for a tooltip component.'; diff --git a/packages/react-aria/docs/tooltip/anatomy.svg b/packages/dev/s2-docs/pages/react-aria/TooltipAnatomy.svg similarity index 100% rename from packages/react-aria/docs/tooltip/anatomy.svg rename to packages/dev/s2-docs/pages/react-aria/TooltipAnatomy.svg diff --git a/packages/dev/s2-docs/pages/react-aria/blog/drag-and-drop.mdx b/packages/dev/s2-docs/pages/react-aria/blog/drag-and-drop.mdx index d4d3a7199c3..80cd8aef90a 100644 --- a/packages/dev/s2-docs/pages/react-aria/blog/drag-and-drop.mdx +++ b/packages/dev/s2-docs/pages/react-aria/blog/drag-and-drop.mdx @@ -7,10 +7,10 @@ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTA OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */} -import Anatomy from '/packages/react-aria/docs/dnd/Anatomy.svg'; -import BetweenDropPosition from '/packages/react-aria/docs/dnd/BetweenDropPosition.svg'; -import OnDropPosition from '/packages/react-aria/docs/dnd/OnDropPosition.svg'; -import RootDropPosition from '/packages/react-aria/docs/dnd/RootDropPosition.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/DndAnatomy.svg'; +import BetweenDropPosition from '/packages/dev/s2-docs/pages/react-aria/DndBetweenDropPosition.svg'; +import OnDropPosition from '/packages/dev/s2-docs/pages/react-aria/DndOnDropPosition.svg'; +import RootDropPosition from '/packages/dev/s2-docs/pages/react-aria/DndRootDropPosition.svg'; import heroVideo from 'url:../../../../docs/pages/assets/dnd.mp4'; import dndKeyboard from 'url:../../../../docs/pages/assets/dnd-keyboard.mp4'; import dndMobile from 'url:../../../../docs/pages/assets/dnd-mobile.mp4'; diff --git a/packages/dev/s2-docs/pages/react-aria/dnd.mdx b/packages/dev/s2-docs/pages/react-aria/dnd.mdx index 96cc497e5f4..c75fd16ecd8 100644 --- a/packages/dev/s2-docs/pages/react-aria/dnd.mdx +++ b/packages/dev/s2-docs/pages/react-aria/dnd.mdx @@ -2,12 +2,12 @@ import {Layout} from '../../src/Layout'; export default Layout; import docs from 'docs:react-aria-components'; -import DropOperation from '/packages/react-aria/docs/dnd/DropOperation.svg'; -import BetweenDropPosition from '/packages/react-aria/docs/dnd/BetweenDropPosition.svg'; -import OnDropPosition from '/packages/react-aria/docs/dnd/OnDropPosition.svg'; -import RootDropPosition from '/packages/react-aria/docs/dnd/RootDropPosition.svg'; -import Anatomy from '/packages/react-aria/docs/dnd/Anatomy.svg'; -import DragAffordance from '/packages/react-aria/docs/dnd/DragAffordance.svg'; +import DropOperation from '/packages/dev/s2-docs/pages/react-aria/DndDropOperation.svg'; +import BetweenDropPosition from '/packages/dev/s2-docs/pages/react-aria/DndBetweenDropPosition.svg'; +import OnDropPosition from '/packages/dev/s2-docs/pages/react-aria/DndOnDropPosition.svg'; +import RootDropPosition from '/packages/dev/s2-docs/pages/react-aria/DndRootDropPosition.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/DndAnatomy.svg'; +import DragAffordance from '/packages/dev/s2-docs/pages/react-aria/DndDragAffordance.svg'; import {GroupedPropTable} from '../../src/PropTable'; import {PokemonListBox} from './PokemonListBox'; import {PokemonGridList} from './PokemonGridList'; diff --git a/packages/dev/s2-docs/pages/s2/dnd.mdx b/packages/dev/s2-docs/pages/s2/dnd.mdx index a64bf628f5a..a0ed449a3db 100644 --- a/packages/dev/s2-docs/pages/s2/dnd.mdx +++ b/packages/dev/s2-docs/pages/s2/dnd.mdx @@ -2,12 +2,12 @@ import {Layout} from '../../src/Layout'; export default Layout; import docs from 'docs:@react-spectrum/s2'; -import DropOperation from '/packages/react-aria/docs/dnd/DropOperation.svg'; -import BetweenDropPosition from '/packages/react-aria/docs/dnd/BetweenDropPosition.svg'; -import OnDropPosition from '/packages/react-aria/docs/dnd/OnDropPosition.svg'; -import RootDropPosition from '/packages/react-aria/docs/dnd/RootDropPosition.svg'; -import Anatomy from '/packages/react-aria/docs/dnd/Anatomy.svg'; -import DragAffordance from '/packages/react-aria/docs/dnd/DragAffordance.svg'; +import DropOperation from '/packages/dev/s2-docs/pages/react-aria/DndDropOperation.svg'; +import BetweenDropPosition from '/packages/dev/s2-docs/pages/react-aria/DndBetweenDropPosition.svg'; +import OnDropPosition from '/packages/dev/s2-docs/pages/react-aria/DndOnDropPosition.svg'; +import RootDropPosition from '/packages/dev/s2-docs/pages/react-aria/DndRootDropPosition.svg'; +import Anatomy from '/packages/dev/s2-docs/pages/react-aria/DndAnatomy.svg'; +import DragAffordance from '/packages/dev/s2-docs/pages/react-aria/DndDragAffordance.svg'; import {GroupedPropTable, PropTable} from '../../src/PropTable'; export const section = 'Guides'; diff --git a/packages/react-aria-components/exports/PreviewTrigger.ts b/packages/react-aria-components/exports/PreviewTrigger.ts new file mode 100644 index 00000000000..d439afed7a6 --- /dev/null +++ b/packages/react-aria-components/exports/PreviewTrigger.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2026 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. + */ + +// Mark as a client only package. This will cause a build time error if you try +// to import it from a React Server Component in a framework like Next.js. +import 'client-only'; + +export {PreviewTrigger} from '../src/PreviewTrigger'; +export type {PreviewTriggerProps} from '../src/PreviewTrigger'; diff --git a/packages/react-aria-components/exports/TokenField.ts b/packages/react-aria-components/exports/TokenField.ts index 5070da538aa..442ad72dfab 100644 --- a/packages/react-aria-components/exports/TokenField.ts +++ b/packages/react-aria-components/exports/TokenField.ts @@ -20,7 +20,7 @@ export type { TokenRenderProps, TokenFieldContext } from '../src/TokenField'; -export {positionToDOMRange} from 'react-aria/useTokenField'; +export {tokenFieldPositionToDOMRange, setTokenFieldSelection} from 'react-aria/useTokenField'; export {TokenFieldValue, Direction} from 'react-stately/useTokenFieldState'; export type { TokenFieldSegment, diff --git a/packages/react-aria-components/src/PreviewTrigger.tsx b/packages/react-aria-components/src/PreviewTrigger.tsx index 51df02d4f4d..2e86981623b 100644 --- a/packages/react-aria-components/src/PreviewTrigger.tsx +++ b/packages/react-aria-components/src/PreviewTrigger.tsx @@ -23,6 +23,18 @@ import {useTooltipTriggerState} from 'react-stately/useTooltipTriggerState'; export interface PreviewTriggerProps extends AriaPreviewTriggerProps { /** The trigger and Popover that make up the preview trigger. */ children: ReactNode; + /** + * The delay time in milliseconds before the preview opens. + * + * @default 600 + */ + delay?: number; + /** + * The delay time in milliseconds before the preview closes. + * + * @default 200 + */ + closeDelay?: number; } /** diff --git a/packages/react-aria-components/stories/TokenField.stories.tsx b/packages/react-aria-components/stories/TokenField.stories.tsx index 89d207dffdb..1c5db91e529 100644 --- a/packages/react-aria-components/stories/TokenField.stories.tsx +++ b/packages/react-aria-components/stories/TokenField.stories.tsx @@ -22,10 +22,10 @@ import {FieldButton, Label} from 'vanilla-starter/Form'; import {Header, Menu, MenuItem, MenuSection} from 'vanilla-starter/Menu'; import {Key} from '@react-types/shared'; import {Popover} from 'vanilla-starter/Popover'; -import {positionToDOMRange} from 'react-aria/useTokenField'; -import {Token, TokenField, TokenInput} from '../src/TokenField'; import 'vanilla-starter/TagGroup.css'; import {Text} from 'react-aria-components/Text'; +import {Token, TokenField, TokenInput} from '../src/TokenField'; +import {tokenFieldPositionToDOMRange} from 'react-aria/useTokenField'; export default { title: 'React Aria Components/TokenField', @@ -206,7 +206,7 @@ export const WithAutocomplete: TokenFieldStory = () => { placement="bottom start" trigger="MenuTrigger" getTargetRect={target => { - return positionToDOMRange(target, filterAnchor!).getBoundingClientRect(); + return tokenFieldPositionToDOMRange(target, filterAnchor!).getBoundingClientRect(); }}> {item => ( diff --git a/packages/react-aria-components/test/utils/tokenFieldBrowserUtils.tsx b/packages/react-aria-components/test/utils/tokenFieldBrowserUtils.tsx index c34831d94a9..e9a56002477 100644 --- a/packages/react-aria-components/test/utils/tokenFieldBrowserUtils.tsx +++ b/packages/react-aria-components/test/utils/tokenFieldBrowserUtils.tsx @@ -11,7 +11,10 @@ */ import {expect} from 'vitest'; -import {getSelection, setSelection} from '../../../react-aria/src/tokenfield/useTokenField'; +import { + getSelection, + setTokenFieldSelection +} from '../../../react-aria/src/tokenfield/useTokenField'; import {type Locator, userEvent} from 'vitest/browser'; import {Position, TokenFieldSegment, TokenFieldValue} from 'react-stately/useTokenFieldState'; import React, {useEffect, useState} from 'react'; @@ -99,7 +102,7 @@ export async function focusField(locator: Locator) { } export function setFieldSelection(textboxEl: Element, start: Position, end: Position): void { - setSelection(textboxEl, start, end); + setTokenFieldSelection(textboxEl, start, end); } /** diff --git a/packages/react-aria/docs/autocomplete/anatomy.svg b/packages/react-aria/docs/autocomplete/anatomy.svg deleted file mode 100644 index d15379993fa..00000000000 --- a/packages/react-aria/docs/autocomplete/anatomy.svg +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - - - - - - - - - - - Label - - - - - Op - Label - - - - - - - - - - - - - - - - - - Option 1 - - Option 3 - - Option 3 - - - Label - - - - - Op - - - - - - - - - - - - - - - - - - - Input - - - - - - - - ClearButton - - - - - - - - SearchIcon - - - - - - - - List box - - - - - - - - - diff --git a/packages/react-aria/docs/autocomplete/useAutocomplete.mdx b/packages/react-aria/docs/autocomplete/useAutocomplete.mdx deleted file mode 100644 index 35c4f8f9d60..00000000000 --- a/packages/react-aria/docs/autocomplete/useAutocomplete.mdx +++ /dev/null @@ -1,67 +0,0 @@ -{/* 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 '@react-spectrum/docs'; -export default Layout; - -import docs from 'docs:@react-aria/autocomplete'; -import {FunctionAPI, HeaderInfo, InterfaceType, TypeContext, TypeLink, PageDescription} from '@react-spectrum/docs'; -import packageData from '@react-aria/autocomplete/package.json'; -import statelyDocs from 'docs:@react-stately/autocomplete'; -import {InlineAlert, Content, Heading} from '@adobe/react-spectrum'; - ---- -category: Pickers -keywords: [autocomplete, autosuggest, typeahead, search, aria] -preRelease: beta ---- - -# useAutocomplete - -{docs.exports.useAutocomplete.description} - - - - - Under construction - This hook is in beta. More documentation is coming soon! - - -## API - - - -## Features - -Autocomplete can be implemented using the [<datalist>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist) HTML element, but this has limited functionality and behaves differently across browsers. -`useAutocomplete` helps achieve accessible text input and collection that can be styled as needed. - -## Anatomy - -An autocomplete consists of a text input that displays the current value and a collection of items. Users can type within the input -to filter the collection. `useAutocomplete` handles exposing the correct ARIA attributes for accessibility for each of the elements comprising the autocomplete. - -`useAutocomplete` returns props that you should spread onto the appropriate elements: - - - - - -State is managed by the hook from `@react-stately/autocomplete`. -The state object should be passed as an option to `useAutocomplete`. - -## Internationalization - -`useAutocomplete` handles some aspects of internationalization automatically. -For example, VoiceOver announcements about the item focus, count, and selection are localized. -You are responsible for localizing all labels and option -content that is passed into the autocomplete. diff --git a/packages/react-aria/docs/breadcrumbs/useBreadcrumbs.mdx b/packages/react-aria/docs/breadcrumbs/useBreadcrumbs.mdx deleted file mode 100644 index e87dd5da3bb..00000000000 --- a/packages/react-aria/docs/breadcrumbs/useBreadcrumbs.mdx +++ /dev/null @@ -1,203 +0,0 @@ -{/* Copyright 2020 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 '@react-spectrum/docs'; -export default Layout; - -import docs from 'docs:@react-aria/breadcrumbs'; -import {HeaderInfo, FunctionAPI, TypeContext, InterfaceType, PageDescription} from '@react-spectrum/docs'; -import {Keyboard} from '@react-spectrum/text'; -import packageData from '@react-aria/breadcrumbs/package.json'; -import Anatomy from './anatomy.svg'; - ---- -category: Navigation -keywords: [breadcrumbs, aria] ---- - -# useBreadcrumbs - -{docs.exports.useBreadcrumbs.description} - - - -## API - - - - -## Features - -Breadcrumbs provide a list of links to parent pages of the current page in hierarchical order. -`useBreadcrumbs` and `useBreadcrumbItem` help implement these in an accessible way. - -* Support for mouse, touch, and keyboard interactions on breadcrumbs -* Support for navigation links via `` elements or custom element types via ARIA -* Localized ARIA labeling support for landmark navigation region -* Support for disabled breadcrumbs - -## Anatomy - - - -Breadcrumbs consist of a navigation landmark element and a list of links, typically with a visual separator -icon between each item. The last link represents the current page in the hierarchy, with the previous links representing the -parent pages of the current page. Each of these parent links can be clicked, tapped, or -triggered via the Enter key to navigate to that page. - -`useBreadcrumbs` returns props to be spread onto the navigation element: - - - - - -`useBreadcrumbItem` returns props to spread onto the individual breadcrumb links: - - - - - -## Example - -This example displays a basic list of breadcrumbs using an HTML -[<nav>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/nav) -element, and a [<ol>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ol) -for the list of links. Each link is a span because we are handling the interactions -locally via `onPress`. `useBreadcrumbItem` automatically handles exposing these -spans as links to assistive technology. - -The chevrons between each link are rendered using a span with `aria-hidden="true"` so that -screen readers do not pick them up. You could also render them similarly using SVG icons, -CSS `:after`, or other techniques. - -The last link is non-interactive since it represents the current page. This is -passed to the last breadcrumb item by cloning the element and adding the `isCurrent` -prop. - -```tsx example -import {useBreadcrumbs, useBreadcrumbItem} from '@react-aria/breadcrumbs'; - -function Breadcrumbs(props) { - let {navProps} = useBreadcrumbs(props); - let childCount = React.Children.count(props.children); - - return ( - - ) -} - -function BreadcrumbItem(props) { - let ref = React.useRef(null); - let {itemProps} = useBreadcrumbItem({...props, elementType: 'span'}, ref); - return ( -
  • - - {props.children} - - {!props.isCurrent && - - } -
  • - ); -} - - - alert('Pressed Folder 1')}>Folder 1 - alert('Pressed Folder 2')}>Folder 2 - Folder 3 - -``` - -## Navigation links - -To render breadcrumbs that navigate to other pages rather than handle events via `onPress`, use an `
    ` element -for each BreadcrumbItem. This is the default `elementType`, so the option can be omitted from `useBreadcrumbItem`. - -```tsx example export=true -///- begin collapse -/// -function Breadcrumbs(props) { - let {navProps} = useBreadcrumbs(props); - let childCount = React.Children.count(props.children); - - return ( - - ); -} -///- end collapse -/// -function BreadcrumbItem(props) { - let ref = React.useRef(null); - let {itemProps} = useBreadcrumbItem(props, ref); - return ( -
  • - - {props.children} - - {!props.isCurrent && - - } -
  • - ); -} - - - Home - React Aria - useBreadcrumbs - -``` - -## Usage - -The following examples show how to use the `Breadcrumbs` component created in the above examples. - -### Disabled - -Breadcrumbs can be disabled using the `isDisabled` prop, passed to each disabled BreadcrumbItem. This indicates that navigation is not currently available. When a breadcrumb is disabled, `onPress` will not be triggered, navigation will not occur, and links will be marked as `aria-disabled` for assistive technologies. - -```tsx example - - Home - React Aria - useBreadcrumbs - -``` diff --git a/packages/react-aria/docs/button/useButton.mdx b/packages/react-aria/docs/button/useButton.mdx deleted file mode 100644 index ec5de0790d5..00000000000 --- a/packages/react-aria/docs/button/useButton.mdx +++ /dev/null @@ -1,160 +0,0 @@ -{/* Copyright 2020 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 '@react-spectrum/docs'; -export default Layout; - -import docs from 'docs:@react-aria/button'; -import typesDocs from 'docs:@react-types/shared/src/events.d.ts'; -import {HeaderInfo, FunctionAPI, TypeLink, PageDescription} from '@react-spectrum/docs'; -import {Keyboard} from '@react-spectrum/text'; -import packageData from '@react-aria/button/package.json'; - ---- -category: Buttons -keywords: [button, aria, form] ---- - -# useButton - -{docs.exports.useButton.description} - - - -## API - - - -## Features - -On the surface, building a custom styled button seems simple. However, there are many -cross browser inconsistencies in interactions and accessibility features to consider. -`useButton` handles all of these interactions for you, so you can focus on the styling. - -* Native HTML ` - ); -} - - -``` - -## Custom element type - -Sometimes you might need to use an element other than a native ` -``` - -## Usage - -The following examples show how to use the `Button` component created in the above example. - -### Events - -`useButton` supports user interactions via mouse, keyboard, and touch. You can handle all of these via the `onPress` prop. This is similar to the standard `onClick` event, but normalized to support all interaction methods equally. In addition, the `onPressStart`, `onPressEnd`, and `onPressChange` events are fired as the user interacts with the button. - -Each of these handlers receives a , which exposes information about the target and the type of event that triggered the interaction. See [usePress](../usePress) for more details. - -```tsx example -function Example() { - let [pointerType, setPointerType] = React.useState(null); - - return ( - <> - -

    {pointerType ? `You are pressing the button with a ${pointerType}!` : 'Ready to be pressed.'}

    - - ) -} -``` - -### Disabled - -A `Button` can be disabled using the `isDisabled` prop. - -```tsx example - -``` - diff --git a/packages/react-aria/docs/button/useToggleButton.mdx b/packages/react-aria/docs/button/useToggleButton.mdx deleted file mode 100644 index ce8437d4420..00000000000 --- a/packages/react-aria/docs/button/useToggleButton.mdx +++ /dev/null @@ -1,143 +0,0 @@ -{/* Copyright 2020 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 '@react-spectrum/docs'; -export default Layout; - -import docs from 'docs:@react-aria/button'; -import statelyDocs from 'docs:@react-stately/toggle'; -import {HeaderInfo, FunctionAPI, TypeContext, InterfaceType, TypeLink, PageDescription} from '@react-spectrum/docs'; -import {Keyboard} from '@react-spectrum/text'; -import packageData from '@react-aria/button/package.json'; - ---- -category: Buttons -keywords: [button, toggle button, aria, form] ---- - -# useToggleButton - -{docs.exports.useToggleButton.description} - - - -## API - - - -## Features - -Toggle buttons are similar to action buttons, but support an additional selection state -that is toggled when a user presses the button. There is no built-in HTML element that -represents a toggle button, so React Aria implements it using ARIA attributes. - -* Native HTML `
    - ); -} - -Pin -``` - -## Usage - -The following examples show how to use the `ToggleButton` component created in the above example. - -### Controlled selection state - -A default selection state for a toggle button can be set using the `defaultSelected` prop, or controlled with the `isSelected` prop. The `onChange` event is fired when the user presses the button, toggling the boolean. See React's documentation on -[uncontrolled components](https://reactjs.org/docs/uncontrolled-components.html) for more info. - -```tsx example -function Example() { - let [isSelected, setSelected] = React.useState(false); - - return ( - - ★ - - ); -} -``` - -### Disabled - -A `ToggleButton` can be disabled using the `isDisabled` prop. - -```tsx example -Pin -``` diff --git a/packages/react-aria/docs/button/useToggleButtonGroup.mdx b/packages/react-aria/docs/button/useToggleButtonGroup.mdx deleted file mode 100644 index 3030d20788c..00000000000 --- a/packages/react-aria/docs/button/useToggleButtonGroup.mdx +++ /dev/null @@ -1,265 +0,0 @@ -{/* Copyright 2024 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 '@react-spectrum/docs'; -export default Layout; - -import docs from 'docs:@react-aria/button'; -import statelyDocs from 'docs:@react-stately/toggle'; -import {HeaderInfo, FunctionAPI, TypeContext, InterfaceType, TypeLink, PageDescription} from '@react-spectrum/docs'; -import {Keyboard} from '@react-spectrum/text'; -import packageData from '@react-aria/button/package.json'; -import ChevronRight from '@spectrum-icons/workflow/ChevronRight'; -import Anatomy from './ToggleButtonGroupAnatomy.svg'; - ---- -category: Buttons -keywords: [button, toggle button, aria, form] ---- - -# useToggleButtonGroup - -{docs.exports.useToggleButtonGroup.description} - - - -## API - - - - -## Features - -There is no built in element for toggle button groups in HTML. `useToggleButtonGroup` helps achieve accessible toggle button groups that can be styled as needed. - -* **Accessible** – Represented as an ARIA [radiogroup](https://www.w3.org/WAI/ARIA/apg/patterns/radio/) when using single selection, or a [toolbar](https://www.w3.org/WAI/ARIA/apg/patterns/toolbar/) when using multiple selection. -* **Keyboard navigation** – Users can navigate between buttons with the arrow keys. Selection can be toggled using the Enter or Space keys. -* **Styleable** – Hover, press, keyboard focus, and selection states are provided for easy styling. - -## Anatomy - - - -A toggle button group consists of a set of toggle buttons, and coordinates the selection state between them. Users can navigate between buttons with the arrow keys in either horizontal or vertical orientations. - -`useToggleButtonGroup` returns props for the toggle button group: - - - - - -`useToggleButtonGroupItem` returns props for an individual toggle button: - - - - - -Selection state is managed by the -hook in `@react-stately/toggle`. The state object should be passed as an option to `useToggleButtonGroup` -and `useToggleButtonGroupItem`. - -**Note:** `useToggleButtonGroupItem` should only be used when it is contained within a toggle button group. For a -standalone toggle button, use the [useToggleButton](../ToggleButton/useToggleButton.html) hook instead. - -## Example - -```tsx example export=true -import type {AriaToggleButtonGroupProps, AriaToggleButtonGroupItemProps} from '@react-aria/button'; -import type {ToggleGroupState} from '@react-stately/toggle'; -import {useToggleButtonGroup, useToggleButtonGroupItem} from '@react-aria/button'; -import {useToggleGroupState} from '@react-stately/toggle'; - -interface ToggleButtonGroupProps extends AriaToggleButtonGroupProps { - children: React.ReactNode -} - -let ToggleButtonGroupContext = React.createContext(null); - -function ToggleButtonGroup(props: ToggleButtonGroupProps) { - let state = useToggleGroupState(props); - let ref = React.useRef(null); - let {groupProps} = useToggleButtonGroup(props, state, ref); - - return ( -
    - - {props.children} - -
    - ); -} - -function ToggleButton(props: AriaToggleButtonGroupItemProps) { - let ref = React.useRef(null); - let state = React.useContext(ToggleButtonGroupContext)!; - let {buttonProps, isPressed, isSelected} = useToggleButtonGroupItem(props, state, ref); - - return ( - - ); -} - - - Left - Center - Right - -``` - -
    - Show CSS - -```css -.toggle-group { - display: flex; - gap: 4px; - - &[aria-orientation=vertical] { - flex-direction: column; - width: fit-content; - } -} - -.toggle-button { - background: lightgray; - color: black; - padding: 10px; - font-size: 16px; - user-select: none; - border: none; - - &[data-pressed=true] { - background: gray; - } - - &[data-selected=true] { - background: green; - color: white; - - &[data-pressed=true] { - background: darkgreen; - } - } - - &:disabled { - opacity: 0.5; - } -} -``` - -
    - -## Selection - -ToggleButtonGroup supports both single and multiple selection modes. Use `defaultSelectedKeys` to provide a default set of selected items (uncontrolled) and `selectedKeys` to set the selected items (controlled). The value of the selected keys must match the `id` prop of the items. - -### Single selection - -By default, the `selectionMode` of a `ToggleButtonGroup` is `"single"`. - -```tsx example - - Grid view - List view - Gallery view - -``` - -### Multiple selection - -Set `selectionMode` prop to `multiple` to allow more than one selection. - -```tsx example - - Bold - Italic - Underline - -``` - -### Controlled selection - -The `selectedKeys` prop can be used to make the selected state controlled. - -```tsx example -import type {Key} from 'react-stately'; - -function Example() { - let [selected, setSelected] = React.useState(new Set(['bold'])); - - return ( - <> - - Bold - Italic - Underline - -

    Current selections (controlled): {[...selected].join(', ')}

    - - ); -} -``` - -## Disabled - -All buttons within a `ToggleButtonGroup` can be disabled using the `isDisabled` prop. - -```tsx example - - Grid view - List view - Gallery view - -``` - -Individual items can be disabled using the `isDisabled` prop on each `ToggleButton`. - -```tsx example - - Grid view - List view - Gallery view - -``` - -## Orientation - -By default, toggle button groups are horizontally oriented. The orientation prop can be set to "vertical" to change the arrow key navigation behavior. - -```tsx example - - Grid - List - Gallery - -``` - -## Accessibility - -A `ToggleButtonGroup` can be labeled using the `aria-label` or `aria-labelledby` props. - -```tsx example - - Bold - Italic - Underline - -``` diff --git a/packages/react-aria/docs/calendar/calendar-tailwind.png b/packages/react-aria/docs/calendar/calendar-tailwind.png deleted file mode 100644 index dd0763ed8e2..00000000000 Binary files a/packages/react-aria/docs/calendar/calendar-tailwind.png and /dev/null differ diff --git a/packages/react-aria/docs/calendar/css-modules.png b/packages/react-aria/docs/calendar/css-modules.png deleted file mode 100644 index 0f270efd264..00000000000 Binary files a/packages/react-aria/docs/calendar/css-modules.png and /dev/null differ diff --git a/packages/react-aria/docs/calendar/styled-components.png b/packages/react-aria/docs/calendar/styled-components.png deleted file mode 100644 index 297f992b491..00000000000 Binary files a/packages/react-aria/docs/calendar/styled-components.png and /dev/null differ diff --git a/packages/react-aria/docs/calendar/tailwind.png b/packages/react-aria/docs/calendar/tailwind.png deleted file mode 100644 index 92ae5e5e7f4..00000000000 Binary files a/packages/react-aria/docs/calendar/tailwind.png and /dev/null differ diff --git a/packages/react-aria/docs/calendar/useCalendar.mdx b/packages/react-aria/docs/calendar/useCalendar.mdx deleted file mode 100644 index a3b546677b5..00000000000 --- a/packages/react-aria/docs/calendar/useCalendar.mdx +++ /dev/null @@ -1,575 +0,0 @@ -{/* Copyright 2020 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 '@react-spectrum/docs'; -export default Layout; - -import docs from 'docs:@react-aria/calendar'; -import statelyDocs from 'docs:@react-stately/calendar'; -import i18nDocs from 'docs:@internationalized/date'; -import {FunctionAPI, HeaderInfo, InterfaceType, TypeContext, TypeLink, PageDescription} from '@react-spectrum/docs'; -import packageData from '@react-aria/calendar/package.json'; -import Anatomy from './calendar-anatomy.svg'; -import ChevronRight from '@spectrum-icons/workflow/ChevronRight'; -import {Keyboard} from '@react-spectrum/text'; -import {ExampleCard} from '@react-spectrum/docs/src/ExampleCard'; -import styledComponentsExample from 'url:./styled-components.png'; -import tailwindExample from 'url:./calendar-tailwind.png'; -import cssModulesExample from 'url:./css-modules.png'; - ---- -category: Date and Time -keywords: [input, form, field, date, time] ---- - -# useCalendar - -{docs.exports.useCalendar.description} - - - -## API - - - - - -## Features - -There is no standalone calendar element in HTML. `` is close, but this is very limited in functionality, lacking in internationalization capabilities, inconsistent between browsers, and difficult to style. `useCalendar` helps achieve accessible and international calendar components that can be styled as needed. - -* **Flexible** – Display one or more months at once, or a custom time range for use cases like a week view. Minimum and maximum values, unavailable dates, and non-contiguous selections are supported as well. -* **International** – Support for 13 calendar systems used around the world, including Gregorian, Buddhist, Islamic, Persian, and more. Locale-specific formatting, number systems, and right-to-left support are available as well. -* **Accessible** – Calendar cells can be navigated and selected using the keyboard, and localized screen reader messages are included to announce when the selection and visible date range change. -* **Customizable** – As with all of React Aria, the DOM structure and styling of all elements can be fully customized. - -Read our [blog post](../blog/date-and-time-pickers-for-all) for more details about the internationalization, accessibility, and user experience features implemented by `useCalendar`. - -## Anatomy - - - -A calendar consists of a grouping element containing one or more date grids (e.g. months), and a previous and next button for navigating between date ranges. Each calendar grid consists of cells containing button elements that can be pressed and navigated to using the arrow keys to select a date. - -### useCalendar - -`useCalendar` returns props that you should spread onto the appropriate elements: - - - - - -### useCalendarGrid - -`useCalendarGrid` returns props for an individual grid of dates, such as one month, along with a list of formatted weekday names in the current locale for use during rendering: - - - - - -### useCalendarCell - -`useCalendarCell` returns props for an individual cell, along with states and information useful during rendering: - - - - - -State is managed by the hook from `@react-stately/calendar`. The state object should be passed as an option to `useCalendar`, `useCalendarGrid`, and `useCalendarCell`. - -Note that much of this anatomy is shared with [range calendars](../RangeCalendar/useRangeCalendar.html). The only difference is that `useCalendarState` is used instead of `useRangeCalendarState`, and `useCalendar` is used instead of `useRangeCalendar`. - -## Date and time values - -Dates are represented in many different ways by cultures around the world. This includes differences in calendar systems, date formatting, numbering systems, weekday and weekend rules, and much more. When building applications that support users around the world, it is important to handle these aspects correctly for each locale. - - uses the [@internationalized/date](../internationalized/date/) library to represent dates and times. This package provides a library of objects and functions to perform date and time related manipulation, queries, and conversions that work across locales and calendars. Date and time objects can be converted to and from native JavaScript `Date` objects or [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) strings. See the [documentation](../internationalized/date/), or the [examples below](#value) for more details. - - requires a `createCalendar` function to be provided, which is used to implement date manipulation across multiple calendar systems. The default implementation in `@internationalized/date` includes all supported calendar systems. While this library is quite small (8 kB minified + Brotli), you can reduce its bundle size further by providing your own implementation that includes only your supported calendars. See [below](#reducing-bundle-size) for an example. - -## Example - -A `Calendar` consists of three components: the main calendar wrapper element with previous and next buttons for navigating, one or more `CalendarGrid` components to display each month, and `CalendarCell` components for each date cell. We'll go through them one by one. - -For simplicity, this example only displays a single month at a time. See the [styled examples](#styled-examples) section for more examples with multiple months, as well as other time ranges like weeks. - -```tsx example render=false export=true -import {useCalendarState} from '@react-stately/calendar'; -import {useCalendar} from '@react-aria/calendar'; -import {useLocale} from '@react-aria/i18n'; -import {createCalendar} from '@internationalized/date'; - -// Reuse the Button from your component library. See below for details. -import {Button} from 'your-component-library'; - -function Calendar(props) { - let {locale} = useLocale(); - let state = useCalendarState({ - createCalendar, - ...props, - locale - }); - - let {calendarProps, prevButtonProps, nextButtonProps, title} = useCalendar(props, state); - - return ( -
    -
    -

    {title}

    - - -
    - -
    - ); -} -``` - -### CalendarGrid - -The `CalendarGrid` component will be responsible for rendering an individual month. It is a separate component so that you can render more than one month at a time if you like. It's rendered as an HTML `` element, and React Aria takes care of adding the proper ARIA roles and event handlers to make it behave as an ARIA grid. You can use the arrow keys to navigate between cells, and the Enter key to select a date. - -The `state.getDatesInWeek` function returns the dates in each week of the month. Note that this always includes 7 values, but some of them may be null, which indicates that the date doesn't exist within the calendar system. You should render a placeholder ` - ); -} -``` - -That's it! Now we can render an example of our `Calendar` component in action. - -```tsx example - -``` - -
    - Show CSS - -```css -.calendar { - width: 220px; -} - -.header { - display: flex; - align-items: center; - gap: 4px; - margin: 0 8px; -} - -.header h2 { - flex: 1; - margin: 0; -} - -.calendar table { - width: 100%; -} - -.cell { - cursor: default; - text-align: center; -} - -.selected { - background: var(--blue); - color: white; -} - -.unavailable { - color: var(--spectrum-global-color-red-600); -} - -.disabled { - color: gray; -} -``` - -
    - -### Button - -The `Button` component is used in the above example to navigate between months. It is built using the [useButton](../Button/useButton.html) hook, and can be shared with many other components. - -
    - Show code - -```tsx example export=true render=false -import {useButton} from '@react-aria/button'; - -function Button(props) { - let ref = React.useRef(null); - let {buttonProps} = useButton(props, ref); - return ; -} -``` - -
    - -## Styled Examples - - - - - - - -## Usage - -The following examples show how to use the `Calendar` component created in the above example. - -### Value - -A `Calendar` has no selection by default. An initial, uncontrolled value can be provided to the `Calendar` using the `defaultValue` prop. Alternatively, a controlled value can be provided using the `value` prop. - -Date values are provided using objects in the [@internationalized/date](../internationalized/date/) package. This library handles correct international date manipulation across calendars, time zones, and other localization concerns. - -`useCalendar` supports values with both date and time components, but only allows users to modify the date. By default, `useCalendar` will emit objects in the `onChange` event, but if a or object is passed as the `value` or `defaultValue`, values of that type will be emitted, changing only the date and preserving the time components. - -```tsx example -import {parseDate} from '@internationalized/date'; - -function Example() { - let [value, setValue] = React.useState(parseDate('2020-02-03')); - - return ( -
    - - -
    - ); -} -``` - -### Events - -`useCalendar` accepts an `onChange` prop which is triggered whenever a date is selected by the user. The example below uses `onChange` to update a separate element with a formatted version of the date in the user's locale. This is done by converting the date to a native JavaScript `Date` object to pass to the formatter. - -```tsx example -import {getLocalTimeZone} from '@internationalized/date'; -import {useDateFormatter} from '@react-aria/i18n'; - -function Example() { - let [date, setDate] = React.useState(parseDate('2022-07-04')); - let formatter = useDateFormatter({dateStyle: 'full'}); - - return ( - <> - -

    Selected date: {formatter.format(date.toDate(getLocalTimeZone()))}

    - - ); -} -``` - -### International calendars - -`useCalendar` supports selecting dates in many calendar systems used around the world, including Gregorian, Hebrew, Indian, Islamic, Buddhist, and more. Dates are automatically displayed in the appropriate calendar system for the user's locale. The calendar system can be overridden using the [Unicode calendar locale extension](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/calendar#adding_a_calendar_in_the_locale_string), passed to the [I18nProvider](../I18nProvider) component. - -Selected dates passed to `onChange` always use the same calendar system as the `value` or `defaultValue` prop. If no `value` or `defaultValue` is provided, then dates passed to `onChange` are always in the Gregorian calendar since this is the most commonly used. This means that even though the user selects dates in their local calendar system, applications are able to deal with dates from all users consistently. - -The below example displays a `Calendar` in the Hindi language, using the Indian calendar. Dates emitted from `onChange` are in the Gregorian calendar. - -```tsx example -import {I18nProvider} from '@react-aria/i18n'; - -function Example() { - let [date, setDate] = React.useState(null); - return ( - - -

    Selected date: {date?.toString()}

    -
    - ); -} -``` - -### Custom calendar systems - -`Calendar` also supports custom calendar systems that implement custom business rules. An example would be a fiscal year calendar that follows a [4-5-4 format](https://nrf.com/resources/4-5-4-calendar), where month ranges don't follow the usual Gregorian calendar. - -The `createCalendar` prop accepts a function that returns an instance of the interface. See the [@internationalized/date docs](../internationalized/date/Calendar.html#custom-calendars) for an example implementation. - -```tsx import -import type {AnyCalendarDate} from '@internationalized/date'; -import {CalendarDate, startOfWeek, toCalendar} from '@internationalized/date'; -``` - -```tsx example -import {GregorianCalendar} from '@internationalized/date'; - -function Example() { - return new Custom454()} />; -} - -class Custom454 extends GregorianCalendar { - // See @internationalized/date docs linked above... - ///- begin collapse -/// - weekPattern = [4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4]; - getDaysInMonth(date) { - return this.weekPattern[date.month - 1] * 7; - } - - fromJulianDay(jd: number): CalendarDate { - let gregorian = super.fromJulianDay(jd); - - let monthStart = startOfWeek(new CalendarDate(gregorian.year, 1, 1), 'en'); - for (let months = 0; months < this.weekPattern.length; months++) { - let weeksInMonth = this.weekPattern[months]; - let monthEnd = monthStart.add({weeks: weeksInMonth}); - if (monthEnd.compare(gregorian) > 0) { - let days = gregorian.compare(monthStart); - return new CalendarDate(this, monthStart.year, months + 1, days + 1); - } - monthStart = monthEnd; - } - - throw Error('Date is not in any month somehow!'); - } - - toJulianDay(date: AnyCalendarDate): number { - let monthStart = startOfWeek(new CalendarDate(date.year, 1, 1), 'en'); - for (let month = 1; month < date.month; month++) { - monthStart = monthStart.add({weeks: this.weekPattern[month - 1]}); - } - - let gregorian = monthStart.add({days: date.day - 1}); - return super.toJulianDay(gregorian); - } - - getFormattableMonth(date) { - let gregorian = toCalendar(date, new GregorianCalendar()); - return gregorian.set({month: date.month, day: 1}); - } - - isEqual(other) { - return other instanceof Custom454; - } - ///- end collapse -/// -} -``` - -### Validation - -By default, `useCalendar` allows selecting any date. The `minValue` and `maxValue` props can also be used to prevent the user from selecting dates outside a certain range. - -This example only accepts dates after today. - -```tsx example -import {today} from '@internationalized/date'; - - -``` - -### Unavailable dates - -`useCalendar` supports marking certain dates as _unavailable_. These dates remain focusable with the keyboard so that navigation is consistent, but cannot be selected by the user. In this example, they are displayed in red. The `isDateUnavailable` prop accepts a callback that is called to evaluate whether each visible date is unavailable. - -This example includes multiple unavailable date ranges, e.g. dates when no appointments are available. In addition, all weekends are unavailable. The `minValue` prop is also used to prevent selecting dates before today. - - -```tsx example -import {today, isWeekend} from '@internationalized/date'; -import {useLocale} from '@react-aria/i18n'; - -function Example() { - let now = today(getLocalTimeZone()); - let disabledRanges = [ - [now, now.add({days: 5})], - [now.add({days: 14}), now.add({days: 16})], - [now.add({days: 23}), now.add({days: 24})], - ]; - - let {locale} = useLocale(); - let isDateUnavailable = (date) => isWeekend(date, locale) || disabledRanges.some((interval) => date.compare(interval[0]) >= 0 && date.compare(interval[1]) <= 0); - - return -} -``` - -### Controlling the focused date - -By default, the selected date is focused when a `Calendar` first mounts. If no `value` or `defaultValue` prop is provided, then the current date is focused. However, `useCalendar` supports controlling which date is focused using the `focusedValue` and `onFocusChange` props. This also determines which month is visible. The `defaultFocusedValue` prop allows setting the initial focused date when the `Calendar` first mounts, without controlling it. - -This example focuses July 1, 2021 by default. The user may change the focused date, and the `onFocusChange` event updates the state. Clicking the button resets the focused date back to the initial value. - -```tsx example -import {CalendarDate} from '@internationalized/date'; - -function Example() { - let defaultDate = new CalendarDate(2021, 7, 1); - let [focusedDate, setFocusedDate] = React.useState(defaultDate); - - return ( -
    - - -
    - ); -} -``` - -### Disabled - -The `isDisabled` boolean prop makes the Calendar disabled. Cells cannot be focused or selected. - -```tsx example - -``` - -### Read only - -The `isReadOnly` boolean prop makes the Calendar's value immutable. Unlike `isDisabled`, the Calendar remains focusable. - -```tsx example - -``` - -### Custom first day of week - -By default, the first day of the week is automatically set based on the current locale. This can be changed by setting the `firstDayOfWeek` prop to `'sun'`, `'mon'`, `'tue'`, `'wed'`, `'thu'`, `'fri'`, or `'sat'`. - -```tsx example - -``` - -### Labeling - -An aria-label must be provided to the `Calendar` for accessibility. If it is labeled by a separate element, an `aria-labelledby` prop must be provided using the `id` of the labeling element instead. - -### Internationalization - -In order to internationalize a `Calendar`, a localized string should be passed to the `aria-label` prop. For languages that are read right-to-left (e.g. Hebrew and Arabic), keyboard navigation is automatically flipped. Ensure that your CSS accounts for this as well. Dates are automatically formatted using the current locale. - -## Advanced topics - -### Reducing bundle size - -In the example above, the function from the [@internationalized/date](../internationalized/date/) package is passed to the hook. This function receives a [calendar identifier](../internationalized/date/Calendar.html#calendar-identifiers) string, and provides instances to React Stately, which are used to implement date manipulation. - -By default, this includes [all calendar systems](../internationalized/date/Calendar.html#implementations) supported by `@internationalized/date`. However, if your application supports a more limited set of regions, or you know you will only be picking dates in a certain calendar system, you can reduce your bundle size by providing your own implementation of `createCalendar` that includes a subset of these `Calendar` implementations. - -For example, if your application only supports Gregorian dates, you could implement a `createCalendar` function like this: - -```jsx -import {useCalendarState} from '@react-stately/datepicker'; -import {useLocale} from '@react-aria/i18n'; -import {GregorianCalendar} from '@internationalized/date'; - -function createCalendar(identifier) { - switch (identifier) { - case 'gregory': - return new GregorianCalendar(); - default: - throw new Error(`Unsupported calendar ${identifier}`); - } -} - -function Calendar(props) { - let {locale} = useLocale(); - let state = useCalendarState({ - ...props, - locale, - createCalendar - }); - - // ... -} -``` - -This way, only `GregorianCalendar` is imported, and the other calendar implementations can be tree-shaken. - -See the [Calendar](../internationalized/date/Calendar.html) documentation in `@internationalized/date` to learn more about the supported calendar systems, and a list of string identifiers. diff --git a/packages/react-aria/docs/calendar/useRangeCalendar.mdx b/packages/react-aria/docs/calendar/useRangeCalendar.mdx deleted file mode 100644 index 7b5f3495c50..00000000000 --- a/packages/react-aria/docs/calendar/useRangeCalendar.mdx +++ /dev/null @@ -1,594 +0,0 @@ -{/* Copyright 2020 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 '@react-spectrum/docs'; -export default Layout; - -import docs from 'docs:@react-aria/calendar'; -import statelyDocs from 'docs:@react-stately/calendar'; -import i18nDocs from 'docs:@internationalized/date'; -import {FunctionAPI, HeaderInfo, InterfaceType, TypeContext, TypeLink, PageDescription} from '@react-spectrum/docs'; -import packageData from '@react-aria/calendar/package.json'; -import Anatomy from './rangecalendar-anatomy.svg'; -import ChevronRight from '@spectrum-icons/workflow/ChevronRight'; -import {Keyboard} from '@react-spectrum/text'; -import {ExampleCard} from '@react-spectrum/docs/src/ExampleCard'; -import tailwindExample from 'url:./tailwind.png'; - ---- -category: Date and Time -keywords: [input, form, field, date, time] ---- - -# useRangeCalendar - -{docs.exports.useRangeCalendar.description} - - - -## API - - - - - -## Features - -There is no standalone range calendar element in HTML. Two separate `` elements could be used, but this is very limited in functionality, lacking in internationalization capabilities, inconsistent between browsers, and difficult to style. `useRangeCalendar` helps achieve accessible and international range calendar components that can be styled as needed. - -* **Flexible** – Display one or more months at once, or a custom time range for use cases like a week view. Minimum and maximum values, unavailable dates, and non-contiguous selections are supported as well. -* **International** – Support for 13 calendar systems used around the world, including Gregorian, Buddhist, Islamic, Persian, and more. Locale-specific formatting, number systems, and right-to-left support are available as well. -* **Accessible** – Calendar cells can be navigated and selected using the keyboard, and localized screen reader messages are included to announce when the selection and visible date range change. -* **Touch friendly** – Date ranges can be selected by dragging over dates in the calendar using a touch screen, and all interactions are accessible using touch-based screen readers. -* **Customizable** – As with all of React Aria, the DOM structure and styling of all elements can be fully customized. - -Read our [blog post](../blog/date-and-time-pickers-for-all) for more details about the internationalization, accessibility, and user experience features implemented by `useRangeCalendar`. - -## Anatomy - - - -A range calendar consists of a grouping element containing one or more date grids (e.g. months), and a previous and next button for navigating through time. Each calendar grid consists of cells containing button elements that can be pressed and navigated to using the arrow keys to select a date range. Once a start date is selected, the user can navigate to another date using the keyboard or by hovering over it, and clicking it or pressing the Enter key commits the selected date range. - -### useRangeCalendar - -`useRangeCalendar` returns props that you should spread onto the appropriate elements: - - - - - -### useCalendarGrid - -`useCalendarGrid` returns props for an individual grid of dates, such as one month, along with a list of formatted weekday names in the current locale for use during rendering: - - - - - -### useCalendarCell - -`useCalendarCell` returns props for an individual cell, along with states and information useful during rendering: - - - - - -State is managed by the hook from `@react-stately/calendar`. The state object should be passed as an option to `useRangeCalendar`, `useCalendarGrid`, and `useCalendarCell`. - -Note that much of this anatomy is shared with [non-range calendars](../Calendar/useCalendar.html). The only difference is that `useRangeCalendarState` is used instead of `useCalendarState`, and `useRangeCalendar` is used instead of `useCalendar`. - -## Date and time values - -Dates are represented in many different ways by cultures around the world. This includes differences in calendar systems, date formatting, numbering systems, weekday and weekend rules, and much more. When building applications that support users around the world, it is important to handle these aspects correctly for each locale. - - uses the [@internationalized/date](../internationalized/date/) library to represent dates and times. This package provides a library of objects and functions to perform date and time related manipulation, queries, and conversions that work across locales and calendars. Date and time objects can be converted to and from native JavaScript `Date` objects or [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) strings. See the [documentation](../internationalized/date/), or the [examples below](#value) for more details. - - requires a `createCalendar` function to be provided, which is used to implement date manipulation across multiple calendar systems. The default implementation in `@internationalized/date` includes all supported calendar systems. While this library is quite small (8 kB minified + Brotli), you can reduce its bundle size further by providing your own implementation that includes only your supported calendars. See [below](#reducing-bundle-size) for an example. - -## Example - -A `RangeCalendar` consists of three components: the main calendar wrapper element with previous and next buttons for navigating, one or more `CalendarGrid` components to display each month, and `CalendarCell` components for each date cell. We'll go through them one by one. - -For simplicity, this example only displays a single month at a time. See the [styled examples](#styled-examples) section for more examples with multiple months, as well as other time ranges like weeks. - -```tsx example render=false export=true -import {useRangeCalendarState} from '@react-stately/calendar'; -import {useRangeCalendar} from '@react-aria/calendar'; -import {useLocale} from '@react-aria/i18n'; -import {createCalendar} from '@internationalized/date'; - -// Reuse the Button from your component library. See below for details. -import {Button} from 'your-component-library'; - -function RangeCalendar(props) { - let {locale} = useLocale(); - let state = useRangeCalendarState({ - createCalendar, - ...props, - locale - }); - - let ref = React.useRef(null); - let {calendarProps, prevButtonProps, nextButtonProps, title} = useRangeCalendar(props, state, ref); - - return ( -
    -
    -

    {title}

    - - -
    - -
    - ); -} -``` - -### CalendarGrid - -The `CalendarGrid` component will be responsible for rendering an individual month. It is a separate component so that you can render more than one month at a time if you like. It's rendered as an HTML `
    ` element in this case so that the cells line up correctly. - -**Note**: this component is the same as the `CalendarGrid` component shown in the [useRangeCalendar](../RangeCalendar/useRangeCalendar.html) docs, and you can reuse it between both `Calendar` and `RangeCalendar`. - -```tsx example render=false export=true -import {useCalendarGrid} from '@react-aria/calendar'; - -function CalendarGrid({state, ...props}) { - let {gridProps, headerProps, weekDays, weeksInMonth} = useCalendarGrid(props, state); - - return ( - - - - {weekDays.map((day, index) => - - )} - - - - {[...new Array(weeksInMonth).keys()].map(weekIndex => ( - - {state.getDatesInWeek(weekIndex).map((date, i) => ( - date ? ( - - ) : - ))} - -
    {day}
    - ))} -
    - ); -} -``` - -### CalendarCell - -Finally, the `CalendarCell` component renders an individual cell in a calendar. It consists of two elements: a `
    ` to represent the grid cell, and a `
    ` to represent a button that can be clicked to select the date. The `useCalendarCell` hook also returns the formatted date string in the current locale, as well as some information about the cell's state that can be useful for styling. See [above](#usecalendarcell) for details. - -**Note**: this component is the same as the `CalendarCell` component shown in the [useRangeCalendar](../RangeCalendar/useRangeCalendar.html) docs, and you can reuse it between both `Calendar` and `RangeCalendar`. - -```tsx example render=false export=true -import {useCalendarCell} from '@react-aria/calendar'; - -function CalendarCell({state, date}) { - let ref = React.useRef(null); - let { - cellProps, - buttonProps, - isSelected, - isOutsideVisibleRange, - isDisabled, - isUnavailable, - formattedDate - } = useCalendarCell({date}, state, ref); - - return ( -
    - -
    ` element, and React Aria takes care of adding the proper ARIA roles and event handlers to make it behave as an ARIA grid. You can use the arrow keys to navigate between cells, and the Enter key to select a date. - -The `state.getDatesInWeek` function returns the dates in each week of the month. Note that this always includes 7 values, but some of them may be null, which indicates that the date doesn't exist within the calendar system. You should render a placeholder ` - ); -} -``` - -That's it! Now we can render an example of our `RangeCalendar` component in action. - -```tsx example - -``` - -
    - Show CSS - -```css -.calendar { - width: 220px; -} - -.header { - display: flex; - align-items: center; - gap: 4px; - margin: 0 8px; -} - -.header h2 { - flex: 1; - margin: 0; -} - -.calendar table { - width: 100%; -} - -.cell { - cursor: default; - text-align: center; -} - -.selected { - background: var(--blue); - color: white; -} - -.unavailable { - color: var(--spectrum-global-color-red-600); -} - -.disabled { - color: gray; -} -``` - -
    - -### Button - -The `Button` component is used in the above example to navigate between months. It is built using the [useButton](../Button/useButton.html) hook, and can be shared with many other components. - -
    - Show code - -```tsx example export=true render=false -import {useButton} from '@react-aria/button'; - -function Button(props) { - let ref = React.useRef(null); - let {buttonProps} = useButton(props, ref); - return ; -} -``` - -
    - -## Styled examples - - - -## Usage - -The following examples show how to use the `RangeCalendar` component created in the above example. - -### Value - -A `RangeCalendar` has no selection by default. An initial, uncontrolled value can be provided to the `RangeCalendar` using the `defaultValue` prop. Alternatively, a controlled value can be provided using the `value` prop. - -Date ranges are objects with `start` and `end` properties containing date values, which are provided using objects in the [@internationalized/date](../internationalized/date/) package. This library handles correct international date manipulation across calendars, time zones, and other localization concerns. - -`useRangeCalendar` supports values with both date and time components, but only allows users to modify the dates. By default, `useRangeCalendar` will emit objects in the `onChange` event, but if a or object is passed as the `value` or `defaultValue`, values of that type will be emitted, changing only the date and preserving the time components. - -```tsx example -import {parseDate} from '@internationalized/date'; - -function Example() { - let [value, setValue] = React.useState({ - start: parseDate('2020-02-03'), - end: parseDate('2020-02-12') - }); - - return ( -
    - - -
    - ); -} -``` - -### Events - -`useRangeCalendar` accepts an `onChange` prop which is triggered whenever a date is selected by the user. The example below uses `onChange` to update a separate element with a formatted version of the date in the user's locale. This is done by converting the date to a native JavaScript `Date` object to pass to the formatter. - -```tsx example -import {getLocalTimeZone} from '@internationalized/date'; -import {useDateFormatter} from '@react-aria/i18n'; - -function Example() { - let [range, setRange] = React.useState({ - start: parseDate('2020-07-03'), - end: parseDate('2020-07-10') - }); - let formatter = useDateFormatter({dateStyle: 'long'}); - - return ( - <> - -

    - Selected date:{' '} - {formatter.formatRange( - range.start.toDate(getLocalTimeZone()), - range.end.toDate(getLocalTimeZone()) - )} -

    - - ); -} -``` - -### International calendars - -`useRangeCalendar` supports selecting dates in many calendar systems used around the world, including Gregorian, Hebrew, Indian, Islamic, Buddhist, and more. Dates are automatically displayed in the appropriate calendar system for the user's locale. The calendar system can be overridden using the [Unicode calendar locale extension](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/calendar#adding_a_calendar_in_the_locale_string), passed to the [I18nProvider](../I18nProvider) component. - -Selected dates passed to `onChange` always use the same calendar system as the `value` or `defaultValue` prop. If no `value` or `defaultValue` is provided, then dates passed to `onChange` are always in the Gregorian calendar since this is the most commonly used. This means that even though the user selects dates in their local calendar system, applications are able to deal with dates from all users consistently. - -The below example displays a `RangeCalendar` in the Hindi language, using the Indian calendar. Dates emitted from `onChange` are in the Gregorian calendar. - -```tsx example -import {I18nProvider} from '@react-aria/i18n'; - -function Example() { - let [range, setRange] = React.useState(null); - return ( - - -

    Start date: {range?.start.toString()}

    -

    End date: {range?.end.toString()}

    -
    - ); -} -``` - -### Custom calendar systems - -`RangeCalendar` also supports custom calendar systems that implement custom business rules. An example would be a fiscal year calendar that follows a [4-5-4 format](https://nrf.com/resources/4-5-4-calendar), where month ranges don't follow the usual Gregorian calendar. - -The `createCalendar` prop accepts a function that returns an instance of the interface. See the [@internationalized/date docs](../internationalized/date/Calendar.html#custom-calendars) for an example implementation. - -```tsx import -import type {AnyCalendarDate} from '@internationalized/date'; -import {CalendarDate, startOfWeek, toCalendar} from '@internationalized/date'; -``` - -```tsx example -import {GregorianCalendar} from '@internationalized/date'; - -function Example() { - return new Custom454()} />; -} - -class Custom454 extends GregorianCalendar { - // See @internationalized/date docs linked above... - ///- begin collapse -/// - weekPattern = [4, 5, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4]; - getDaysInMonth(date) { - return this.weekPattern[date.month - 1] * 7; - } - - fromJulianDay(jd: number): CalendarDate { - let gregorian = super.fromJulianDay(jd); - - let monthStart = startOfWeek(new CalendarDate(gregorian.year, 1, 1), 'en'); - for (let months = 0; months < this.weekPattern.length; months++) { - let weeksInMonth = this.weekPattern[months]; - let monthEnd = monthStart.add({weeks: weeksInMonth}); - if (monthEnd.compare(gregorian) > 0) { - let days = gregorian.compare(monthStart); - return new CalendarDate(this, monthStart.year, months + 1, days + 1); - } - monthStart = monthEnd; - } - - throw Error('Date is not in any month somehow!'); - } - - toJulianDay(date: AnyCalendarDate): number { - let monthStart = startOfWeek(new CalendarDate(date.year, 1, 1), 'en'); - for (let month = 1; month < date.month; month++) { - monthStart = monthStart.add({weeks: this.weekPattern[month - 1]}); - } - - let gregorian = monthStart.add({days: date.day - 1}); - return super.toJulianDay(gregorian); - } - - getFormattableMonth(date) { - let gregorian = toCalendar(date, new GregorianCalendar()); - return gregorian.set({month: date.month, day: 1}); - } - - isEqual(other) { - return other instanceof Custom454; - } - ///- end collapse -/// -} -``` - -### Validation - -By default, `useRangeCalendar` allows selecting any date range. The `minValue` and `maxValue` props can also be used to prevent the user from selecting dates outside a certain range. - -This example only accepts dates after today. - -```tsx example -import {today} from '@internationalized/date'; - - -``` - -### Unavailable dates - -`useRangeCalendar` supports marking certain dates as _unavailable_. These dates remain focusable with the keyboard so that navigation is consistent, but cannot be selected by the user. In this example, they are displayed in red. The `isDateUnavailable` prop accepts a callback that is called to evaluate whether each visible date is unavailable. - -Note that by default, users may not select non-contiguous ranges, i.e. ranges that contain unavailable dates within them. Once a start date is selected, enabled dates will be restricted to subsequent dates until an unavailable date is hit. See [below](#non-contiguous-ranges) for an example of how to allow non-contiguous ranges. - -This example includes multiple unavailable date ranges, e.g. dates when a rental house is not available. The `minValue` prop is also used to prevent selecting dates before today. - -```tsx example -import {today} from '@internationalized/date'; - -function Example() { - let now = today(getLocalTimeZone()); - let disabledRanges = [ - [now, now.add({days: 5})], - [now.add({days: 14}), now.add({days: 16})], - [now.add({days: 23}), now.add({days: 24})], - ]; - - let isDateUnavailable = (date) => disabledRanges.some((interval) => date.compare(interval[0]) >= 0 && date.compare(interval[1]) <= 0); - - return -} -``` - -### Non-contiguous ranges - -The `allowsNonContiguousRanges` prop enables a range to be selected even if there are unavailable dates in the middle. The value emitted in the `onChange` event will still be a single range with a `start` and `end` property, but unavailable dates will not be displayed as selected. It is up to applications to split the full selected range into multiple as needed for business logic. - -This example prevents selecting weekends, but allows selecting ranges that span multiple weeks. - -```tsx example -import {isWeekend} from '@internationalized/date'; - -function Example() { - let {locale} = useLocale(); - - return isWeekend(date, locale)} allowsNonContiguousRanges /> -} -``` - -### Controlling the focused date - -By default, the first selected date is focused when a `RangeCalendar` first mounts. If no `value` or `defaultValue` prop is provided, then the current date is focused. However, `useRangeCalendar` supports controlling which date is focused using the `focusedValue` and `onFocusChange` props. This also determines which month is visible. The `defaultFocusedValue` prop allows setting the initial focused date when the `RangeCalendar` first mounts, without controlling it. - -This example focuses July 1, 2021 by default. The user may change the focused date, and the `onFocusChange` event updates the state. Clicking the button resets the focused date back to the initial value. - -```tsx example -import {CalendarDate} from '@internationalized/date'; - -function Example() { - let defaultDate = new CalendarDate(2021, 7, 1); - let [focusedDate, setFocusedDate] = React.useState(defaultDate); - - return ( -
    - - -
    - ); -} -``` - -### Disabled - -The `isDisabled` boolean prop makes the RangeCalendar disabled. Cells cannot be focused or selected. - -```tsx example - -``` - -### Read only - -The `isReadOnly` boolean prop makes the RangeCalendar's value immutable. Unlike `isDisabled`, the RangeCalendar remains focusable. - -```tsx example - -``` - -### Custom first day of week - -By default, the first day of the week is automatically set based on the current locale. This can be changed by setting the `firstDayOfWeek` prop to `'sun'`, `'mon'`, `'tue'`, `'wed'`, `'thu'`, `'fri'`, or `'sat'`. - -```tsx example - -``` - -### Labeling - -An aria-label must be provided to the `RangeCalendar` for accessibility. If it is labeled by a separate element, an `aria-labelledby` prop must be provided using the `id` of the labeling element instead. - -### Internationalization - -In order to internationalize a `RangeCalendar`, a localized string should be passed to the `aria-label` prop. For languages that are read right-to-left (e.g. Hebrew and Arabic), keyboard navigation is automatically flipped. Ensure that your CSS accounts for this as well. Dates are automatically formatted using the current locale. - -## Advanced topics - -### Reducing bundle size - -In the example above, the function from the [@internationalized/date](../internationalized/date/) package is passed to the hook. This function receives a [calendar identifier](../internationalized/date/Calendar.html#calendar-identifiers) string, and provides instances to React Stately, which are used to implement date manipulation. - -By default, this includes [all calendar systems](../internationalized/date/Calendar.html#implementations) supported by `@internationalized/date`. However, if your application supports a more limited set of regions, or you know you will only be picking dates in a certain calendar system, you can reduce your bundle size by providing your own implementation of `createCalendar` that includes a subset of these `Calendar` implementations. - -For example, if your application only supports Gregorian dates, you could implement a `createCalendar` function like this: - -```jsx -import {useRangeCalendarState} from '@react-stately/datepicker'; -import {useLocale} from '@react-aria/i18n'; -import {GregorianCalendar} from '@internationalized/date'; - -function createCalendar(identifier) { - switch (identifier) { - case 'gregory': - return new GregorianCalendar(); - default: - throw new Error(`Unsupported calendar ${identifier}`); - } -} - -function RangeCalendar(props) { - let {locale} = useLocale(); - let state = useRangeCalendarState({ - ...props, - locale, - createCalendar - }); - - // ... -} -``` - -This way, only `GregorianCalendar` is imported, and the other calendar implementations can be tree-shaken. - -See the [Calendar](../internationalized/date/Calendar.html) documentation in `@internationalized/date` to learn more about the supported calendar systems, and a list of string identifiers. diff --git a/packages/react-aria/docs/checkbox/buttongroup-example.png b/packages/react-aria/docs/checkbox/buttongroup-example.png deleted file mode 100644 index 91b6e9a8df2..00000000000 Binary files a/packages/react-aria/docs/checkbox/buttongroup-example.png and /dev/null differ diff --git a/packages/react-aria/docs/checkbox/tailwind-example.png b/packages/react-aria/docs/checkbox/tailwind-example.png deleted file mode 100644 index 533cebc207b..00000000000 Binary files a/packages/react-aria/docs/checkbox/tailwind-example.png and /dev/null differ diff --git a/packages/react-aria/docs/checkbox/useCheckbox.mdx b/packages/react-aria/docs/checkbox/useCheckbox.mdx deleted file mode 100644 index 080f82e8971..00000000000 --- a/packages/react-aria/docs/checkbox/useCheckbox.mdx +++ /dev/null @@ -1,254 +0,0 @@ -{/* Copyright 2020 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 '@react-spectrum/docs'; -export default Layout; - -import docs from 'docs:@react-aria/checkbox'; -import hiddenDocs from 'docs:@react-aria/visually-hidden'; -import focusDocs from 'docs:@react-aria/focus'; -import statelyDocs from 'docs:@react-stately/toggle'; -import {HeaderInfo, FunctionAPI, TypeContext, InterfaceType, TypeLink, PageDescription} from '@react-spectrum/docs'; -import {Keyboard} from '@react-spectrum/text'; -import packageData from '@react-aria/checkbox/package.json'; -import Anatomy from './checkbox-anatomy.svg'; -import {ExampleCard} from '@react-spectrum/docs/src/ExampleCard'; -import tailwindPreview from 'url:./tailwind-example.png'; - ---- -category: Forms -keywords: [checkbox, input, aria] ---- - -# useCheckbox - -{docs.exports.useCheckbox.description} - - - -## API - - - -## Features - -Checkboxes can be built with the [<input>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) -HTML element, but this can be difficult to style. `useCheckbox` helps achieve accessible checkboxes -that can be styled as needed. - -* Built with a native HTML `` element, which can be optionally visually - hidden to allow custom styling -* Full support for browser features like form autofill -* Keyboard focus management and cross browser normalization -* Labeling support for assistive technology -* Indeterminate state support - -## Anatomy - - - -A checkbox consists of a visual selection indicator and a label. Checkboxes support three -selection states: checked, unchecked, and indeterminate. Users may click or touch a checkbox -to toggle the selection state, or use the Tab key to navigate to it and the Space key to toggle it. - -`useCheckbox` returns props to be spread onto its input element: - - - - - -Selection state is managed by the -hook in `@react-stately/toggle`. The state object should be passed as an option to `useCheckbox`. - -In most cases, checkboxes should have a visual label. If the checkbox does not have a visible label, -an `aria-label` or `aria-labelledby` prop must be passed instead to identify the element to assistive -technology. - -## Example - -```tsx example -import {useCheckbox} from '@react-aria/checkbox'; -import {useToggleState} from '@react-stately/toggle'; - -function Checkbox(props) { - let {children} = props; - let state = useToggleState(props); - let ref = React.useRef(null); - let {inputProps, labelProps} = useCheckbox(props, state, ref); - - return ( - - ); -} - -Unsubscribe -``` - -## Styling - -To build a custom styled checkbox, you can make the native input element visually hidden. -This is possible using the <> -utility component from `@react-aria/visually-hidden`. It is still in the DOM and accessible to -assistive technology, but invisible. This example uses SVG to build the visual checkbox, -which is hidden from screen readers with `aria-hidden`. - -For keyboard accessibility, a focus ring is important to indicate which element has keyboard focus. -This is implemented with the -hook from `@react-aria/focus`. When `isFocusVisible` is true, an extra SVG element is -rendered to indicate focus. The focus ring is only visible when the user is interacting -with a keyboard, not with a mouse or touch. - -```tsx example export=true -import {VisuallyHidden} from '@react-aria/visually-hidden'; -import {useFocusRing} from '@react-aria/focus'; -import {mergeProps} from '@react-aria/utils'; - -function Checkbox(props) { - let state = useToggleState(props); - let ref = React.useRef(null); - let {inputProps, labelProps} = useCheckbox(props, state, ref); - let {isFocusVisible, focusProps} = useFocusRing(); - let isSelected = state.isSelected && !props.isIndeterminate; - - return ( - - ); -} - -Unsubscribe -``` - -## Styled examples - - - -## Usage - -The following examples show how to use the `Checkbox` component created in the above example. - -### Default value - -Checkboxes are not selected by default. The `defaultSelected` prop can be used to set the default state. - -```tsx example -Subscribe -``` - -### Controlled value - -The `isSelected` prop can be used to make the selected state controlled. The `onChange` event is fired when the user presses the checkbox, and receives the new value. - -```tsx example -function Example() { - let [selected, setSelection] = React.useState(false); - - return ( - <> - - Subscribe - -

    {`You are ${selected ? 'subscribed' : 'unsubscribed'}`}

    - - ); - } -``` - -### Indeterminate - -A Checkbox can be in an indeterminate state, controlled using the `isIndeterminate` prop. -This overrides the appearance of the Checkbox, whether selection is controlled or uncontrolled. -The Checkbox will visually remain indeterminate until the `isIndeterminate` prop is set to false, regardless of user interaction. - -```tsx example -Subscribe -``` - -### Disabled - -Checkboxes can be disabled using the `isDisabled` prop. - -```tsx example -Subscribe -``` - -### Read only - -The `isReadOnly` prop makes the selection immutable. Unlike `isDisabled`, the Checkbox remains focusable. -See the [MDN docs](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/readonly) for more information. - -```tsx example -Agree -``` - - -### HTML forms - -Checkbox supports the `name` and `value` props for integration with HTML forms. - -```tsx example -Subscribe -``` - -## Internationalization - -### RTL - -In right-to-left languages, the checkbox should be mirrored. The checkbox should be placed on the right -side of the label. Ensure that your CSS accounts for this. diff --git a/packages/react-aria/docs/checkbox/useCheckboxGroup.mdx b/packages/react-aria/docs/checkbox/useCheckboxGroup.mdx deleted file mode 100644 index dbf7c32fa71..00000000000 --- a/packages/react-aria/docs/checkbox/useCheckboxGroup.mdx +++ /dev/null @@ -1,303 +0,0 @@ -{/* Copyright 2020 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 '@react-spectrum/docs'; -export default Layout; - -import docs from 'docs:@react-aria/checkbox'; -import hiddenDocs from 'docs:@react-aria/visually-hidden'; -import focusDocs from 'docs:@react-aria/focus'; -import statelyDocs from 'docs:@react-stately/checkbox'; -import {HeaderInfo, FunctionAPI, TypeContext, InterfaceType, TypeLink, PageDescription} from '@react-spectrum/docs'; -import {Keyboard} from '@react-spectrum/text'; -import packageData from '@react-aria/checkbox/package.json'; -import Anatomy from './checkboxgroup-anatomy.svg'; -import {ExampleCard} from '@react-spectrum/docs/src/ExampleCard'; -import buttongroupPreview from 'url:./buttongroup-example.png'; - ---- -category: Forms -keywords: [checkbox, input, aria] -after_version: 3.1.0 ---- - -# useCheckboxGroup - -{docs.exports.useCheckboxGroup.description} - - - -## API - - - - -## Features - -Checkbox groups can be built in HTML with the -[<fieldset>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/fieldset) -and [<input>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) elements, -however these can be difficult to style. `useCheckboxGroup` and `useCheckboxGroupItem` help achieve accessible -checkbox groups that can be styled as needed. - -* Checkbox groups are exposed to assistive technology via ARIA -* Each checkbox is built with a native HTML `` element, which can be optionally visually - hidden to allow custom styling -* Full support for browser features like form autofill and validation -* Keyboard focus management and cross browser normalization -* Group and checkbox labeling support for assistive technology - -## Anatomy - - - -A checkbox group consists of a set of checkboxes, and a label. Each checkbox -includes a label and a visual selection indicator. Zero or more checkboxes -within the group can be selected at a time. Users may click or touch a checkbox -to select it, or use the Tab key to navigate to it -and the Space key to toggle it. - -`useCheckboxGroup` returns props for the group and its label, which you should spread -onto the appropriate element: - - - - - -`useCheckboxGroupItem` returns props for an individual checkbox: - - - - - -Selection state is managed by the -hook in `@react-stately/checkbox`. The state object should be passed as an option to `useCheckboxGroup` -and `useCheckboxGroupItem`. - -Individual checkboxes must have a visual label. If the checkbox group does not have a visible label, -an `aria-label` or `aria-labelledby` prop must be passed instead to identify the element to assistive -technology. - -**Note:** `useCheckboxGroupItem` should only be used when it is contained within a checkbox group. For a -standalone checkbox, use the [useCheckbox](../Checkbox/useCheckbox.html) hook instead. - -## Example - -This example uses native input elements for the checkboxes, and React context to share state from the group -to each checkbox. An HTML `
    ` element in this case so that the cells line up correctly. - -**Note**: this component is the same as the `CalendarGrid` component shown in the [useCalendar](../Calendar/useCalendar.html) docs, and you can reuse it between both `Calendar` and `RangeCalendar`. - -```tsx example render=false export=true -import {useCalendarGrid} from '@react-aria/calendar'; - -function CalendarGrid({state, ...props}) { - let {gridProps, headerProps, weekDays, weeksInMonth} = useCalendarGrid(props, state); - - return ( - - - - {weekDays.map((day, index) => - - )} - - - - {[...new Array(weeksInMonth).keys()].map(weekIndex => ( - - {state.getDatesInWeek(weekIndex).map((date, i) => ( - date ? ( - - ) : - ))} - -
    {day}
    - ))} -
    - ); -} -``` - -### CalendarCell - -Finally, the `CalendarCell` component renders an individual cell in a calendar. It consists of two elements: a `
    ` to represent the grid cell, and a `
    ` to represent a button that can be clicked to select the date. The `useCalendarCell` hook also returns the formatted date string in the current locale, as well as some information about the cell's state that can be useful for styling. See [above](#usecalendarcell) for details. - -**Note**: this component is the same as the `CalendarCell` component shown in the [useCalendar](../Calendar/useCalendar.html) docs, and you can reuse it between both `Calendar` and `RangeCalendar`. - -```tsx example render=false export=true -import {useCalendarCell} from '@react-aria/calendar'; - -function CalendarCell({state, date}) { - let ref = React.useRef(null); - let { - cellProps, - buttonProps, - isSelected, - isOutsideVisibleRange, - isDisabled, - isUnavailable, - formattedDate - } = useCalendarCell({date}, state, ref); - - return ( -
    - -
    - - - {weekDays.map((day, index) => - - )} - - - - {[...new Array(weeksInMonth).keys()].map(weekIndex => ( - - {state.getDatesInWeek(weekIndex).map((date, i) => ( - date ? ( - - ) : - ))} - -
    {day}
    - ))} -
    - ); -} - -function CalendarCell({state, date}) { - let ref = React.useRef(null); - let { - cellProps, - buttonProps, - isSelected, - isOutsideVisibleRange, - isDisabled, - isUnavailable, - formattedDate - } = useCalendarCell({date}, state, ref); - - return ( - - - - ); -} -``` - -
    - Show CSS - -```css -.calendar { - width: 220px; -} - -.header { - display: flex; - align-items: center; - gap: 4px; - margin: 0 8px; -} - -.header h2 { - flex: 1; - margin: 0; -} - -.calendar table { - width: 100%; -} - -.cell { - cursor: default; - text-align: center; -} - -.selected { - background: var(--blue); - color: white; -} - -.unavailable { - color: var(--spectrum-global-color-red-600); -} - -.disabled { - color: gray; -} -``` - -
    - - - -## Styled examples - - - - - -## Usage - -The following examples show how to use the `DatePicker` component created in the above example. - -### Value - -A `DatePicker` displays a placeholder by default. An initial, uncontrolled value can be provided to the `DatePicker` using the `defaultValue` prop. Alternatively, a controlled value can be provided using the `value` prop. - -Date values are provided using objects in the [@internationalized/date](../internationalized/date/) package. This library handles correct international date manipulation across calendars, time zones, and other localization concerns. `useDatePicker` supports values of the following types: - -* – a date without any time components. May be parsed from a string representation using the function. Use this type to represent dates where the time is not important, such as a birthday or an all day calendar event. -* – a date with a time, but not in any specific time zone. May be parsed from a string representation using the function. Use this type to represent times that occur at the same local time regardless of the time zone, such as the time of New Years Eve fireworks which always occur at midnight. Most times are better stored as a `ZonedDateTime`. -* – a date with a time in a specific time zone. May be parsed from a string representation using the , , or functions. Use this type to represent an exact moment in time at a particular location on Earth. - -```tsx example -import {parseDate} from '@internationalized/date'; - -function Example() { - let [value, setValue] = React.useState(parseDate('2020-02-03')); - - return ( - <> - - - - ); -} -``` - -### Events - -`useDatePicker` accepts an `onChange` prop which is triggered whenever the date is edited by the user. The example below uses `onChange` to update a separate element with a formatted version of the date in the user's locale and local time zone. This is done by converting the date to a native JavaScript `Date` object to pass to the formatter. - -```tsx example -import {getLocalTimeZone} from '@internationalized/date'; -import {useDateFormatter} from '@react-aria/i18n'; - -function Example() { - let [date, setDate] = React.useState(parseDate('1985-07-03')); - let formatter = useDateFormatter({dateStyle: 'full'}); - - return ( - <> - -

    Selected date: {date ? formatter.format(date.toDate(getLocalTimeZone())) : '--'}

    - - ); -} -``` - -### Time zones - -`useDatePicker` is time zone aware when a object is provided as the value. In this case, the time zone abbreviation is displayed, and time zone concerns such as daylight saving time are taken into account when the value is manipulated. - -In most cases, your data will come from and be sent to a server as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) formatted string. [@internationalized/date](../internationalized/date/) includes functions for parsing strings in multiple formats into objects. Which format you use will depend on what information you need to store. - -* – This function parses a date with an explicit time zone and optional UTC offset attached (e.g. `"2021-11-07T00:45[America/Los_Angeles]"` or `"2021-11-07T00:45-07:00[America/Los_Angeles]"`). This format preserves the maximum amount of information. If the exact local time and time zone that a user selected is important, use this format. Storing the time zone and offset that was selected rather than converting to UTC ensures that the local time is correct regardless of daylight saving rule changes (e.g. if a locale abolishes DST). Examples where this applies include calendar events, reminders, and other times that occur in a particular location. -* – This function parses an absolute date and time that occurs at the same instant at all locations on Earth. It can be represented in UTC (e.g. `"2021-11-07T07:45:00Z"`), or stored with a particular offset (e.g. `"2021-11-07T07:45:00-07:00"`). A time zone identifier, e.g. `America/Los_Angeles`, must be passed, and the result will be converted into that time zone. Absolute times are the best way to represent events that occurred in the past, or future events where an exact time is needed, regardless of time zone. -* – This function parses an absolute date and time into the current user's local time zone. It is a shortcut for `parseAbsolute`, and accepts the same formats. - -```tsx example -import {parseZonedDateTime} from '@internationalized/date'; - - -``` - -`useDatePicker` displays times in the time zone included in the `ZonedDateTime` object. The above example is always displayed in Pacific Standard Time because the `America/Los_Angeles` time zone identifier is provided. [@internationalized/date](../internationalized/date/) includes functions for converting dates between time zones, or parsing a date directly into a specific time zone or the user's local time zone, as shown below. - -```tsx example -import {parseAbsoluteToLocal} from '@internationalized/date'; - - -``` - -### Granularity - -The `granularity` prop allows you to control the smallest unit that is displayed by `useDatePicker`. By default, `CalendarDate` values are displayed with `"day"` granularity (year, month, and day), and `CalendarDateTime` and `ZonedDateTime` values are displayed with `"minute"` granularity. More granular time values can be displayed by setting the `granularity` prop to `"second"`. - -In addition, when a value with a time is provided but you wish to only display the date, you can set the granularity to `"day"`. This has no effect on the actual value (it still has a time component), only on what fields are displayed. In the following example, two DatePickers are synchronized with the same value, but display different granularities. - -```tsx example -function Example() { - let [date, setDate] = React.useState(parseAbsoluteToLocal('2021-04-07T18:45:22Z')); - - return ( - <> - - - - ); -} -``` - -If no `value` or `defaultValue` prop is passed, then the `granularity` prop also affects which type of value is emitted from the `onChange` event. Note that by default, time values will not have a time zone because none was supplied. You can override this by setting the `placeholderValue` prop explicitly. Values emitted from `onChange` will use the time zone of the placeholder value. - -```tsx example -import {now} from '@internationalized/date'; - - - -``` - -### International calendars - -`useDatePicker` supports selecting dates in many calendar systems used around the world, including Gregorian, Hebrew, Indian, Islamic, Buddhist, and more. Dates are automatically displayed in the appropriate calendar system for the user's locale. The calendar system can be overridden using the [Unicode calendar locale extension](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/calendar#adding_a_calendar_in_the_locale_string), passed to the [I18nProvider](../I18nProvider) component. - -Selected dates passed to `onChange` always use the same calendar system as the `value` or `defaultValue` prop. If no `value` or `defaultValue` is provided, then dates passed to `onChange` are always in the Gregorian calendar since this is the most commonly used. This means that even though the user selects dates in their local calendar system, applications are able to deal with dates from all users consistently. - -The below example displays a `DatePicker` in the Hindi language, using the Indian calendar. Dates emitted from `onChange` are in the Gregorian calendar. - -```tsx example -import {I18nProvider} from '@react-aria/i18n'; - -function Example() { - let [date, setDate] = React.useState(null); - return ( - - -

    Selected date: {date?.toString()}

    -
    - ); -} -``` - -### Minimum and maximum values - -The `minValue` and `maxValue` props can also be used to perform builtin validation. This prevents the user from selecting dates outside the valid range in the calendar, and marks the date field as invalid using ARIA. You should implement a visual indication that the date picker is invalid as well. - -This example only accepts dates after today. - -```tsx example -import {today} from '@internationalized/date'; - - -``` - -### Unavailable dates - -`useDatePicker` supports marking certain dates as _unavailable_. These dates remain focusable with the keyboard in the calendar so that navigation is consistent, but cannot be selected by the user. When an unavailable date is entered into the date field, it is marked as invalid. The `isDateUnavailable` prop accepts a callback that is called to evaluate whether each visible date is unavailable. - -This example includes multiple unavailable date ranges, e.g. dates when no appointments are available. In addition, all weekends are unavailable. The `minValue` prop is also used to prevent selecting dates before today. - -```tsx example -import {today, isWeekend} from '@internationalized/date'; -import {useLocale} from '@react-aria/i18n'; - -function Example() { - let now = today(getLocalTimeZone()); - let disabledRanges = [ - [now, now.add({days: 5})], - [now.add({days: 14}), now.add({days: 16})], - [now.add({days: 23}), now.add({days: 24})], - ]; - - let {locale} = useLocale(); - let isDateUnavailable = (date) => isWeekend(date, locale) || disabledRanges.some((interval) => date.compare(interval[0]) >= 0 && date.compare(interval[1]) <= 0); - - return -} -``` - -### Placeholder value - -When no value is set, a placeholder is shown. The format of the placeholder is influenced by the `granularity` and `placeholderValue` props. `placeholderValue` also controls the default values of each segment when the user first interacts with them, e.g. using the up and down arrow keys, as well as the default month shown in the calendar popover. By default, the `placeholderValue` is the current date at midnight, but you can set it to a more appropriate value if needed. - -```tsx example -import {CalendarDate} from '@internationalized/date'; - - -``` - -### Hide time zone - -When a `ZonedDateTime` object is provided as the value to `useDatePicker`, the time zone abbreviation is displayed by default. However, if this is displayed elsewhere or implicit based on the usecase, it can be hidden using the `hideTimeZone` option. - -```tsx example - -``` - -### Hour cycle - -By default, `useDatePicker` displays times in either 12 or 24 hour hour format depending on the user's locale. However, this can be overridden using the `hourCycle` prop if needed for a specific usecase. This example forces `useDatePicker` to use 24-hour time, regardless of the locale. - -```tsx example - -``` - -### Custom first day of week - -By default, the first day of the week is automatically set based on the current locale. This can be changed by setting the `firstDayOfWeek` prop to `'sun'`, `'mon'`, `'tue'`, `'wed'`, `'thu'`, `'fri'`, or `'sat'`. - -```tsx example - -``` \ No newline at end of file diff --git a/packages/react-aria/docs/datepicker/useDateRangePicker.mdx b/packages/react-aria/docs/datepicker/useDateRangePicker.mdx deleted file mode 100644 index 6e0fa84aec9..00000000000 --- a/packages/react-aria/docs/datepicker/useDateRangePicker.mdx +++ /dev/null @@ -1,765 +0,0 @@ -{/* Copyright 2020 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 '@react-spectrum/docs'; -export default Layout; - -import docs from 'docs:@react-aria/datepicker'; -import statelyDocs from 'docs:@react-stately/datepicker'; -import i18nDocs from 'docs:@internationalized/date'; -import {FunctionAPI, HeaderInfo, InterfaceType, TypeContext, TypeLink, PageDescription} from '@react-spectrum/docs'; -import packageData from '@react-aria/datepicker/package.json'; -import Anatomy from './daterangepicker-anatomy.svg'; -import ChevronRight from '@spectrum-icons/workflow/ChevronRight'; -import {ExampleCard} from '@react-spectrum/docs/src/ExampleCard'; -import tailwindExample from 'url:./DateRangePicker-tailwind.png'; -import chakraExample from 'url:./DateRangePicker-chakra.png'; - ---- -category: Date and Time -keywords: [input, form, field, date, time] ---- - -# useDateRangePicker - -{docs.exports.useDateRangePicker.description} - - - -## API - - - -## Features - -A date range picker can be built using two separate `` elements, but this is very limited in functionality, lacking in internationalization capabilities, inconsistent between browsers, and difficult to style. `useDateRangePicker` helps achieve accessible and international date and time range pickers that can be styled as needed. - -* **Dates and times** – Support for dates and times with configurable granularity. -* **International** – Support for 13 calendar systems used around the world, including Gregorian, Buddhist, Islamic, Persian, and more. Locale-specific formatting, number systems, hour cycles, and right-to-left support are available as well. -* **Time zone aware** – Dates and times can optionally include a time zone. All modifications follow time zone rules such as daylight saving time. -* **Accessible** – Each date and time unit is displayed as an individually focusable and editable segment, which allows users an easy way to edit dates using the keyboard, in any date format and locale. Users can also open a calendar popover to select date ranges in a standard month grid. Localized screen reader messages are included to announce when the selection and visible date range change. -* **Touch friendly** – Date segments are editable using an easy to use numeric keypad, date ranges can be selected by dragging over dates in the calendar using a touch screen, and all interactions are accessible using touch-based screen readers. -* **Customizable** – As with all of React Aria, the DOM structure and styling of all elements can be fully customized. - -Read our [blog post](../blog/date-and-time-pickers-for-all) for more details about the internationalization, accessibility, and user experience features implemented by `useDateRangePicker`. - -## Anatomy - - - -A date range picker consists of a label, and group containing two [date fields](../DateField/useDateField.html) and a button. Clicking the button opens a popup containing a [range calendar](../RangeCalendar/useRangeCalendar.html). The date fields include segments representing each unit of a date and time (e.g. years, months, days, etc.), each of which is individually focusable and editable using the keyboard. The calendar popup offers a more visual way of choosing a date range. - -`useDateRangePicker` also supports optional description and error message elements, which can be used -to provide more context about the field, and any validation messages. These are linked with the -input via the `aria-describedby` attribute. - -`useDateRangePicker` returns props that you should spread onto the appropriate elements: - - - - - -State is managed by the hook from `@react-stately/datepicker`. The state object should be passed as an argument to `useDateRangePicker`. - -If the date range picker does not have a visible label, an `aria-label` or `aria-labelledby` prop must be passed instead to -identify it to assistive technology. - -## Date and time values - -Dates and times are represented in many different ways by cultures around the world. This includes differences in calendar systems, time zones, daylight saving time rules, date and time formatting, weekday and weekend rules, and much more. When building applications that support users around the world, it is important to handle these aspects correctly for each locale. - - uses the [@internationalized/date](../internationalized/date/) library to represent dates and times. This package provides a library of objects and functions to perform date and time related manipulation, queries, and conversions that work across locales and calendars. Date and time objects can be converted to and from native JavaScript `Date` objects or [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) strings. See the [documentation](../internationalized/date/), or the [examples below](#value) for more details. - -## Example - -A `DateRangePicker` composes several other components to produce a composite element that can be used to enter date ranges with a keyboard, or select them on a calendar. The `DateField`, `Popover`, `Calendar`, and `Button` components used in this example are independent and can be used separately from the `DateRangePicker`. The code is available below, and documentation is available on the corresponding pages. - -```tsx example export=true -import {useDateRangePickerState} from '@react-stately/datepicker'; -import {useDateRangePicker} from '@react-aria/datepicker'; - -// Reuse the DateField, Popover, Dialog, RangeCalendar, and Button from your component library. -import {DateField, Popover, Dialog, RangeCalendar, Button} from 'your-component-library'; - -function DateRangePicker(props) { - let state = useDateRangePickerState(props); - let ref = React.useRef(null); - let {labelProps, groupProps, startFieldProps, endFieldProps, buttonProps, dialogProps, calendarProps} = useDateRangePicker(props, state, ref); - - return ( -
    - {props.label} -
    -
    - - - - {state.isInvalid && - - } -
    - -
    - {state.isOpen && - - - - - - } -
    - ); -} - - -``` - -### Button - -The `Button` component is used in the above example to trigger the calendar popover. It is built using the [useButton](../Button/useButton.html) hook, and can be shared with many other components. - -
    - Show code - -```tsx example export=true render=false -import {useButton} from '@react-aria/button'; - -function Button(props) { - let ref = React.useRef(null); - let {buttonProps} = useButton(props, ref); - return ; -} -``` - -
    - -### DateField - -The `DateField` component implements the keyboard editable inputs used in a `DateRangePicker`. It can also be used standalone, or within a single date picker. See [useDateField](../DateField/useDateField.html) for more examples and documentation. - -
    - Show code - -```tsx example export=true render=false -import {useLocale} from '@react-aria/i18n'; -import {useDateFieldState} from '@react-stately/datepicker'; -import {useDateField, useDateSegment} from '@react-aria/datepicker'; - -function DateField(props) { - let {locale} = useLocale(); - let state = useDateFieldState({ - ...props, - locale, - createCalendar - }); - - let ref = React.useRef(null); - let {labelProps, fieldProps} = useDateField(props, state, ref); - - return ( -
    - {props.label} -
    - {state.segments.map((segment, i) => - - )} -
    -
    - ); -} - -function DateSegment({segment, state}) { - let ref = React.useRef(null); - let {segmentProps} = useDateSegment(segment, state, ref); - - return ( -
    - {segment.text} -
    - ); -} -``` - -
    - Show CSS - -```css -.wrapper { - display: flex; - flex-direction: column; - align-items: flex-start; -} - -.field { - display: inline-flex; - padding: 2px 4px; - border-radius: 2px; - border: 1px solid var(--gray); - background: var(--spectrum-global-color-gray-50); - max-width: 100%; - overflow: auto; -} - -.field:focus-within { - border-color: var(--blue); -} - -.field .field { - all: initial; - display: inline-flex; - color: inherit; -} - -.segment { - padding: 0 2px; - font-variant-numeric: tabular-nums; - text-align: end; -} - -.segment.placeholder { - color: var(--spectrum-gray-600); -} - -.segment:focus { - color: white; - background: var(--blue); - outline: none; - border-radius: 2px; -} -``` - -
    - -
    - -### Popover - -The `Popover` component is used to contain the popup calendar for the `DateRangePicker`. -It can be shared between many other components, including [Select](../Select/useSelect.html), -[Menu](../Menu/useMenu.html), and others. -See [usePopover](../Popover/usePopover.html) for more examples of popovers. - -
    - Show code - -```tsx example export=true render=false -import type {AriaPopoverProps} from 'react-aria'; -import type {OverlayTriggerState} from 'react-stately'; -import {usePopover, Overlay, DismissButton} from '@react-aria/overlays'; - -interface PopoverProps extends Omit { - children: React.ReactNode, - state: OverlayTriggerState -} - -function Popover({children, state, ...props}: PopoverProps) { - let popoverRef = React.useRef(null); - let {popoverProps, underlayProps} = usePopover({ - ...props, - popoverRef - }, state); - - return ( - -
    -
    - - {children} - -
    - - ); -} -``` - -
    - -### Dialog - -The `Dialog` component is rendered within the `Popover` component. It is built using the [useDialog](../Modal/useDialog.html) hook, and can be shared with many other components. - -
    - Show code - -```tsx example export=true render=false -import type {AriaDialogProps} from '@react-aria/dialog'; -import {useDialog} from '@react-aria/dialog'; - -interface DialogProps extends AriaDialogProps { - title?: React.ReactNode, - children: React.ReactNode -} - -function Dialog({title, children, ...props}: DialogProps) { - let ref = React.useRef(null); - let {dialogProps, titleProps} = useDialog(props, ref); - - return ( -
    - {title && -

    - {title} -

    - } - {children} -
    - ); -} -``` - -
    - -### RangeCalendar - -The `RangeCalendar` component implements the month grid shown within the `DateRangePicker` popover. It can also be used standalone, or within other components. See [useRangeCalendar](../RangeCalendar/useRangeCalendar.html) for more examples and documentation. - -
    - Show code - -```tsx example export=true render=false -import {useRangeCalendarState} from '@react-stately/calendar'; -import {useRangeCalendar, useCalendarGrid, useCalendarCell} from '@react-aria/calendar'; -import {createCalendar, getWeeksInMonth} from '@internationalized/date'; - -function RangeCalendar(props) { - let {locale} = useLocale(); - let state = useRangeCalendarState({ - ...props, - locale, - createCalendar - }); - - let ref = React.useRef(null); - let {calendarProps, prevButtonProps, nextButtonProps, title} = useRangeCalendar(props, state, ref); - - return ( -
    -
    -

    {title}

    - - -
    - -
    - ); -} - -function CalendarGrid({state, ...props}) { - let {locale} = useLocale(); - let {gridProps, headerProps, weekDays} = useCalendarGrid(props, state); - - // Get the number of weeks in the month so we can render the proper number of rows. - let weeksInMonth = getWeeksInMonth(state.visibleRange.start, locale, props.firstDayOfWeek); - - return ( - - - - {weekDays.map((day, index) => - - )} - - - - {[...new Array(weeksInMonth).keys()].map(weekIndex => ( - - {state.getDatesInWeek(weekIndex).map((date, i) => ( - date ? ( - - ) : - ))} - -
    {day}
    - ))} -
    - ); -} - -function CalendarCell({state, date}) { - let ref = React.useRef(null); - let { - cellProps, - buttonProps, - isSelected, - isOutsideVisibleRange, - isDisabled, - isUnavailable, - formattedDate - } = useCalendarCell({date}, state, ref); - - return ( - - - - ); -} -``` - -
    - Show CSS - -```css -.calendar { - width: 220px; -} - -.header { - display: flex; - align-items: center; - gap: 4px; - margin: 0 8px; -} - -.header h2 { - flex: 1; - margin: 0; -} - -.calendar table { - width: 100%; -} - -.cell { - cursor: default; - text-align: center; -} - -.selected { - background: var(--blue); - color: white; -} - -.unavailable { - color: var(--spectrum-global-color-red-600); -} - -.disabled { - color: gray; -} -``` - -
    - -
    - -## Styled examples - - - - - -## Usage - -The following examples show how to use the `DateRangePicker` component created in the above example. - -### Value - -A `DateRangePicker` displays a placeholder by default. An initial, uncontrolled value can be provided to the `DateRangePicker` using the `defaultValue` prop. Alternatively, a controlled value can be provided using the `value` prop. - -Date ranges are objects with `start` and `end` properties containing date values, which are provided using objects in the [@internationalized/date](../internationalized/date/) package. This library handles correct international date manipulation across calendars, time zones, and other localization concerns. `useDateRangePicker` supports values of the following types: - -* – a date without any time components. May be parsed from a string representation using the function. Use this type to represent dates where the time is not important, such as a birthday or an all day calendar event. -* – a date with a time, but not in any specific time zone. May be parsed from a string representation using the function. Use this type to represent times that occur at the same local time regardless of the time zone, such as the time of New Years Eve fireworks which always occur at midnight. Most times are better stored as a `ZonedDateTime`. -* – a date with a time in a specific time zone. May be parsed from a string representation using the , , or functions. Use this type to represent an exact moment in time at a particular location on Earth. - -```tsx example -import {parseDate} from '@internationalized/date'; - -function Example() { - let [value, setValue] = React.useState({ - start: parseDate('2020-02-03'), - end: parseDate('2020-02-08') - }); - - return ( - <> - - - - ); -} -``` - -### Events - -`useDateRangePicker` accepts an `onChange` prop which is triggered whenever the start or end date is edited by the user. The example below uses `onChange` to update a separate element with a formatted version of the date range in the user's locale and local time zone. This is done by converting the dates to native JavaScript `Date` objects to pass to the formatter. - -```tsx example -import {getLocalTimeZone} from '@internationalized/date'; -import {useDateFormatter} from '@react-aria/i18n'; - -function Example() { - let [range, setRange] = React.useState({ - start: parseDate('2020-07-03'), - end: parseDate('2020-07-10') - }); - let formatter = useDateFormatter({dateStyle: 'long'}); - - return ( - <> - -

    Selected date: {range ? formatter.formatRange(range.start.toDate(getLocalTimeZone()), range.end.toDate(getLocalTimeZone())) : '--'}

    - - ); -} -``` - -### Time zones - -`useDateRangePicker` is time zone aware when objects are provided as the value. In this case, the time zone abbreviation is displayed, and time zone concerns such as daylight saving time are taken into account when the value is manipulated. - -In most cases, your data will come from and be sent to a server as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) formatted string. [@internationalized/date](../internationalized/date/) includes functions for parsing strings in multiple formats into objects. Which format you use will depend on what information you need to store. - -* – This function parses a date with an explicit time zone and optional UTC offset attached (e.g. `"2021-11-07T00:45[America/Los_Angeles]"` or `"2021-11-07T00:45-07:00[America/Los_Angeles]"`). This format preserves the maximum amount of information. If the exact local time and time zone that a user selected is important, use this format. Storing the time zone and offset that was selected rather than converting to UTC ensures that the local time is correct regardless of daylight saving rule changes (e.g. if a locale abolishes DST). Examples where this applies include calendar events, reminders, and other times that occur in a particular location. -* – This function parses an absolute date and time that occurs at the same instant at all locations on Earth. It can be represented in UTC (e.g. `"2021-11-07T07:45:00Z"`), or stored with a particular offset (e.g. `"2021-11-07T07:45:00-07:00"`). A time zone identifier, e.g. `America/Los_Angeles`, must be passed, and the result will be converted into that time zone. Absolute times are the best way to represent events that occurred in the past, or future events where an exact time is needed, regardless of time zone. -* – This function parses an absolute date and time into the current user's local time zone. It is a shortcut for `parseAbsolute`, and accepts the same formats. - -```tsx example -import {parseZonedDateTime} from '@internationalized/date'; - - -``` - -`useDateRangePicker` displays times in the time zone included in the `ZonedDateTime` object. The above example is always displayed in Pacific Standard Time because the `America/Los_Angeles` time zone identifier is provided. [@internationalized/date](../internationalized/date/) includes functions for converting dates between time zones, or parsing a date directly into a specific time zone or the user's local time zone, as shown below. - -```tsx example -import {parseAbsoluteToLocal} from '@internationalized/date'; - - -``` - -### Granularity - -The `granularity` prop allows you to control the smallest unit that is displayed by `useDateRangePicker`. By default, `CalendarDate` values are displayed with `"day"` granularity (year, month, and day), and `CalendarDateTime` and `ZonedDateTime` values are displayed with `"minute"` granularity. More granular time values can be displayed by setting the `granularity` prop to `"second"`. - -In addition, when a value with a time is provided but you wish to only display the date, you can set the granularity to `"day"`. This has no effect on the actual value (it still has a time component), only on what fields are displayed. In the following example, two DateRangePickers are synchronized with the same value, but display different granularities. - -```tsx example -function Example() { - let [date, setDate] = React.useState({ - start: parseAbsoluteToLocal('2021-04-07T18:45:22Z'), - end: parseAbsoluteToLocal('2021-04-08T20:00:00Z') - }); - - return ( - <> - - - - ); -} -``` - - -If no `value` or `defaultValue` prop is passed, then the `granularity` prop also affects which type of value is emitted from the `onChange` event. Note that by default, time values will not have a time zone because none was supplied. You can override this by setting the `placeholderValue` prop explicitly. Values emitted from `onChange` will use the time zone of the placeholder value. - -```tsx example -import {now} from '@internationalized/date'; - - - -``` - -### International calendars - -`useDateRangePicker` supports selecting dates in many calendar systems used around the world, including Gregorian, Hebrew, Indian, Islamic, Buddhist, and more. Dates are automatically displayed in the appropriate calendar system for the user's locale. The calendar system can be overridden using the [Unicode calendar locale extension](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/calendar#adding_a_calendar_in_the_locale_string), passed to the [I18nProvider](../I18nProvider) component. - -Selected dates passed to `onChange` always use the same calendar system as the `value` or `defaultValue` prop. If no `value` or `defaultValue` is provided, then dates passed to `onChange` are always in the Gregorian calendar since this is the most commonly used. This means that even though the user selects dates in their local calendar system, applications are able to deal with dates from all users consistently. - -The below example displays a `DateRangePicker` in the Hindi language, using the Indian calendar. Dates emitted from `onChange` are in the Gregorian calendar. - -```tsx example -import {I18nProvider} from '@react-aria/i18n'; - -function Example() { - let [range, setRange] = React.useState(null); - return ( - - -

    Start date: {range?.start.toString()}

    -

    End date: {range?.end.toString()}

    -
    - ); -} -``` - -### Minimum and maximum values - -The `minValue` and `maxValue` props can also be used to perform builtin validation. This prevents the user from selecting dates outside the valid range in the calendar, and marks the date fields as invalid using ARIA. `useDateRangePicker` also validates that the end date is after the start date. You should implement a visual indication that the date range picker is invalid as well. - -This example only accepts dates after today. - -```tsx example -import {today} from '@internationalized/date'; - - -``` - -### Unavailable dates - -`useDateRangePicker` supports marking certain dates as _unavailable_. These dates remain focusable with the keyboard in the calendar so that navigation is consistent, but cannot be selected by the user. The `isDateUnavailable` prop accepts a callback that is called to evaluate whether each visible date is unavailable. - -Note that by default, users may not select non-contiguous ranges, i.e. ranges that contain unavailable dates within them. Once a start date is selected in the calendar, enabled dates will be restricted to subsequent dates until an unavailable date is hit. While this is handled automatically in the calendar, additional validation logic must be provided to ensure an invalid state is displayed in the date field. This can be achieved using the `isInvalid` prop. See [below](#non-contiguous-ranges) for an example of how to allow non-contiguous ranges. - -This example includes multiple unavailable date ranges, e.g. dates when a rental house is not available. The `minValue` prop is also used to prevent selecting dates before today. The `isInvalid` prop is used to mark selected date ranges with unavailable dates in the middle as invalid. - -```tsx example -import {today} from '@internationalized/date'; - -function Example() { - let now = today(getLocalTimeZone()); - let disabledRanges = [ - [now, now.add({days: 5})], - [now.add({days: 14}), now.add({days: 16})], - [now.add({days: 23}), now.add({days: 24})], - ]; - - let isDateUnavailable = (date) => disabledRanges.some((interval) => date.compare(interval[0]) >= 0 && date.compare(interval[1]) <= 0); - let [value, setValue] = React.useState(null); - let isInvalid = value && disabledRanges.some(interval => value.end.compare(interval[0]) >= 0 && value.start.compare(interval[1]) <= 0); - - return ( - - ); -} -``` - -### Non-contiguous ranges - -The `allowsNonContiguousRanges` prop enables a range to be selected even if there are unavailable dates in the middle. The value emitted in the `onChange` event will still be a single range with a `start` and `end` property, but unavailable dates will not be displayed as selected. It is up to applications to split the full selected range into multiple as needed for business logic. - -This example prevents selecting weekends, but allows selecting ranges that span multiple weeks. - -```tsx example -import {isWeekend} from '@internationalized/date'; - -function Example() { - let {locale} = useLocale(); - - return isWeekend(date, locale)} allowsNonContiguousRanges /> -} -``` - -### Placeholder value - -When no value is set, a placeholder is shown. The format of the placeholder is influenced by the `granularity` and `placeholderValue` props. `placeholderValue` also controls the default values of each segment when the user first interacts with them, e.g. using the up and down arrow keys, as well as the default month shown in the calendar popover. By default, the `placeholderValue` is the current date at midnight, but you can set it to a more appropriate value if needed. - -```tsx example -import {CalendarDate} from '@internationalized/date'; - - -``` - -### Hide time zone - -When `ZonedDateTime` objects are provided as the value of to `useDateRangePicker`, the time zone abbreviation is displayed by default. However, if this is displayed elsewhere or implicit based on the usecase, it can be hidden using the `hideTimeZone` option. - -```tsx example - -``` - -### Hour cycle - -By default, `useDateRangePicker` displays times in either 12 or 24 hour hour format depending on the user's locale. However, this can be overridden using the `hourCycle` prop if needed for a specific usecase. This example forces the `DateRangePicker` to use 24-hour time, regardless of the locale. - -```tsx example - -``` - -### Custom first day of week - -By default, the first day of the week is automatically set based on the current locale. This can be changed by setting the `firstDayOfWeek` prop to `'sun'`, `'mon'`, `'tue'`, `'wed'`, `'thu'`, `'fri'`, or `'sat'`. - -```tsx example - -``` \ No newline at end of file diff --git a/packages/react-aria/docs/datepicker/useTimeField.mdx b/packages/react-aria/docs/datepicker/useTimeField.mdx deleted file mode 100644 index 7ad7282085a..00000000000 --- a/packages/react-aria/docs/datepicker/useTimeField.mdx +++ /dev/null @@ -1,325 +0,0 @@ -{/* Copyright 2020 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 '@react-spectrum/docs'; -export default Layout; - -import docs from 'docs:@react-aria/datepicker'; -import statelyDocs from 'docs:@react-stately/datepicker'; -import i18nDocs from 'docs:@internationalized/date'; -import {FunctionAPI, HeaderInfo, InterfaceType, TypeContext, TypeLink, PageDescription} from '@react-spectrum/docs'; -import packageData from '@react-aria/datepicker/package.json'; -import Anatomy from './timefield-anatomy.svg'; -import ChevronRight from '@spectrum-icons/workflow/ChevronRight'; -import {ExampleCard} from '@react-spectrum/docs/src/ExampleCard'; -import tailwindExample from 'url:./TimeField-tailwind.png'; - ---- -category: Date and Time -keywords: [input, form, field, date, time] ---- - -# useTimeField - -{docs.exports.useTimeField.description} - - - -## API - - - - -## Features - -A time field can be built using ``, but this is very limited in functionality, lacking in internationalization capabilities, inconsistent between browsers, and difficult to style. `useTimeField` helps achieve accessible and international time fields that can be styled as needed. - -* **International** – Support for locale-specific formatting, number systems, hour cycles, and right-to-left layout. -* **Time zone aware** – Times can optionally include a time zone. All modifications follow time zone rules such as daylight saving time. -* **Accessible** – Each time unit is displayed as an individually focusable and editable segment, which allows users an easy way to edit times using the keyboard, in any format and locale. -* **Touch friendly** – Time segments are editable using an easy to use numeric keypad, and all interactions are accessible using touch-based screen readers. -* **Customizable** – As with all of React Aria, the DOM structure and styling of all elements can be fully customized. - -Read our [blog post](../blog/date-and-time-pickers-for-all) for more details about the internationalization, accessibility, and user experience features implemented by `useTimeField`. - -## Anatomy - - - -A time field consists of a label, and a group of segments representing each unit of a time (e.g. hours, minutes, and seconds). Each segment is individually focusable and editable by the user, by typing or using the arrow keys to increment and decrement the value. This approach allows values to be formatted and parsed correctly regardless of the locale or time format, and offers an easy and error-free way to edit times using the keyboard. - -`useTimeField` also supports optional description and error message elements, which can be used -to provide more context about the field, and any validation messages. These are linked with the -input via the `aria-describedby` attribute. - -`useTimeField` returns props that you should spread onto the appropriate elements: - - - - - -`useDateSegment` returns props for an individual time segment: - - - - - -Note that most of this anatomy is shared with [useDateField](../DateField/useDateField.html), so you can reuse many components between them if you have both. - -State is managed by the hook from `@react-stately/datepicker`. The state object should be passed as an option to `useTimeField` and `useDateSegment`. - -If the time field does not have a visible label, an `aria-label` or `aria-labelledby` prop must be passed instead to -identify it to assistive technology. - -## Time values - -Times are represented in many different ways by cultures around the world. This includes differences in hour cycles, time zones, daylight saving time rules, formatting, and much more. When building applications that support users around the world, it is important to handle these aspects correctly for each locale. - - uses the [@internationalized/date](../internationalized/date/) library to represent times. This package provides a library of objects and functions to perform date and time related manipulation, queries, and conversions that work across locales and calendars. Date and time objects can be converted to and from native JavaScript `Date` objects or [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) strings. See the [documentation](../internationalized/date/), or the [examples below](#value) for more details. - -## Example - -```tsx example export=true -import {useTimeFieldState} from '@react-stately/datepicker'; -import {useTimeField, useDateSegment} from '@react-aria/datepicker'; -import {useLocale} from '@react-aria/i18n'; - -export function TimeField(props) { - let {locale} = useLocale(); - let state = useTimeFieldState({ - ...props, - locale - }); - - let ref = React.useRef(null); - let {labelProps, fieldProps} = useTimeField(props, state, ref); - - return ( -
    - {props.label} -
    - {state.segments.map((segment, i) => - - )} - {state.isInvalid && - - } -
    -
    - ); -} - -// Note: this component is the same as in the useDateField docs. -function DateSegment({segment, state}) { - let ref = React.useRef(null); - let {segmentProps} = useDateSegment(segment, state, ref); - - return ( -
    - {segment.text} -
    - ); -} - - -``` - -
    - Show CSS - -```css -.wrapper { - display: flex; - flex-direction: column; - align-items: flex-start; -} - -.field { - display: inline-flex; - padding: 2px 4px; - border-radius: 2px; - border: 1px solid var(--gray); - background: var(--spectrum-global-color-gray-50); -} - -.field:focus-within { - border-color: var(--blue); -} - -.segment { - padding: 0 2px; - font-variant-numeric: tabular-nums; - text-align: end; -} - -.segment.placeholder { - color: var(--spectrum-gray-600); -} - -.segment:focus { - color: white; - background: var(--blue); - outline: none; - border-radius: 2px; -} -``` - -
    - -## Styled examples - - - -## Usage - -The following examples show how to use the `TimeField` component created in the above example. - -### Value - -A `TimeField` displays a placeholder by default. An initial, uncontrolled value can be provided to the `TimeField` using the `defaultValue` prop. Alternatively, a controlled value can be provided using the `value` prop. - -Time values are provided using objects in the [@internationalized/date](../internationalized/date/) package. This library handles correct international date and time manipulation across calendars, time zones, and other localization concerns. - -`useTimeField` only supports selecting times, but values with date components are also accepted. By default, `useTimeField` will emit objects in the `onChange` event, but if a or object is passed as the `value` or `defaultValue`, values of that type will be emitted, changing only the time and preserving the date components. - -```tsx example -import {Time} from '@internationalized/date'; - -function Example() { - let [value, setValue] = React.useState(new Time(11, 45)); - - return ( - <> - - - - ); -} -``` - -`Time` values may also be parsed from strings using the function. This accepts [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601#Times) formatted time strings such as `"04:45:23.123"`. The `toString` method of a `Time` object can also be used to convert a time object to a string. - -### Time zones - -`useTimeField` is time zone aware when a object is provided as the value. In this case, the time zone abbreviation is displayed, and time zone concerns such as daylight saving time are taken into account when the value is manipulated. - -In most cases, your data will come from and be sent to a server as an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) formatted string. [@internationalized/date](../internationalized/date/) includes functions for parsing strings in multiple formats into objects. Which format you use will depend on what information you need to store. - -* – This function parses a date with an explicit time zone and optional UTC offset attached (e.g. `"2021-11-07T00:45[America/Los_Angeles]"` or `"2021-11-07T00:45-07:00[America/Los_Angeles]"`). This format preserves the maximum amount of information. If the exact local time and time zone that a user selected is important, use this format. Storing the time zone and offset that was selected rather than converting to UTC ensures that the local time is correct regardless of daylight saving rule changes (e.g. if a locale abolishes DST). Examples where this applies include calendar events, reminders, and other times that occur in a particular location. -* – This function parses an absolute date and time that occurs at the same instant at all locations on Earth. It can be represented in UTC (e.g. `"2021-11-07T07:45:00Z"`), or stored with a particular offset (e.g. `"2021-11-07T07:45:00-07:00"`). A time zone identifier, e.g. `America/Los_Angeles`, must be passed, and the result will be converted into that time zone. Absolute times are the best way to represent events that occurred in the past, or future events where an exact time is needed, regardless of time zone. -* – This function parses an absolute date and time into the current user's local time zone. It is a shortcut for `parseAbsolute`, and accepts the same formats. - -```tsx example -import {parseZonedDateTime} from '@internationalized/date'; - - -``` - -`useTimeField` displays times in the time zone included in the `ZonedDateTime` object. The above example is always displayed in Pacific Standard Time because the `America/Los_Angeles` time zone identifier is provided. [@internationalized/date](../internationalized/date/) includes functions for converting dates between time zones, or parsing a date directly into a specific time zone or the user's local time zone, as shown below. - -```tsx example -import {parseAbsoluteToLocal} from '@internationalized/date'; - - -``` - -### Events - -`useTimeField` accepts an `onChange` prop which is triggered whenever the time is edited by the user. The example below uses `onChange` to update a separate element with a formatted version of the date in the user's locale and local time zone. This is done by converting the date to a native JavaScript `Date` object to pass to the formatter. `useTimeField` allows editing the time components while keeping the date fixed. - -```tsx example -import {useDateFormatter} from '@react-aria/i18n'; - -function Example() { - let [date, setDate] = React.useState(parseAbsoluteToLocal('2021-04-07T18:45:22Z')); - let formatter = useDateFormatter({dateStyle: 'long', timeStyle: 'long'}); - - return ( - <> - -

    Selected date and time: {(date?.toDate && formatter.format(date.toDate())) || (date && date.toString()) || '--'}

    - - ); -} -``` - -### Granularity - -The `granularity` prop allows you to control the smallest unit that is displayed by `useTimeField`. By default, times are displayed with `"minute"` granularity. More granular time values can be displayed by setting the `granularity` prop to `"second"`. - -```tsx example - -``` - -### Minimum and maximum values - -The `minValue` and `maxValue` props can also be used to perform builtin validation. This marks the time field as invalid using ARIA if the user enters an invalid time. You should implement a visual indication that the time field is invalid as well. - -This example only accepts times between 9 AM and 5 PM. - -```tsx example - -``` - -### Placeholder value - -When no value is set, a placeholder is shown. The format of the placeholder is influenced by the `granularity` and `placeholderValue` props. `placeholderValue` also controls the default values of each segment when the user first interacts with them, e.g. using the up and down arrow keys. By default, the `placeholderValue` is midnight, but you can set it to a more appropriate value if needed. - -```tsx example - -``` - -### Hide time zone - -When a `ZonedDateTime` object is provided as the value to `useTimeField`, the time zone abbreviation is displayed by default. However, if this is displayed elsewhere or implicit based on the usecase, it can be hidden using the `hideTimeZone` option. - -```tsx example - -``` - -### Hour cycle - -By default, `useTimeField` displays times in either 12 or 24 hour hour format depending on the user's locale. However, this can be overridden using the `hourCycle` prop if needed for a specific usecase. This example forces `useTimeField` to use 24-hour time, regardless of the locale. - -```tsx example - -``` diff --git a/packages/react-aria/docs/dialog/anatomy.svg b/packages/react-aria/docs/dialog/anatomy.svg deleted file mode 100644 index 4f2bf8c5f5f..00000000000 --- a/packages/react-aria/docs/dialog/anatomy.svg +++ /dev/null @@ -1,40 +0,0 @@ - - Dialog anatomy diagram - Shows a dialog component with labels pointing to its parts, including the title, and dialog container elements. - - - - - - - - - Cancel - - - - Enable - - - - Enable smart filters? - - - Smart filters are nondestructive and will preserve your - original images. - - - - - Title - - - - - - Dialog - - - - - diff --git a/packages/react-aria/docs/dialog/tailwind.png b/packages/react-aria/docs/dialog/tailwind.png deleted file mode 100644 index 311cbae3290..00000000000 Binary files a/packages/react-aria/docs/dialog/tailwind.png and /dev/null differ diff --git a/packages/react-aria/docs/dialog/useDialog.mdx b/packages/react-aria/docs/dialog/useDialog.mdx deleted file mode 100644 index e5294ea9339..00000000000 --- a/packages/react-aria/docs/dialog/useDialog.mdx +++ /dev/null @@ -1,221 +0,0 @@ -{/* Copyright 2020 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 '@react-spectrum/docs'; -export default Layout; - -import docs from 'docs:@react-aria/dialog'; -import overlaysDocs from 'docs:@react-aria/overlays'; -import focusDocs from 'docs:@react-aria/focus'; -import {HeaderInfo, FunctionAPI, TypeContext, InterfaceType, TypeLink, PageDescription} from '@react-spectrum/docs'; -import {Keyboard} from '@react-spectrum/text'; -import packageData from '@react-aria/dialog/package.json'; -import Anatomy from './anatomy.svg'; -import ChevronRight from '@spectrum-icons/workflow/ChevronRight'; -import {ExampleCard} from '@react-spectrum/docs/src/ExampleCard'; -import tailwindPreview from 'url:./tailwind.png'; - ---- -category: Overlays -keywords: [overlays, dialog, modal, aria] ---- - -# useDialog - -{docs.exports.useDialog.description} - - - -## API - - - -## Features - -The HTML [<dialog>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog) element -can be used to build dialogs. However, it is not yet widely supported across browsers, and -building fully accessible custom dialogs from scratch is very difficult and error prone. -`useDialog` helps achieve accessible dialogs that can be styled as needed. - -* **Flexible** – Dialogs can be used within a [modal](../Modal/useModalOverlay.html) or [popover](../Popover/usePopover.html) to create many types of overlay elements. -* **Accessible** – Exposed to assistive technology as a `dialog` or `alertdialog` with ARIA. The dialog is labeled by its title element, and content outside the dialog is hidden from assistive technologies while it is open. -* **Focus management** – Focus is moved into the dialog on mount, and restored to the trigger element on unmount. While open, focus is contained within the dialog, preventing the user from tabbing outside. - -## Anatomy - - - -A dialog consists of a container element and an optional title. `useDialog` handles -exposing this to assistive technology using ARIA. It can be combined -with or , -to create modal dialogs, popovers, and other types of overlays. - -`useDialog` returns props that you should spread onto the appropriate element: - - - - - -If a dialog does not have a visible title element, an `aria-label` or `aria-labelledby` -prop must be passed instead to identify the element to assistive technology. - -## Example - -This example shows how to build a typical modal dialog, by combining `useDialog` with [useModalOverlay](../Modal/useModalOverlay.html). The code for the `Modal` component is available below. The `Dialog` component may also be used within a [popover](../Popover/usePopover.html). See the docs for more details on overlay containers. - -```tsx example -import type {AriaDialogProps} from '@react-aria/dialog'; -import {useDialog} from '@react-aria/dialog'; - -// Reuse the Button and Modal from your component library. See below for details. -import {Button, Modal, ModalTrigger} from 'your-component-library'; - -interface DialogProps extends AriaDialogProps { - title?: React.ReactNode, - children: React.ReactNode -} - -function Dialog({title, children, ...props}: DialogProps) { - let ref = React.useRef(null); - let {dialogProps, titleProps} = useDialog(props, ref); - - return ( -
    - {title && -

    - {title} -

    - } - {children} -
    - ); -} - - - {close => - -
    - - - - - -
    -
    - } -
    -``` - -### Modal - -The `Modal` and `ModalTrigger` components render the dialog within a typical modal container with a partially transparent underlay. See [useModalOverlay](../Modal/useModalOverlay.html) for more details. - -
    - Show code - -```tsx example export=true render=false -import {useOverlayTriggerState} from '@react-stately/overlays'; -import {Overlay, useModalOverlay, useOverlayTrigger} from '@react-aria/overlays'; -import {useViewportSize} from '@react-aria/utils'; - -function Modal({state, children, ...props}) { - let ref = React.useRef(null); - let {modalProps, underlayProps} = useModalOverlay({...props, isDismissable: true}, state, ref); - - return ( - -
    -
    -
    - {children} -
    -
    - - ); -} - -function ModalTrigger({label, children, ...props}) { - let state = useOverlayTriggerState(props); - let {triggerProps, overlayProps} = useOverlayTrigger({type: 'dialog'}, state); - - return <> - - {state.isOpen && - - {React.cloneElement(children(state.close), overlayProps)} - - } - ; -} -``` - -
    - -### Button - -The `Button` component is used in the above example to open and close the dialog. It is built using the [useButton](../Button/useButton.html) hook, and can be shared with many other components. - -
    - Show code - -```tsx example export=true render=false -import {useButton} from '@react-aria/button'; - -function Button(props) { - let ref = React.useRef(null); - let {buttonProps} = useButton(props, ref); - return ; -} -``` - -
    - -## Styled examples - - diff --git a/packages/react-aria/docs/disclosure/anatomy.svg b/packages/react-aria/docs/disclosure/anatomy.svg deleted file mode 100644 index 64a51e24d63..00000000000 --- a/packages/react-aria/docs/disclosure/anatomy.svg +++ /dev/null @@ -1,23 +0,0 @@ - - - - Landscape - - - - - - - - - - Button - Landscape content - Panel - - - - - - - diff --git a/packages/react-aria/docs/disclosure/useDisclosure.mdx b/packages/react-aria/docs/disclosure/useDisclosure.mdx deleted file mode 100644 index e21bdfe8b0d..00000000000 --- a/packages/react-aria/docs/disclosure/useDisclosure.mdx +++ /dev/null @@ -1,351 +0,0 @@ -{/* Copyright 2024 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 '@react-spectrum/docs'; -export default Layout; - -import docs from 'docs:@react-aria/disclosure'; -import utilsDocs from 'docs:@react-aria/utils'; -import statelyDocs from 'docs:@react-stately/disclosure'; -import {HeaderInfo, FunctionAPI, TypeContext, InterfaceType, TypeLink, PageDescription} from '@react-spectrum/docs'; -import {Keyboard} from '@react-spectrum/text'; -import packageData from '@react-aria/disclosure/package.json'; -import ChevronRight from '@spectrum-icons/workflow/ChevronRight'; -import Anatomy from './anatomy.svg'; - ---- -category: Navigation -keywords: [disclosure, collapse, expand, aria] ---- - -# useDisclosure - -{docs.exports.useDisclosure.description} - - - -## API - - - -## Features - -A disclosure is a collapsible section of content. It is composed of a trigger button and a panel that contains the content. `useDisclosure` can be used to implement these in an accessible way. - -* Support for mouse, touch, and keyboard interactions to open and close the disclosure -* Support for disabled disclosures -* Follows the disclosure ARIA pattern, semantically linking the trigger button and panel -* Uses [hidden="until-found"](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/hidden#the_hidden_until_found_state) in supported browsers, enabling find-in-page search support and improved search engine visibility for collapsed content - -## Anatomy - - - -A disclosure consists of a trigger button and a panel. Clicking on or pressing Enter or Space while the trigger button is focused toggles the visibility of the panel. - -`useDisclosure` returns props to spread onto the trigger button and panel. - - - - - - -State is managed by the -hook in `@react-stately/disclosure`. The state object should be passed as an option to `useDisclosure`. - -## Example - -This example displays a basic disclosure with a button that toggles the visibility of the panel. - -```tsx example export=true -import {useDisclosureState} from '@react-stately/disclosure'; -import {useDisclosure} from '@react-aria/disclosure'; -import {useButton} from '@react-aria/button'; -import {mergeProps, useFocusRing} from 'react-aria'; - -function Disclosure(props) { - let state = useDisclosureState(props); - let panelRef = React.useRef(null); - let triggerRef = React.useRef(null); - let {buttonProps: triggerProps, panelProps} = useDisclosure(props, state, panelRef); - let {buttonProps} = useButton(triggerProps, triggerRef); - let {isFocusVisible, focusProps} = useFocusRing(); - - return ( -
    -

    - -

    -
    -

    - {props.children} -

    -
    -
    - ); -}; - - - Details about system requirements here. - -``` - -
    - Show CSS - -```css -@import "@react-aria/example-theme"; - -.disclosure { - .trigger { - background: none; - border: none; - box-shadow: none; - font-weight: bold; - font-size: 16px; - display: flex; - align-items: center; - gap: 8px; - color: var(--text-color); - - svg { - rotate: 0deg; - transition: rotate 200ms; - width: 12px; - height: 12px; - fill: none; - stroke: currentColor; - stroke-width: 3px; - } - - &[aria-expanded="true"] svg { - rotate: 90deg; - } - - &:disabled { - color: var(--gray-300); - } - } -} - -.panel { - margin-left: 32px; -} -``` - -
    - -## Usage - -The following examples show how to use the `Disclosure` component created in the above example. - -### Default expansion - -Whether or not the disclosure is expanded or not by default can be set with the `defaultExpanded` prop. - -```tsx example - - Details about system requirements here. - -``` - -### Controlled expansion - -Expansion can be controlled using the `isExpanded` prop, paired with the `onExpandedChange` event. The `onExpandedChange` event is fired when the user presses the trigger button. - -```tsx example -function ControlledDisclosure(props) { - let [isExpanded, setExpanded] = React.useState(false); - - return ( - - Details about system requirements here. - - ); -} -``` - -### Disabled - -A disclosure can be disabled with the `isDisabled` prop. This will disable the trigger button and prevent the panel from being opened or closed. - -```tsx example - - Details about system requirements here. - -``` - -## Disclosure Group - -A disclosure group (i.e. accordion) is a set of disclosures where only one disclosure can be expanded at a time. The following example shows how to create a `DisclosureGroup` component with the `useDisclosureGroupState` hook. We'll also create a `DisclosureItem` component that uses the `DisclosureGroupState` context for managing its state. - -```tsx example export=true render=false -import {useDisclosureGroupState} from '@react-stately/disclosure'; -import {useId} from '@react-aria/utils'; - -const DisclosureGroupStateContext = React.createContext(null); - -function DisclosureGroup(props) { - let state = useDisclosureGroupState(props); - - return ( -
    - - {props.children} - -
    - ); -} - -function DisclosureItem(props) { - let defaultId = useId(); - let id = props.id || defaultId; - let groupState = React.useContext(DisclosureGroupStateContext); - let isExpanded = groupState ? groupState.expandedKeys.has(id) : props.isExpanded; - let state = useDisclosureState({ - ...props, - isExpanded, - onExpandedChange(isExpanded) { - if (groupState) { - groupState.toggleKey(id); - } - - props.onExpandedChange?.(isExpanded); - } - }); - - let panelRef = React.useRef(null); - let triggerRef = React.useRef(null); - let isDisabled = props.isDisabled || groupState?.isDisabled || false; - let {buttonProps: triggerProps, panelProps} = useDisclosure({ - ...props, - isExpanded, - isDisabled - }, state, panelRef); - let {buttonProps} = useButton(triggerProps, triggerRef); - let {isFocusVisible, focusProps} = useFocusRing(); - - return ( -
    -

    - -

    -
    -

    - {props.children} -

    -
    -
    - ); -}; -``` - -### Usage - -The following examples show how to use the `DisclosureGroup` component created in the above example. - -```tsx example - - - Personal information form here. - - - Billing address form here. - - -``` - -#### Default expansion - -Which disclosure is expanded by default can be set with the `defaultExpandedKeys` prop. - -```tsx example - - - Personal information form here. - - - Billing address form here. - - -``` - -#### Controlled expansion - -Expansion can be controlled using the `expandedKeys` prop, paired with the `onExpandedChange` event. The `onExpandedChange` event is fired when one of the disclosures is expanded or collapsed. - -```tsx example -function ControlledDisclosureGroup(props) { - let [expandedKeys, setExpandedKeys] = React.useState(['personal']); - - return ( - - - Personal information form here. - - - Billing address form here. - - - ); -} -``` - -#### Multiple expanded - -Multiple disclosures can be expanded at the same time by setting the `allowsMultipleExpanded` prop to `true`. - -```tsx example - - - Personal information form here. - - - Billing address form here. - - -``` - -#### Disabled - -An entire disclosure group can be disabled with the `isDisabled` prop. This will disable all trigger buttons and prevent the panels from being opened or closed. - -```tsx example - - - Personal information form here. - - - Billing address form here. - - -``` diff --git a/packages/react-aria/docs/dnd/useDraggableCollection.mdx b/packages/react-aria/docs/dnd/useDraggableCollection.mdx deleted file mode 100644 index 450f0239b27..00000000000 --- a/packages/react-aria/docs/dnd/useDraggableCollection.mdx +++ /dev/null @@ -1,672 +0,0 @@ -{/* Copyright 2020 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 '@react-spectrum/docs'; -export default Layout; - -import docs from 'docs:@react-aria/dnd'; -import statelyDocs from 'docs:@react-stately/dnd'; -import sharedDocs from 'docs:@react-types/shared'; -import selectionDocs from 'docs:@react-aria/selection'; -import {HeaderInfo, FunctionAPI, TypeContext, InterfaceType, TypeLink, PageDescription} from '@react-spectrum/docs'; -import packageData from '@react-aria/dnd/package.json'; -import ChevronRight from '@spectrum-icons/workflow/ChevronRight'; -import {Keyboard} from '@react-spectrum/text'; - ---- -category: Drag and Drop -keywords: [drag, drop, dnd, drag and drop, aria, accessibility] -type: interaction ---- - -# useDraggableCollection - -{docs.exports.useDraggableCollection.description} - - - -## API - - - - - -## Introduction - -Collection components built with hooks such as [useListBox](ListBox/useListBox.html), [useTable](Table/useTable.html), and [useGridList](GridList/useGridList.html) can support drag and drop interactions. Users can drag multiple selected items at once, or drag individual non-selected items. - -React Aria supports traditional mouse and touch based drag and drop, but also implements keyboard and screen reader friendly interactions. Users can press Enter on a draggable element to enter drag and drop mode. Then, they can press Tab to navigate between drop targets, and Enter to drop or Escape to cancel. Touch screen reader users can also drag by double tapping to activate drag and drop mode, swiping between drop targets, and double tapping again to drop. - -See the [drag and drop introduction](dnd) to learn more. - -### Implementation - -The hook implements drag interactions within any [collection component](v3:collections.html), using state managed by . The hook should be added to each individual item within the collection to make it draggable, combining props from the relevant hook (e.g. `useOption`). These hooks integrate with React Aria's [selection](v3:selection.html) system to enable dragging multiple selected items at once. - -## Example - -This example renders a ListBox using the [useListBox](ListBox/useListBox.html) hook, and adds support for dragging items. The highlighted code sections below show the main additions for drag and drop compared with a normal listbox. - -```tsx example export=true -import {useListBox, useOption} from '@react-aria/listbox'; -import {useListState} from '@react-stately/list'; -import {Item} from '@react-stately/collections'; -import {useFocusRing} from '@react-aria/focus'; -import {mergeProps} from '@react-aria/utils'; -import {useDraggableCollectionState} from '@react-stately/dnd'; -import {useDraggableCollection, useDraggableItem} from '@react-aria/dnd'; - -function ListBox(props) { - // Setup listbox as normal. See the useListBox docs for more details. - let state = useListState(props); - let ref = React.useRef(null); - let {listBoxProps} = useListBox({ - ...props, - ///- begin highlight -/// - // Prevent dragging from changing selection. - shouldSelectOnPressUp: true - ///- end highlight -/// - }, state, ref); - - ///- begin highlight -/// - // Setup drag state for the collection. - let dragState = useDraggableCollectionState({ - // Pass through events from props. - ...props, - - // Collection and selection manager come from list state. - collection: state.collection, - selectionManager: state.selectionManager, - - // Provide data for each dragged item. This function could - // also be provided by the user of the component. - getItems: props.getItems || ((keys) => { - return [...keys].map(key => { - let item = state.collection.getItem(key); - - return { - 'text/plain': item.textValue - }; - }); - }) - }); - - useDraggableCollection(props, dragState, ref); - ///- end highlight -/// - - return ( -
      - {[...state.collection].map((item) => ( -
    - ); -} - -function Option({ item, state, dragState }) { - // Setup listbox option as normal. See useListBox docs for details. - let ref = React.useRef(null); - let {optionProps} = useOption({key: item.key}, state, ref); - let {isFocusVisible, focusProps} = useFocusRing(); - - ///- begin highlight -/// - // Register the item as a drag source. - let {dragProps} = useDraggableItem({ - key: item.key - }, dragState); - ///- end highlight -/// - - // Merge option props and dnd props, and render the item. - return ( - ///- begin highlight -/// -
  • - {item.rendered} -
  • - ); -} - - - Animals - People - Plants - - -``` - -
    - Show CSS - -```css -[role=listbox] { - padding: 0; - margin: 5px 0; - list-style: none; - box-shadow: inset 0 0 0 1px gray; - max-width: 250px; - outline: none; - min-height: 50px; - overflow: auto; -} - -.option { - padding: 3px 6px; - outline: none; -} - -.option[aria-selected=true] { - background: blueviolet; - color: white; -} - -.option.focus-visible { - box-shadow: inset 0 0 0 2px orange; -} - -.option.drop-target { - border-color: transparent; - box-shadow: inset 0 0 0 2px var(--blue); -} -``` - -
    - -### DropTarget - -The `DropTarget` component used above is defined below. See [useDrop](useDrop) for more details and documentation. - -
    - Show code - -```tsx example render=false export=true -import type {TextDropItem} from '@react-aria/dnd'; -import {useDrop} from '@react-aria/dnd'; -import {useButton} from '@react-aria/button'; - -function DropTarget() { - let [dropped, setDropped] = React.useState(null); - let ref = React.useRef(null); - let {dropProps, isDropTarget} = useDrop({ - ref, - async onDrop(e) { - let items = await Promise.all( - e.items - .filter(item => item.kind === 'text' && (item.types.has('text/plain') || item.types.has('my-app-custom-type'))) - .map(async (item: TextDropItem) => { - if (item.types.has('my-app-custom-type')) { - return JSON.parse(await item.getText('my-app-custom-type')); - } else { - return {name: await item.getText('text/plain'), style: 'span'}; - } - }) - ); - setDropped(items); - } - }); - - let {buttonProps} = useButton({elementType: 'div'}, ref); - - let message = ['Drop here']; - if (dropped) { - message = dropped.map((item, i) =>
    {item.name}
    ); - } - - return ( -
    - {message} -
    - ); -} -``` - -
    - Show CSS - -```css -.droppable { - width: 100px; - height: 50px; - border-radius: 6px; - display: inline-block; - padding: 20px; - border: 2px dotted gray; - white-space: pre-wrap; -} - -.droppable.target { - border: 2px solid var(--blue); -} -``` - -
    - -
    - -## Drag data - -Data for a draggable element can be provided in multiple formats at once. This allows drop targets to choose data in a format that they understand. For example, you could serialize a complex object as JSON in a custom format for use within your own application, and also provide plain text and/or rich HTML fallbacks that can be used when a user drops data in an external application (e.g. an email message). - -This can be done by returning multiple keys for an item from the `getItems` function. Types can either be a standard [mime type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types) for interoperability with external applications, or a custom string for use within your own app. - -This example provides representations of each item as plain text, HTML, and a custom app-specific data format. Dropping on the drop targets in this page will use the custom data format to render formatted items. If you drop in an external application supporting rich text, the HTML representation will be used. Dropping in a text editor will use the plain text format. - -```tsx example -let items = new Map([ - ['ps', {name: 'Photoshop', style: 'strong'}], - ['xd', {name: 'XD', style: 'strong'}], - ['id', {name: 'InDesign', style: 'strong'}], - ['dw', {name: 'Dreamweaver', style: 'em'}], - ['co', {name: 'Connect', style: 'em'}] -]); - -/*- begin highlight -*/ -let getItems = (keys) => ( - [...keys].map(key => { - let item = items.get(key); - return { - 'text/plain': item.name, - 'text/html': `<${item.style}>${item.name}`, - 'my-app-custom-type': JSON.stringify(item) - }; - }) -); -/*- end highlight -*/ - - - {([id, item]) => ( - - {item.name} - - )} - - -``` - -## Drag previews - -By default, the drag preview shown under the user's pointer or finger is a copy of the original element that started the drag. A custom preview can be rendered using the `` component. This accepts a function as a child which receives the dragged data that was returned by `getItems`, and returns a rendered preview for those items. The `DragPreview` is linked with `useDraggableCollectionState` via a ref, passed to the `preview` property. The `DragPreview` should be placed in the component hierarchy appropriately, so that it receives any React context or inherited styles that it needs to render correctly. - -This example renders a custom drag preview which shows the number of items being dragged, or the contents if there is only one. - -```tsx example -///- begin highlight -/// -import {DragPreview} from '@react-aria/dnd'; -///- end highlight -/// - -function ListBox(props) { - ///- begin collapse -/// - // Setup listbox as normal. See the useListBox docs for more details. - let state = useListState(props); - let ref = React.useRef(null); - let {listBoxProps} = useListBox({ - ...props, - shouldSelectOnPressUp: true - }, state, ref); - ///- end collapse -/// - // ... - - ///- begin highlight -/// - let preview = React.useRef(null); - ///- end highlight -/// - let dragState = useDraggableCollectionState({ - collection: state.collection, - selectionManager: state.selectionManager, - ///- begin highlight -/// - preview, - ///- end highlight -/// - getItems(keys) { - return [...keys].map(key => { - let item = state.collection.getItem(key); - return { - 'text/plain': item.textValue - }; - }); - }, - }); - - useDraggableCollection(props, dragState, ref); - - return ( -
      - {[...state.collection].map((item) => ( -
    - ); -} -///- begin collapse -/// -function Option({ item, state, dragState }) { - // Setup listbox option as normal. See useListBox docs for details. - let ref = React.useRef(null); - let {optionProps} = useOption({key: item.key}, state, ref); - let {isFocusVisible, focusProps} = useFocusRing(); - - ///- begin highlight -/// - // Register the item as a drag source. - let {dragProps} = useDraggableItem({ - key: item.key - }, dragState); - ///- end highlight -/// - - // Merge option props and dnd props, and render the item. - return ( - ///- begin highlight -/// -
  • - {item.rendered} -
  • - ); -} -///- end collapse -/// - - - Animals - People - Plants - - -``` - -## Drop operations - -A is an indication of what will happen when dragged data is dropped on a particular drop target. These are: - -* `move` – indicates that the dragged data will be moved from its source location to the target location. -* `copy` – indicates that the dragged data will be copied to the target destination. -* `link` – indicates that there will be a relationship established between the source and target locations. -* `cancel` – indicates that the drag and drop operation will be canceled, resulting in no changes made to the source or target. - -Many operating systems display these in the form of a cursor change, e.g. a plus sign to indicate a copy operation. The user may also be able to use a modifier key to choose which drop operation to perform, such as Option or Alt to switch from move to copy. - -The `onDragEnd` event allows the drag source to respond when a drag that it initiated ends, either because it was dropped or because it was canceled by the user. The `dropOperation` property of the event object indicates the operation that was performed. For example, when data is moved, the UI could be updated to reflect this change by removing the original dragged items. - -This example removes the dragged items from the UI when a move operation is completed. It uses the [useListData](useListData.html) hook to help manage and update the list of items. Try holding the Option or Alt keys to change the operation to copy, and see how the behavior changes. - -```tsx example -import {useListData} from '@react-stately/data'; - -function Example() { - let list = useListData({ - initialItems: [ - {id: 'a', textValue: 'Photoshop'}, - {id: 'b', textValue: 'XD'}, - {id: 'c', textValue: 'Dreamweaver'}, - {id: 'd', textValue: 'InDesign'}, - {id: 'e', textValue: 'Connect'} - ] - }); - - /*- begin highlight -*/ - let onDragEnd = e => { - if (e.dropOperation === 'move') { - list.remove(...e.keys); - } - }; - /*- end highlight -*/ - - return <> - - {item => {item.textValue}} - - - ; -} -``` - -The drag source can also control which drop operations are allowed for the data. For example, if moving data is not allowed, and only copying is supported, the `getAllowedDropOperations` function could be implemented to indicate this. When you drag the element below, the cursor now shows the copy affordance by default, and pressing a modifier to switch drop operations results in the drop being canceled. - -```tsx example - ['copy']} - //- end highlight -// - selectionMode="multiple" - > - Animals - People - Plants - - -``` - -## Reordering - -Drag and drop can be combined in the same collection component to allow reordering items. See [useDroppableCollection](./useDroppableCollection.html) for more details. - -```tsx example export=true render=false -import {useDroppableCollectionState} from '@react-stately/dnd'; -import {useDroppableCollection, useDropIndicator, ListDropTargetDelegate} from '@react-aria/dnd'; -import {ListKeyboardDelegate} from '@react-aria/selection'; - -function ReorderableListBox(props) { - ///- begin collapse -/// - // See useListBox docs for more details. - let state = useListState(props); - let ref = React.useRef(null); - let { listBoxProps } = useListBox({ - ...props, - shouldSelectOnPressUp: true - }, state, ref); - - // Setup drag state for the collection. - let dragState = useDraggableCollectionState({ - ...props, - // Collection and selection manager come from list state. - collection: state.collection, - selectionManager: state.selectionManager, - // Provide data for each dragged item. This function could - // also be provided by the user of the component. - getItems: props.getItems || ((keys) => { - return [...keys].map(key => { - let item = state.collection.getItem(key); - - return { - 'text/plain': item.textValue - }; - }); - }), - }); - - useDraggableCollection(props, dragState, ref); - ///- end collapse -/// - // ... - - ///- begin highlight -/// - // Setup react-stately and react-aria hooks for dropping. - let dropState = useDroppableCollectionState({ - ...props, - collection: state.collection, - selectionManager: state.selectionManager - }); - - let {collectionProps} = useDroppableCollection({ - ...props, - // Provide drop targets for keyboard and pointer-based drag and drop. - keyboardDelegate: new ListKeyboardDelegate(state.collection, state.disabledKeys, ref), - dropTargetDelegate: new ListDropTargetDelegate(state.collection, ref) - }, dropState, ref); - ///- end highlight -/// - - return ( - /*- begin highlight -*/ -
      - {[...state.collection].map((item) => ( - - ))} -
    - ); -} - -function ReorderableOption({ item, state, dragState, dropState }) { - ///- begin collapse -/// - let ref = React.useRef(null); - let { optionProps } = useOption({key: item.key}, state, ref); - let { isFocusVisible, focusProps } = useFocusRing(); - - // Register the item as a drag source. - let {dragProps} = useDraggableItem({ - key: item.key - }, dragState); - ///- end collapse -/// - // ... - - return ( - <> - {/*- begin highlight -*/} - - {/*- end highlight -*/} -
  • - {item.rendered} -
  • - {/*- begin highlight -*/} - {state.collection.getKeyAfter(item.key) == null && - - } - {/*- end highlight -*/} - - ); -} - -///- begin highlight -/// -function DropIndicator(props) { - let ref = React.useRef(null); - let {dropIndicatorProps, isHidden, isDropTarget} = useDropIndicator(props, props.dropState, ref); - if (isHidden) { - return null; - } - - return ( -
  • - ); -} -///- end highlight -/// -``` - -Now, we can render an example ListBox, which allows the user to reorder items. The `onReorder` event is triggered when the user drops dragged items which originated within the same collection. As above, [useListData](./useListData.html) is used to manage the list items in this example, but it is not a requirement. - -```tsx example -import {useListData} from '@react-stately/data'; - -function Example() { - let list = useListData({ - initialItems: [ - {id: 1, name: 'Cat'}, - {id: 2, name: 'Dog'}, - {id: 3, name: 'Kangaroo'}, - {id: 4, name: 'Panda'}, - {id: 5, name: 'Snake'} - ] - }); - - let onReorder = e => { - if (e.target.dropPosition === 'before') { - list.moveBefore(e.target.key, e.keys); - } else if (e.target.dropPosition === 'after') { - list.moveAfter(e.target.key, e.keys); - } - }; - - return ( - - {item => {item.name}} - - ); -} -``` - -
    - Show CSS - -```css -.drop-indicator { - width: 100%; - margin-left: 0; - height: 2px; - margin-bottom: -2px; - outline: none; - background: transparent; -} - -.drop-indicator:last-child { - margin-bottom: 0; - margin-top: -2px; -} - -.drop-indicator.drop-target { - background: var(--blue); -} -``` - -
    - -## Props - -The full list of props supported by draggable collections is available below. - - - - diff --git a/packages/react-aria/docs/dnd/useDroppableCollection.mdx b/packages/react-aria/docs/dnd/useDroppableCollection.mdx deleted file mode 100644 index d35297b6c6c..00000000000 --- a/packages/react-aria/docs/dnd/useDroppableCollection.mdx +++ /dev/null @@ -1,1036 +0,0 @@ -{/* Copyright 2020 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 '@react-spectrum/docs'; -export default Layout; - -import docs from 'docs:@react-aria/dnd'; -import statelyDocs from 'docs:@react-stately/dnd'; -import sharedDocs from 'docs:@react-types/shared'; -import selectionDocs from 'docs:@react-aria/selection'; -import {HeaderInfo, FunctionAPI, TypeContext, InterfaceType, TypeLink, PageDescription} from '@react-spectrum/docs'; -import packageData from '@react-aria/dnd/package.json'; -import ChevronRight from '@spectrum-icons/workflow/ChevronRight'; -import {Keyboard} from '@react-spectrum/text'; - ---- -category: Drag and Drop -keywords: [drag, drop, dnd, drag and drop, aria, accessibility] -type: interaction ---- - -# useDroppableCollection - -{docs.exports.useDroppableCollection.description} - - - -## API - - - - - - -## Introduction - -Collection components built with hooks such as [useListBox](ListBox/useListBox.html), [useTable](Table/useTable.html), and [useGridList](GridList/useGridList.html) can support drag and drop interactions. Users can drop data on the collection as a whole, on individual items, insert new items between existing ones, or reorder items. - -React Aria supports traditional mouse and touch based drag and drop, but also implements keyboard and screen reader friendly interactions. Users can press Enter on a draggable element to enter drag and drop mode. Then, they can press Tab to navigate between drop targets. A droppable collection is treated as a single drop target, so that users can easily tab past it to get to the next drop target. Within a droppable collection, keys such as ArrowDown and ArrowUp can be used to select a _drop position_, such as on an item, or between items. These are represented using objects. - -The keyboard interactions used within a collection may differ depending on the type or layout. For example, in a grid the ArrowLeft and ArrowRight may also be used, whereas they may not within a list. In general, the keyboard interactions used during drag and drop match those used when navigating the collection normally. - -See the [drag and drop introduction](dnd) to learn more. - -### Implementation - -The hook implements drop interactions within any [collection component](v3:collections.html), using state managed by . The props it returns should be combined with those from the collection component you're using, such as `useListBox`. The hook should be added to each individual item within the collection, combining props from the relevant hook (e.g. `useOption`). - -To support dropping between items, the hook can be used to add additional elements between each item, for example, rendering a line when a user drags between two items. These elements must be implemented according to the relevant ARIA pattern. For example, within a listbox, drop indicators must be implemented using `role="option"`, and within a grid, they must use `role="row"` and `role="gridcell"` to ensure the accessibility tree is valid. - -Interactions like keyboard navigation, and drop target positioning may differ depending on the component and layout of items. These are implemented using the and interfaces, provided to `useDroppableCollection`. In most cases, you can use a default implementation provided by React Aria such as and , but you may also provide your own if you need to customize the behavior. - -## Dropping on items - -This example renders a ListBox using the [useListBox](ListBox/useListBox.html) hook, and adds support for dropping data onto items. The highlighted code sections below show the main additions for drag and drop compared with a normal listbox. - -```tsx example -import {useListBox, useOption} from '@react-aria/listbox'; -import {useListState} from '@react-stately/list'; -import {Item} from '@react-stately/collections'; -import {useFocusRing} from '@react-aria/focus'; -import {mergeProps} from '@react-aria/utils'; -import {useDroppableCollectionState} from '@react-stately/dnd'; -import {useDroppableCollection, useDroppableItem, ListDropTargetDelegate} from '@react-aria/dnd'; -import {ListKeyboardDelegate} from '@react-aria/selection'; - -function ListBox(props) { - // Setup listbox as normal. See the useListBox docs for more details. - let state = useListState(props); - let ref = React.useRef(null); - let {listBoxProps} = useListBox(props, state, ref); - - ///- begin highlight -/// - // Setup react-stately and react-aria hooks for drag and drop. - let dropState = useDroppableCollectionState({ - ...props, - // Collection and selection manager come from list state. - collection: state.collection, - selectionManager: state.selectionManager - }); - - let {collectionProps} = useDroppableCollection({ - ...props, - // Provide drop targets for keyboard and pointer-based drag and drop. - keyboardDelegate: new ListKeyboardDelegate(state.collection, state.disabledKeys, ref), - dropTargetDelegate: new ListDropTargetDelegate(state.collection, ref) - }, dropState, ref); - ///- end highlight -/// - - // Merge listbox props and dnd props, and render the items as normal. - return ( - /*- begin highlight -*/ -
      - {/*- end highlight -*/} - {[...state.collection].map((item) => ( -
    - ); -} - -function Option({ item, state, dropState }) { - // Setup listbox option as normal. See useListBox docs for details. - let ref = React.useRef(null); - let {optionProps} = useOption({key: item.key}, state, ref); - let {isFocusVisible, focusProps} = useFocusRing(); - - ///- begin highlight -/// - // Register the item as a drop target. - let {dropProps, isDropTarget} = useDroppableItem({ - target: {type: 'item', key: item.key, dropPosition: 'on'} - }, dropState, ref); - ///- end highlight -/// - - // Merge option props and dnd props, and render the item. - return ( - ///- begin highlight -/// -
  • - {item.rendered} -
  • - ); -} - -Octopus - alert(`Dropped on ${e.target.key}`)}> - Animals - People - Plants - -``` - -
    - Show CSS - -```css -[role=listbox] { - padding: 0; - margin: 5px 0; - list-style: none; - box-shadow: inset 0 0 0 1px gray; - max-width: 250px; - outline: none; - min-height: 50px; - overflow: auto; -} - -[role=listbox]:empty { - box-sizing: border-box; - border: 1px dashed gray; - box-shadow: none; -} - -.option { - padding: 3px 6px; - outline: none; -} - -.option[aria-selected=true] { - background: blueviolet; - color: white; -} - -.option.focus-visible { - box-shadow: inset 0 0 0 2px orange; -} - -.option.drop-target { - border-color: transparent; - box-shadow: inset 0 0 0 2px var(--blue); -} -``` - -
    - - -### Draggable - -The `Draggable` component used above is defined below. See [useDrag](useDrag) for more details and documentation. - -
    - Show code - -```tsx example render=false export=true -import {useDrag} from '@react-aria/dnd'; -import {mergeProps} from '@react-aria/utils'; -import {useButton} from '@react-aria/button'; - -function Draggable({children}) { - let {dragProps, dragButtonProps, isDragging} = useDrag({ - getAllowedDropOperations: () => ['copy'], - getItems() { - return [{ - 'text/plain': children, - 'my-app-custom-type': JSON.stringify({message: children}) - }]; - } - }); - - let ref = React.useRef(null); - let {buttonProps} = useButton({...dragButtonProps, elementType: 'div'}, ref); - - return ( -
    - {' '} - {children} -
    - ); -} -``` - -
    - Show CSS - -```css -.draggable { - display: inline-block; - vertical-align: top; - border: 1px solid gray; - padding: 5px 10px; - margin-right: 20px; -} - -.draggable.dragging { - opacity: 0.5; -} -``` - -
    - -
    - -## Dropping between items - -To add support for dropping between items, first implement the `DropIndicator` component using the hook. This will render a line between items indicating the insertion position. Within a listbox, these must have `role=option`, and since our listbox is rendered as a `