From e6db3dd4c1db625b53f1b288aed6f3a0e39699f0 Mon Sep 17 00:00:00 2001 From: Michael Jordan Date: Tue, 30 Nov 2021 18:10:39 -0500 Subject: [PATCH 1/7] fix(#2599): TableView: space key on link within cell scrolls the TableView down a page --- .../@react-aria/interactions/src/usePress.ts | 106 +++++++++++++----- 1 file changed, 77 insertions(+), 29 deletions(-) diff --git a/packages/@react-aria/interactions/src/usePress.ts b/packages/@react-aria/interactions/src/usePress.ts index 59d9cbdcc3d..2a795793eba 100644 --- a/packages/@react-aria/interactions/src/usePress.ts +++ b/packages/@react-aria/interactions/src/usePress.ts @@ -228,27 +228,48 @@ export function usePress(props: PressHookProps): PressResult { let pressProps: HTMLAttributes = { onKeyDown(e) { - if (isValidKeyboardEvent(e.nativeEvent) && e.currentTarget.contains(e.target as HTMLElement)) { - e.preventDefault(); - e.stopPropagation(); - - // If the event is repeating, it may have started on a different element - // after which focus moved to the current element. Ignore these events and - // only handle the first key down event. - if (!state.isPressed && !e.repeat) { - state.target = e.currentTarget as HTMLElement; - state.isPressed = true; - triggerPressStart(e, 'keyboard'); - - // Focus may move before the key up event, so register the event on the document - // instead of the same element where the key down event occurred. - addGlobalListener(document, 'keyup', onKeyUp, false); + const {currentTarget, target, nativeEvent, repeat} = e; + if (currentTarget.contains(target as HTMLElement)) { + const isValid = isValidKeyboardEvent(nativeEvent); + if (isValid) { + e.stopPropagation(); + } + if (isValid || + ( + currentTarget !== target && + isHTMLAnchorLink(target as HTMLElement) && + isSpaceKey(nativeEvent) + ) + ) { + e.preventDefault(); + // If the event is repeating, it may have started on a different element + // after which focus moved to the current element. Ignore these events and + // only handle the first key down event. + if (!state.isPressed && !repeat) { + state.target = currentTarget as HTMLElement; + state.isPressed = true; + triggerPressStart(e, 'keyboard'); + + // Focus may move before the key up event, so register the event on the document + // instead of the same element where the key down event occurred. + addGlobalListener(document, 'keyup', onKeyUp, false); + } } } }, onKeyUp(e) { - if (isValidKeyboardEvent(e.nativeEvent) && !e.repeat && e.currentTarget.contains(e.target as HTMLElement)) { - triggerPressUp(createEvent(state.target, e), 'keyboard'); + const {currentTarget, target, nativeEvent, repeat} = e; + if (!repeat && currentTarget.contains(target as HTMLElement)) { + if ( + isValidKeyboardEvent(nativeEvent) || + ( + currentTarget !== target && + isHTMLAnchorLink(target as HTMLElement) && + isSpaceKey(nativeEvent) + ) + ) { + triggerPressUp(createEvent(state.target, e), 'keyboard'); + } } }, onClick(e) { @@ -282,18 +303,32 @@ export function usePress(props: PressHookProps): PressResult { }; let onKeyUp = (e: KeyboardEvent) => { - if (state.isPressed && isValidKeyboardEvent(e)) { + const target = e.target as HTMLElement; + const isValid = isValidKeyboardEvent(e); + if ( + state.isPressed && + ( + isValid || + ( + state.target.contains(target) && + (isHTMLAnchorLink(target) || target.getAttribute('role') === 'link') && + isSpaceKey(e) + ) + ) + ) { + if (isValid) { + e.stopPropagation(); + } + e.preventDefault(); - e.stopPropagation(); state.isPressed = false; - let target = e.target as HTMLElement; triggerPressEnd(createEvent(state.target, e), 'keyboard', state.target.contains(target)); removeAllGlobalListeners(); // If the target is a link, trigger the click method to open the URL, // but defer triggering pressEnd until onClick event handler. - if (state.target.contains(target) && isHTMLAnchorLink(state.target) || state.target.getAttribute('role') === 'link') { + if (state.target.contains(target) && (isHTMLAnchorLink(state.target) || state.target.getAttribute('role') === 'link')) { state.target.click(); } } @@ -658,27 +693,40 @@ export function usePress(props: PressHookProps): PressResult { }; } +function isEditableText(event: KeyboardEvent):boolean { + const {tagName, isContentEditable} = event.target as HTMLElement; + return (tagName === 'INPUT' || tagName === 'TEXTAREA' || isContentEditable === true); +} + +function isEnterKey(event: KeyboardEvent):boolean { + return event.key === 'Enter'; +} + +function isSpaceKey(event: KeyboardEvent):boolean { + const {key, code} = event; + return key === ' ' || key === 'Spacebar' || code === 'Space'; +} + function isHTMLAnchorLink(target: HTMLElement): boolean { return target.tagName === 'A' && target.hasAttribute('href'); } function isValidKeyboardEvent(event: KeyboardEvent): boolean { - const {key, code, target} = event; + const {target} = event; const element = target as HTMLElement; - const {tagName, isContentEditable} = element; const role = element.getAttribute('role'); + const isEnter = isEnterKey(event); + // Accessibility for keyboards. Space and Enter only. // "Spacebar" is for IE 11 return ( - (key === 'Enter' || key === ' ' || key === 'Spacebar' || code === 'Space') && - (tagName !== 'INPUT' && - tagName !== 'TEXTAREA' && - isContentEditable !== true) && + (isEnter || isSpaceKey(event)) && + !isEditableText(event) && // A link with a valid href should be handled natively, // unless it also has role='button' and was triggered using Space. - (!isHTMLAnchorLink(element) || (role === 'button' && key !== 'Enter')) && + (!isHTMLAnchorLink(element) || (role === 'button' && !isEnter)) && // An element with role='link' should only trigger with Enter key - !(role === 'link' && key !== 'Enter') + !(role === 'link' && !isEnter) ); } From 59a132a3243f4a018c3bb477b8c17acd28cb2c87 Mon Sep 17 00:00:00 2001 From: Michael Jordan Date: Thu, 2 Dec 2021 16:42:05 -0500 Subject: [PATCH 2/7] fix(#2599): TableView: space key on link add tests --- .../@react-aria/interactions/src/usePress.ts | 4 +- .../@react-spectrum/table/test/Table.test.js | 44 +++++++++++++++++-- 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/packages/@react-aria/interactions/src/usePress.ts b/packages/@react-aria/interactions/src/usePress.ts index 2a795793eba..5c0508a3076 100644 --- a/packages/@react-aria/interactions/src/usePress.ts +++ b/packages/@react-aria/interactions/src/usePress.ts @@ -237,7 +237,7 @@ export function usePress(props: PressHookProps): PressResult { if (isValid || ( currentTarget !== target && - isHTMLAnchorLink(target as HTMLElement) && + (isHTMLAnchorLink(target as HTMLElement) || (target as HTMLElement).getAttribute('role') === 'link') && isSpaceKey(nativeEvent) ) ) { @@ -264,7 +264,7 @@ export function usePress(props: PressHookProps): PressResult { isValidKeyboardEvent(nativeEvent) || ( currentTarget !== target && - isHTMLAnchorLink(target as HTMLElement) && + (isHTMLAnchorLink(target as HTMLElement) || (target as HTMLElement).getAttribute('role') === 'link') && isSpaceKey(nativeEvent) ) ) { diff --git a/packages/@react-spectrum/table/test/Table.test.js b/packages/@react-spectrum/table/test/Table.test.js index b2b8afa8856..3e8b673769b 100644 --- a/packages/@react-spectrum/table/test/Table.test.js +++ b/packages/@react-spectrum/table/test/Table.test.js @@ -762,8 +762,8 @@ describe('TableView', function () { ); - let renderMany = () => render( - + let renderMany = (withLinks = false) => render( + {column => {column.name} @@ -772,7 +772,7 @@ describe('TableView', function () { {item => ( - {key => {item[key]}} + {key => {withLinks && key === 'foo' ? {item[key]} : item[key]}} ) } @@ -1539,6 +1539,44 @@ describe('TableView', function () { expect(document.activeElement).toHaveTextContent('Bar'); expect(body.scrollTop).toBe(1000); }); + + describe('Space key with focus on a link within a cell', () => { + it('should toggle selection and prevent scrolling of the table', () => { + let tree = renderMany(true); + + let row = tree.getAllByRole('row')[1]; + + expect(row).toHaveAttribute('aria-selected', 'false'); + + let link = within(row).getByRole('link'); + expect(link.textContent).toBe('Foo 1'); + + act(() => { + link.focus(); + fireEvent.keyDown(link, {key: ' '}); + fireEvent.keyUp(link, {key: ' '}); + jest.runAllTimers(); + }); + + row = tree.getAllByRole('row')[1]; + + expect(row).toHaveAttribute('aria-selected', 'true'); + + act(() => { + link.focus(); + fireEvent.keyDown(link, {key: ' '}); + fireEvent.keyUp(link, {key: ' '}); + jest.runAllTimers(); + }); + + row = tree.getAllByRole('row')[1]; + link = within(row).getByRole('link'); + + expect(row).toHaveAttribute('aria-selected', 'false'); + + expect(link.textContent).toBe('Foo 1'); + }); + }); }); }); From 77100caa773ab0120736a4726d3e5d679f24afe1 Mon Sep 17 00:00:00 2001 From: Michael Jordan Date: Fri, 3 Dec 2021 16:13:21 -0500 Subject: [PATCH 3/7] fix(#2599): updates per code review 1. update Table test to better evaluate preventDefault of scrolling. 2. add `isLinkRole` utility method to `usePress`. --- .../@react-aria/interactions/src/usePress.ts | 23 ++++--- .../@react-spectrum/table/test/Table.test.js | 63 +++++++++++-------- 2 files changed, 52 insertions(+), 34 deletions(-) diff --git a/packages/@react-aria/interactions/src/usePress.ts b/packages/@react-aria/interactions/src/usePress.ts index 5c0508a3076..7fd59fe708c 100644 --- a/packages/@react-aria/interactions/src/usePress.ts +++ b/packages/@react-aria/interactions/src/usePress.ts @@ -237,7 +237,7 @@ export function usePress(props: PressHookProps): PressResult { if (isValid || ( currentTarget !== target && - (isHTMLAnchorLink(target as HTMLElement) || (target as HTMLElement).getAttribute('role') === 'link') && + isLinkRole(target as HTMLElement) && isSpaceKey(nativeEvent) ) ) { @@ -259,17 +259,18 @@ export function usePress(props: PressHookProps): PressResult { }, onKeyUp(e) { const {currentTarget, target, nativeEvent, repeat} = e; - if (!repeat && currentTarget.contains(target as HTMLElement)) { - if ( + if ( + !repeat && + currentTarget.contains(target as HTMLElement) && + ( isValidKeyboardEvent(nativeEvent) || ( currentTarget !== target && - (isHTMLAnchorLink(target as HTMLElement) || (target as HTMLElement).getAttribute('role') === 'link') && + isLinkRole(target as HTMLElement) && isSpaceKey(nativeEvent) ) - ) { - triggerPressUp(createEvent(state.target, e), 'keyboard'); - } + )) { + triggerPressUp(createEvent(state.target, e), 'keyboard'); } }, onClick(e) { @@ -311,7 +312,7 @@ export function usePress(props: PressHookProps): PressResult { isValid || ( state.target.contains(target) && - (isHTMLAnchorLink(target) || target.getAttribute('role') === 'link') && + isLinkRole(target as HTMLElement) && isSpaceKey(e) ) ) @@ -328,7 +329,7 @@ export function usePress(props: PressHookProps): PressResult { // If the target is a link, trigger the click method to open the URL, // but defer triggering pressEnd until onClick event handler. - if (state.target.contains(target) && (isHTMLAnchorLink(state.target) || state.target.getAttribute('role') === 'link')) { + if (state.target.contains(target) && isLinkRole(target as HTMLElement)) { state.target.click(); } } @@ -711,6 +712,10 @@ function isHTMLAnchorLink(target: HTMLElement): boolean { return target.tagName === 'A' && target.hasAttribute('href'); } +function isLinkRole(target: HTMLElement): boolean { + return isHTMLAnchorLink(target) || target.getAttribute('role') === 'link'; +} + function isValidKeyboardEvent(event: KeyboardEvent): boolean { const {target} = event; const element = target as HTMLElement; diff --git a/packages/@react-spectrum/table/test/Table.test.js b/packages/@react-spectrum/table/test/Table.test.js index 3e8b673769b..6d199b22bbb 100644 --- a/packages/@react-spectrum/table/test/Table.test.js +++ b/packages/@react-spectrum/table/test/Table.test.js @@ -762,22 +762,25 @@ describe('TableView', function () { ); - let renderMany = (withLinks = false) => render( - - - {column => - {column.name} - } - - - {item => - ( - {key => {withLinks && key === 'foo' ? {item[key]} : item[key]}} - ) - } - - - ); + let renderMany = (props = {}) => { + let {withLinks = false, ...otherProps} = props; + return render( + + + {column => + {column.name} + } + + + {item => + ( + {key => {withLinks && key === 'foo' ? {item[key]} : item[key]}} + ) + } + + + ); + }; let focusCell = (tree, text) => act(() => getCell(tree, text).focus()); let moveFocus = (key, opts = {}) => {fireEvent.keyDown(document.activeElement, {key, ...opts});}; @@ -1542,34 +1545,44 @@ describe('TableView', function () { describe('Space key with focus on a link within a cell', () => { it('should toggle selection and prevent scrolling of the table', () => { - let tree = renderMany(true); - - let row = tree.getAllByRole('row')[1]; + let tree = renderMany({withLinks: true, selectionMode: 'multiple'}); + let body = tree.getByRole('grid').childNodes[1]; + expect(body.scrollTop).toBe(0); + + let row = within(body).getAllByRole('row')[0]; expect(row).toHaveAttribute('aria-selected', 'false'); let link = within(row).getByRole('link'); expect(link.textContent).toBe('Foo 1'); + let isNotPrevented = true; + act(() => { link.focus(); - fireEvent.keyDown(link, {key: ' '}); - fireEvent.keyUp(link, {key: ' '}); + isNotPrevented = fireEvent.keyDown(link, {key: ' '}); + isNotPrevented = fireEvent.keyUp(link, {key: ' '}); + expect(isNotPrevented).toBe(false); jest.runAllTimers(); }); - row = tree.getAllByRole('row')[1]; + expect(body.scrollTop).toBe(0); + row = within(body).getAllByRole('row')[0]; expect(row).toHaveAttribute('aria-selected', 'true'); + isNotPrevented = true; + act(() => { link.focus(); - fireEvent.keyDown(link, {key: ' '}); - fireEvent.keyUp(link, {key: ' '}); + isNotPrevented = fireEvent.keyDown(link, {key: ' '}); + isNotPrevented = fireEvent.keyUp(link, {key: ' '}); + expect(isNotPrevented).toBe(false); jest.runAllTimers(); }); - row = tree.getAllByRole('row')[1]; + expect(body.scrollTop).toBe(0); + row = within(body).getAllByRole('row')[0]; link = within(row).getByRole('link'); expect(row).toHaveAttribute('aria-selected', 'false'); From 6200f332575536a98248c8a3bdf139788e7dc995 Mon Sep 17 00:00:00 2001 From: Michael Jordan Date: Thu, 23 Dec 2021 13:26:23 -0500 Subject: [PATCH 4/7] fix(#2599): onKeyUp should not trigger click and focus currentTarget --- packages/@react-aria/interactions/src/usePress.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@react-aria/interactions/src/usePress.ts b/packages/@react-aria/interactions/src/usePress.ts index 7fd59fe708c..41756cb0464 100644 --- a/packages/@react-aria/interactions/src/usePress.ts +++ b/packages/@react-aria/interactions/src/usePress.ts @@ -329,7 +329,7 @@ export function usePress(props: PressHookProps): PressResult { // If the target is a link, trigger the click method to open the URL, // but defer triggering pressEnd until onClick event handler. - if (state.target.contains(target) && isLinkRole(target as HTMLElement)) { + if (state.target.contains(target) && isLinkRole(state.target as HTMLElement)) { state.target.click(); } } From 784ebd7208cb892483545f3a188c2e5cb378ca93 Mon Sep 17 00:00:00 2001 From: Michael Jordan Date: Thu, 23 Dec 2021 18:45:45 -0500 Subject: [PATCH 5/7] fix(#2599): Refactor to pass currentTarget to isValidKeyboardEvent. --- .../@react-aria/interactions/src/usePress.ts | 29 +++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/packages/@react-aria/interactions/src/usePress.ts b/packages/@react-aria/interactions/src/usePress.ts index 41756cb0464..65d162054da 100644 --- a/packages/@react-aria/interactions/src/usePress.ts +++ b/packages/@react-aria/interactions/src/usePress.ts @@ -235,6 +235,8 @@ export function usePress(props: PressHookProps): PressResult { e.stopPropagation(); } if (isValid || + // If the target is a link, but the currentTarget containing it is something else, + // like a table row, treat the space key as a valid keyboard event as well. ( currentTarget !== target && isLinkRole(target as HTMLElement) && @@ -258,18 +260,12 @@ export function usePress(props: PressHookProps): PressResult { } }, onKeyUp(e) { - const {currentTarget, target, nativeEvent, repeat} = e; + const {currentTarget = state.target, target, nativeEvent, repeat} = e; if ( !repeat && currentTarget.contains(target as HTMLElement) && - ( - isValidKeyboardEvent(nativeEvent) || - ( - currentTarget !== target && - isLinkRole(target as HTMLElement) && - isSpaceKey(nativeEvent) - ) - )) { + isValidKeyboardEvent(nativeEvent, currentTarget) + ) { triggerPressUp(createEvent(state.target, e), 'keyboard'); } }, @@ -716,9 +712,9 @@ function isLinkRole(target: HTMLElement): boolean { return isHTMLAnchorLink(target) || target.getAttribute('role') === 'link'; } -function isValidKeyboardEvent(event: KeyboardEvent): boolean { +function isValidKeyboardEvent(event: KeyboardEvent, currentTarget?: HTMLElement): boolean { const {target} = event; - const element = target as HTMLElement; + const element = (currentTarget || target) as HTMLElement; const role = element.getAttribute('role'); const isEnter = isEnterKey(event); @@ -727,10 +723,13 @@ function isValidKeyboardEvent(event: KeyboardEvent): boolean { return ( (isEnter || isSpaceKey(event)) && !isEditableText(event) && - // A link with a valid href should be handled natively, - // unless it also has role='button' and was triggered using Space. - (!isHTMLAnchorLink(element) || (role === 'button' && !isEnter)) && - // An element with role='link' should only trigger with Enter key + ( + // A link with a valid href should be handled natively, + !isHTMLAnchorLink(element) || + // unless it also has role='button' and was triggered using Space. + (role === 'button' && !isEnter) + ) && + // An element with role='link' should only trigger with Enter key. !(role === 'link' && !isEnter) ); } From 44909d50ba9d6963ec3e1c8171b93b0c3c76d68b Mon Sep 17 00:00:00 2001 From: Michael Jordan Date: Thu, 27 Jan 2022 16:00:41 -0500 Subject: [PATCH 6/7] fix(#2599): refactor isValidKeyboardEvent with currentTarget prop --- .../@react-aria/interactions/src/usePress.ts | 98 +++++++++---------- 1 file changed, 44 insertions(+), 54 deletions(-) diff --git a/packages/@react-aria/interactions/src/usePress.ts b/packages/@react-aria/interactions/src/usePress.ts index 269cb032d2e..4379921f0e7 100644 --- a/packages/@react-aria/interactions/src/usePress.ts +++ b/packages/@react-aria/interactions/src/usePress.ts @@ -229,35 +229,23 @@ export function usePress(props: PressHookProps): PressResult { let pressProps: HTMLAttributes = { onKeyDown(e) { const {currentTarget, target, nativeEvent, repeat} = e; - if (currentTarget.contains(target as HTMLElement)) { - const isValid = isValidKeyboardEvent(nativeEvent); - if (isValid) { - e.stopPropagation(); + if (currentTarget.contains(target as HTMLElement) && isValidKeyboardEvent(nativeEvent, currentTarget)) { + e.stopPropagation(); + + if (shouldPreventDefaultKeyboard(target as Element)) { + e.preventDefault(); } - if (isValid || - // If the target is a link, but the currentTarget containing it is something else, - // like a table row, treat the space key as a valid keyboard event as well. - ( - currentTarget !== target && - isLinkRole(target as HTMLElement) && - isSpaceKey(nativeEvent) - ) - ) { - if (shouldPreventDefaultKeyboard(target as Element)) { - e.preventDefault(); - } - // If the event is repeating, it may have started on a different element - // after which focus moved to the current element. Ignore these events and - // only handle the first key down event. - if (!state.isPressed && !repeat) { - state.target = currentTarget as HTMLElement; - state.isPressed = true; - triggerPressStart(e, 'keyboard'); - - // Focus may move before the key up event, so register the event on the document - // instead of the same element where the key down event occurred. - addGlobalListener(document, 'keyup', onKeyUp, false); - } + // If the event is repeating, it may have started on a different element + // after which focus moved to the current element. Ignore these events and + // only handle the first key down event. + if (!state.isPressed && !repeat) { + state.target = currentTarget as HTMLElement; + state.isPressed = true; + triggerPressStart(e, 'keyboard'); + + // Focus may move before the key up event, so register the event on the document + // instead of the same element where the key down event occurred. + addGlobalListener(document, 'keyup', onKeyUp, false); } } }, @@ -303,21 +291,8 @@ export function usePress(props: PressHookProps): PressResult { let onKeyUp = (e: KeyboardEvent) => { const target = e.target as HTMLElement; - const isValid = isValidKeyboardEvent(e); - if ( - state.isPressed && - ( - isValid || - ( - state.target.contains(target) && - isLinkRole(target as HTMLElement) && - isSpaceKey(e) - ) - ) - ) { - if (isValid) { - e.stopPropagation(); - } + if (state.isPressed && isValidKeyboardEvent(e, state.target)) { + e.stopPropagation(); if (shouldPreventDefaultKeyboard(target as Element)) { e.preventDefault(); @@ -681,9 +656,10 @@ export function usePress(props: PressHookProps): PressResult { // Remove user-select: none in case component unmounts immediately after pressStart // eslint-disable-next-line arrow-body-style useEffect(() => { + let refCurrent = ref.current; return () => { if (!allowTextSelectionOnPress) { - restoreTextSelection(ref.current.target); + restoreTextSelection(refCurrent.target); } }; }, [allowTextSelectionOnPress]); @@ -716,9 +692,10 @@ function isLinkRole(target: HTMLElement): boolean { return isHTMLAnchorLink(target) || target.getAttribute('role') === 'link'; } -function isValidKeyboardEvent(event: KeyboardEvent, currentTarget?: HTMLElement): boolean { +function isValidKeyboardEvent(event: KeyboardEvent, currentTarget?: EventTarget): boolean { const {target} = event; - const element = (currentTarget || target) as HTMLElement; + const element = target as HTMLElement; + const currentTargetElement = currentTarget as HTMLElement; const role = element.getAttribute('role'); const isEnter = isEnterKey(event); @@ -726,15 +703,28 @@ function isValidKeyboardEvent(event: KeyboardEvent, currentTarget?: HTMLElement) // "Spacebar" is for IE 11 return ( (isEnter || isSpaceKey(event)) && - !isEditableText(event) && ( - // A link with a valid href should be handled natively, - !isHTMLAnchorLink(element) || - // unless it also has role='button' and was triggered using Space. - (role === 'button' && !isEnter) - ) && - // An element with role='link' should only trigger with Enter key. - !(role === 'link' && !isEnter) + ( + !isEditableText(event) && + ( + // A link with a valid href should be handled natively, + !isHTMLAnchorLink(element) || + // unless it also has role='button' and was triggered using Space. + (role === 'button' && !isEnter) + ) && + // An element with role='link' should only trigger with Enter key. + !(role === 'link' && !isEnter) + ) || + ( + // If the target is a link, but the currentTarget containing it is something else, + // like a table row, treat the space key as a valid keyboard event as well. + currentTargetElement && + currentTargetElement !== element && + currentTargetElement.contains(currentTargetElement as HTMLElement) && + isLinkRole(element) && + !isEnter + ) + ) ); } From 16c206e00fdeb65067e99bb54df81fc1e023646b Mon Sep 17 00:00:00 2001 From: Michael Jordan Date: Fri, 22 Jul 2022 12:46:41 -0400 Subject: [PATCH 7/7] Merge branch 'main' into Issue-2599-TableView-scroll-on-space-key --- .../components/button/skin.css | 14 +++ .../components/calendar/skin.css | 55 +++++++++++ .../components/checkbox/skin.css | 61 +++++++++++- .../components/colorarea/skin.css | 11 ++- .../components/colorhandle/skin.css | 10 ++ .../components/colorloupe/skin.css | 8 ++ .../components/colorslider/skin.css | 10 ++ .../components/colorwheel/skin.css | 10 ++ .../components/dropdown/skin.css | 32 +++++- .../components/inputgroup/skin.css | 78 +++++++++++++++ .../components/menu/skin.css | 46 +++++++++ .../components/radio/skin.css | 42 ++++++++ .../components/slider/skin.css | 51 +++++++++- .../components/stepper/skin.css | 56 +++++++++++ .../components/tabs/skin.css | 7 ++ .../components/textfield/index.css | 6 +- .../components/textfield/skin.css | 33 ++++++- .../components/toggle/skin.css | 57 +++++++++++ packages/@internationalized/date/src/index.ts | 18 +++- .../@internationalized/message/src/index.ts | 5 +- packages/@react-aria/accordion/src/index.ts | 3 +- .../@react-aria/accordion/src/useAccordion.ts | 8 +- packages/@react-aria/actiongroup/src/index.ts | 6 +- .../actiongroup/src/useActionGroup.ts | 7 +- .../actiongroup/src/useActionGroupItem.ts | 7 +- .../@react-aria/autocomplete/src/index.ts | 3 +- .../autocomplete/src/useSearchAutocomplete.ts | 5 +- packages/@react-aria/breadcrumbs/src/index.ts | 5 +- .../breadcrumbs/src/useBreadcrumbItem.ts | 9 +- .../breadcrumbs/src/useBreadcrumbs.ts | 4 +- packages/@react-aria/button/package.json | 3 +- packages/@react-aria/button/src/index.ts | 6 +- packages/@react-aria/button/src/useButton.ts | 3 +- .../@react-aria/button/src/useToggleButton.ts | 3 +- .../calendar/src/useCalendarBase.ts | 7 +- .../calendar/src/useCalendarCell.ts | 7 +- .../calendar/src/useCalendarGrid.ts | 7 +- .../calendar/src/useRangeCalendar.ts | 7 +- packages/@react-aria/checkbox/package.json | 3 +- packages/@react-aria/checkbox/src/index.ts | 8 +- .../checkbox/src/useCheckboxGroup.ts | 6 +- packages/@react-aria/color/src/index.ts | 9 +- .../@react-aria/color/src/useColorArea.ts | 35 +++---- .../color/src/useColorAreaGradient.ts | 7 +- .../@react-aria/color/src/useColorSlider.ts | 26 +++-- .../@react-aria/color/src/useColorWheel.ts | 20 ++-- packages/@react-aria/combobox/src/index.ts | 3 +- .../@react-aria/combobox/src/useComboBox.ts | 18 ++-- .../datepicker/src/useDateField.ts | 17 ++-- .../datepicker/src/useDatePicker.ts | 13 +-- .../datepicker/src/useDatePickerGroup.ts | 14 +-- .../datepicker/src/useDateRangePicker.ts | 13 +-- .../datepicker/src/useDateSegment.ts | 5 +- packages/@react-aria/dialog/docs/tailwind.png | Bin 0 -> 95678 bytes .../@react-aria/dialog/docs/useDialog.mdx | 10 ++ packages/@react-aria/dialog/package.json | 3 +- packages/@react-aria/dialog/src/index.ts | 3 +- packages/@react-aria/dialog/src/useDialog.ts | 9 +- packages/@react-aria/dnd/src/DragManager.ts | 8 +- packages/@react-aria/dnd/src/index.ts | 22 +++-- packages/@react-aria/dnd/src/useClipboard.ts | 6 +- .../@react-aria/dnd/src/useVirtualDrop.ts | 4 +- .../dnd/stories/DraggableCollection.tsx | 2 +- packages/@react-aria/focus/src/FocusScope.tsx | 85 ++++++++-------- packages/@react-aria/focus/src/focusSafely.ts | 3 +- packages/@react-aria/focus/src/index.ts | 12 +-- .../@react-aria/focus/src/useFocusRing.ts | 5 +- .../@react-aria/focus/src/useFocusable.tsx | 16 +-- packages/@react-aria/grid/src/index.ts | 21 ++-- packages/@react-aria/grid/src/useGrid.ts | 8 +- packages/@react-aria/grid/src/useGridCell.ts | 34 +++---- packages/@react-aria/grid/src/useGridRow.ts | 10 +- .../@react-aria/grid/src/useGridRowGroup.ts | 4 +- packages/@react-aria/i18n/src/index.ts | 13 +-- .../interactions/src/DOMPropsContext.ts | 11 ++- .../interactions/src/DOMPropsResponder.tsx | 2 +- .../interactions/src/PressResponder.tsx | 3 +- .../interactions/src/Pressable.tsx | 7 +- .../@react-aria/interactions/src/context.ts | 3 +- .../@react-aria/interactions/src/index.ts | 36 ++++--- .../interactions/src/textSelection.ts | 10 +- .../interactions/src/useDOMPropsResponder.ts | 2 +- .../@react-aria/interactions/src/useFocus.ts | 6 +- .../interactions/src/useFocusWithin.ts | 5 +- .../@react-aria/interactions/src/useHover.ts | 11 ++- .../interactions/src/useKeyboard.ts | 5 +- .../interactions/src/useLongPress.ts | 6 +- .../@react-aria/interactions/src/useMove.ts | 8 +- .../@react-aria/interactions/src/usePress.ts | 92 +++++++++--------- packages/@react-aria/label/src/index.ts | 7 +- packages/@react-aria/label/src/useField.ts | 7 +- packages/@react-aria/label/src/useLabel.ts | 4 +- packages/@react-aria/landmark/src/index.ts | 4 +- .../@react-aria/landmark/src/useLandmark.ts | 30 +++--- packages/@react-aria/link/src/index.ts | 4 +- packages/@react-aria/link/src/useLink.ts | 9 +- packages/@react-aria/list/src/useList.ts | 8 +- packages/@react-aria/list/src/useListItem.ts | 32 +++--- packages/@react-aria/listbox/src/index.ts | 10 +- .../@react-aria/listbox/src/useListBox.ts | 8 +- .../listbox/src/useListBoxSection.ts | 9 +- packages/@react-aria/listbox/src/useOption.ts | 11 ++- .../@react-aria/live-announcer/src/index.ts | 3 +- packages/@react-aria/menu/docs/tailwind.png | Bin 0 -> 23982 bytes .../@react-aria/menu/docs/useMenuTrigger.mdx | 10 ++ packages/@react-aria/menu/src/useMenu.ts | 6 +- packages/@react-aria/menu/src/useMenuItem.ts | 14 +-- .../@react-aria/menu/src/useMenuSection.ts | 9 +- .../@react-aria/menu/src/useMenuTrigger.ts | 2 +- packages/@react-aria/meter/src/index.ts | 3 +- packages/@react-aria/meter/src/useMeter.ts | 6 +- packages/@react-aria/numberfield/src/index.ts | 3 +- .../numberfield/src/useNumberField.ts | 9 +- .../overlays/src/ariaHideOutside.ts | 8 +- .../overlays/src/calculatePosition.ts | 16 +-- packages/@react-aria/overlays/src/index.ts | 15 ++- .../overlays/src/useCloseOnScroll.ts | 4 +- .../@react-aria/overlays/src/useModal.tsx | 9 +- .../@react-aria/overlays/src/useOverlay.ts | 23 ++--- .../overlays/src/useOverlayPosition.ts | 15 +-- .../overlays/src/useOverlayTrigger.ts | 7 +- packages/@react-aria/pagination/src/index.ts | 3 +- packages/@react-aria/progress/src/index.ts | 3 +- .../progress/src/useProgressBar.ts | 6 +- packages/@react-aria/radio/package.json | 3 +- packages/@react-aria/radio/src/index.ts | 5 +- packages/@react-aria/radio/src/useRadio.ts | 4 +- .../@react-aria/radio/src/useRadioGroup.ts | 6 +- packages/@react-aria/searchfield/src/index.ts | 3 +- .../searchfield/src/useSearchField.ts | 7 +- .../@react-aria/select/docs/popup-example.png | Bin 0 -> 29771 bytes .../@react-aria/select/docs/useSelect.mdx | 7 ++ .../@react-aria/select/src/HiddenSelect.tsx | 5 +- packages/@react-aria/select/src/index.ts | 5 +- packages/@react-aria/select/src/useSelect.ts | 14 +-- packages/@react-aria/selection/src/index.ts | 11 ++- .../selection/src/useSelectableCollection.ts | 14 +-- .../selection/src/useSelectableItem.ts | 8 +- .../selection/src/useSelectableList.ts | 6 +- .../selection/src/useTypeSelect.ts | 6 +- packages/@react-aria/separator/src/index.ts | 4 +- .../@react-aria/separator/src/useSeparator.ts | 5 +- packages/@react-aria/sidenav/src/index.ts | 5 +- .../@react-aria/sidenav/src/useSideNav.ts | 8 +- .../@react-aria/sidenav/src/useSideNavItem.ts | 5 +- packages/@react-aria/slider/package.json | 1 + packages/@react-aria/slider/src/index.ts | 5 +- packages/@react-aria/slider/src/useSlider.ts | 25 ++--- .../@react-aria/slider/src/useSliderThumb.ts | 16 +-- .../slider/stories/StoryMultiSlider.tsx | 2 +- .../@react-aria/slider/test/useSlider.test.js | 8 +- .../slider/test/useSliderThumb.test.js | 8 +- packages/@react-aria/spinbutton/src/index.ts | 4 +- .../spinbutton/src/useSpinButton.ts | 6 +- packages/@react-aria/ssr/src/index.ts | 3 +- packages/@react-aria/switch/src/index.ts | 4 +- packages/@react-aria/table/src/index.ts | 16 +-- .../@react-aria/table/src/useTableCell.ts | 7 +- .../table/src/useTableColumnHeader.ts | 9 +- .../table/src/useTableColumnResize.ts | 5 +- .../table/src/useTableHeaderRow.ts | 7 +- packages/@react-aria/table/src/useTableRow.ts | 3 +- packages/@react-aria/tabs/src/index.ts | 7 +- packages/@react-aria/tabs/src/useTab.ts | 7 +- packages/@react-aria/tabs/src/useTabList.ts | 5 +- packages/@react-aria/tabs/src/useTabPanel.ts | 7 +- packages/@react-aria/tag/src/index.ts | 7 +- packages/@react-aria/tag/src/useTag.ts | 11 ++- packages/@react-aria/tag/src/useTagGroup.ts | 8 +- packages/@react-aria/textfield/src/index.ts | 6 +- .../@react-aria/textfield/src/useTextField.ts | 8 +- packages/@react-aria/toast/src/index.ts | 3 +- packages/@react-aria/toast/src/useToast.ts | 6 +- packages/@react-aria/toggle/src/index.ts | 4 +- packages/@react-aria/toggle/src/useToggle.ts | 2 +- packages/@react-aria/tooltip/src/index.ts | 5 +- .../@react-aria/tooltip/src/useTooltip.ts | 4 +- .../tooltip/src/useTooltipTrigger.ts | 10 +- packages/@react-aria/tree/src/index.ts | 3 +- .../@react-aria/utils/src/filterDOMProps.ts | 3 +- .../utils/src/focusWithoutScrolling.ts | 6 +- packages/@react-aria/utils/src/index.ts | 49 +++++----- packages/@react-aria/utils/src/number.ts | 13 --- packages/@react-aria/utils/src/platform.ts | 2 +- .../@react-aria/utils/src/useDescription.ts | 2 +- .../utils/src/useResizeObserver.ts | 2 +- packages/@react-aria/virtualizer/src/index.ts | 11 ++- packages/@react-aria/virtualizer/src/utils.ts | 4 +- .../@react-aria/visually-hidden/package.json | 1 + .../visually-hidden/src/VisuallyHidden.tsx | 7 +- .../@react-aria/visually-hidden/src/index.ts | 3 +- .../@react-spectrum/accordion/src/index.ts | 4 +- .../@react-spectrum/actionbar/src/index.ts | 6 +- .../@react-spectrum/actiongroup/src/index.ts | 4 +- packages/@react-spectrum/alert/src/index.ts | 4 +- .../@react-spectrum/autocomplete/src/index.ts | 4 +- packages/@react-spectrum/avatar/src/index.ts | 4 +- .../@react-spectrum/breadcrumbs/src/index.ts | 4 +- packages/@react-spectrum/button/src/index.ts | 14 ++- .../@react-spectrum/buttongroup/src/index.ts | 4 +- packages/@react-spectrum/card/src/index.ts | 14 ++- .../@react-spectrum/checkbox/src/index.ts | 6 +- .../checkbox/stories/Checkbox.stories.tsx | 35 +++++++ .../@react-spectrum/color/src/ColorArea.tsx | 3 + packages/@react-spectrum/color/src/index.ts | 9 +- .../color/stories/ColorArea.stories.tsx | 53 ++++++---- .../color/test/ColorArea.test.tsx | 8 ++ .../color/test/ColorSlider.test.tsx | 4 +- .../@react-spectrum/combobox/src/index.ts | 2 +- .../combobox/stories/ComboBox.stories.tsx | 41 ++++++++ .../contextualhelp/src/index.ts | 2 +- .../@react-spectrum/datepicker/src/styles.css | 20 ++++ packages/@react-spectrum/dialog/src/index.ts | 10 +- .../@react-spectrum/divider/src/Divider.tsx | 1 + packages/@react-spectrum/divider/src/index.ts | 2 +- packages/@react-spectrum/dnd/src/index.ts | 6 +- packages/@react-spectrum/form/src/index.ts | 2 +- packages/@react-spectrum/icon/src/index.ts | 6 +- .../illustratedmessage/src/index.ts | 2 +- packages/@react-spectrum/image/src/index.ts | 2 +- .../@react-spectrum/label/intl/ar-AE.json | 3 +- .../@react-spectrum/label/intl/en-US.json | 3 +- packages/@react-spectrum/label/package.json | 2 + packages/@react-spectrum/label/src/Field.tsx | 25 ++++- .../label/src/ReadOnlyField.tsx | 82 ++++++++++++++++ packages/@react-spectrum/label/src/index.ts | 6 +- packages/@react-spectrum/layout/src/index.ts | 4 +- packages/@react-spectrum/link/src/index.ts | 2 +- .../@react-spectrum/list/src/ListViewItem.tsx | 4 +- packages/@react-spectrum/list/src/styles.css | 18 ++-- .../list/stories/ListView.stories.tsx | 2 +- packages/@react-spectrum/listbox/src/index.ts | 4 +- packages/@react-spectrum/menu/src/index.ts | 6 +- packages/@react-spectrum/meter/src/index.ts | 2 +- .../@react-spectrum/numberfield/src/index.ts | 2 +- .../@react-spectrum/overlays/src/index.ts | 10 +- .../@react-spectrum/pagination/src/index.ts | 2 +- packages/@react-spectrum/picker/src/index.ts | 2 +- .../@react-spectrum/progress/src/index.ts | 6 +- .../@react-spectrum/provider/src/index.ts | 2 +- packages/@react-spectrum/radio/src/index.ts | 4 +- .../@react-spectrum/searchfield/src/index.ts | 2 +- .../@react-spectrum/searchwithin/src/index.ts | 2 +- packages/@react-spectrum/sidenav/src/index.ts | 2 +- packages/@react-spectrum/slider/src/index.ts | 4 +- .../slider/test/RangeSlider.test.tsx | 3 +- .../slider/test/Slider.test.tsx | 6 +- .../@react-spectrum/statuslight/src/index.ts | 2 +- .../@react-spectrum/story-utils/src/index.ts | 4 +- packages/@react-spectrum/switch/src/index.ts | 2 +- .../switch/stories/Switch.stories.tsx | 21 ++++ packages/@react-spectrum/tabs/src/index.ts | 2 +- packages/@react-spectrum/text/src/index.ts | 6 +- .../textfield/src/TextArea.tsx | 9 +- .../textfield/src/TextField.tsx | 7 +- .../textfield/src/TextFieldBase.tsx | 14 ++- .../@react-spectrum/textfield/src/index.ts | 6 +- .../textfield/stories/TextArea.stories.js | 72 +++++++++++++- .../textfield/stories/Textfield.stories.js | 69 +++++++++++++ packages/@react-spectrum/toast/src/index.ts | 6 +- packages/@react-spectrum/tooltip/src/index.ts | 4 +- packages/@react-spectrum/tree/src/index.ts | 2 +- packages/@react-spectrum/utils/src/index.ts | 32 +++--- .../@react-spectrum/utils/src/useDOMRef.ts | 6 +- .../utils/src/useResizeObserver.ts | 13 --- packages/@react-spectrum/view/src/index.ts | 10 +- packages/@react-spectrum/well/src/index.ts | 4 +- packages/@react-stately/checkbox/src/index.ts | 4 +- .../@react-stately/collections/src/index.ts | 10 +- packages/@react-stately/color/src/index.ts | 13 ++- .../color/src/useColorAreaState.ts | 4 + .../color/src/useColorWheelState.ts | 2 + packages/@react-stately/combobox/src/index.ts | 4 +- packages/@react-stately/data/src/index.ts | 6 +- packages/@react-stately/dnd/src/index.ts | 6 +- packages/@react-stately/grid/src/index.ts | 6 +- packages/@react-stately/layout/src/index.ts | 6 +- packages/@react-stately/list/src/index.ts | 8 +- packages/@react-stately/menu/src/index.ts | 4 +- .../@react-stately/numberfield/src/index.ts | 4 +- packages/@react-stately/overlays/src/index.ts | 4 +- .../@react-stately/pagination/src/index.ts | 4 +- packages/@react-stately/radio/src/index.ts | 4 +- .../@react-stately/searchfield/src/index.ts | 4 +- packages/@react-stately/select/src/index.ts | 4 +- .../@react-stately/selection/src/index.ts | 7 +- packages/@react-stately/slider/src/index.ts | 4 +- packages/@react-stately/table/src/index.ts | 19 ++-- packages/@react-stately/tabs/src/index.ts | 4 +- packages/@react-stately/toast/src/index.ts | 5 +- packages/@react-stately/toggle/src/index.ts | 4 +- packages/@react-stately/tooltip/src/index.ts | 4 +- packages/@react-stately/tree/src/index.ts | 4 +- packages/@react-stately/utils/src/index.ts | 5 +- .../@react-stately/virtualizer/src/index.ts | 16 +-- packages/@react-types/label/src/index.d.ts | 8 +- packages/@react-types/shared/src/dom.d.ts | 15 +++ packages/@react-types/shared/src/events.d.ts | 2 +- ...l+transformer-typescript-types+2.2.1.patch | 13 +++ 299 files changed, 2129 insertions(+), 1030 deletions(-) create mode 100644 packages/@react-aria/dialog/docs/tailwind.png create mode 100644 packages/@react-aria/menu/docs/tailwind.png create mode 100644 packages/@react-aria/select/docs/popup-example.png delete mode 100644 packages/@react-aria/utils/src/number.ts create mode 100644 packages/@react-spectrum/label/src/ReadOnlyField.tsx delete mode 100644 packages/@react-spectrum/utils/src/useResizeObserver.ts create mode 100644 patches/@parcel+transformer-typescript-types+2.2.1.patch diff --git a/packages/@adobe/spectrum-css-temp/components/button/skin.css b/packages/@adobe/spectrum-css-temp/components/button/skin.css index 54797e05ef8..b6e0dfb8c33 100644 --- a/packages/@adobe/spectrum-css-temp/components/button/skin.css +++ b/packages/@adobe/spectrum-css-temp/components/button/skin.css @@ -1337,6 +1337,8 @@ governing permissions and limitations under the License. --spectrum-logicbutton-or-text-color: ButtonText; --spectrum-button-primary-focus-ring-color-key-focus: CanvasText; --spectrum-button-primary-focus-ring-size-key-focus: 3px; + --spectrum-dropdown-border-color-key-focus: Highlight; + } .spectrum-Button--overBackground { @@ -1371,4 +1373,16 @@ governing permissions and limitations under the License. --spectrum-actionbutton-emphasized-icon-color-key-focus: HighlightText; } } + .spectrum-FieldButton { + &:focus-ring, + &.is-focused { + &:not(.spectrum-FieldButton--quiet) { + outline:2px solid Highlight; + } + &.spectrum-FieldButton--quiet { + forced-color-adjust: none; + box-shadow: 0 2px 0 0 var(--spectrum-dropdown-border-color-key-focus); + } + } + } } diff --git a/packages/@adobe/spectrum-css-temp/components/calendar/skin.css b/packages/@adobe/spectrum-css-temp/components/calendar/skin.css index a13194dae7f..4ed1f7be39f 100644 --- a/packages/@adobe/spectrum-css-temp/components/calendar/skin.css +++ b/packages/@adobe/spectrum-css-temp/components/calendar/skin.css @@ -151,3 +151,58 @@ governing permissions and limitations under the License. } } } + +@media (forced-colors: active) { + .spectrum-Calendar-date { + color: CanvasText; + forced-color-adjust: none; + --spectrum-calendar-button-icon-color: ButtonText; + --spectrum-calendar-day-background-color-cap-selected: Highlight; + --spectrum-calendar-day-background-color-cap-invalid-selected: Highlight; + --spectrum-calendar-day-background-color-cap-invalid-selected-down: Highlight; + --spectrum-calendar-day-background-color-cap-invalid-selected-hover: Highlight; + --spectrum-calendar-day-background-color-cap-selected-down: Highlight; + --spectrum-calendar-day-background-color-cap-selected-hover: Highlight; + --spectrum-calendar-day-background-color-disabled: ButtonFace; + --spectrum-calendar-day-background-color-down: ButtonFace; + --spectrum-calendar-day-background-color-hover: ButtonFace; + --spectrum-calendar-day-background-color-invalid-selected: Highlight; + --spectrum-calendar-day-background-color-selected-hover: Highlight; + --spectrum-calendar-day-background-color-selected: Highlight; + --spectrum-calendar-day-border-color-key-focus: ButtonText; + --spectrum-calendar-day-text-color-cap-selected: HighlightText; + --spectrum-calendar-day-text-color-cap-selected-hover: HighlightText; + --spectrum-calendar-day-text-color-disabled: GrayText; + --spectrum-calendar-day-text-color-hover: ButtonText; + --spectrum-calendar-day-text-color-selected-hover: HighlightText; + --spectrum-calendar-day-title-text-color: CanvasText; + --spectrum-calendar-day-today-background-color: ButtonText; + --spectrum-calendar-day-today-background-color-down: ButtonText; + --spectrum-calendar-day-today-background-color-hover: ButtonText; + --spectrum-calendar-day-today-text-color: ButtonFace; + &.is-range-selection { + color: HighlightText; + } + &.is-disabled { + &.is-range-selection { + background: Highlight; + color: HighlightText; + } + + &.is-selected { + background: Highlight; + color: HighlightText; + } + } + &.is-unavailable { + &.is-today { + color: var(--spectrum-calendar-day-text-color-hover); + } + } + &:hover { + &.is-today { + color: var(--spectrum-calendar-day-today-text-color); + } + } + } +} diff --git a/packages/@adobe/spectrum-css-temp/components/checkbox/skin.css b/packages/@adobe/spectrum-css-temp/components/checkbox/skin.css index 71a8eba846b..76dd006d733 100644 --- a/packages/@adobe/spectrum-css-temp/components/checkbox/skin.css +++ b/packages/@adobe/spectrum-css-temp/components/checkbox/skin.css @@ -1,4 +1,4 @@ -/* +/* Copyright 2019 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 @@ -207,3 +207,62 @@ governing permissions and limitations under the License. } } } +@media (forced-colors: active) { + .spectrum-Checkbox { + forced-color-adjust: none; + --spectrum-checkbox-box-border-color: ButtonText; + --spectrum-checkbox-box-border-color-error: Highlight; + --spectrum-checkbox-box-border-color-error-down: Highlight; + --spectrum-checkbox-box-border-color-error-hover: Highlight; + --spectrum-checkbox-box-border-color-key-focus: Highlight; + --spectrum-checkbox-checkmark-color: HighlightText; + --spectrum-checkbox-emphasized-box-background-color: ButtonFace; + --spectrum-checkbox-emphasized-box-background-color-disabled: ButtonFace; + --spectrum-checkbox-emphasized-box-border-color: ButtonText; + --spectrum-checkbox-emphasized-box-border-color-disabled: GrayText; + --spectrum-checkbox-emphasized-box-border-color-down: Highlight; + --spectrum-checkbox-emphasized-box-border-color-hover: Highlight; + --spectrum-checkbox-emphasized-box-border-color-selected: Highlight; + --spectrum-checkbox-emphasized-box-border-color-selected-down: Highlight; + --spectrum-checkbox-emphasized-box-border-color-selected-hover: Highlight; + --spectrum-checkbox-emphasized-box-border-color-selected-key-focus: Highlight; + --spectrum-checkbox-emphasized-text-color: FieldText; + --spectrum-checkbox-emphasized-text-color-down: FieldText; + --spectrum-checkbox-emphasized-text-color-hover: FieldText; + --spectrum-checkbox-emphasized-text-color-key-focus: FieldText; + --spectrum-checkbox-focus-ring-color-key-focus: ButtonText; + --spectrum-checkbox-quiet-box-border-color-selected: Highlight; + --spectrum-checkbox-quiet-box-border-color-selected-down: Highlight; + --spectrum-checkbox-quiet-box-border-color-selected-hover: Highlight; + --spectrum-checkbox-text-color: FieldText; + --spectrum-checkbox-text-color-disabled: GrayText; + --spectrum-checkbox-text-color-error: FieldText; + --spectrum-checkbox-text-color-error-down: FieldText; + --spectrum-checkbox-text-color-error-hover: FieldText; + + &.is-invalid { + .spectrum-Checkbox-box { + &:before { + border-color: var(--spectrum-checkbox-box-border-color); + } + } + &.is-indeterminate .spectrum-Checkbox-box { + &:before { + border-color: var(--spectrum-checkbox-box-border-color-error); + } + } + &.is-disabled .spectrum-Checkbox-input:checked + .spectrum-Checkbox-box { + &:before { + border-color: GrayText; + } + } + /* Extra-specific selectors added here to handle checked state */ + .spectrum-Checkbox-input:checked + .spectrum-Checkbox-box + { + &:before { + border-color: var(--spectrum-checkbox-box-border-color-error); + } + } + } + } +} diff --git a/packages/@adobe/spectrum-css-temp/components/colorarea/skin.css b/packages/@adobe/spectrum-css-temp/components/colorarea/skin.css index e558a2e2542..3f6a2b20b97 100644 --- a/packages/@adobe/spectrum-css-temp/components/colorarea/skin.css +++ b/packages/@adobe/spectrum-css-temp/components/colorarea/skin.css @@ -9,28 +9,29 @@ .spectrum-ColorHandle-color { forced-color-adjust: none; } - + .spectrum-ColorArea { &.is-disabled { background: var(--spectrum-colorarea-fill-color-disabled); - + &:before { box-shadow: inset 0 0 0 var(--spectrum-colorarea-border-size) var(--spectrum-colorarea-border-color-disabled); } - + .spectrum-ColorArea-gradient { display: none; } } } - + @media (forced-colors: active) { .spectrum-ColorArea { --spectrum-colorarea-fill-color-disabled : GrayText; } .spectrum-ColorArea { + forced-color-adjust: none; &.is-disabled { forced-color-adjust: none; } } - } \ No newline at end of file + } diff --git a/packages/@adobe/spectrum-css-temp/components/colorhandle/skin.css b/packages/@adobe/spectrum-css-temp/components/colorhandle/skin.css index 86ef9874b03..6a0ca1347f6 100644 --- a/packages/@adobe/spectrum-css-temp/components/colorhandle/skin.css +++ b/packages/@adobe/spectrum-css-temp/components/colorhandle/skin.css @@ -34,3 +34,13 @@ .spectrum-ColorHandle-color { box-shadow: inset 0 0 0 var(--spectrum-colorhandle-outer-border-size) var(--spectrum-colorhandle-outer-border-color); } + +@media (forced-colors: active) { + .spectrum-ColorHandle { + forced-color-adjust: none; + --spectrum-colorhandle-inner-border-color-disabled: GrayText; + --spectrum-colorhandle-fill-color-disabled: GrayText; + --spectrum-colorhandle-inner-border-color: ButtonText; + --spectrum-colorhandle-outer-border-color: ButtonFace; + } +} \ No newline at end of file diff --git a/packages/@adobe/spectrum-css-temp/components/colorloupe/skin.css b/packages/@adobe/spectrum-css-temp/components/colorloupe/skin.css index 322fb484d59..9efd1222765 100644 --- a/packages/@adobe/spectrum-css-temp/components/colorloupe/skin.css +++ b/packages/@adobe/spectrum-css-temp/components/colorloupe/skin.css @@ -9,3 +9,11 @@ .spectrum-ColorLoupe-inner-checker { fill: var(--spectrum-global-color-static-gray-500); } + +@media (forced-colors: active) { + .spectrum-ColorLoupe { + forced-color-adjust: none; + --spectrum-colorloupe-inner-border-color: ButtonText; + --spectrum-colorloupe-outer-border-color: ButtonFace; + } +} \ No newline at end of file diff --git a/packages/@adobe/spectrum-css-temp/components/colorslider/skin.css b/packages/@adobe/spectrum-css-temp/components/colorslider/skin.css index 592abad2da9..662aa3b5224 100644 --- a/packages/@adobe/spectrum-css-temp/components/colorslider/skin.css +++ b/packages/@adobe/spectrum-css-temp/components/colorslider/skin.css @@ -25,3 +25,13 @@ margin: 0; } } + +@media (forced-colors: active) { + .spectrum-ColorSlider { + forced-color-adjust: none; + --spectrum-colorslider-fill-color-disabled: GrayText; + --spectrum-colorslider-border-color-disabled: GrayText; + --spectrum-colorslider-border-color: ButtonText; + --spectrum-colorarea-border-color: ButtonText; + } +} \ No newline at end of file diff --git a/packages/@adobe/spectrum-css-temp/components/colorwheel/skin.css b/packages/@adobe/spectrum-css-temp/components/colorwheel/skin.css index dd5804877d4..317f67258af 100644 --- a/packages/@adobe/spectrum-css-temp/components/colorwheel/skin.css +++ b/packages/@adobe/spectrum-css-temp/components/colorwheel/skin.css @@ -27,3 +27,13 @@ border-color: var(--spectrum-colorwheel-border-color); } } + +@media (forced-colors: active) { + .spectrum-ColorWheel { + forced-color-adjust: none; + --spectrum-colorwheel-fill-color-disabled: GrayText; + --spectrum-colorwheel-border-color-disabled: GrayText; + --spectrum-colorwheel-border-color: ButtonText; + --spectrum-colorarea-border-color: ButtonText; + } +} \ No newline at end of file diff --git a/packages/@adobe/spectrum-css-temp/components/dropdown/skin.css b/packages/@adobe/spectrum-css-temp/components/dropdown/skin.css index 44d3776f166..6b7dc91c848 100644 --- a/packages/@adobe/spectrum-css-temp/components/dropdown/skin.css +++ b/packages/@adobe/spectrum-css-temp/components/dropdown/skin.css @@ -11,6 +11,9 @@ governing permissions and limitations under the License. */ .spectrum-Dropdown { + /* Override to gray-800 to meet WCAG */ + --spectrum-dropdown-placeholder-text-color: var(--spectrum-global-color-gray-800); + .spectrum-Dropdown-trigger:hover { .spectrum-Dropdown-chevron { color: var(--spectrum-dropdown-icon-color-hover); @@ -62,8 +65,7 @@ governing permissions and limitations under the License. .spectrum-Dropdown-label { &.is-placeholder { - /* Override to gray-800 to meet WCAG */ - color: var(--spectrum-global-color-gray-800); + color: var(--spectrum-dropdown-placeholder-text-color); } } @@ -85,3 +87,29 @@ governing permissions and limitations under the License. color: var(--spectrum-dropdown-icon-color-key-focus) } } + +@media (forced-colors: active) { + .spectrum-Dropdown { + --spectrum-dropdown-icon-color: ButtonText; + --spectrum-dropdown-icon-color-disabled: GrayText; + --spectrum-dropdown-icon-color-hover: ButtonText; + --spectrum-dropdown-icon-color-key-focus: ButtonText; + --spectrum-dropdown-placeholder-text-color: GrayText; + --spectrum-dropdown-placeholder-text-color-disabled: GrayText; + --spectrum-dropdown-placeholder-text-color-down: GrayText; + --spectrum-dropdown-placeholder-text-color-hover: GrayText; + --spectrum-dropdown-placeholder-text-color-key-focus: GrayText; + --spectrum-dropdown-placeholder-text-color-mouse-focus: GrayText; + --spectrum-dropdown-text-color-disabled: GrayText; + --spectrum-dropdown-validation-icon-color-error: ButtonText; + } + /*Override the label behaviour when NOT a placeholder*/ + .spectrum-Dropdown-trigger { + &:hover .spectrum-Dropdown-label { + color: ButtonText; + &.is-placeholder { + color: GrayText; + } + } + } +} diff --git a/packages/@adobe/spectrum-css-temp/components/inputgroup/skin.css b/packages/@adobe/spectrum-css-temp/components/inputgroup/skin.css index df0007a567c..fe9922e371a 100644 --- a/packages/@adobe/spectrum-css-temp/components/inputgroup/skin.css +++ b/packages/@adobe/spectrum-css-temp/components/inputgroup/skin.css @@ -217,3 +217,81 @@ governing permissions and limitations under the License. } } } +@media (forced-colors: active) { + .spectrum-InputGroup { + --spectrum-dropdown-border-color-error: Highlight; + --spectrum-dropdown-border-color-key-focus: Highlight; + --spectrum-textfield-border-color-error: Highlight; + --spectrum-textfield-border-color-hover: Highlight; + --spectrum-textfield-border-color-key-focus: Highlight; + --spectrum-textfield-quiet-border-color: CanvasText; + --spectrum-textfield-quiet-border-color-disabled: GrayText; + --spectrum-textfield-quiet-border-color-hover: Highlight; + --spectrum-textfield-quiet-border-color-key-focus: Highlight; + --spectrum-textfield-text-color-disabled: GrayText; + forced-color-adjust: none; + .spectrum-InputGroup-input { + forced-color-adjust: auto; + } + .spectrum-FieldButton { + forced-color-adjust: auto; + } + + &.is-disabled:not(.spectrum-InputGroup--invalid):not(.spectrum-InputGroup--quiet) .spectrum-FieldButton { + border-color: GrayText; + border-width: 1px; + } + } + + .spectrum-InputGroup--quiet { + /* + specifically for readonly inputgroups that aren't disabled since the button will have the disabled class + but we don't want the border color to be the disabled quiet one + */ + &:not(.is-disabled) { + .spectrum-FieldButton { + &:disabled, + &:disabled:hover { + border-color: var(--spectrum-textfield-quiet-border-color); + } + } + } + &.is-disabled { + .spectrum-FieldButton { + border-color: GrayText; + } + } + } + .spectrum-InputGroup--invalid { + .spectrum-FieldButton { + border-color: Highlight; + } + &.is-disabled:not(.spectrum-InputGroup--quiet) .spectrum-InputGroup-input { + border-color: GrayText; + } + &.is-disabled { + &.spectrum-InputGroup--quiet .spectrum-InputGroup-input { + border-color: GrayText; + } + &.spectrum-InputGroup--quiet .spectrum-FieldButton { + border-color: GrayText; + } + } + &.is-disabled:not(.spectrum-InputGroup--quiet) .spectrum-FieldButton { + border-color: GrayText; + } + } + + .spectrum-InputGroup { + &:focus-ring + { + &:not(.spectrum-InputGroup--quiet) { + outline:2px solid Highlight; + } + &.spectrum-InputGroup--quiet { + box-shadow: 0 2px 0 0 var(--spectrum-dropdown-border-color-key-focus); + } + } + } +} + diff --git a/packages/@adobe/spectrum-css-temp/components/menu/skin.css b/packages/@adobe/spectrum-css-temp/components/menu/skin.css index 2cc6d19c58d..c328dce0a7d 100644 --- a/packages/@adobe/spectrum-css-temp/components/menu/skin.css +++ b/packages/@adobe/spectrum-css-temp/components/menu/skin.css @@ -69,3 +69,49 @@ governing permissions and limitations under the License. .spectrum-Menu-description { color: var(--spectrum-global-color-gray-700); } +@media (forced-colors: active) { + .spectrum-Menu-divider { + background-color: CanvasText; + forced-color-adjust: none; + } + .spectrum-Menu-item { + forced-color-adjust: none; + --spectrum-heading-subtitle3-text-color: ButtonText; + --spectrum-selectlist-background-color: ButtonFace; + --spectrum-selectlist-divider-color: ButtonText; + --spectrum-selectlist-option-background-color: ButtonFace; + --spectrum-selectlist-option-background-color-disabled: ButtonFace; + --spectrum-selectlist-option-background-color-down: ButtonFace; + --spectrum-selectlist-option-background-color-hover: Highlight; + --spectrum-selectlist-option-background-color-key-focus: Highlight; + --spectrum-selectlist-option-focus-indicator-color: Highlight; + --spectrum-selectlist-option-icon-color-selected: Highlight; + --spectrum-selectlist-option-text-color: ButtonText; + --spectrum-selectlist-option-text-color-disabled: GrayText; + --spectrum-selectlist-option-text-color-hover: HighlightText; + --spectrum-selectlist-option-text-color-key-focus: HighlightText; + --spectrum-alias-border-color-focus: Highlight; + /* Overriding menu descriptions which were hardcoded to gray-700 */ + .spectrum-Menu-description { + color: var(--spectrum-selectlist-option-text-color); + } + &:hover, + &:focus, + &.is-highlighted, + &.is-open, + &:focus-ring, + &.is-focused { + &.is-selected { + .spectrum-Menu-checkmark { + color: HighlightText; + } + } + .spectrum-Menu-description { + color: var(--spectrum-selectlist-option-text-color-key-focus); + background-color: var(--spectrum-selectlist-option-background-color-key-focus); + } + background-color: var(--spectrum-selectlist-option-background-color-key-focus); + color: var(--spectrum-selectlist-option-text-color-key-focus); + } + } +} \ No newline at end of file diff --git a/packages/@adobe/spectrum-css-temp/components/radio/skin.css b/packages/@adobe/spectrum-css-temp/components/radio/skin.css index e6763ac9bef..3a362c5c18b 100644 --- a/packages/@adobe/spectrum-css-temp/components/radio/skin.css +++ b/packages/@adobe/spectrum-css-temp/components/radio/skin.css @@ -189,3 +189,45 @@ governing permissions and limitations under the License. } } } + +@media (forced-colors: active) { + .spectrum-Radio { + forced-color-adjust: none; + --spectrum-radio-circle-border-color-error-key-focus: ButtonText; + --spectrum-radio-circle-border-color-error-selected-key-focus: Highlight; + --spectrum-radio-circle-border-color-key-focus: Highlight; + --spectrum-radio-emphasized-circle-background-color: ButtonFace; + --spectrum-radio-emphasized-circle-border-color-disabled: GrayText; + --spectrum-radio-emphasized-circle-border-color-down: Highlight; + --spectrum-radio-emphasized-circle-border-color-error: ButtonText; + --spectrum-radio-emphasized-circle-border-color-error-down: Highlight; + --spectrum-radio-emphasized-circle-border-color-error-hover: Highlight; + --spectrum-radio-emphasized-circle-border-color-hover: Highlight; + --spectrum-radio-emphasized-circle-border-color-selected-down: Highlight; + --spectrum-radio-emphasized-circle-border-color-selected-hover: Highlight; + --spectrum-radio-emphasized-circle-border-color-selected-key-focus: Highlight; + --spectrum-radio-emphasized-circle-border-color-selected: Highlight; + --spectrum-radio-emphasized-circle-border-color: ButtonText; + --spectrum-radio-emphasized-text-color-disabled: GrayText; + --spectrum-radio-emphasized-text-color-down: CanvasText; + --spectrum-radio-emphasized-text-color-hover: CanvasText; + --spectrum-radio-emphasized-text-color: CanvasText; + --spectrum-radio-focus-ring-color-key-focus: ButtonText; + --spectrum-radio-quiet-circle-border-color-selected: Highlight; + --spectrum-radio-quiet-circle-border-color-selected-down: Highlight; + --spectrum-radio-quiet-circle-border-color-selected-hover: Highlight; + --spectrum-radio-quiet-circle-border-color-selected-key-focus: Highlight; + --spectrum-radio-focus-ring-gap-key-focus: var(--spectrum-global-dimension-static-size-25); + --spectrum-radio-focus-ring-size: var(--spectrum-global-dimension-static-size-40); + &.is-invalid:hover { + .spectrum-Radio-label { + color: var(--spectrum-radio-emphasized-circle-border-color-error); + } + } + &.is-invalid:active { + .spectrum-Radio-label { + color: var(--spectrum-radio-emphasized-circle-border-color-error); + } + } + } +} diff --git a/packages/@adobe/spectrum-css-temp/components/slider/skin.css b/packages/@adobe/spectrum-css-temp/components/slider/skin.css index c6644b4a30c..4426f0bc6d7 100644 --- a/packages/@adobe/spectrum-css-temp/components/slider/skin.css +++ b/packages/@adobe/spectrum-css-temp/components/slider/skin.css @@ -9,12 +9,14 @@ 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. */ - +@media (forced-colors: none) { + :root { + --spectrum-slider-handle-border-color-key-focus: var(--spectrum-global-color-gray-800); + } +} :root { --spectrum-slider-focus-ring-size-key-focus: var(--spectrum-alias-focus-ring-size); - /* todo: remove when fixed */ - --spectrum-slider-handle-border-color-key-focus: var(--spectrum-global-color-gray-800); --spectrum-slider-handle-focus-ring-color-key-focus: var(--spectrum-alias-focus-color); } @@ -327,3 +329,46 @@ governing permissions and limitations under the License. margin-inline-end: var(--spectrum-slider-label-gap-x); } } +@media (forced-colors: active) { + :root { + --spectrum-slider-handle-border-color-key-focus: Highlight; + } + .spectrum-Dial { + --spectrum-dial-border-color-hover: Highlight; + --spectrum-dial-handle-marker-color: ButtonText; + --spectrum-dial-handle-marker-color-hover: Highlight; + --spectrum-dial-handle-marker-color-key-focus: Highlight; + --spectrum-dial-min-max-tick-color: GrayText; + } + .spectrum-Slider { + forced-color-adjust: none; + --spectrum-alias-background-color-default: ButtonFace; + --spectrum-alias-focus-color: ButtonText; + --spectrum-label-text-color: CanvasText; + --spectrum-label-text-color-disabled: GrayText; + --spectrum-slider-color-handle-border-color: ButtonText; + --spectrum-slider-color-handle-border-color-disabled: GrayText; + --spectrum-slider-color-handle-color: ButtonFace; + --spectrum-slider-color-handle-color-disabled: ButtonFace; + --spectrum-slider-color-handle-outset-border-color-key-focus: ButtonText; + --spectrum-slider-color-track-border-color: ButtonText; + --spectrum-slider-color-track-color-disabled: GrayText; + --spectrum-slider-fill-track-color: Highlight; + --spectrum-slider-fill-track-color-disabled: GrayText; + --spectrum-slider-handle-background-color: ButtonFace; + --spectrum-slider-handle-background-color-down: ButtonFace; + --spectrum-slider-handle-background-color-key-focus: Highlight; + --spectrum-slider-handle-border-color: ButtonText; + --spectrum-slider-handle-border-color-disabled: GrayText; + --spectrum-slider-handle-border-color-down: Highlight; + --spectrum-slider-handle-border-color-hover: Highlight; + --spectrum-slider-handle-border-color-key-focus: Highlight; + --spectrum-slider-handle-focus-ring-color-key-focus: ButtonText; + --spectrum-slider-player-track-buffer-color: ButtonText; + --spectrum-slider-player-track-buffer-color-disabled: GrayText; + --spectrum-slider-ramp-track-color-disabled: GrayText; + --spectrum-slider-tick-mark-color: ButtonText; + --spectrum-slider-track-color: ButtonText; + --spectrum-slider-track-color-disabled: GrayText; + } +} diff --git a/packages/@adobe/spectrum-css-temp/components/stepper/skin.css b/packages/@adobe/spectrum-css-temp/components/stepper/skin.css index 8af0d086df7..6d295c6f7c4 100644 --- a/packages/@adobe/spectrum-css-temp/components/stepper/skin.css +++ b/packages/@adobe/spectrum-css-temp/components/stepper/skin.css @@ -245,3 +245,59 @@ governing permissions and limitations under the License. } } } +@media (forced-colors: active) { + .spectrum-Stepper { + --spectrum-textfield-border-color-disabled: GrayText; + --spectrum-textfield-border-color-error: Highlight; + --spectrum-textfield-border-color-hover: Highlight; + --spectrum-textfield-border-color-key-focus: Highlight; + --spectrum-textfield-quiet-border-color-disabled: GrayText; + --spectrum-textfield-quiet-border-color-error: Highlight; + --spectrum-textfield-quiet-border-color-hover: Highlight; + --spectrum-textfield-quiet-border-color-key-focus: Highlight; + --spectrum-textfield-quiet-border-color: ButtonText; + --spectrum-textfield-quiet-text-color-disabled: GrayText; + --spectrum-textfield-text-color-disabled: GrayText; + &:not(.spectrum-Stepper--isQuiet) { + &.is-focused, &:focus { + outline: 2px solid Highlight; + } + } + &.spectrum-Stepper--readonly { + &.spectrum-Stepper--isQuiet { + .spectrum-Stepper-button { + border-color: var(--spectrum-textfield-quiet-border-color); + } + } + } + &.spectrum-Stepper--isQuiet { + &.is-focused, + &:focus, + &.focus-ring, + &:focus-ring { + forced-color-adjust: none; + box-shadow: 0 1px 0 0 var(--spectrum-textfield-quiet-border-color-key-focus); + .spectrum-Stepper-input { + forced-color-adjust: auto; + border-color: var(--spectrum-textfield-quiet-border-color-key-focus); + } + .spectrum-Stepper-button { + forced-color-adjust: auto; + border-color: var(--spectrum-textfield-quiet-border-color-key-focus); + } + } + &.is-hovered, + &:hover { + .spectrum-Stepper-input { + border-color: var(--spectrum-textfield-quiet-border-color-key-focus); + } + .spectrum-Stepper-button { + border-color: var(--spectrum-textfield-quiet-border-color-key-focus); + } + } + } + + + + } +} diff --git a/packages/@adobe/spectrum-css-temp/components/tabs/skin.css b/packages/@adobe/spectrum-css-temp/components/tabs/skin.css index 4f042d1a2c5..e9b79e53684 100644 --- a/packages/@adobe/spectrum-css-temp/components/tabs/skin.css +++ b/packages/@adobe/spectrum-css-temp/components/tabs/skin.css @@ -213,5 +213,12 @@ governing permissions and limitations under the License. color: HighlightText; } } + &.spectrum-Tabs--isCollapsed { + :focus-ring { + /* FieldButton Picker focus ring override */ + outline: none; + } + + } } } diff --git a/packages/@adobe/spectrum-css-temp/components/textfield/index.css b/packages/@adobe/spectrum-css-temp/components/textfield/index.css index 6b6439294aa..facf9f784c7 100644 --- a/packages/@adobe/spectrum-css-temp/components/textfield/index.css +++ b/packages/@adobe/spectrum-css-temp/components/textfield/index.css @@ -35,11 +35,12 @@ governing permissions and limitations under the License. min-width: var(--spectrum-textfield-min-width); width: var(--spectrum-component-single-line-width); - &:not(.spectrum-Textfield--quiet).spectrum-Textfield--multiline .spectrum-Textfield-input:not(:disabled) { + &:not(.spectrum-Textfield--quiet, .spectrum-Textfield--readonly).spectrum-Textfield--multiline .spectrum-Textfield-input:not(:disabled){ resize: vertical; } - &.spectrum-Textfield--quiet.spectrum-Textfield--multiline .spectrum-Textfield-input { + &.spectrum-Textfield--quiet.spectrum-Textfield--multiline .spectrum-Textfield-input, + &.spectrum-Textfield--readonly .spectrum-Textfield-input { height: var(--spectrum-textfield-height); min-height: var(--spectrum-textfield-height); overflow-x: hidden; @@ -259,7 +260,6 @@ governing permissions and limitations under the License. } } - /* same positioning as invalid icon */ .spectrum-Textfield--loadable .spectrum-Textfield-circleLoader { position: absolute; diff --git a/packages/@adobe/spectrum-css-temp/components/textfield/skin.css b/packages/@adobe/spectrum-css-temp/components/textfield/skin.css index 39aca89964a..cea3d3401fe 100644 --- a/packages/@adobe/spectrum-css-temp/components/textfield/skin.css +++ b/packages/@adobe/spectrum-css-temp/components/textfield/skin.css @@ -130,6 +130,10 @@ governing permissions and limitations under the License. } } + .spectrum-Textfield--readonly & { + border-color: var(--spectrum-textfield-quiet-background-color); + } + .spectrum-Textfield.spectrum-Textfield--invalid & { border-color: var(--spectrum-textfield-border-color-error); @@ -148,7 +152,6 @@ governing permissions and limitations under the License. } } } - } .spectrum-Textfield-icon { @@ -164,3 +167,31 @@ governing permissions and limitations under the License. fill: var(--spectrum-alert-error-icon-color); } } +@media (forced-colors: active) { + .spectrum-Textfield-input { + --spectrum-textfield-text-color-disabled: GrayText; + --spectrum-textfield-border-color-disabled: GrayText; + --spectrum-textfield-quiet-border-color-disabled: GrayText; + --spectrum-textfield-quiet-border-color-key-focus: Highlight; + --spectrum-textfield-border-color-key-focus: Highlight; + --spectrum-textfield-placeholder-text-color: GrayText; + --spectrum-textfield-placeholder-text-color-hover: GrayText; + &.focus-ring, + &:focus-ring { + outline: 2px solid Highlight; + } + .spectrum-Textfield--quiet & { + &.focus-ring, + &:focus-ring { + forced-color-adjust: none; + outline: 0; + box-shadow: 0 1px 0 0 var(--spectrum-textfield-quiet-border-color-key-focus); + .spectrum-Textfield-input { + border-color: var(--spectrum-textfield-quiet-border-color-key-focus); + } + } + } + + + } +} \ No newline at end of file diff --git a/packages/@adobe/spectrum-css-temp/components/toggle/skin.css b/packages/@adobe/spectrum-css-temp/components/toggle/skin.css index ea8287f79b4..ce4dad68b11 100644 --- a/packages/@adobe/spectrum-css-temp/components/toggle/skin.css +++ b/packages/@adobe/spectrum-css-temp/components/toggle/skin.css @@ -224,3 +224,60 @@ governing permissions and limitations under the License. } } } + +@media (forced-colors: active) { + .spectrum-ToggleSwitch { + forced-color-adjust: none; + --spectrum-switch-emphasized-handle-background-color: ButtonFace; + --spectrum-switch-emphasized-handle-border-color: ButtonText; + --spectrum-switch-emphasized-handle-border-color-disabled: GrayText; + --spectrum-switch-emphasized-handle-border-color-down: Highlight; + --spectrum-switch-emphasized-handle-border-color-hover: Highlight; + --spectrum-switch-emphasized-handle-border-color-key-focus: ButtonText; + --spectrum-switch-emphasized-handle-border-color-selected: Highlight; + --spectrum-switch-emphasized-handle-border-color-selected-disabled: GrayText; + --spectrum-switch-emphasized-handle-border-color-selected-down: Highlight; + --spectrum-switch-emphasized-handle-border-color-selected-hover: Highlight; + --spectrum-switch-emphasized-handle-border-color-selected-key-focus: Highlight; + --spectrum-switch-emphasized-text-color: CanvasText; + --spectrum-switch-emphasized-text-color-disabled: GrayText; + --spectrum-switch-emphasized-text-color-down: CanvasText; + --spectrum-switch-emphasized-text-color-hover: CanvasText; + --spectrum-switch-emphasized-text-color-selected-disabled: GrayText; + --spectrum-switch-emphasized-track-color: ButtonFace; + --spectrum-switch-emphasized-track-color-disabled: ButtonFace; + --spectrum-switch-emphasized-track-color-selected: Highlight; + --spectrum-switch-emphasized-track-color-selected-disabled: GrayText; + --spectrum-switch-emphasized-track-color-selected-down: Highlight; + --spectrum-switch-emphasized-track-color-selected-hover: Highlight; + --spectrum-switch-emphasized-track-color-selected-key-focus: Highlight; + --spectrum-switch-focus-ring-color-key-focus: ButtonText; + --spectrum-switch-handle-border-color: ButtonText; + --spectrum-switch-handle-border-color-selected: Highlight; + --spectrum-switch-quiet-handle-border-color-selected: Highlight; + --spectrum-switch-quiet-handle-border-color-selected-down: Highlight; + --spectrum-switch-quiet-handle-border-color-selected-hover: Highlight; + --spectrum-switch-quiet-handle-border-color-selected-key-focus: Highlight; + --spectrum-switch-quiet-track-color-selected: Highlight; + --spectrum-switch-quiet-track-color-selected-down: Highlight; + --spectrum-switch-quiet-track-color-selected-hover: Highlight; + --spectrum-switch-quiet-track-color-selected-key-focus: Highlight; + --spectrum-switch-track-color: ButtonFace; + --spectrum-switch-track-color-disabled: ButtonFace; + /* Add a box-shadow on the switch track when not checked*/ + .spectrum-ToggleSwitch-input:not(:checked) + .spectrum-ToggleSwitch-switch { + box-shadow: inset 0 0 0 1px var(--spectrum-switch-handle-border-color); + } + &:hover { + .spectrum-ToggleSwitch-input:not(:checked) + .spectrum-ToggleSwitch-switch { + box-shadow: inset 0 0 0 1px var(--spectrum-switch-emphasized-handle-border-color-hover); + } + } + /* Disabled is the same colour as when checked so no need to only do this when unchecked*/ + .spectrum-ToggleSwitch-input:disabled + .spectrum-ToggleSwitch-switch { + box-shadow: inset 0 0 0 1px var(--spectrum-switch-emphasized-handle-border-color-disabled); + } + + + } +} diff --git a/packages/@internationalized/date/src/index.ts b/packages/@internationalized/date/src/index.ts index 4f95ead9c34..8f10fa5e335 100644 --- a/packages/@internationalized/date/src/index.ts +++ b/packages/@internationalized/date/src/index.ts @@ -10,6 +10,23 @@ * governing permissions and limitations under the License. */ +export type { + AnyCalendarDate, + AnyTime, + AnyDateTime, + Calendar, + DateDuration, + TimeDuration, + DateTimeDuration, + DateFields, + TimeFields, + DateField, + TimeField, + Disambiguation, + CycleOptions, + CycleTimeOptions +} from './types'; + export {CalendarDate, CalendarDateTime, Time, ZonedDateTime} from './CalendarDate'; export {GregorianCalendar} from './calendars/GregorianCalendar'; export {JapaneseCalendar} from './calendars/JapaneseCalendar'; @@ -49,7 +66,6 @@ export { isWeekend, isWeekday } from './queries'; -export * from './types'; export { parseDate, parseDateTime, diff --git a/packages/@internationalized/message/src/index.ts b/packages/@internationalized/message/src/index.ts index 931b4b0d306..2a8416bdaa5 100644 --- a/packages/@internationalized/message/src/index.ts +++ b/packages/@internationalized/message/src/index.ts @@ -10,5 +10,6 @@ * governing permissions and limitations under the License. */ -export * from './MessageDictionary'; -export * from './MessageFormatter'; +export type {LocalizedStrings} from './MessageDictionary'; +export {MessageDictionary} from './MessageDictionary'; +export {MessageFormatter} from './MessageFormatter'; diff --git a/packages/@react-aria/accordion/src/index.ts b/packages/@react-aria/accordion/src/index.ts index fd58fc6651c..ba7a10c4e75 100644 --- a/packages/@react-aria/accordion/src/index.ts +++ b/packages/@react-aria/accordion/src/index.ts @@ -9,5 +9,4 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './useAccordion'; +export {useAccordionItem, useAccordion} from './useAccordion'; diff --git a/packages/@react-aria/accordion/src/useAccordion.ts b/packages/@react-aria/accordion/src/useAccordion.ts index 41d44ed8ec5..b83124f4092 100644 --- a/packages/@react-aria/accordion/src/useAccordion.ts +++ b/packages/@react-aria/accordion/src/useAccordion.ts @@ -11,16 +11,16 @@ */ import {AriaAccordionProps} from '@react-types/accordion'; -import {ButtonHTMLAttributes, HTMLAttributes, RefObject} from 'react'; +import {ButtonHTMLAttributes, RefObject} from 'react'; +import {DOMAttributes, Node} from '@react-types/shared'; import {mergeProps, useId} from '@react-aria/utils'; -import {Node} from '@react-types/shared'; import {TreeState} from '@react-stately/tree'; import {useButton} from '@react-aria/button'; import {useSelectableItem, useSelectableList} from '@react-aria/selection'; interface AccordionAria { /** Props for the accordion container element. */ - accordionProps: HTMLAttributes + accordionProps: DOMAttributes } interface AccordionItemAriaProps { item: Node @@ -30,7 +30,7 @@ interface AccordionItemAria { /** Props for the accordion item button. */ buttonProps: ButtonHTMLAttributes, /** Props for the accordion item content element. */ - regionProps: HTMLAttributes + regionProps: DOMAttributes } export function useAccordionItem(props: AccordionItemAriaProps, state: TreeState, ref: RefObject): AccordionItemAria { diff --git a/packages/@react-aria/actiongroup/src/index.ts b/packages/@react-aria/actiongroup/src/index.ts index 5ea89ecdd7a..43d5cb27401 100644 --- a/packages/@react-aria/actiongroup/src/index.ts +++ b/packages/@react-aria/actiongroup/src/index.ts @@ -9,6 +9,6 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './useActionGroup'; -export * from './useActionGroupItem'; +export type {ActionGroupAria} from './useActionGroup'; +export {useActionGroup} from './useActionGroup'; +export {useActionGroupItem} from './useActionGroupItem'; diff --git a/packages/@react-aria/actiongroup/src/useActionGroup.ts b/packages/@react-aria/actiongroup/src/useActionGroup.ts index ae70d13ca78..573dd64afa2 100644 --- a/packages/@react-aria/actiongroup/src/useActionGroup.ts +++ b/packages/@react-aria/actiongroup/src/useActionGroup.ts @@ -12,10 +12,11 @@ import {AriaActionGroupProps} from '@react-types/actiongroup'; import {createFocusManager} from '@react-aria/focus'; +import {DOMAttributes, FocusableElement} from '@react-types/shared'; import {filterDOMProps} from '@react-aria/utils'; -import {HTMLAttributes, RefObject} from 'react'; import {ListState} from '@react-stately/list'; import {Orientation} from '@react-types/shared'; +import {RefObject} from 'react'; import {useLocale} from '@react-aria/i18n'; const BUTTON_GROUP_ROLES = { @@ -25,10 +26,10 @@ const BUTTON_GROUP_ROLES = { }; export interface ActionGroupAria { - actionGroupProps: HTMLAttributes + actionGroupProps: DOMAttributes } -export function useActionGroup(props: AriaActionGroupProps, state: ListState, ref: RefObject): ActionGroupAria { +export function useActionGroup(props: AriaActionGroupProps, state: ListState, ref: RefObject): ActionGroupAria { let { isDisabled, orientation = 'horizontal' as Orientation diff --git a/packages/@react-aria/actiongroup/src/useActionGroupItem.ts b/packages/@react-aria/actiongroup/src/useActionGroupItem.ts index d8e8f38ad79..ec5c7911577 100644 --- a/packages/@react-aria/actiongroup/src/useActionGroupItem.ts +++ b/packages/@react-aria/actiongroup/src/useActionGroupItem.ts @@ -10,7 +10,8 @@ * governing permissions and limitations under the License. */ -import {HTMLAttributes, Key, RefObject, useEffect, useRef} from 'react'; +import {DOMAttributes, FocusableElement} from '@react-types/shared'; +import {Key, RefObject, useEffect, useRef} from 'react'; import {ListState} from '@react-stately/list'; import {mergeProps} from '@react-aria/utils'; import {PressProps} from '@react-aria/interactions'; @@ -20,7 +21,7 @@ interface ActionGroupItemProps { } interface ActionGroupItemAria { - buttonProps: HTMLAttributes & PressProps + buttonProps: DOMAttributes & PressProps } const BUTTON_ROLES = { @@ -30,7 +31,7 @@ const BUTTON_ROLES = { }; // eslint-disable-next-line @typescript-eslint/no-unused-vars -export function useActionGroupItem(props: ActionGroupItemProps, state: ListState, ref?: RefObject): ActionGroupItemAria { +export function useActionGroupItem(props: ActionGroupItemProps, state: ListState, ref?: RefObject): ActionGroupItemAria { let selectionMode = state.selectionManager.selectionMode; let buttonProps = { role: BUTTON_ROLES[selectionMode] diff --git a/packages/@react-aria/autocomplete/src/index.ts b/packages/@react-aria/autocomplete/src/index.ts index 8f5e64eb617..372b79b98fa 100644 --- a/packages/@react-aria/autocomplete/src/index.ts +++ b/packages/@react-aria/autocomplete/src/index.ts @@ -9,5 +9,4 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './useSearchAutocomplete'; +export {useSearchAutocomplete} from './useSearchAutocomplete'; diff --git a/packages/@react-aria/autocomplete/src/useSearchAutocomplete.ts b/packages/@react-aria/autocomplete/src/useSearchAutocomplete.ts index 5a97d367bf9..244ed383b32 100644 --- a/packages/@react-aria/autocomplete/src/useSearchAutocomplete.ts +++ b/packages/@react-aria/autocomplete/src/useSearchAutocomplete.ts @@ -13,7 +13,8 @@ import {AriaButtonProps} from '@react-types/button'; import {AriaListBoxOptions} from '@react-aria/listbox'; import {ComboBoxState} from '@react-stately/combobox'; -import {HTMLAttributes, InputHTMLAttributes, RefObject} from 'react'; +import {DOMAttributes} from '@react-types/shared'; +import {InputHTMLAttributes, RefObject} from 'react'; import {KeyboardDelegate} from '@react-types/shared'; import {mergeProps} from '@react-aria/utils'; import {SearchAutocompleteProps} from '@react-types/autocomplete'; @@ -33,7 +34,7 @@ interface AriaSearchAutocompleteProps extends SearchAutocompleteProps { interface SearchAutocompleteAria { /** Props for the label element. */ - labelProps: HTMLAttributes, + labelProps: DOMAttributes, /** Props for the search input element. */ inputProps: InputHTMLAttributes, /** Props for the list box, to be passed to [useListBox](useListBox.html). */ diff --git a/packages/@react-aria/breadcrumbs/src/index.ts b/packages/@react-aria/breadcrumbs/src/index.ts index e6f07707e4a..d6832e0fe7a 100644 --- a/packages/@react-aria/breadcrumbs/src/index.ts +++ b/packages/@react-aria/breadcrumbs/src/index.ts @@ -9,6 +9,5 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './useBreadcrumbItem'; -export * from './useBreadcrumbs'; +export {useBreadcrumbItem} from './useBreadcrumbItem'; +export {useBreadcrumbs} from './useBreadcrumbs'; diff --git a/packages/@react-aria/breadcrumbs/src/useBreadcrumbItem.ts b/packages/@react-aria/breadcrumbs/src/useBreadcrumbItem.ts index 8a21aea811b..ab8831880ad 100644 --- a/packages/@react-aria/breadcrumbs/src/useBreadcrumbItem.ts +++ b/packages/@react-aria/breadcrumbs/src/useBreadcrumbItem.ts @@ -11,19 +11,20 @@ */ import {AriaBreadcrumbItemProps} from '@react-types/breadcrumbs'; -import {HTMLAttributes, RefObject} from 'react'; +import {DOMAttributes, FocusableElement} from '@react-types/shared'; +import {RefObject} from 'react'; import {useLink} from '@react-aria/link'; interface BreadcrumbItemAria { /** Props for the breadcrumb item link element. */ - itemProps: HTMLAttributes + itemProps: DOMAttributes } /** * Provides the behavior and accessibility implementation for an in a breadcrumbs component. * See `useBreadcrumbs` for details about breadcrumbs. */ -export function useBreadcrumbItem(props: AriaBreadcrumbItemProps, ref: RefObject): BreadcrumbItemAria { +export function useBreadcrumbItem(props: AriaBreadcrumbItemProps, ref: RefObject): BreadcrumbItemAria { let { isCurrent, isDisabled, @@ -34,7 +35,7 @@ export function useBreadcrumbItem(props: AriaBreadcrumbItemProps, ref: RefObject let {linkProps} = useLink({isDisabled: isDisabled || isCurrent, elementType, ...otherProps}, ref); let isHeading = /^h[1-6]$/.test(elementType); - let itemProps: HTMLAttributes = {}; + let itemProps: DOMAttributes = {}; if (!isHeading) { itemProps = linkProps; diff --git a/packages/@react-aria/breadcrumbs/src/useBreadcrumbs.ts b/packages/@react-aria/breadcrumbs/src/useBreadcrumbs.ts index ede7e7860a6..706942d0f04 100644 --- a/packages/@react-aria/breadcrumbs/src/useBreadcrumbs.ts +++ b/packages/@react-aria/breadcrumbs/src/useBreadcrumbs.ts @@ -11,15 +11,15 @@ */ import {AriaBreadcrumbsProps} from '@react-types/breadcrumbs'; +import {DOMAttributes} from '@react-types/shared'; import {filterDOMProps} from '@react-aria/utils'; -import {HTMLAttributes} from 'react'; // @ts-ignore import intlMessages from '../intl/*.json'; import {useMessageFormatter} from '@react-aria/i18n'; interface BreadcrumbsAria { /** Props for the breadcrumbs navigation element. */ - navProps: HTMLAttributes + navProps: DOMAttributes } /** diff --git a/packages/@react-aria/button/package.json b/packages/@react-aria/button/package.json index 687a3f7274f..e6d01ded38f 100644 --- a/packages/@react-aria/button/package.json +++ b/packages/@react-aria/button/package.json @@ -22,7 +22,8 @@ "@react-aria/interactions": "^3.9.1", "@react-aria/utils": "^3.13.1", "@react-stately/toggle": "^3.3.1", - "@react-types/button": "^3.5.1" + "@react-types/button": "^3.5.1", + "@react-types/shared": "^3.13.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" diff --git a/packages/@react-aria/button/src/index.ts b/packages/@react-aria/button/src/index.ts index 42f07fa17cf..ffe59a4ab67 100644 --- a/packages/@react-aria/button/src/index.ts +++ b/packages/@react-aria/button/src/index.ts @@ -9,6 +9,6 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './useButton'; -export * from './useToggleButton'; +export type {ButtonAria} from './useButton'; +export {useButton} from './useButton'; +export {useToggleButton} from './useToggleButton'; diff --git a/packages/@react-aria/button/src/useButton.ts b/packages/@react-aria/button/src/useButton.ts index e82a2474670..326cc587d47 100644 --- a/packages/@react-aria/button/src/useButton.ts +++ b/packages/@react-aria/button/src/useButton.ts @@ -19,6 +19,7 @@ import { RefObject } from 'react'; import {AriaButtonProps} from '@react-types/button'; +import {DOMAttributes} from '@react-types/shared'; import {filterDOMProps} from '@react-aria/utils'; import {mergeProps} from '@react-aria/utils'; import {useFocusable} from '@react-aria/focus'; @@ -38,7 +39,7 @@ export function useButton(props: AriaButtonProps<'a'>, ref: RefObject, ref: RefObject): ButtonAria>; export function useButton(props: AriaButtonProps<'input'>, ref: RefObject): ButtonAria>; export function useButton(props: AriaButtonProps<'span'>, ref: RefObject): ButtonAria>; -export function useButton(props: AriaButtonProps, ref: RefObject): ButtonAria>; +export function useButton(props: AriaButtonProps, ref: RefObject): ButtonAria; /** * Provides the behavior and accessibility implementation for a button component. Handles mouse, keyboard, and touch interactions, * focus behavior, and ARIA props for both native button elements and custom element types. diff --git a/packages/@react-aria/button/src/useToggleButton.ts b/packages/@react-aria/button/src/useToggleButton.ts index 8bb71d1f23f..e47e2267530 100644 --- a/packages/@react-aria/button/src/useToggleButton.ts +++ b/packages/@react-aria/button/src/useToggleButton.ts @@ -21,6 +21,7 @@ import { import {AriaToggleButtonProps} from '@react-types/button'; import {ButtonAria, useButton} from './useButton'; import {chain} from '@react-aria/utils'; +import {DOMAttributes} from '@react-types/shared'; import {mergeProps} from '@react-aria/utils'; import {ToggleState} from '@react-stately/toggle'; @@ -29,7 +30,7 @@ export function useToggleButton(props: AriaToggleButtonProps<'button'>, state: T export function useToggleButton(props: AriaToggleButtonProps<'div'>, state: ToggleState, ref: RefObject): ButtonAria>; export function useToggleButton(props: AriaToggleButtonProps<'input'>, state: ToggleState, ref: RefObject): ButtonAria>; export function useToggleButton(props: AriaToggleButtonProps<'span'>, state: ToggleState, ref: RefObject): ButtonAria>; -export function useToggleButton(props: AriaToggleButtonProps, state: ToggleState, ref: RefObject): ButtonAria>; +export function useToggleButton(props: AriaToggleButtonProps, state: ToggleState, ref: RefObject): ButtonAria; /** * Provides the behavior and accessibility implementation for a toggle button component. * ToggleButtons allow users to toggle a selection on or off, for example switching between two states or modes. diff --git a/packages/@react-aria/calendar/src/useCalendarBase.ts b/packages/@react-aria/calendar/src/useCalendarBase.ts index 7fd3c8735ae..c663c88a4da 100644 --- a/packages/@react-aria/calendar/src/useCalendarBase.ts +++ b/packages/@react-aria/calendar/src/useCalendarBase.ts @@ -14,23 +14,24 @@ import {announce} from '@react-aria/live-announcer'; import {AriaButtonProps} from '@react-types/button'; import {CalendarPropsBase} from '@react-types/calendar'; import {CalendarState, RangeCalendarState} from '@react-stately/calendar'; +import {DOMAttributes} from '@react-types/shared'; import {DOMProps} from '@react-types/shared'; import {filterDOMProps, mergeProps, useLabels, useSlotId, useUpdateEffect} from '@react-aria/utils'; import {hookData, useSelectedDateDescription, useVisibleRangeDescription} from './utils'; -import {HTMLAttributes, useRef} from 'react'; // @ts-ignore import intlMessages from '../intl/*.json'; import {useMessageFormatter} from '@react-aria/i18n'; +import {useRef} from 'react'; export interface CalendarAria { /** Props for the calendar grouping element. */ - calendarProps: HTMLAttributes, + calendarProps: DOMAttributes, /** Props for the next button. */ nextButtonProps: AriaButtonProps, /** Props for the previous button. */ prevButtonProps: AriaButtonProps, /** Props for the error message element, if any. */ - errorMessageProps: HTMLAttributes, + errorMessageProps: DOMAttributes, /** A description of the visible date range, for use in the calendar title. */ title: string } diff --git a/packages/@react-aria/calendar/src/useCalendarCell.ts b/packages/@react-aria/calendar/src/useCalendarCell.ts index 183bf895f09..7b2ed870ec7 100644 --- a/packages/@react-aria/calendar/src/useCalendarCell.ts +++ b/packages/@react-aria/calendar/src/useCalendarCell.ts @@ -12,13 +12,14 @@ import {CalendarDate, isEqualDay, isSameDay, isToday} from '@internationalized/date'; import {CalendarState, RangeCalendarState} from '@react-stately/calendar'; +import {DOMAttributes} from '@react-types/shared'; import {focusWithoutScrolling, getScrollParent, scrollIntoView, useDescription} from '@react-aria/utils'; import {getEraFormat, hookData} from './utils'; import {getInteractionModality, usePress} from '@react-aria/interactions'; -import {HTMLAttributes, RefObject, useEffect, useMemo, useRef} from 'react'; // @ts-ignore import intlMessages from '../intl/*.json'; import {mergeProps} from '@react-aria/utils'; +import {RefObject, useEffect, useMemo, useRef} from 'react'; import {useDateFormatter, useMessageFormatter} from '@react-aria/i18n'; export interface AriaCalendarCellProps { @@ -33,9 +34,9 @@ export interface AriaCalendarCellProps { export interface CalendarCellAria { /** Props for the grid cell element (e.g. ``). */ - cellProps: HTMLAttributes, + cellProps: DOMAttributes, /** Props for the button element within the cell. */ - buttonProps: HTMLAttributes, + buttonProps: DOMAttributes, /** Whether the cell is currently being pressed. */ isPressed: boolean, /** Whether the cell is selected. */ diff --git a/packages/@react-aria/calendar/src/useCalendarGrid.ts b/packages/@react-aria/calendar/src/useCalendarGrid.ts index 2bc7f37470a..671924b2bbb 100644 --- a/packages/@react-aria/calendar/src/useCalendarGrid.ts +++ b/packages/@react-aria/calendar/src/useCalendarGrid.ts @@ -12,8 +12,9 @@ import {CalendarDate, startOfWeek, today} from '@internationalized/date'; import {CalendarState, RangeCalendarState} from '@react-stately/calendar'; +import {DOMAttributes} from '@react-types/shared'; import {hookData, useVisibleRangeDescription} from './utils'; -import {HTMLAttributes, KeyboardEvent, useMemo} from 'react'; +import {KeyboardEvent, useMemo} from 'react'; import {mergeProps, useLabels} from '@react-aria/utils'; import {useDateFormatter, useLocale} from '@react-aria/i18n'; @@ -34,9 +35,9 @@ export interface AriaCalendarGridProps { export interface CalendarGridAria { /** Props for the date grid element (e.g. ``). */ - gridProps: HTMLAttributes, + gridProps: DOMAttributes, /** Props for the grid header element (e.g. ``). */ - headerProps: HTMLAttributes, + headerProps: DOMAttributes, /** A list of week day abbreviations formatted for the current locale, typically used in column headers. */ weekDays: string[] } diff --git a/packages/@react-aria/calendar/src/useRangeCalendar.ts b/packages/@react-aria/calendar/src/useRangeCalendar.ts index 60233126db1..b4ef1d72e9f 100644 --- a/packages/@react-aria/calendar/src/useRangeCalendar.ts +++ b/packages/@react-aria/calendar/src/useRangeCalendar.ts @@ -12,6 +12,7 @@ import {CalendarAria, useCalendarBase} from './useCalendarBase'; import {DateValue, RangeCalendarProps} from '@react-types/calendar'; +import {FocusableElement} from '@react-types/shared'; import {RangeCalendarState} from '@react-stately/calendar'; import {RefObject, useRef} from 'react'; import {useEvent} from '@react-aria/utils'; @@ -20,7 +21,7 @@ import {useEvent} from '@react-aria/utils'; * Provides the behavior and accessibility implementation for a range calendar component. * A range calendar displays one or more date grids and allows users to select a contiguous range of dates. */ -export function useRangeCalendar(props: RangeCalendarProps, state: RangeCalendarState, ref: RefObject): CalendarAria { +export function useRangeCalendar(props: RangeCalendarProps, state: RangeCalendarState, ref: RefObject): CalendarAria { let res = useCalendarBase(props, state); // We need to ignore virtual pointer events from VoiceOver due to these bugs. @@ -37,7 +38,7 @@ export function useRangeCalendar(props: RangeCalendarProps< // Stop range selection when pressing or releasing a pointer outside the calendar body, // except when pressing the next or previous buttons to switch months. - let endDragging = e => { + let endDragging = (e: PointerEvent) => { if (isVirtualClick.current) { isVirtualClick.current = false; return; @@ -48,7 +49,7 @@ export function useRangeCalendar(props: RangeCalendarProps< return; } - let target = e.target as HTMLElement; + let target = e.target as Element; let body = document.getElementById(res.calendarProps.id); if ( body && diff --git a/packages/@react-aria/checkbox/package.json b/packages/@react-aria/checkbox/package.json index ee4d59672ba..d71f7254879 100644 --- a/packages/@react-aria/checkbox/package.json +++ b/packages/@react-aria/checkbox/package.json @@ -23,7 +23,8 @@ "@react-aria/utils": "^3.13.1", "@react-stately/checkbox": "^3.1.1", "@react-stately/toggle": "^3.3.1", - "@react-types/checkbox": "^3.3.1" + "@react-types/checkbox": "^3.3.1", + "@react-types/shared": "^3.13.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" diff --git a/packages/@react-aria/checkbox/src/index.ts b/packages/@react-aria/checkbox/src/index.ts index 63e2a88c34d..58f9f9e329c 100644 --- a/packages/@react-aria/checkbox/src/index.ts +++ b/packages/@react-aria/checkbox/src/index.ts @@ -9,7 +9,7 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './useCheckbox'; -export * from './useCheckboxGroup'; -export * from './useCheckboxGroupItem'; +export type {CheckboxAria} from './useCheckbox'; +export {useCheckbox} from './useCheckbox'; +export {useCheckboxGroup} from './useCheckboxGroup'; +export {useCheckboxGroupItem} from './useCheckboxGroupItem'; diff --git a/packages/@react-aria/checkbox/src/useCheckboxGroup.ts b/packages/@react-aria/checkbox/src/useCheckboxGroup.ts index ecf2e2f20aa..204fc080e25 100644 --- a/packages/@react-aria/checkbox/src/useCheckboxGroup.ts +++ b/packages/@react-aria/checkbox/src/useCheckboxGroup.ts @@ -13,15 +13,15 @@ import {AriaCheckboxGroupProps} from '@react-types/checkbox'; import {checkboxGroupNames} from './utils'; import {CheckboxGroupState} from '@react-stately/checkbox'; +import {DOMAttributes} from '@react-types/shared'; import {filterDOMProps, mergeProps} from '@react-aria/utils'; -import {HTMLAttributes} from 'react'; import {useLabel} from '@react-aria/label'; interface CheckboxGroupAria { /** Props for the checkbox group wrapper element. */ - groupProps: HTMLAttributes, + groupProps: DOMAttributes, /** Props for the checkbox group's visible label (if any). */ - labelProps: HTMLAttributes + labelProps: DOMAttributes } /** diff --git a/packages/@react-aria/color/src/index.ts b/packages/@react-aria/color/src/index.ts index 64b356d1a53..cde7711f432 100644 --- a/packages/@react-aria/color/src/index.ts +++ b/packages/@react-aria/color/src/index.ts @@ -9,8 +9,7 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './useColorArea'; -export * from './useColorSlider'; -export * from './useColorWheel'; -export * from './useColorField'; +export {useColorArea} from './useColorArea'; +export {useColorSlider} from './useColorSlider'; +export {useColorWheel} from './useColorWheel'; +export {useColorField} from './useColorField'; diff --git a/packages/@react-aria/color/src/useColorArea.ts b/packages/@react-aria/color/src/useColorArea.ts index fbc3ddd13bc..d7f2d7213b6 100644 --- a/packages/@react-aria/color/src/useColorArea.ts +++ b/packages/@react-aria/color/src/useColorArea.ts @@ -12,10 +12,11 @@ import {AriaColorAreaProps, ColorChannel} from '@react-types/color'; import {ColorAreaState} from '@react-stately/color'; +import {DOMAttributes} from '@react-types/shared'; import {focusWithoutScrolling, isAndroid, isIOS, mergeProps, useGlobalListeners, useLabels} from '@react-aria/utils'; // @ts-ignore import intlMessages from '../intl/*.json'; -import React, {ChangeEvent, HTMLAttributes, InputHTMLAttributes, RefObject, useCallback, useRef} from 'react'; +import React, {ChangeEvent, InputHTMLAttributes, RefObject, useCallback, useRef} from 'react'; import {useColorAreaGradient} from './useColorAreaGradient'; import {useFocus, useFocusWithin, useKeyboard, useMove} from '@react-aria/interactions'; import {useLocale, useMessageFormatter} from '@react-aria/i18n'; @@ -23,11 +24,11 @@ import {useVisuallyHidden} from '@react-aria/visually-hidden'; interface ColorAreaAria { /** Props for the color area container element. */ - colorAreaProps: HTMLAttributes, + colorAreaProps: DOMAttributes, /** Props for the color area gradient foreground element. */ - gradientProps: HTMLAttributes, + gradientProps: DOMAttributes, /** Props for the thumb element. */ - thumbProps: HTMLAttributes, + thumbProps: DOMAttributes, /** Props for the visually hidden horizontal range input element. */ xInputProps: InputHTMLAttributes, /** Props for the visually hidden vertical range input element. */ @@ -36,16 +37,16 @@ interface ColorAreaAria { interface ColorAreaAriaProps extends AriaColorAreaProps { /** A ref to the input that represents the x axis of the color area. */ - inputXRef: RefObject, + inputXRef: RefObject, /** A ref to the input that represents the y axis of the color area. */ - inputYRef: RefObject, + inputYRef: RefObject, /** A ref to the color area containing element. */ - containerRef: RefObject + containerRef: RefObject } /** - * Provides the behavior and accessibility implementation for a color wheel component. - * Color wheels allow users to adjust the hue of an HSL or HSB color value on a circular track. + * Provides the behavior and accessibility implementation for a color area component. + * Color area allows users to adjust two channels of an RGB, HSL or HSB color value against a two-dimensional gradient background. */ export function useColorArea(props: ColorAreaAriaProps, state: ColorAreaState): ColorAreaAria { let { @@ -61,9 +62,9 @@ export function useColorArea(props: ColorAreaAriaProps, state: ColorAreaState): let {direction, locale} = useLocale(); - let focusedInputRef = useRef(null); + let focusedInputRef = useRef(null); - let focusInput = useCallback((inputRef:RefObject = inputXRef) => { + let focusInput = useCallback((inputRef:RefObject = inputXRef) => { if (inputRef.current) { focusWithoutScrolling(inputRef.current); } @@ -351,10 +352,10 @@ export function useColorArea(props: ColorAreaAriaProps, state: ColorAreaState): 'aria-label': ariaLabel ? formatMessage('colorInputLabel', {label: ariaLabel, channelLabel: colorPickerLabel}) : colorPickerLabel }); - let colorAriaLabellingProps = useLabels( + let colorAreaLabellingProps = useLabels( { ...props, - 'aria-label': ariaLabel ? `${ariaLabel} ${colorPickerLabel}` : undefined + 'aria-label': ariaLabel ? `${ariaLabel}, ${colorPickerLabel}` : undefined }, isMobile ? colorPickerLabel : undefined ); @@ -382,7 +383,7 @@ export function useColorArea(props: ColorAreaAriaProps, state: ColorAreaState): return { colorAreaProps: { - ...colorAriaLabellingProps, + ...colorAreaLabellingProps, ...colorAreaInteractions, ...colorAreaStyleProps, role: 'group' @@ -414,7 +415,7 @@ export function useColorArea(props: ColorAreaAriaProps, state: ColorAreaState): add aria-hidden="true" to the unfocused control when the value has not changed via the keyboard, but remove aria-hidden to reveal the input for each channel when the value has changed with the keyboard. */ - 'aria-hidden': (!isMobile && focusedInputRef.current === inputYRef.current && !valueChangedViaKeyboard.current ? 'true' : undefined), + 'aria-hidden': (isMobile || !focusedInputRef.current || focusedInputRef.current === inputXRef.current || valueChangedViaKeyboard.current ? undefined : 'true'), onChange: (e: ChangeEvent) => { state.setXValue(parseFloat(e.target.value)); } @@ -432,13 +433,13 @@ export function useColorArea(props: ColorAreaAriaProps, state: ColorAreaState): 'aria-orientation': 'vertical', disabled: isDisabled, value: state.value.getChannelValue(yChannel), - tabIndex: (isMobile || focusedInputRef.current === inputYRef.current ? undefined : -1), + tabIndex: (isMobile || (focusedInputRef.current && focusedInputRef.current === inputYRef.current) ? undefined : -1), /* So that only a single "2d slider" control shows up when listing form elements for screen readers, add aria-hidden="true" to the unfocused input when the value has not changed via the keyboard, but remove aria-hidden to reveal the input for each channel when the value has changed with the keyboard. */ - 'aria-hidden': (isMobile || focusedInputRef.current === inputYRef.current || valueChangedViaKeyboard.current ? undefined : 'true'), + 'aria-hidden': (isMobile || (focusedInputRef.current && focusedInputRef.current === inputYRef.current) || valueChangedViaKeyboard.current ? undefined : 'true'), onChange: (e: ChangeEvent) => { state.setYValue(parseFloat(e.target.value)); } diff --git a/packages/@react-aria/color/src/useColorAreaGradient.ts b/packages/@react-aria/color/src/useColorAreaGradient.ts index 4206be8a128..50b59fd46cc 100644 --- a/packages/@react-aria/color/src/useColorAreaGradient.ts +++ b/packages/@react-aria/color/src/useColorAreaGradient.ts @@ -218,17 +218,21 @@ export function useColorAreaGradient({direction, state, zChannel, xChannel, isDi x = 1 - x; } + let forcedColorAdjustNoneStyle = {forcedColorAdjust: 'none'}; + return { colorAreaStyleProps: { style: { position: 'relative', touchAction: 'none', + ...forcedColorAdjustNoneStyle, ...background.colorAreaStyles } }, gradientStyleProps: { style: { touchAction: 'none', + ...forcedColorAdjustNoneStyle, ...background.gradientStyles } }, @@ -238,7 +242,8 @@ export function useColorAreaGradient({direction, state, zChannel, xChannel, isDi left: `${x * 100}%`, top: `${y * 100}%`, transform: 'translate(0%, 0%)', - touchAction: 'none' + touchAction: 'none', + ...forcedColorAdjustNoneStyle } } }; diff --git a/packages/@react-aria/color/src/useColorSlider.ts b/packages/@react-aria/color/src/useColorSlider.ts index 83c13b433c4..420ec2fac6b 100644 --- a/packages/@react-aria/color/src/useColorSlider.ts +++ b/packages/@react-aria/color/src/useColorSlider.ts @@ -12,29 +12,30 @@ import {AriaColorSliderProps} from '@react-types/color'; import {ColorSliderState} from '@react-stately/color'; -import {HTMLAttributes, RefObject} from 'react'; +import {DOMAttributes} from '@react-types/shared'; +import {InputHTMLAttributes, RefObject} from 'react'; import {mergeProps} from '@react-aria/utils'; import {useLocale} from '@react-aria/i18n'; import {useSlider, useSliderThumb} from '@react-aria/slider'; interface ColorSliderAriaOptions extends AriaColorSliderProps { /** A ref for the track element. */ - trackRef: RefObject, + trackRef: RefObject, /** A ref for the input element. */ inputRef: RefObject } interface ColorSliderAria { /** Props for the label element. */ - labelProps: HTMLAttributes, + labelProps: DOMAttributes, /** Props for the track element. */ - trackProps: HTMLAttributes, + trackProps: DOMAttributes, /** Props for the thumb element. */ - thumbProps: HTMLAttributes, + thumbProps: DOMAttributes, /** Props for the visually hidden range input element. */ - inputProps: HTMLAttributes, + inputProps: InputHTMLAttributes, /** Props for the output element, displaying the value of the color slider. */ - outputProps: HTMLAttributes + outputProps: DOMAttributes } /** @@ -99,16 +100,25 @@ export function useColorSlider(props: ColorSliderAriaOptions, state: ColorSlider } }; + let forcedColorAdjustNoneStyle = {forcedColorAdjust: 'none'}; + return { trackProps: { ...mergeProps(groupProps, trackProps), style: { ...trackProps.style, + ...forcedColorAdjustNoneStyle, background: generateBackground() } }, inputProps, - thumbProps, + thumbProps: { + ...thumbProps, + style: { + ...thumbProps.style, + ...forcedColorAdjustNoneStyle + } + }, labelProps, outputProps }; diff --git a/packages/@react-aria/color/src/useColorWheel.ts b/packages/@react-aria/color/src/useColorWheel.ts index 1c3ea44d93d..bd93071ae47 100644 --- a/packages/@react-aria/color/src/useColorWheel.ts +++ b/packages/@react-aria/color/src/useColorWheel.ts @@ -12,8 +12,9 @@ import {AriaColorWheelProps} from '@react-types/color'; import {ColorWheelState} from '@react-stately/color'; +import {DOMAttributes} from '@react-types/shared'; import {focusWithoutScrolling, mergeProps, useGlobalListeners, useLabels} from '@react-aria/utils'; -import React, {ChangeEvent, HTMLAttributes, InputHTMLAttributes, RefObject, useCallback, useRef} from 'react'; +import React, {ChangeEvent, InputHTMLAttributes, RefObject, useCallback, useRef} from 'react'; import {useKeyboard, useMove} from '@react-aria/interactions'; import {useLocale} from '@react-aria/i18n'; @@ -26,9 +27,9 @@ interface ColorWheelAriaProps extends AriaColorWheelProps { interface ColorWheelAria { /** Props for the track element. */ - trackProps: HTMLAttributes, + trackProps: DOMAttributes, /** Props for the thumb element. */ - thumbProps: HTMLAttributes, + thumbProps: DOMAttributes, /** Props for the visually hidden range input element. */ inputProps: InputHTMLAttributes } @@ -37,7 +38,7 @@ interface ColorWheelAria { * Provides the behavior and accessibility implementation for a color wheel component. * Color wheels allow users to adjust the hue of an HSL or HSB color value on a circular track. */ -export function useColorWheel(props: ColorWheelAriaProps, state: ColorWheelState, inputRef: RefObject): ColorWheelAria { +export function useColorWheel(props: ColorWheelAriaProps, state: ColorWheelState, inputRef: RefObject): ColorWheelAria { let { isDisabled, innerRadius, @@ -257,6 +258,11 @@ export function useColorWheel(props: ColorWheelAriaProps, state: ColorWheelState }); let {minValue, maxValue, step} = state.value.getChannelRange('hue'); + + let forcedColorAdjustNoneStyle = { + forcedColorAdjust: 'none' + }; + return { trackProps: { ...trackInteractions, @@ -283,7 +289,8 @@ export function useColorWheel(props: ColorWheelAriaProps, state: ColorWheelState hsl(360, 100%, 50%) ) `, - clipPath: `path(evenodd, "${circlePath(outerRadius, outerRadius, outerRadius)} ${circlePath(outerRadius, outerRadius, innerRadius)}")` + clipPath: `path(evenodd, "${circlePath(outerRadius, outerRadius, outerRadius)} ${circlePath(outerRadius, outerRadius, innerRadius)}")`, + ...forcedColorAdjustNoneStyle } }, thumbProps: { @@ -293,7 +300,8 @@ export function useColorWheel(props: ColorWheelAriaProps, state: ColorWheelState left: '50%', top: '50%', transform: `translate(calc(${x}px - 50%), calc(${y}px - 50%))`, - touchAction: 'none' + touchAction: 'none', + ...forcedColorAdjustNoneStyle } }, inputProps: mergeProps( diff --git a/packages/@react-aria/combobox/src/index.ts b/packages/@react-aria/combobox/src/index.ts index 0bb6befd8d6..73931b9a043 100644 --- a/packages/@react-aria/combobox/src/index.ts +++ b/packages/@react-aria/combobox/src/index.ts @@ -9,5 +9,4 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './useComboBox'; +export {useComboBox} from './useComboBox'; diff --git a/packages/@react-aria/combobox/src/useComboBox.ts b/packages/@react-aria/combobox/src/useComboBox.ts index cfffe3d5a20..4100c60e3bf 100644 --- a/packages/@react-aria/combobox/src/useComboBox.ts +++ b/packages/@react-aria/combobox/src/useComboBox.ts @@ -17,11 +17,11 @@ import {ariaHideOutside} from '@react-aria/overlays'; import {AriaListBoxOptions, getItemId, listData} from '@react-aria/listbox'; import {chain, isAppleDevice, mergeProps, useLabels} from '@react-aria/utils'; import {ComboBoxState} from '@react-stately/combobox'; -import {FocusEvent, HTMLAttributes, InputHTMLAttributes, KeyboardEvent, RefObject, TouchEvent, useEffect, useMemo, useRef} from 'react'; +import {DOMAttributes, KeyboardDelegate, PressEvent} from '@react-types/shared'; +import {FocusEvent, InputHTMLAttributes, KeyboardEvent, RefObject, TouchEvent, useEffect, useMemo, useRef} from 'react'; import {getItemCount} from '@react-stately/collections'; // @ts-ignore import intlMessages from '../intl/*.json'; -import {KeyboardDelegate, PressEvent} from '@react-types/shared'; import {ListKeyboardDelegate, useSelectableCollection} from '@react-aria/selection'; import {useMenuTrigger} from '@react-aria/menu'; import {useMessageFormatter} from '@react-aria/i18n'; @@ -31,18 +31,18 @@ interface AriaComboBoxOptions extends AriaComboBoxProps { /** The ref for the input element. */ inputRef: RefObject, /** The ref for the list box popover. */ - popoverRef: RefObject, + popoverRef: RefObject, /** The ref for the list box. */ listBoxRef: RefObject, /** The ref for the optional list box popup trigger button. */ - buttonRef?: RefObject, + buttonRef?: RefObject, /** An optional keyboard delegate implementation, to override the default. */ keyboardDelegate?: KeyboardDelegate } interface ComboBoxAria { /** Props for the label element. */ - labelProps: HTMLAttributes, + labelProps: DOMAttributes, /** Props for the combo box input element. */ inputProps: InputHTMLAttributes, /** Props for the list box, to be passed to [useListBox](useListBox.html). */ @@ -50,9 +50,9 @@ interface ComboBoxAria { /** Props for the optional trigger button, to be passed to [useButton](useButton.html). */ buttonProps: AriaButtonProps, /** Props for the combo box description element, if any. */ - descriptionProps: HTMLAttributes, + descriptionProps: DOMAttributes, /** Props for the combo box error message element, if any. */ - errorMessageProps: HTMLAttributes + errorMessageProps: DOMAttributes } /** @@ -136,7 +136,7 @@ export function useComboBox(props: AriaComboBoxOptions, state: ComboBoxSta let onBlur = (e: FocusEvent) => { // Ignore blur if focused moved to the button or into the popover. - if (e.relatedTarget === buttonRef?.current || popoverRef.current?.contains(e.relatedTarget as HTMLElement)) { + if (e.relatedTarget === buttonRef?.current || popoverRef.current?.contains(e.relatedTarget)) { return; } @@ -211,7 +211,7 @@ export function useComboBox(props: AriaComboBoxOptions, state: ComboBoxSta return; } - let rect = (e.target as HTMLElement).getBoundingClientRect(); + let rect = (e.target as Element).getBoundingClientRect(); let touch = e.changedTouches[0]; let centerX = Math.ceil(rect.left + .5 * rect.width); diff --git a/packages/@react-aria/datepicker/src/useDateField.ts b/packages/@react-aria/datepicker/src/useDateField.ts index 31e55facc84..227db0b30ac 100644 --- a/packages/@react-aria/datepicker/src/useDateField.ts +++ b/packages/@react-aria/datepicker/src/useDateField.ts @@ -13,10 +13,11 @@ import {AriaDatePickerProps, AriaTimeFieldProps, DateValue, TimeValue} from '@react-types/datepicker'; import {createFocusManager, FocusManager} from '@react-aria/focus'; import {DateFieldState} from '@react-stately/datepicker'; +import {DOMAttributes} from '@react-types/shared'; import {filterDOMProps, mergeProps, useDescription} from '@react-aria/utils'; -import {HTMLAttributes, RefObject, useEffect, useMemo, useRef} from 'react'; // @ts-ignore import intlMessages from '../intl/*.json'; +import {RefObject, useEffect, useMemo, useRef} from 'react'; import {useDatePickerGroup} from './useDatePickerGroup'; import {useField} from '@react-aria/label'; import {useFocusWithin} from '@react-aria/interactions'; @@ -27,13 +28,13 @@ export interface AriaDateFieldProps extends Omit, + labelProps: DOMAttributes, /** Props for the field grouping element. */ - fieldProps: HTMLAttributes, + fieldProps: DOMAttributes, /** Props for the description element, if any. */ - descriptionProps: HTMLAttributes, + descriptionProps: DOMAttributes, /** Props for the error message element, if any. */ - errorMessageProps: HTMLAttributes + errorMessageProps: DOMAttributes } // Data that is passed between useDateField and useDateSegment. @@ -56,7 +57,7 @@ export const focusManagerSymbol = '__focusManager_' + Date.now(); * A date field allows users to enter and edit date and time values using a keyboard. * Each part of a date value is displayed in an individually editable segment. */ -export function useDateField(props: AriaDateFieldProps, state: DateFieldState, ref: RefObject): DateFieldAria { +export function useDateField(props: AriaDateFieldProps, state: DateFieldState, ref: RefObject): DateFieldAria { let {labelProps, fieldProps, descriptionProps, errorMessageProps} = useField({ ...props, labelElementType: 'span' @@ -97,7 +98,7 @@ export function useDateField(props: AriaDateFieldProps, // rather than role="group". Since the date picker/date range picker already has a role="group" // with a label and description, and the segments are already labeled by this as well, this // avoids very verbose duplicate announcements. - let fieldDOMProps: HTMLAttributes; + let fieldDOMProps: DOMAttributes; if (props[roleSymbol] === 'presentation') { fieldDOMProps = { role: 'presentation' @@ -136,6 +137,6 @@ export function useDateField(props: AriaDateFieldProps, * A time field allows users to enter and edit time values using a keyboard. * Each part of a time value is displayed in an individually editable segment. */ -export function useTimeField(props: AriaTimeFieldProps, state: DateFieldState, ref: RefObject): DateFieldAria { +export function useTimeField(props: AriaTimeFieldProps, state: DateFieldState, ref: RefObject): DateFieldAria { return useDateField(props, state, ref); } diff --git a/packages/@react-aria/datepicker/src/useDatePicker.ts b/packages/@react-aria/datepicker/src/useDatePicker.ts index bf5b00383ed..1ae62dd85c9 100644 --- a/packages/@react-aria/datepicker/src/useDatePicker.ts +++ b/packages/@react-aria/datepicker/src/useDatePicker.ts @@ -16,10 +16,11 @@ import {AriaDialogProps} from '@react-types/dialog'; import {CalendarProps} from '@react-types/calendar'; import {createFocusManager} from '@react-aria/focus'; import {DatePickerState} from '@react-stately/datepicker'; +import {DOMAttributes} from '@react-types/shared'; import {filterDOMProps, mergeProps, useDescription, useId} from '@react-aria/utils'; -import {HTMLAttributes, RefObject, useMemo} from 'react'; // @ts-ignore import intlMessages from '../intl/*.json'; +import {RefObject, useMemo} from 'react'; import {roleSymbol} from './useDateField'; import {useDatePickerGroup} from './useDatePickerGroup'; import {useField} from '@react-aria/label'; @@ -27,17 +28,17 @@ import {useLocale, useMessageFormatter} from '@react-aria/i18n'; export interface DatePickerAria { /** Props for the date picker's visible label element, if any. */ - labelProps: HTMLAttributes, + labelProps: DOMAttributes, /** Props for the grouping element containing the date field and button. */ - groupProps: HTMLAttributes, + groupProps: DOMAttributes, /** Props for the date field. */ fieldProps: AriaDatePickerProps, /** Props for the popover trigger button. */ buttonProps: AriaButtonProps, /** Props for the description element, if any. */ - descriptionProps: HTMLAttributes, + descriptionProps: DOMAttributes, /** Props for the error message element, if any. */ - errorMessageProps: HTMLAttributes, + errorMessageProps: DOMAttributes, /** Props for the popover dialog. */ dialogProps: AriaDialogProps, /** Props for the calendar within the popover dialog. */ @@ -48,7 +49,7 @@ export interface DatePickerAria { * Provides the behavior and accessibility implementation for a date picker component. * A date picker combines a DateField and a Calendar popover to allow users to enter or select a date and time value. */ -export function useDatePicker(props: AriaDatePickerProps, state: DatePickerState, ref: RefObject): DatePickerAria { +export function useDatePicker(props: AriaDatePickerProps, state: DatePickerState, ref: RefObject): DatePickerAria { let buttonId = useId(); let dialogId = useId(); let formatMessage = useMessageFormatter(intlMessages); diff --git a/packages/@react-aria/datepicker/src/useDatePickerGroup.ts b/packages/@react-aria/datepicker/src/useDatePickerGroup.ts index c11eab72299..d09cbc5cc43 100644 --- a/packages/@react-aria/datepicker/src/useDatePickerGroup.ts +++ b/packages/@react-aria/datepicker/src/useDatePickerGroup.ts @@ -1,12 +1,12 @@ import {createFocusManager, getFocusableTreeWalker} from '@react-aria/focus'; import {DateFieldState, DatePickerState, DateRangePickerState} from '@react-stately/datepicker'; -import {KeyboardEvent} from '@react-types/shared'; +import {FocusableElement, KeyboardEvent} from '@react-types/shared'; import {mergeProps} from '@react-aria/utils'; import {RefObject, useMemo} from 'react'; import {useLocale} from '@react-aria/i18n'; import {usePress} from '@react-aria/interactions'; -export function useDatePickerGroup(state: DatePickerState | DateRangePickerState | DateFieldState, ref: RefObject, disableArrowNavigation?: boolean) { +export function useDatePickerGroup(state: DatePickerState | DateRangePickerState | DateFieldState, ref: RefObject, disableArrowNavigation?: boolean) { let {direction} = useLocale(); let focusManager = useMemo(() => createFocusManager(ref), [ref]); @@ -47,18 +47,18 @@ export function useDatePickerGroup(state: DatePickerState | DateRangePickerState // Focus the first placeholder segment from the end on mouse down/touch up in the field. let focusLast = () => { // Try to find the segment prior to the element that was clicked on. - let target = window.event?.target as HTMLElement; + let target = window.event?.target as FocusableElement; let walker = getFocusableTreeWalker(ref.current, {tabbable: true}); if (target) { walker.currentNode = target; - target = walker.previousNode() as HTMLElement; + target = walker.previousNode() as FocusableElement; } // If no target found, find the last element from the end. if (!target) { - let last: HTMLElement; + let last: FocusableElement; do { - last = walker.lastChild() as HTMLElement; + last = walker.lastChild() as FocusableElement; if (last) { target = last; } @@ -67,7 +67,7 @@ export function useDatePickerGroup(state: DatePickerState | DateRangePickerState // Now go backwards until we find an element that is not a placeholder. while (target?.hasAttribute('data-placeholder')) { - let prev = walker.previousNode() as HTMLElement; + let prev = walker.previousNode() as FocusableElement; if (prev && prev.hasAttribute('data-placeholder')) { target = prev; } else { diff --git a/packages/@react-aria/datepicker/src/useDateRangePicker.ts b/packages/@react-aria/datepicker/src/useDateRangePicker.ts index 0351219a34f..f55e5bce88e 100644 --- a/packages/@react-aria/datepicker/src/useDateRangePicker.ts +++ b/packages/@react-aria/datepicker/src/useDateRangePicker.ts @@ -15,21 +15,22 @@ import {AriaDatePickerProps, AriaDateRangePickerProps, DateValue} from '@react-t import {AriaDialogProps} from '@react-types/dialog'; import {createFocusManager} from '@react-aria/focus'; import {DateRangePickerState} from '@react-stately/datepicker'; +import {DOMAttributes} from '@react-types/shared'; import {filterDOMProps, mergeProps, useDescription, useId} from '@react-aria/utils'; import {focusManagerSymbol, roleSymbol} from './useDateField'; -import {HTMLAttributes, RefObject, useMemo} from 'react'; // @ts-ignore import intlMessages from '../intl/*.json'; import {RangeCalendarProps} from '@react-types/calendar'; +import {RefObject, useMemo} from 'react'; import {useDatePickerGroup} from './useDatePickerGroup'; import {useField} from '@react-aria/label'; import {useLocale, useMessageFormatter} from '@react-aria/i18n'; export interface DateRangePickerAria { /** Props for the date range picker's visible label element, if any. */ - labelProps: HTMLAttributes, + labelProps: DOMAttributes, /** Props for the grouping element containing the date fields and button. */ - groupProps: HTMLAttributes, + groupProps: DOMAttributes, /** Props for the start date field. */ startFieldProps: AriaDatePickerProps, /** Props for the end date field. */ @@ -37,9 +38,9 @@ export interface DateRangePickerAria { /** Props for the popover trigger button. */ buttonProps: AriaButtonProps, /** Props for the description element, if any. */ - descriptionProps: HTMLAttributes, + descriptionProps: DOMAttributes, /** Props for the error message element, if any. */ - errorMessageProps: HTMLAttributes, + errorMessageProps: DOMAttributes, /** Props for the popover dialog. */ dialogProps: AriaDialogProps, /** Props for the range calendar within the popover dialog. */ @@ -51,7 +52,7 @@ export interface DateRangePickerAria { * A date range picker combines two DateFields and a RangeCalendar popover to allow * users to enter or select a date and time range. */ -export function useDateRangePicker(props: AriaDateRangePickerProps, state: DateRangePickerState, ref: RefObject): DateRangePickerAria { +export function useDateRangePicker(props: AriaDateRangePickerProps, state: DateRangePickerState, ref: RefObject): DateRangePickerAria { let formatMessage = useMessageFormatter(intlMessages); let {labelProps, fieldProps, descriptionProps, errorMessageProps} = useField({ ...props, diff --git a/packages/@react-aria/datepicker/src/useDateSegment.ts b/packages/@react-aria/datepicker/src/useDateSegment.ts index bafc68e0053..80d40f12ed4 100644 --- a/packages/@react-aria/datepicker/src/useDateSegment.ts +++ b/packages/@react-aria/datepicker/src/useDateSegment.ts @@ -12,17 +12,18 @@ import {CalendarDate, toCalendar} from '@internationalized/date'; import {DateFieldState, DateSegment} from '@react-stately/datepicker'; +import {DOMAttributes} from '@react-types/shared'; import {getScrollParent, isIOS, isMac, mergeProps, scrollIntoView, useEvent, useId, useLabels, useLayoutEffect} from '@react-aria/utils'; import {hookData} from './useDateField'; import {NumberParser} from '@internationalized/number'; -import React, {HTMLAttributes, RefObject, useMemo, useRef} from 'react'; +import React, {RefObject, useMemo, useRef} from 'react'; import {useDateFormatter, useFilter, useLocale} from '@react-aria/i18n'; import {useDisplayNames} from './useDisplayNames'; import {useSpinButton} from '@react-aria/spinbutton'; export interface DateSegmentAria { /** Props for the segment element. */ - segmentProps: HTMLAttributes + segmentProps: DOMAttributes } /** diff --git a/packages/@react-aria/dialog/docs/tailwind.png b/packages/@react-aria/dialog/docs/tailwind.png new file mode 100644 index 0000000000000000000000000000000000000000..311cbae32908937aa45cacdac47c94faa4acd461 GIT binary patch literal 95678 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 MBb|pOD%K(Y2U(&sg#Z8m literal 0 HcmV?d00001 diff --git a/packages/@react-aria/dialog/docs/useDialog.mdx b/packages/@react-aria/dialog/docs/useDialog.mdx index 1277be70ee2..d74448d4c06 100644 --- a/packages/@react-aria/dialog/docs/useDialog.mdx +++ b/packages/@react-aria/dialog/docs/useDialog.mdx @@ -17,6 +17,8 @@ import {HeaderInfo, FunctionAPI, TypeContext, InterfaceType, TypeLink, PageDescr import {Keyboard} from '@react-spectrum/text'; import packageData from '@react-aria/dialog/package.json'; import Anatomy from './anatomy.svg'; +import {ExampleCard} from '@react-spectrum/docs/src/ExampleCard'; +import tailwindPreview from 'url:./tailwind.png'; --- category: Overlays @@ -217,3 +219,11 @@ function Example() { ``` + +## Styled examples + + diff --git a/packages/@react-aria/dialog/package.json b/packages/@react-aria/dialog/package.json index da628a3fed8..45520b887c5 100644 --- a/packages/@react-aria/dialog/package.json +++ b/packages/@react-aria/dialog/package.json @@ -24,7 +24,8 @@ "@react-aria/focus": "^3.6.1", "@react-aria/utils": "^3.13.1", "@react-stately/overlays": "^3.3.1", - "@react-types/dialog": "^3.4.1" + "@react-types/dialog": "^3.4.1", + "@react-types/shared": "^3.13.1" }, "publishConfig": { "access": "public" diff --git a/packages/@react-aria/dialog/src/index.ts b/packages/@react-aria/dialog/src/index.ts index dea42bbf357..6faae5f3db0 100644 --- a/packages/@react-aria/dialog/src/index.ts +++ b/packages/@react-aria/dialog/src/index.ts @@ -9,5 +9,4 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './useDialog'; +export {useDialog} from './useDialog'; diff --git a/packages/@react-aria/dialog/src/useDialog.ts b/packages/@react-aria/dialog/src/useDialog.ts index 79d1768573e..170a458e53e 100644 --- a/packages/@react-aria/dialog/src/useDialog.ts +++ b/packages/@react-aria/dialog/src/useDialog.ts @@ -11,23 +11,24 @@ */ import {AriaDialogProps} from '@react-types/dialog'; +import {DOMAttributes, FocusableElement} from '@react-types/shared'; import {filterDOMProps, useSlotId} from '@react-aria/utils'; import {focusSafely} from '@react-aria/focus'; -import {HTMLAttributes, RefObject, useEffect} from 'react'; +import {RefObject, useEffect} from 'react'; interface DialogAria { /** Props for the dialog container element. */ - dialogProps: HTMLAttributes, + dialogProps: DOMAttributes, /** Props for the dialog title element. */ - titleProps: HTMLAttributes + titleProps: DOMAttributes } /** * Provides the behavior and accessibility implementation for a dialog component. * A dialog is an overlay shown above other content in an application. */ -export function useDialog(props: AriaDialogProps, ref: RefObject): DialogAria { +export function useDialog(props: AriaDialogProps, ref: RefObject): DialogAria { let {role = 'dialog'} = props; let titleId = useSlotId(); titleId = props['aria-label'] ? undefined : titleId; diff --git a/packages/@react-aria/dnd/src/DragManager.ts b/packages/@react-aria/dnd/src/DragManager.ts index 38e72dd1261..ebaec478152 100644 --- a/packages/@react-aria/dnd/src/DragManager.ts +++ b/packages/@react-aria/dnd/src/DragManager.ts @@ -12,7 +12,7 @@ import {announce} from '@react-aria/live-announcer'; import {ariaHideOutside} from '@react-aria/overlays'; -import {DragEndEvent, DragItem, DropActivateEvent, DropEnterEvent, DropEvent, DropExitEvent, DropItem, DropOperation, DropTarget as DroppableCollectionTarget} from '@react-types/shared'; +import {DragEndEvent, DragItem, DropActivateEvent, DropEnterEvent, DropEvent, DropExitEvent, DropItem, DropOperation, DropTarget as DroppableCollectionTarget, FocusableElement} from '@react-types/shared'; import {getDragModality, getTypes} from './utils'; import {getInteractionModality} from '@react-aria/interactions'; import {useEffect, useState} from 'react'; @@ -23,7 +23,7 @@ let dragSession: DragSession = null; let subscriptions = new Set<() => void>(); interface DropTarget { - element: HTMLElement, + element: FocusableElement, getDropOperation?: (types: Set, allowedOperations: DropOperation[]) => DropOperation, onDropEnter?: (e: DropEnterEvent, dragTarget: DragTarget) => void, onDropExit?: (e: DropExitEvent) => void, @@ -43,7 +43,7 @@ export function registerDropTarget(target: DropTarget) { } interface DroppableItem { - element: HTMLElement, + element: FocusableElement, target: DroppableCollectionTarget, getDropOperation?: (types: Set, allowedOperations: DropOperation[]) => DropOperation } @@ -56,7 +56,7 @@ export function registerDropItem(item: DroppableItem) { } interface DragTarget { - element: HTMLElement, + element: FocusableElement, items: DragItem[], allowedDropOperations: DropOperation[], onDragEnd?: (e: DragEndEvent) => void diff --git a/packages/@react-aria/dnd/src/index.ts b/packages/@react-aria/dnd/src/index.ts index 9bcd591546f..3362d9e4007 100644 --- a/packages/@react-aria/dnd/src/index.ts +++ b/packages/@react-aria/dnd/src/index.ts @@ -10,13 +10,17 @@ * governing permissions and limitations under the License. */ -export * from './useDrag'; -export * from './useDrop'; -export * from './useDroppableCollection'; -export * from './useDroppableItem'; -export * from './useDropIndicator'; -export * from './useDraggableItem'; -export * from './useClipboard'; -export {DragPreview} from './DragPreview'; - +export type {DroppableCollectionOptions, DroppableCollectionResult} from './useDroppableCollection'; +export type {DroppableItemOptions, DroppableItemResult} from './useDroppableItem'; +export type {DropIndicatorProps, DropIndicatorAria} from './useDropIndicator'; +export type {DraggableItemProps, DraggableItemResult} from './useDraggableItem'; export type {DragPreviewProps} from './DragPreview'; + +export {useDrag} from './useDrag'; +export {useDrop} from './useDrop'; +export {useDroppableCollection} from './useDroppableCollection'; +export {useDroppableItem} from './useDroppableItem'; +export {useDropIndicator} from './useDropIndicator'; +export {useDraggableItem} from './useDraggableItem'; +export {useClipboard} from './useClipboard'; +export {DragPreview} from './DragPreview'; diff --git a/packages/@react-aria/dnd/src/useClipboard.ts b/packages/@react-aria/dnd/src/useClipboard.ts index 91fc2c65e0d..11264a1b7f9 100644 --- a/packages/@react-aria/dnd/src/useClipboard.ts +++ b/packages/@react-aria/dnd/src/useClipboard.ts @@ -11,9 +11,9 @@ */ import {chain} from '@react-aria/utils'; -import {DragItem, DropItem} from '@react-types/shared'; -import {HTMLAttributes, useEffect, useRef} from 'react'; +import {DOMAttributes, DragItem, DropItem} from '@react-types/shared'; import {readFromDataTransfer, writeToDataTransfer} from './utils'; +import {useEffect, useRef} from 'react'; import {useFocus} from '@react-aria/interactions'; interface ClipboardProps { @@ -24,7 +24,7 @@ interface ClipboardProps { } interface ClipboardResult { - clipboardProps: HTMLAttributes + clipboardProps: DOMAttributes } const globalEvents = new Map(); diff --git a/packages/@react-aria/dnd/src/useVirtualDrop.ts b/packages/@react-aria/dnd/src/useVirtualDrop.ts index 848c0010631..350c3ced9a1 100644 --- a/packages/@react-aria/dnd/src/useVirtualDrop.ts +++ b/packages/@react-aria/dnd/src/useVirtualDrop.ts @@ -10,8 +10,8 @@ * governing permissions and limitations under the License. */ +import {DOMAttributes} from '@react-types/shared'; import * as DragManager from './DragManager'; -import {HTMLAttributes} from 'react'; // @ts-ignore import intlMessages from '../intl/*.json'; import {useDescription} from '@react-aria/utils'; @@ -19,7 +19,7 @@ import {useDragModality} from './utils'; import {useMessageFormatter} from '@react-aria/i18n'; interface VirtualDropResult { - dropProps: HTMLAttributes + dropProps: DOMAttributes } const MESSAGES = { diff --git a/packages/@react-aria/dnd/stories/DraggableCollection.tsx b/packages/@react-aria/dnd/stories/DraggableCollection.tsx index 63cf72d3760..2af2ce25238 100644 --- a/packages/@react-aria/dnd/stories/DraggableCollection.tsx +++ b/packages/@react-aria/dnd/stories/DraggableCollection.tsx @@ -175,7 +175,7 @@ function DraggableCollectionItem({item, state, dragState}) { })}>
} + {...buttonProps} ref={buttonRef} className={classNames(dndStyles, 'drag-handle')}> diff --git a/packages/@react-aria/focus/src/FocusScope.tsx b/packages/@react-aria/focus/src/FocusScope.tsx index 3412bc053b5..da8307b239d 100644 --- a/packages/@react-aria/focus/src/FocusScope.tsx +++ b/packages/@react-aria/focus/src/FocusScope.tsx @@ -10,6 +10,7 @@ * governing permissions and limitations under the License. */ +import {FocusableElement} from '@react-types/shared'; import {focusSafely} from './focusSafely'; import {isElementVisible} from './isElementVisible'; import React, {ReactNode, RefObject, useContext, useEffect, useRef} from 'react'; @@ -40,7 +41,7 @@ interface FocusScopeProps { interface FocusManagerOptions { /** The element to start searching from. The currently focused element by default. */ - from?: HTMLElement, + from?: Element, /** Whether to only include tabbable elements, or all focusable elements. */ tabbable?: boolean, /** Whether focus should wrap around when it reaches the end of the scope. */ @@ -51,16 +52,16 @@ interface FocusManagerOptions { export interface FocusManager { /** Moves focus to the next focusable or tabbable element in the focus scope. */ - focusNext(opts?: FocusManagerOptions): HTMLElement, + focusNext(opts?: FocusManagerOptions): FocusableElement, /** Moves focus to the previous focusable or tabbable element in the focus scope. */ - focusPrevious(opts?: FocusManagerOptions): HTMLElement, + focusPrevious(opts?: FocusManagerOptions): FocusableElement, /** Moves focus to the first focusable or tabbable element in the focus scope. */ - focusFirst(opts?: FocusManagerOptions): HTMLElement, + focusFirst(opts?: FocusManagerOptions): FocusableElement, /** Moves focus to the last focusable or tabbable element in the focus scope. */ - focusLast(opts?: FocusManagerOptions): HTMLElement + focusLast(opts?: FocusManagerOptions): FocusableElement } -type ScopeRef = RefObject; +type ScopeRef = RefObject; interface IFocusContext { scopeRef: ScopeRef, focusManager: FocusManager @@ -87,7 +88,7 @@ export function FocusScope(props: FocusScopeProps) { let {children, contain, restoreFocus, autoFocus} = props; let startRef = useRef(); let endRef = useRef(); - let scopeRef = useRef([]); + let scopeRef = useRef([]); let ctx = useContext(FocusContext); let parentScope = ctx?.scopeRef; @@ -143,7 +144,7 @@ export function useFocusManager(): FocusManager { return useContext(FocusContext)?.focusManager; } -function createFocusManagerForScope(scopeRef: React.RefObject): FocusManager { +function createFocusManagerForScope(scopeRef: React.RefObject): FocusManager { return { focusNext(opts: FocusManagerOptions = {}) { let scope = scopeRef.current; @@ -152,10 +153,10 @@ function createFocusManagerForScope(scopeRef: React.RefObject): F let sentinel = scope[0].previousElementSibling; let walker = getFocusableTreeWalker(getScopeRoot(scope), {tabbable, accept}, scope); walker.currentNode = isElementInScope(node, scope) ? node : sentinel; - let nextNode = walker.nextNode() as HTMLElement; + let nextNode = walker.nextNode() as FocusableElement; if (!nextNode && wrap) { walker.currentNode = sentinel; - nextNode = walker.nextNode() as HTMLElement; + nextNode = walker.nextNode() as FocusableElement; } if (nextNode) { focusElement(nextNode, true); @@ -169,10 +170,10 @@ function createFocusManagerForScope(scopeRef: React.RefObject): F let sentinel = scope[scope.length - 1].nextElementSibling; let walker = getFocusableTreeWalker(getScopeRoot(scope), {tabbable, accept}, scope); walker.currentNode = isElementInScope(node, scope) ? node : sentinel; - let previousNode = walker.previousNode() as HTMLElement; + let previousNode = walker.previousNode() as FocusableElement; if (!previousNode && wrap) { walker.currentNode = sentinel; - previousNode = walker.previousNode() as HTMLElement; + previousNode = walker.previousNode() as FocusableElement; } if (previousNode) { focusElement(previousNode, true); @@ -184,7 +185,7 @@ function createFocusManagerForScope(scopeRef: React.RefObject): F let {tabbable, accept} = opts; let walker = getFocusableTreeWalker(getScopeRoot(scope), {tabbable, accept}, scope); walker.currentNode = scope[0].previousElementSibling; - let nextNode = walker.nextNode() as HTMLElement; + let nextNode = walker.nextNode() as FocusableElement; if (nextNode) { focusElement(nextNode, true); } @@ -195,7 +196,7 @@ function createFocusManagerForScope(scopeRef: React.RefObject): F let {tabbable, accept} = opts; let walker = getFocusableTreeWalker(getScopeRoot(scope), {tabbable, accept}, scope); walker.currentNode = scope[scope.length - 1].nextElementSibling; - let previousNode = walker.previousNode() as HTMLElement; + let previousNode = walker.previousNode() as FocusableElement; if (previousNode) { focusElement(previousNode, true); } @@ -225,12 +226,12 @@ const FOCUSABLE_ELEMENT_SELECTOR = focusableElements.join(':not([hidden]),') + ' focusableElements.push('[tabindex]:not([tabindex="-1"]):not([disabled])'); const TABBABLE_ELEMENT_SELECTOR = focusableElements.join(':not([hidden]):not([tabindex="-1"]),'); -function getScopeRoot(scope: HTMLElement[]) { +function getScopeRoot(scope: Element[]) { return scope[0].parentElement; } -function useFocusContainment(scopeRef: RefObject, contain: boolean) { - let focusedNode = useRef(); +function useFocusContainment(scopeRef: RefObject, contain: boolean) { + let focusedNode = useRef(); let raf = useRef(null); useLayoutEffect(() => { @@ -250,7 +251,7 @@ function useFocusContainment(scopeRef: RefObject, contain: boolea return; } - let focusedElement = document.activeElement as HTMLElement; + let focusedElement = document.activeElement; let scope = scopeRef.current; if (!isElementInScope(focusedElement, scope)) { return; @@ -258,10 +259,10 @@ function useFocusContainment(scopeRef: RefObject, contain: boolea let walker = getFocusableTreeWalker(getScopeRoot(scope), {tabbable: true}, scope); walker.currentNode = focusedElement; - let nextElement = (e.shiftKey ? walker.previousNode() : walker.nextNode()) as HTMLElement; + let nextElement = (e.shiftKey ? walker.previousNode() : walker.nextNode()) as FocusableElement; if (!nextElement) { walker.currentNode = e.shiftKey ? scope[scope.length - 1].nextElementSibling : scope[0].previousElementSibling; - nextElement = (e.shiftKey ? walker.previousNode() : walker.nextNode()) as HTMLElement; + nextElement = (e.shiftKey ? walker.previousNode() : walker.nextNode()) as FocusableElement; } e.preventDefault(); @@ -332,7 +333,7 @@ function isElementInAnyScope(element: Element) { return false; } -function isElementInScope(element: Element, scope: HTMLElement[]) { +function isElementInScope(element: Element, scope: Element[]) { return scope.some(node => node.contains(element)); } @@ -361,7 +362,7 @@ function isAncestorScope(ancestor: ScopeRef, scope: ScopeRef) { return isAncestorScope(ancestor, parent); } -function focusElement(element: HTMLElement | null, scroll = false) { +function focusElement(element: FocusableElement | null, scroll = false) { if (element != null && !scroll) { try { focusSafely(element); @@ -377,14 +378,14 @@ function focusElement(element: HTMLElement | null, scroll = false) { } } -function focusFirstInScope(scope: HTMLElement[]) { +function focusFirstInScope(scope: Element[]) { let sentinel = scope[0].previousElementSibling; let walker = getFocusableTreeWalker(getScopeRoot(scope), {tabbable: true}, scope); walker.currentNode = sentinel; - focusElement(walker.nextNode() as HTMLElement); + focusElement(walker.nextNode() as FocusableElement); } -function useAutoFocus(scopeRef: RefObject, autoFocus: boolean) { +function useAutoFocus(scopeRef: RefObject, autoFocus: boolean) { const autoFocusRef = React.useRef(autoFocus); useEffect(() => { if (autoFocusRef.current) { @@ -397,9 +398,9 @@ function useAutoFocus(scopeRef: RefObject, autoFocus: boolean) { }, []); } -function useRestoreFocus(scopeRef: RefObject, restoreFocus: boolean, contain: boolean) { +function useRestoreFocus(scopeRef: RefObject, restoreFocus: boolean, contain: boolean) { // create a ref during render instead of useLayoutEffect so the active element is saved before a child with autoFocus=true mounts. - const nodeToRestoreRef = useRef(typeof document !== 'undefined' ? document.activeElement as HTMLElement : null); + const nodeToRestoreRef = useRef(typeof document !== 'undefined' ? document.activeElement as FocusableElement : null); // useLayoutEffect instead of useEffect so the active element is saved synchronously instead of asynchronously. useLayoutEffect(() => { @@ -417,7 +418,7 @@ function useRestoreFocus(scopeRef: RefObject, restoreFocus: boole return; } - let focusedElement = document.activeElement as HTMLElement; + let focusedElement = document.activeElement as FocusableElement; if (!isElementInScope(focusedElement, scopeRef.current)) { return; } @@ -427,7 +428,7 @@ function useRestoreFocus(scopeRef: RefObject, restoreFocus: boole // Find the next tabbable element after the currently focused element walker.currentNode = focusedElement; - let nextElement = (e.shiftKey ? walker.previousNode() : walker.nextNode()) as HTMLElement; + let nextElement = (e.shiftKey ? walker.previousNode() : walker.nextNode()) as FocusableElement; if (!document.body.contains(nodeToRestore) || nodeToRestore === document.body) { nodeToRestore = null; @@ -440,7 +441,7 @@ function useRestoreFocus(scopeRef: RefObject, restoreFocus: boole // Skip over elements within the scope, in case the scope immediately follows the node to restore. do { - nextElement = (e.shiftKey ? walker.previousNode() : walker.nextNode()) as HTMLElement; + nextElement = (e.shiftKey ? walker.previousNode() : walker.nextNode()) as FocusableElement; } while (isElementInScope(nextElement, scopeRef.current)); e.preventDefault(); @@ -485,7 +486,7 @@ function useRestoreFocus(scopeRef: RefObject, restoreFocus: boole * Create a [TreeWalker]{@link https://developer.mozilla.org/en-US/docs/Web/API/TreeWalker} * that matches all focusable/tabbable elements. */ -export function getFocusableTreeWalker(root: HTMLElement, opts?: FocusManagerOptions, scope?: HTMLElement[]) { +export function getFocusableTreeWalker(root: Element, opts?: FocusManagerOptions, scope?: Element[]) { let selector = opts?.tabbable ? TABBABLE_ELEMENT_SELECTOR : FOCUSABLE_ELEMENT_SELECTOR; let walker = document.createTreeWalker( root, @@ -497,9 +498,9 @@ export function getFocusableTreeWalker(root: HTMLElement, opts?: FocusManagerOpt return NodeFilter.FILTER_REJECT; } - if ((node as HTMLElement).matches(selector) - && isElementVisible(node as HTMLElement) - && (!scope || isElementInScope(node as HTMLElement, scope)) + if ((node as Element).matches(selector) + && isElementVisible(node as Element) + && (!scope || isElementInScope(node as Element, scope)) && (!opts?.accept || opts.accept(node as Element)) ) { return NodeFilter.FILTER_ACCEPT; @@ -520,7 +521,7 @@ export function getFocusableTreeWalker(root: HTMLElement, opts?: FocusManagerOpt /** * Creates a FocusManager object that can be used to move focus within an element. */ -export function createFocusManager(ref: RefObject, defaultOptions: FocusManagerOptions = {}): FocusManager { +export function createFocusManager(ref: RefObject, defaultOptions: FocusManagerOptions = {}): FocusManager { return { focusNext(opts: FocusManagerOptions = {}) { let root = ref.current; @@ -533,10 +534,10 @@ export function createFocusManager(ref: RefObject, defaultOptions: if (root.contains(node)) { walker.currentNode = node; } - let nextNode = walker.nextNode() as HTMLElement; + let nextNode = walker.nextNode() as FocusableElement; if (!nextNode && wrap) { walker.currentNode = root; - nextNode = walker.nextNode() as HTMLElement; + nextNode = walker.nextNode() as FocusableElement; } if (nextNode) { focusElement(nextNode, true); @@ -560,7 +561,7 @@ export function createFocusManager(ref: RefObject, defaultOptions: } return next; } - let previousNode = walker.previousNode() as HTMLElement; + let previousNode = walker.previousNode() as FocusableElement; if (!previousNode && wrap) { walker.currentNode = root; previousNode = last(walker); @@ -577,7 +578,7 @@ export function createFocusManager(ref: RefObject, defaultOptions: } let {tabbable = defaultOptions.tabbable, accept = defaultOptions.accept} = opts; let walker = getFocusableTreeWalker(root, {tabbable, accept}); - let nextNode = walker.nextNode() as HTMLElement; + let nextNode = walker.nextNode() as FocusableElement; if (nextNode) { focusElement(nextNode, true); } @@ -600,10 +601,10 @@ export function createFocusManager(ref: RefObject, defaultOptions: } function last(walker: TreeWalker) { - let next: HTMLElement; - let last: HTMLElement; + let next: FocusableElement; + let last: FocusableElement; do { - last = walker.lastChild() as HTMLElement; + last = walker.lastChild() as FocusableElement; if (last) { next = last; } diff --git a/packages/@react-aria/focus/src/focusSafely.ts b/packages/@react-aria/focus/src/focusSafely.ts index 8476ed275b7..b18447a9e0e 100644 --- a/packages/@react-aria/focus/src/focusSafely.ts +++ b/packages/@react-aria/focus/src/focusSafely.ts @@ -10,6 +10,7 @@ * governing permissions and limitations under the License. */ +import {FocusableElement} from '@react-types/shared'; import {focusWithoutScrolling, runAfterTransition} from '@react-aria/utils'; import {getInteractionModality} from '@react-aria/interactions'; @@ -17,7 +18,7 @@ import {getInteractionModality} from '@react-aria/interactions'; * A utility function that focuses an element while avoiding undesired side effects such * as page scrolling and screen reader issues with CSS transitions. */ -export function focusSafely(element: HTMLElement) { +export function focusSafely(element: FocusableElement) { // If the user is interacting with a virtual cursor, e.g. screen reader, then // wait until after any animated transitions that are currently occurring on // the page before shifting focus. This avoids issues with VoiceOver on iOS diff --git a/packages/@react-aria/focus/src/index.ts b/packages/@react-aria/focus/src/index.ts index a0edd0d8b4e..65808a982a7 100644 --- a/packages/@react-aria/focus/src/index.ts +++ b/packages/@react-aria/focus/src/index.ts @@ -9,9 +9,9 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './FocusScope'; -export * from './FocusRing'; -export * from './useFocusable'; -export * from './useFocusRing'; -export * from './focusSafely'; +export type {FocusManager} from './FocusScope'; +export {FocusScope, useFocusManager, getFocusableTreeWalker, createFocusManager} from './FocusScope'; +export {FocusRing} from './FocusRing'; +export {FocusableProvider, useFocusable} from './useFocusable'; +export {useFocusRing} from './useFocusRing'; +export {focusSafely} from './focusSafely'; diff --git a/packages/@react-aria/focus/src/useFocusRing.ts b/packages/@react-aria/focus/src/useFocusRing.ts index 6a6dd540cf0..97714ca02fc 100644 --- a/packages/@react-aria/focus/src/useFocusRing.ts +++ b/packages/@react-aria/focus/src/useFocusRing.ts @@ -1,5 +1,6 @@ -import {HTMLAttributes, useCallback, useState} from 'react'; +import {DOMAttributes} from '@react-types/shared'; import {isFocusVisible, useFocus, useFocusVisibleListener, useFocusWithin} from '@react-aria/interactions'; +import {useCallback, useState} from 'react'; import {useRef} from 'react'; interface FocusRingProps { @@ -26,7 +27,7 @@ interface FocusRingAria { isFocusVisible: boolean, /** Props to apply to the container element with the focus ring. */ - focusProps: HTMLAttributes + focusProps: DOMAttributes } /** diff --git a/packages/@react-aria/focus/src/useFocusable.tsx b/packages/@react-aria/focus/src/useFocusable.tsx index 239c550301f..184cbca11e4 100644 --- a/packages/@react-aria/focus/src/useFocusable.tsx +++ b/packages/@react-aria/focus/src/useFocusable.tsx @@ -10,10 +10,10 @@ * governing permissions and limitations under the License. */ -import {FocusableDOMProps, FocusableProps} from '@react-types/shared'; +import {DOMAttributes, FocusableDOMProps, FocusableElement, FocusableProps} from '@react-types/shared'; import {focusSafely} from './'; import {mergeProps, useSyncRef} from '@react-aria/utils'; -import React, {HTMLAttributes, MutableRefObject, ReactNode, RefObject, useContext, useEffect, useRef} from 'react'; +import React, {MutableRefObject, ReactNode, RefObject, useContext, useEffect, useRef} from 'react'; import {useFocus, useKeyboard} from '@react-aria/interactions'; interface FocusableOptions extends FocusableProps, FocusableDOMProps { @@ -21,18 +21,18 @@ interface FocusableOptions extends FocusableProps, FocusableDOMProps { isDisabled?: boolean } -interface FocusableProviderProps extends HTMLAttributes { +interface FocusableProviderProps extends DOMAttributes { /** The child element to provide DOM props to. */ children?: ReactNode } interface FocusableContextValue extends FocusableProviderProps { - ref?: MutableRefObject + ref?: MutableRefObject } let FocusableContext = React.createContext(null); -function useFocusableContext(ref: RefObject): FocusableContextValue { +function useFocusableContext(ref: RefObject): FocusableContextValue { let context = useContext(FocusableContext) || {}; useSyncRef(context, ref); @@ -44,7 +44,7 @@ function useFocusableContext(ref: RefObject): FocusableContextValue /** * Provides DOM props to the nearest focusable child. */ -function FocusableProvider(props: FocusableProviderProps, ref: RefObject) { +function FocusableProvider(props: FocusableProviderProps, ref: RefObject) { let {children, ...otherProps} = props; let context = { ...otherProps, @@ -63,13 +63,13 @@ export {_FocusableProvider as FocusableProvider}; interface FocusableAria { /** Props for the focusable element. */ - focusableProps: HTMLAttributes + focusableProps: DOMAttributes } /** * Used to make an element focusable and capable of auto focus. */ -export function useFocusable(props: FocusableOptions, domRef: RefObject): FocusableAria { +export function useFocusable(props: FocusableOptions, domRef: RefObject): FocusableAria { let {focusProps} = useFocus(props); let {keyboardProps} = useKeyboard(props); let interactions = mergeProps(focusProps, keyboardProps); diff --git a/packages/@react-aria/grid/src/index.ts b/packages/@react-aria/grid/src/index.ts index 89f7bb87de0..1dff56cec36 100644 --- a/packages/@react-aria/grid/src/index.ts +++ b/packages/@react-aria/grid/src/index.ts @@ -10,11 +10,16 @@ * governing permissions and limitations under the License. */ -export * from './GridKeyboardDelegate'; -export * from './useGrid'; -export * from './useGridRowGroup'; -export * from './useGridRow'; -export * from './useGridCell'; -export * from './useGridSelectionCheckbox'; -export * from './useHighlightSelectionDescription'; -export * from './useGridSelectionAnnouncement'; +export type {GridKeyboardDelegateOptions} from './GridKeyboardDelegate'; +export type {GridProps, GridAria} from './useGrid'; +export type {GridRowGroupAria} from './useGridRowGroup'; +export type {GridRowProps, GridRowAria} from './useGridRow'; + +export {GridKeyboardDelegate} from './GridKeyboardDelegate'; +export {useGrid} from './useGrid'; +export {useGridRowGroup} from './useGridRowGroup'; +export {useGridRow} from './useGridRow'; +export {useGridCell} from './useGridCell'; +export {useGridSelectionCheckbox} from './useGridSelectionCheckbox'; +export {useHighlightSelectionDescription} from './useHighlightSelectionDescription'; +export {useGridSelectionAnnouncement} from './useGridSelectionAnnouncement'; diff --git a/packages/@react-aria/grid/src/useGrid.ts b/packages/@react-aria/grid/src/useGrid.ts index e82746eefb6..a886e842669 100644 --- a/packages/@react-aria/grid/src/useGrid.ts +++ b/packages/@react-aria/grid/src/useGrid.ts @@ -10,13 +10,13 @@ * governing permissions and limitations under the License. */ -import {AriaLabelingProps, DOMProps, KeyboardDelegate} from '@react-types/shared'; +import {AriaLabelingProps, DOMAttributes, DOMProps, KeyboardDelegate} from '@react-types/shared'; import {filterDOMProps, mergeProps, useId} from '@react-aria/utils'; import {GridCollection} from '@react-types/grid'; import {GridKeyboardDelegate} from './GridKeyboardDelegate'; import {gridMap} from './utils'; import {GridState} from '@react-stately/grid'; -import {HTMLAttributes, Key, RefObject, useMemo} from 'react'; +import {Key, RefObject, useMemo} from 'react'; import {useCollator, useLocale} from '@react-aria/i18n'; import {useGridSelectionAnnouncement} from './useGridSelectionAnnouncement'; import {useHighlightSelectionDescription} from './useHighlightSelectionDescription'; @@ -52,7 +52,7 @@ export interface GridProps extends DOMProps, AriaLabelingProps { export interface GridAria { /** Props for the grid element. */ - gridProps: HTMLAttributes + gridProps: DOMAttributes } /** @@ -107,7 +107,7 @@ export function useGrid(props: GridProps, state: GridState = mergeProps( + let gridProps: DOMAttributes = mergeProps( domProps, { role: 'grid', diff --git a/packages/@react-aria/grid/src/useGridCell.ts b/packages/@react-aria/grid/src/useGridCell.ts index ddfc7b3890b..808ee33f915 100644 --- a/packages/@react-aria/grid/src/useGridCell.ts +++ b/packages/@react-aria/grid/src/useGridCell.ts @@ -10,14 +10,14 @@ * governing permissions and limitations under the License. */ +import {DOMAttributes, FocusableElement, Node as RSNode} from '@react-types/shared'; import {focusSafely, getFocusableTreeWalker} from '@react-aria/focus'; import {GridCollection} from '@react-types/grid'; import {gridMap} from './utils'; import {GridState} from '@react-stately/grid'; -import {HTMLAttributes, KeyboardEvent as ReactKeyboardEvent, RefObject} from 'react'; import {isFocusVisible} from '@react-aria/interactions'; import {mergeProps} from '@react-aria/utils'; -import {Node as RSNode} from '@react-types/shared'; +import {KeyboardEvent as ReactKeyboardEvent, RefObject} from 'react'; import {useLocale} from '@react-aria/i18n'; import {useSelectableItem} from '@react-aria/selection'; @@ -40,7 +40,7 @@ interface GridCellProps { interface GridCellAria { /** Props for the grid cell element. */ - gridCellProps: HTMLAttributes, + gridCellProps: DOMAttributes, /** Whether the cell is currently in a pressed state. */ isPressed: boolean } @@ -50,7 +50,7 @@ interface GridCellAria { * @param props - Props for the cell. * @param state - State of the parent grid, as returned by `useGridState`. */ -export function useGridCell>(props: GridCellProps, state: GridState, ref: RefObject): GridCellAria { +export function useGridCell>(props: GridCellProps, state: GridState, ref: RefObject): GridCellAria { let { node, isVirtualized, @@ -74,7 +74,7 @@ export function useGridCell>(props: GridCellProps let focusable = state.selectionManager.childFocusStrategy === 'last' ? last(treeWalker) - : treeWalker.firstChild() as HTMLElement; + : treeWalker.firstChild() as FocusableElement; if (focusable) { focusSafely(focusable); return; @@ -97,7 +97,7 @@ export function useGridCell>(props: GridCellProps }); let onKeyDownCapture = (e: ReactKeyboardEvent) => { - if (!e.currentTarget.contains(e.target as HTMLElement) || state.isKeyboardNavigationDisabled) { + if (!e.currentTarget.contains(e.target as Element) || state.isKeyboardNavigationDisabled) { return; } @@ -108,8 +108,8 @@ export function useGridCell>(props: GridCellProps case 'ArrowLeft': { // Find the next focusable element within the cell. let focusable = direction === 'rtl' - ? walker.nextNode() as HTMLElement - : walker.previousNode() as HTMLElement; + ? walker.nextNode() as FocusableElement + : walker.previousNode() as FocusableElement; // Don't focus the cell itself if focusMode is "child" if (focusMode === 'child' && focusable === ref.current) { @@ -138,7 +138,7 @@ export function useGridCell>(props: GridCellProps } else { walker.currentNode = ref.current; focusable = direction === 'rtl' - ? walker.firstChild() as HTMLElement + ? walker.firstChild() as FocusableElement : last(walker); if (focusable) { focusSafely(focusable); @@ -149,8 +149,8 @@ export function useGridCell>(props: GridCellProps } case 'ArrowRight': { let focusable = direction === 'rtl' - ? walker.previousNode() as HTMLElement - : walker.nextNode() as HTMLElement; + ? walker.previousNode() as FocusableElement + : walker.nextNode() as FocusableElement; if (focusMode === 'child' && focusable === ref.current) { focusable = null; @@ -174,7 +174,7 @@ export function useGridCell>(props: GridCellProps walker.currentNode = ref.current; focusable = direction === 'rtl' ? last(walker) - : walker.firstChild() as HTMLElement; + : walker.firstChild() as FocusableElement; if (focusable) { focusSafely(focusable); } @@ -187,7 +187,7 @@ export function useGridCell>(props: GridCellProps // Prevent this event from reaching cell children, e.g. menu buttons. We want arrow keys to navigate // to the cell above/below instead. We need to re-dispatch the event from a higher parent so it still // bubbles and gets handled by useSelectableCollection. - if (!e.altKey && ref.current.contains(e.target as HTMLElement)) { + if (!e.altKey && ref.current.contains(e.target as Element)) { e.stopPropagation(); e.preventDefault(); ref.current.parentElement.dispatchEvent( @@ -223,7 +223,7 @@ export function useGridCell>(props: GridCellProps }); }; - let gridCellProps: HTMLAttributes = mergeProps(itemProps, { + let gridCellProps: DOMAttributes = mergeProps(itemProps, { role: 'gridcell', onKeyDownCapture, onFocus @@ -240,10 +240,10 @@ export function useGridCell>(props: GridCellProps } function last(walker: TreeWalker) { - let next: HTMLElement; - let last: HTMLElement; + let next: FocusableElement; + let last: FocusableElement; do { - last = walker.lastChild() as HTMLElement; + last = walker.lastChild() as FocusableElement; if (last) { next = last; } diff --git a/packages/@react-aria/grid/src/useGridRow.ts b/packages/@react-aria/grid/src/useGridRow.ts index 4c6f50f703c..0785d3b453f 100644 --- a/packages/@react-aria/grid/src/useGridRow.ts +++ b/packages/@react-aria/grid/src/useGridRow.ts @@ -10,11 +10,11 @@ * governing permissions and limitations under the License. */ +import {DOMAttributes, FocusableElement, Node} from '@react-types/shared'; import {GridCollection} from '@react-types/grid'; import {gridMap} from './utils'; import {GridState} from '@react-stately/grid'; -import {HTMLAttributes, RefObject} from 'react'; -import {Node} from '@react-types/shared'; +import {RefObject} from 'react'; import {useSelectableItem} from '@react-aria/selection'; export interface GridRowProps { @@ -34,7 +34,7 @@ export interface GridRowProps { export interface GridRowAria { /** Props for the grid row element. */ - rowProps: HTMLAttributes, + rowProps: DOMAttributes, /** Whether the row is currently in a pressed state. */ isPressed: boolean } @@ -44,7 +44,7 @@ export interface GridRowAria { * @param props - Props for the row. * @param state - State of the parent grid, as returned by `useGridState`. */ -export function useGridRow, S extends GridState>(props: GridRowProps, state: S, ref: RefObject): GridRowAria { +export function useGridRow, S extends GridState>(props: GridRowProps, state: S, ref: RefObject): GridRowAria { let { node, isVirtualized, @@ -64,7 +64,7 @@ export function useGridRow, S extends GridState = { + let rowProps: DOMAttributes = { role: 'row', 'aria-selected': state.selectionManager.selectionMode !== 'none' ? isSelected : undefined, ...itemProps diff --git a/packages/@react-aria/grid/src/useGridRowGroup.ts b/packages/@react-aria/grid/src/useGridRowGroup.ts index 819460ce9d3..fc1c4bddb54 100644 --- a/packages/@react-aria/grid/src/useGridRowGroup.ts +++ b/packages/@react-aria/grid/src/useGridRowGroup.ts @@ -10,11 +10,11 @@ * governing permissions and limitations under the License. */ -import {HTMLAttributes} from 'react'; +import {DOMAttributes} from '@react-types/shared'; export interface GridRowGroupAria { /** Props for the row group element. */ - rowGroupProps: HTMLAttributes + rowGroupProps: DOMAttributes } /** diff --git a/packages/@react-aria/i18n/src/index.ts b/packages/@react-aria/i18n/src/index.ts index de728dc6dbf..d63d1660e2a 100644 --- a/packages/@react-aria/i18n/src/index.ts +++ b/packages/@react-aria/i18n/src/index.ts @@ -10,9 +10,10 @@ * governing permissions and limitations under the License. */ -export * from './context'; -export * from './useMessageFormatter'; -export * from './useDateFormatter'; -export * from './useNumberFormatter'; -export * from './useCollator'; -export * from './useFilter'; +export type {FormatMessage} from './useMessageFormatter'; +export {I18nProvider, useLocale} from './context'; +export {useMessageFormatter} from './useMessageFormatter'; +export {useDateFormatter} from './useDateFormatter'; +export {useNumberFormatter} from './useNumberFormatter'; +export {useCollator} from './useCollator'; +export {useFilter} from './useFilter'; diff --git a/packages/@react-aria/interactions/src/DOMPropsContext.ts b/packages/@react-aria/interactions/src/DOMPropsContext.ts index be4be383286..31d9f2a90c1 100644 --- a/packages/@react-aria/interactions/src/DOMPropsContext.ts +++ b/packages/@react-aria/interactions/src/DOMPropsContext.ts @@ -10,16 +10,17 @@ * governing permissions and limitations under the License. */ +import {DOMAttributes} from '@react-types/shared'; import {mergeProps, useSyncRef} from '@react-aria/utils'; -import React, {HTMLAttributes, MutableRefObject, RefObject, useContext} from 'react'; +import React, {MutableRefObject, RefObject, useContext} from 'react'; -interface DOMPropsResponderProps extends HTMLAttributes { - ref?: RefObject +interface DOMPropsResponderProps extends DOMAttributes { + ref?: RefObject } -interface IDOMPropsResponderContext extends HTMLAttributes { +interface IDOMPropsResponderContext extends DOMAttributes { register(): void, - ref?: MutableRefObject + ref?: MutableRefObject } export const DOMPropsResponderContext = React.createContext(null); diff --git a/packages/@react-aria/interactions/src/DOMPropsResponder.tsx b/packages/@react-aria/interactions/src/DOMPropsResponder.tsx index 6031e9f96f5..0502deb44ba 100644 --- a/packages/@react-aria/interactions/src/DOMPropsResponder.tsx +++ b/packages/@react-aria/interactions/src/DOMPropsResponder.tsx @@ -18,7 +18,7 @@ interface DOMPropsResponderProps extends HoverProps { children: ReactNode } -export const DOMPropsResponder = React.forwardRef(({children, ...props}: DOMPropsResponderProps, ref: RefObject) => { +export const DOMPropsResponder = React.forwardRef(({children, ...props}: DOMPropsResponderProps, ref: RefObject) => { let isRegistered = useRef(false); let context = { ...props, diff --git a/packages/@react-aria/interactions/src/PressResponder.tsx b/packages/@react-aria/interactions/src/PressResponder.tsx index 27de806fbd6..d630b9e4d2d 100644 --- a/packages/@react-aria/interactions/src/PressResponder.tsx +++ b/packages/@react-aria/interactions/src/PressResponder.tsx @@ -10,6 +10,7 @@ * governing permissions and limitations under the License. */ +import {FocusableElement} from '@react-types/shared'; import {mergeProps, useSyncRef} from '@react-aria/utils'; import {PressProps} from './usePress'; import {PressResponderContext} from './context'; @@ -19,7 +20,7 @@ interface PressResponderProps extends PressProps { children: ReactNode } -export const PressResponder = React.forwardRef(({children, ...props}: PressResponderProps, ref: RefObject) => { +export const PressResponder = React.forwardRef(({children, ...props}: PressResponderProps, ref: RefObject) => { let isRegistered = useRef(false); let prevContext = useContext(PressResponderContext); let context = mergeProps(prevContext || {}, { diff --git a/packages/@react-aria/interactions/src/Pressable.tsx b/packages/@react-aria/interactions/src/Pressable.tsx index 1b76269510d..d73f185f21d 100644 --- a/packages/@react-aria/interactions/src/Pressable.tsx +++ b/packages/@react-aria/interactions/src/Pressable.tsx @@ -10,15 +10,16 @@ * governing permissions and limitations under the License. */ +import {DOMAttributes} from '@react-types/shared'; import {mergeProps} from '@react-aria/utils'; import {PressProps, usePress} from './usePress'; -import React, {HTMLAttributes, ReactElement, RefObject, useRef} from 'react'; +import React, {ReactElement, RefObject, useRef} from 'react'; interface PressableProps extends PressProps { - children: ReactElement, string> + children: ReactElement } -export const Pressable = React.forwardRef(({children, ...props}: PressableProps, ref: RefObject) => { +export const Pressable = React.forwardRef(({children, ...props}: PressableProps, ref: RefObject) => { let newRef = useRef(); ref = ref ?? newRef; let {pressProps} = usePress({...props, ref}); diff --git a/packages/@react-aria/interactions/src/context.ts b/packages/@react-aria/interactions/src/context.ts index aef90620d7e..3599671abe8 100644 --- a/packages/@react-aria/interactions/src/context.ts +++ b/packages/@react-aria/interactions/src/context.ts @@ -10,12 +10,13 @@ * governing permissions and limitations under the License. */ +import {FocusableElement} from '@react-types/shared'; import {PressProps} from './usePress'; import React, {MutableRefObject} from 'react'; interface IPressResponderContext extends PressProps { register(): void, - ref?: MutableRefObject + ref?: MutableRefObject } export const PressResponderContext = React.createContext(null); diff --git a/packages/@react-aria/interactions/src/index.ts b/packages/@react-aria/interactions/src/index.ts index f8778189e62..13555d54d8b 100644 --- a/packages/@react-aria/interactions/src/index.ts +++ b/packages/@react-aria/interactions/src/index.ts @@ -10,15 +10,27 @@ * governing permissions and limitations under the License. */ -export * from './Pressable'; -export * from './PressResponder'; -export * from './useFocus'; -export * from './useFocusVisible'; -export * from './useFocusWithin'; -export * from './useHover'; -export * from './useInteractOutside'; -export * from './useKeyboard'; -export * from './useMove'; -export * from './usePress'; -export * from './useScrollWheel'; -export * from './useLongPress'; +export type {HoverProps} from './useHover'; +export type {KeyboardProps} from './useKeyboard'; +export type {PressProps, PressHookProps, PressResult} from './usePress'; +export type {ScrollWheelProps} from './useScrollWheel'; + +export {Pressable} from './Pressable'; +export {PressResponder} from './PressResponder'; +export {useFocus} from './useFocus'; +export { + isFocusVisible, + getInteractionModality, + setInteractionModality, + useInteractionModality, + useFocusVisible, + useFocusVisibleListener +} from './useFocusVisible'; +export {useFocusWithin} from './useFocusWithin'; +export {useHover} from './useHover'; +export {useInteractOutside} from './useInteractOutside'; +export {useKeyboard} from './useKeyboard'; +export {useMove} from './useMove'; +export {usePress} from './usePress'; +export {useScrollWheel} from './useScrollWheel'; +export {useLongPress} from './useLongPress'; diff --git a/packages/@react-aria/interactions/src/textSelection.ts b/packages/@react-aria/interactions/src/textSelection.ts index 4a5ebc53a03..f9b88bde196 100644 --- a/packages/@react-aria/interactions/src/textSelection.ts +++ b/packages/@react-aria/interactions/src/textSelection.ts @@ -31,9 +31,9 @@ type State = 'default' | 'disabled' | 'restoring'; // rather than at the document level so we just need to apply/remove user-select: none for each pressed element individually let state: State = 'default'; let savedUserSelect = ''; -let modifiedElementMap = new WeakMap(); +let modifiedElementMap = new WeakMap(); -export function disableTextSelection(target?: HTMLElement) { +export function disableTextSelection(target?: Element) { if (isIOS()) { if (state === 'default') { savedUserSelect = document.documentElement.style.webkitUserSelect; @@ -41,7 +41,7 @@ export function disableTextSelection(target?: HTMLElement) { } state = 'disabled'; - } else if (target) { + } else if (target instanceof HTMLElement || target instanceof SVGElement) { // If not iOS, store the target's original user-select and change to user-select: none // Ignore state since it doesn't apply for non iOS modifiedElementMap.set(target, target.style.userSelect); @@ -49,7 +49,7 @@ export function disableTextSelection(target?: HTMLElement) { } } -export function restoreTextSelection(target?: HTMLElement) { +export function restoreTextSelection(target?: Element) { if (isIOS()) { // If the state is already default, there's nothing to do. // If it is restoring, then there's no need to queue a second restore. @@ -76,7 +76,7 @@ export function restoreTextSelection(target?: HTMLElement) { } }); }, 300); - } else { + } else if (target instanceof HTMLElement || target instanceof SVGElement) { // If not iOS, restore the target's original user-select if any // Ignore state since it doesn't apply for non iOS if (target && modifiedElementMap.has(target)) { diff --git a/packages/@react-aria/interactions/src/useDOMPropsResponder.ts b/packages/@react-aria/interactions/src/useDOMPropsResponder.ts index 435851a521d..415d62eb74c 100644 --- a/packages/@react-aria/interactions/src/useDOMPropsResponder.ts +++ b/packages/@react-aria/interactions/src/useDOMPropsResponder.ts @@ -13,7 +13,7 @@ import {RefObject} from 'react'; import {useDOMPropsResponderContext} from './DOMPropsContext'; -export function useDOMPropsResponder(domRef: RefObject) { +export function useDOMPropsResponder(domRef: RefObject) { let domProps = useDOMPropsResponderContext({ref: domRef}) || {}; diff --git a/packages/@react-aria/interactions/src/useFocus.ts b/packages/@react-aria/interactions/src/useFocus.ts index cdf6be05923..b38ac6c2ecf 100644 --- a/packages/@react-aria/interactions/src/useFocus.ts +++ b/packages/@react-aria/interactions/src/useFocus.ts @@ -15,8 +15,8 @@ // NOTICE file in the root directory of this source tree. // See https://github.com/facebook/react/tree/cc7c1aece46a6b69b41958d731e0fd27c94bfc6c/packages/react-interactions -import {FocusEvent, HTMLAttributes, useCallback} from 'react'; -import {FocusEvents} from '@react-types/shared'; +import {DOMAttributes, FocusEvents} from '@react-types/shared'; +import {FocusEvent, useCallback} from 'react'; import {useSyntheticBlurEvent} from './utils'; interface FocusProps extends FocusEvents { @@ -26,7 +26,7 @@ interface FocusProps extends FocusEvents { interface FocusResult { /** Props to spread onto the target element. */ - focusProps: HTMLAttributes + focusProps: DOMAttributes } /** diff --git a/packages/@react-aria/interactions/src/useFocusWithin.ts b/packages/@react-aria/interactions/src/useFocusWithin.ts index cd36a2f26b9..9d6bd0fd346 100644 --- a/packages/@react-aria/interactions/src/useFocusWithin.ts +++ b/packages/@react-aria/interactions/src/useFocusWithin.ts @@ -15,7 +15,8 @@ // NOTICE file in the root directory of this source tree. // See https://github.com/facebook/react/tree/cc7c1aece46a6b69b41958d731e0fd27c94bfc6c/packages/react-interactions -import {FocusEvent, HTMLAttributes, useCallback, useRef} from 'react'; +import {DOMAttributes} from '@react-types/shared'; +import {FocusEvent, useCallback, useRef} from 'react'; import {useSyntheticBlurEvent} from './utils'; interface FocusWithinProps { @@ -31,7 +32,7 @@ interface FocusWithinProps { interface FocusWithinResult { /** Props to spread onto the target element. */ - focusWithinProps: HTMLAttributes + focusWithinProps: DOMAttributes } /** diff --git a/packages/@react-aria/interactions/src/useHover.ts b/packages/@react-aria/interactions/src/useHover.ts index e9a3e55ebb0..111528c97ba 100644 --- a/packages/@react-aria/interactions/src/useHover.ts +++ b/packages/@react-aria/interactions/src/useHover.ts @@ -15,8 +15,9 @@ // NOTICE file in the root directory of this source tree. // See https://github.com/facebook/react/tree/cc7c1aece46a6b69b41958d731e0fd27c94bfc6c/packages/react-interactions +import {DOMAttributes} from '@react-types/shared'; import {HoverEvents} from '@react-types/shared'; -import {HTMLAttributes, useEffect, useMemo, useRef, useState} from 'react'; +import {useEffect, useMemo, useRef, useState} from 'react'; export interface HoverProps extends HoverEvents { /** Whether the hover events should be disabled. */ @@ -25,7 +26,7 @@ export interface HoverProps extends HoverEvents { interface HoverResult { /** Props to spread on the target element. */ - hoverProps: HTMLAttributes, + hoverProps: DOMAttributes, isHovered: boolean } @@ -152,7 +153,7 @@ export function useHover(props: HoverProps): HoverResult { setHovered(false); }; - let hoverProps: HTMLAttributes = {}; + let hoverProps: DOMAttributes = {}; if (typeof PointerEvent !== 'undefined') { hoverProps.onPointerEnter = (e) => { @@ -164,7 +165,7 @@ export function useHover(props: HoverProps): HoverResult { }; hoverProps.onPointerLeave = (e) => { - if (!isDisabled && e.currentTarget.contains(e.target as HTMLElement)) { + if (!isDisabled && e.currentTarget.contains(e.target as Element)) { triggerHoverEnd(e, e.pointerType); } }; @@ -182,7 +183,7 @@ export function useHover(props: HoverProps): HoverResult { }; hoverProps.onMouseLeave = (e) => { - if (!isDisabled && e.currentTarget.contains(e.target as HTMLElement)) { + if (!isDisabled && e.currentTarget.contains(e.target as Element)) { triggerHoverEnd(e, 'mouse'); } }; diff --git a/packages/@react-aria/interactions/src/useKeyboard.ts b/packages/@react-aria/interactions/src/useKeyboard.ts index a6aa8452f18..6dc32e59a32 100644 --- a/packages/@react-aria/interactions/src/useKeyboard.ts +++ b/packages/@react-aria/interactions/src/useKeyboard.ts @@ -11,8 +11,7 @@ */ import {createEventHandler} from './createEventHandler'; -import {HTMLAttributes} from 'react'; -import {KeyboardEvents} from '@react-types/shared'; +import {DOMAttributes, KeyboardEvents} from '@react-types/shared'; export interface KeyboardProps extends KeyboardEvents { /** Whether the keyboard events should be disabled. */ @@ -21,7 +20,7 @@ export interface KeyboardProps extends KeyboardEvents { interface KeyboardResult { /** Props to spread onto the target element. */ - keyboardProps: HTMLAttributes + keyboardProps: DOMAttributes } /** diff --git a/packages/@react-aria/interactions/src/useLongPress.ts b/packages/@react-aria/interactions/src/useLongPress.ts index 63480a82421..c6316211da9 100644 --- a/packages/@react-aria/interactions/src/useLongPress.ts +++ b/packages/@react-aria/interactions/src/useLongPress.ts @@ -10,10 +10,10 @@ * governing permissions and limitations under the License. */ -import {HTMLAttributes, useRef} from 'react'; -import {LongPressEvent} from '@react-types/shared'; +import {DOMAttributes, LongPressEvent} from '@react-types/shared'; import {mergeProps, useDescription, useGlobalListeners} from '@react-aria/utils'; import {usePress} from './usePress'; +import {useRef} from 'react'; interface LongPressProps { /** Whether long press events should be disabled. */ @@ -44,7 +44,7 @@ interface LongPressProps { interface LongPressResult { /** Props to spread on the target element. */ - longPressProps: HTMLAttributes + longPressProps: DOMAttributes } const DEFAULT_THRESHOLD = 500; diff --git a/packages/@react-aria/interactions/src/useMove.ts b/packages/@react-aria/interactions/src/useMove.ts index e514eb75979..f0dde71e9a7 100644 --- a/packages/@react-aria/interactions/src/useMove.ts +++ b/packages/@react-aria/interactions/src/useMove.ts @@ -11,13 +11,13 @@ */ import {disableTextSelection, restoreTextSelection} from './textSelection'; -import {MoveEvents, PointerType} from '@react-types/shared'; -import React, {HTMLAttributes, useMemo, useRef} from 'react'; +import {DOMAttributes, MoveEvents, PointerType} from '@react-types/shared'; +import React, {useMemo, useRef} from 'react'; import {useGlobalListeners} from '@react-aria/utils'; interface MoveResult { /** Props to spread on the target element. */ - moveProps: HTMLAttributes + moveProps: DOMAttributes } interface EventBase { @@ -44,7 +44,7 @@ export function useMove(props: MoveEvents): MoveResult { let {addGlobalListener, removeGlobalListener} = useGlobalListeners(); let moveProps = useMemo(() => { - let moveProps: HTMLAttributes = {}; + let moveProps: DOMAttributes = {}; let start = () => { disableTextSelection(); diff --git a/packages/@react-aria/interactions/src/usePress.ts b/packages/@react-aria/interactions/src/usePress.ts index f250b8d826e..74a4279eeca 100644 --- a/packages/@react-aria/interactions/src/usePress.ts +++ b/packages/@react-aria/interactions/src/usePress.ts @@ -16,11 +16,11 @@ // See https://github.com/facebook/react/tree/cc7c1aece46a6b69b41958d731e0fd27c94bfc6c/packages/react-interactions import {disableTextSelection, restoreTextSelection} from './textSelection'; +import {DOMAttributes, FocusableElement, PointerType, PressEvents} from '@react-types/shared'; import {focusWithoutScrolling, mergeProps, useGlobalListeners, useSyncRef} from '@react-aria/utils'; -import {HTMLAttributes, RefObject, useContext, useEffect, useMemo, useRef, useState} from 'react'; import {isVirtualClick} from './utils'; -import {PointerType, PressEvents} from '@react-types/shared'; import {PressResponderContext} from './context'; +import {RefObject, useContext, useEffect, useMemo, useRef, useState} from 'react'; export interface PressProps extends PressEvents { /** Whether the target is in a controlled press state (e.g. an overlay it triggers is open). */ @@ -42,7 +42,7 @@ export interface PressProps extends PressEvents { export interface PressHookProps extends PressProps { /** A ref to the target element. */ - ref?: RefObject + ref?: RefObject } interface PressState { @@ -51,7 +51,7 @@ interface PressState { ignoreClickAfterPress: boolean, didFirePressStart: boolean, activePointerId: any, - target: HTMLElement | null, + target: FocusableElement | null, isOverTarget: boolean, pointerType: PointerType, userSelect?: string @@ -69,7 +69,7 @@ export interface PressResult { /** Whether the target is currently pressed. */ isPressed: boolean, /** Props to spread on the target element. */ - pressProps: HTMLAttributes + pressProps: DOMAttributes } function usePressResponderContext(props: PressHookProps): PressHookProps { @@ -135,7 +135,7 @@ export function usePress(props: PressHookProps): PressResult { onPressStart({ type: 'pressstart', pointerType, - target: originalEvent.currentTarget as HTMLElement, + target: originalEvent.currentTarget as Element, shiftKey: originalEvent.shiftKey, metaKey: originalEvent.metaKey, ctrlKey: originalEvent.ctrlKey, @@ -164,7 +164,7 @@ export function usePress(props: PressHookProps): PressResult { onPressEnd({ type: 'pressend', pointerType, - target: originalEvent.currentTarget as HTMLElement, + target: originalEvent.currentTarget as Element, shiftKey: originalEvent.shiftKey, metaKey: originalEvent.metaKey, ctrlKey: originalEvent.ctrlKey, @@ -182,7 +182,7 @@ export function usePress(props: PressHookProps): PressResult { onPress({ type: 'press', pointerType, - target: originalEvent.currentTarget as HTMLElement, + target: originalEvent.currentTarget as Element, shiftKey: originalEvent.shiftKey, metaKey: originalEvent.metaKey, ctrlKey: originalEvent.ctrlKey, @@ -201,7 +201,7 @@ export function usePress(props: PressHookProps): PressResult { onPressUp({ type: 'pressup', pointerType, - target: originalEvent.currentTarget as HTMLElement, + target: originalEvent.currentTarget as Element, shiftKey: originalEvent.shiftKey, metaKey: originalEvent.metaKey, ctrlKey: originalEvent.ctrlKey, @@ -226,12 +226,12 @@ export function usePress(props: PressHookProps): PressResult { } }; - let pressProps: HTMLAttributes = { + let pressProps: DOMAttributes = { onKeyDown(e) { const {currentTarget, target, nativeEvent, repeat} = e; - if (currentTarget.contains(target as HTMLElement) && isValidKeyboardEvent(nativeEvent, currentTarget)) { + if (currentTarget.contains(target as Element) && isValidKeyboardEvent(nativeEvent, currentTarget)) { e.stopPropagation(); - + if (shouldPreventDefaultKeyboard(target as Element)) { e.preventDefault(); } @@ -239,7 +239,7 @@ export function usePress(props: PressHookProps): PressResult { // after which focus moved to the current element. Ignore these events and // only handle the first key down event. if (!state.isPressed && !repeat) { - state.target = currentTarget as HTMLElement; + state.target = currentTarget; state.isPressed = true; triggerPressStart(e, 'keyboard'); @@ -253,14 +253,14 @@ export function usePress(props: PressHookProps): PressResult { const {currentTarget = state.target, target, nativeEvent, repeat} = e; if ( !repeat && - currentTarget.contains(target as HTMLElement) && + currentTarget.contains(target as Element) && isValidKeyboardEvent(nativeEvent, currentTarget) ) { triggerPressUp(createEvent(state.target, e), 'keyboard'); } }, onClick(e) { - if (e && !e.currentTarget.contains(e.target as HTMLElement)) { + if (e && !e.currentTarget.contains(e.target as Element)) { return; } @@ -290,11 +290,11 @@ export function usePress(props: PressHookProps): PressResult { }; let onKeyUp = (e: KeyboardEvent) => { - const target = e.target as HTMLElement; + let target = e.target as Element; if (state.isPressed && isValidKeyboardEvent(e, state.target)) { e.stopPropagation(); - if (shouldPreventDefaultKeyboard(target as Element)) { + if (shouldPreventDefaultKeyboard(target)) { e.preventDefault(); } @@ -304,7 +304,7 @@ export function usePress(props: PressHookProps): PressResult { // If the target is a link, trigger the click method to open the URL, // but defer triggering pressEnd until onClick event handler. - if (state.target.contains(target) && isLinkRole(state.target as HTMLElement)) { + if (state.target instanceof HTMLElement && state.target.contains(target) && isLinkRole(state.target as Element)) { state.target.click(); } } @@ -313,7 +313,7 @@ export function usePress(props: PressHookProps): PressResult { if (typeof PointerEvent !== 'undefined') { pressProps.onPointerDown = (e) => { // Only handle left clicks, and ignore events that bubbled through portals. - if (e.button !== 0 || !e.currentTarget.contains(e.target as HTMLElement)) { + if (e.button !== 0 || !e.currentTarget.contains(e.target as Element)) { return; } @@ -328,7 +328,7 @@ export function usePress(props: PressHookProps): PressResult { // Due to browser inconsistencies, especially on mobile browsers, we prevent // default on pointer down and handle focusing the pressable element ourselves. - if (shouldPreventDefault(e.currentTarget as HTMLElement)) { + if (shouldPreventDefault(e.currentTarget as Element)) { e.preventDefault(); } @@ -358,7 +358,7 @@ export function usePress(props: PressHookProps): PressResult { }; pressProps.onMouseDown = (e) => { - if (!e.currentTarget.contains(e.target as HTMLElement)) { + if (!e.currentTarget.contains(e.target as Element)) { return; } @@ -366,7 +366,7 @@ export function usePress(props: PressHookProps): PressResult { // Chrome and Firefox on touch Windows devices require mouse down events // to be canceled in addition to pointer events, or an extra asynchronous // focus event will be fired. - if (shouldPreventDefault(e.currentTarget as HTMLElement)) { + if (shouldPreventDefault(e.currentTarget as Element)) { e.preventDefault(); } @@ -376,7 +376,7 @@ export function usePress(props: PressHookProps): PressResult { pressProps.onPointerUp = (e) => { // iOS fires pointerup with zero width and height, so check the pointerType recorded during pointerdown. - if (!e.currentTarget.contains(e.target as HTMLElement) || state.pointerType === 'virtual') { + if (!e.currentTarget.contains(e.target as Element) || state.pointerType === 'virtual') { return; } @@ -434,7 +434,7 @@ export function usePress(props: PressHookProps): PressResult { }; pressProps.onDragStart = (e) => { - if (!e.currentTarget.contains(e.target as HTMLElement)) { + if (!e.currentTarget.contains(e.target as Element)) { return; } @@ -444,13 +444,13 @@ export function usePress(props: PressHookProps): PressResult { } else { pressProps.onMouseDown = (e) => { // Only handle left clicks - if (e.button !== 0 || !e.currentTarget.contains(e.target as HTMLElement)) { + if (e.button !== 0 || !e.currentTarget.contains(e.target as Element)) { return; } // Due to browser inconsistencies, especially on mobile browsers, we prevent // default on mouse down and handle focusing the pressable element ourselves. - if (shouldPreventDefault(e.currentTarget as HTMLElement)) { + if (shouldPreventDefault(e.currentTarget)) { e.preventDefault(); } @@ -474,7 +474,7 @@ export function usePress(props: PressHookProps): PressResult { }; pressProps.onMouseEnter = (e) => { - if (!e.currentTarget.contains(e.target as HTMLElement)) { + if (!e.currentTarget.contains(e.target as Element)) { return; } @@ -486,7 +486,7 @@ export function usePress(props: PressHookProps): PressResult { }; pressProps.onMouseLeave = (e) => { - if (!e.currentTarget.contains(e.target as HTMLElement)) { + if (!e.currentTarget.contains(e.target as Element)) { return; } @@ -501,7 +501,7 @@ export function usePress(props: PressHookProps): PressResult { }; pressProps.onMouseUp = (e) => { - if (!e.currentTarget.contains(e.target as HTMLElement)) { + if (!e.currentTarget.contains(e.target as Element)) { return; } @@ -534,7 +534,7 @@ export function usePress(props: PressHookProps): PressResult { }; pressProps.onTouchStart = (e) => { - if (!e.currentTarget.contains(e.target as HTMLElement)) { + if (!e.currentTarget.contains(e.target as Element)) { return; } @@ -566,7 +566,7 @@ export function usePress(props: PressHookProps): PressResult { }; pressProps.onTouchMove = (e) => { - if (!e.currentTarget.contains(e.target as HTMLElement)) { + if (!e.currentTarget.contains(e.target as Element)) { return; } @@ -591,7 +591,7 @@ export function usePress(props: PressHookProps): PressResult { }; pressProps.onTouchEnd = (e) => { - if (!e.currentTarget.contains(e.target as HTMLElement)) { + if (!e.currentTarget.contains(e.target as Element)) { return; } @@ -619,7 +619,7 @@ export function usePress(props: PressHookProps): PressResult { }; pressProps.onTouchCancel = (e) => { - if (!e.currentTarget.contains(e.target as HTMLElement)) { + if (!e.currentTarget.contains(e.target as Element)) { return; } @@ -630,7 +630,7 @@ export function usePress(props: PressHookProps): PressResult { }; let onScroll = (e: Event) => { - if (state.isPressed && (e.target as HTMLElement).contains(state.target)) { + if (state.isPressed && (e.target as Element).contains(state.target)) { cancel({ currentTarget: state.target, shiftKey: false, @@ -642,7 +642,7 @@ export function usePress(props: PressHookProps): PressResult { }; pressProps.onDragStart = (e) => { - if (!e.currentTarget.contains(e.target as HTMLElement)) { + if (!e.currentTarget.contains(e.target as Element)) { return; } @@ -688,14 +688,14 @@ function isHTMLAnchorLink(target: HTMLElement): boolean { return target.tagName === 'A' && target.hasAttribute('href'); } -function isLinkRole(target: HTMLElement): boolean { - return isHTMLAnchorLink(target) || target.getAttribute('role') === 'link'; +function isLinkRole(target: Element): boolean { + return isHTMLAnchorLink(target as HTMLElement) || target.getAttribute('role') === 'link'; } function isValidKeyboardEvent(event: KeyboardEvent, currentTarget?: EventTarget): boolean { const {target} = event; - const element = target as HTMLElement; - const currentTargetElement = currentTarget as HTMLElement; + const element = target as Element; + const currentTargetElement = currentTarget as Element; const role = element.getAttribute('role'); const isEnter = isEnterKey(event); @@ -708,7 +708,7 @@ function isValidKeyboardEvent(event: KeyboardEvent, currentTarget?: EventTarget) !isEditableText(event) && ( // A link with a valid href should be handled natively, - !isHTMLAnchorLink(element) || + !isHTMLAnchorLink(element as HTMLElement) || // unless it also has role='button' and was triggered using Space. (role === 'button' && !isEnter) ) && @@ -716,11 +716,11 @@ function isValidKeyboardEvent(event: KeyboardEvent, currentTarget?: EventTarget) !(role === 'link' && !isEnter) ) || ( - // If the target is a link, but the currentTarget containing it is something else, + // If the target is a link, but the currentTarget containing it is something else, // like a table row, treat the space key as a valid keyboard event as well. currentTargetElement && currentTargetElement !== element && - currentTargetElement.contains(currentTargetElement as HTMLElement) && + currentTargetElement.contains(currentTargetElement as Element) && isLinkRole(element) && !isEnter ) @@ -750,7 +750,7 @@ function getTouchById( return null; } -function createEvent(target: HTMLElement, e: EventBase): EventBase { +function createEvent(target: FocusableElement, e: EventBase): EventBase { return { currentTarget: target, shiftKey: e.shiftKey, @@ -800,15 +800,15 @@ function areRectanglesOverlapping(a: Rect, b: Rect) { return true; } -function isOverTarget(point: EventPoint, target: HTMLElement) { +function isOverTarget(point: EventPoint, target: Element) { let rect = target.getBoundingClientRect(); let pointRect = getPointClientRect(point); return areRectanglesOverlapping(rect, pointRect); } -function shouldPreventDefault(target: HTMLElement) { +function shouldPreventDefault(target: Element) { // We cannot prevent default if the target is a draggable element. - return !target.draggable; + return !(target instanceof HTMLElement) || !target.draggable; } function shouldPreventDefaultKeyboard(target: Element) { diff --git a/packages/@react-aria/label/src/index.ts b/packages/@react-aria/label/src/index.ts index bfd049fe58e..c05861e1124 100644 --- a/packages/@react-aria/label/src/index.ts +++ b/packages/@react-aria/label/src/index.ts @@ -10,5 +10,8 @@ * governing permissions and limitations under the License. */ -export * from './useField'; -export * from './useLabel'; +export type {FieldAria} from './useField'; +export type {LabelAriaProps, LabelAria} from './useLabel'; + +export {useField} from './useField'; +export {useLabel} from './useLabel'; diff --git a/packages/@react-aria/label/src/useField.ts b/packages/@react-aria/label/src/useField.ts index cf0f4a48274..f995e6b905f 100644 --- a/packages/@react-aria/label/src/useField.ts +++ b/packages/@react-aria/label/src/useField.ts @@ -10,8 +10,7 @@ * governing permissions and limitations under the License. */ -import {HelpTextProps, Validation} from '@react-types/shared'; -import {HTMLAttributes} from 'react'; +import {DOMAttributes, HelpTextProps, Validation} from '@react-types/shared'; import {LabelAria, LabelAriaProps, useLabel} from './useLabel'; import {mergeProps, useSlotId} from '@react-aria/utils'; @@ -19,9 +18,9 @@ interface AriaFieldProps extends LabelAriaProps, HelpTextProps, Omit, + descriptionProps: DOMAttributes, /** Props for the error message element, if any. */ - errorMessageProps: HTMLAttributes + errorMessageProps: DOMAttributes } /** diff --git a/packages/@react-aria/label/src/useLabel.ts b/packages/@react-aria/label/src/useLabel.ts index 129490b4e5c..0aa072ea2d7 100644 --- a/packages/@react-aria/label/src/useLabel.ts +++ b/packages/@react-aria/label/src/useLabel.ts @@ -10,7 +10,7 @@ * governing permissions and limitations under the License. */ -import {AriaLabelingProps, DOMProps, LabelableProps} from '@react-types/shared'; +import {AriaLabelingProps, DOMAttributes, DOMProps, LabelableProps} from '@react-types/shared'; import {ElementType, LabelHTMLAttributes} from 'react'; import {useId, useLabels} from '@react-aria/utils'; @@ -24,7 +24,7 @@ export interface LabelAriaProps extends LabelableProps, DOMProps, AriaLabelingPr export interface LabelAria { /** Props to apply to the label container element. */ - labelProps: LabelHTMLAttributes, + labelProps: DOMAttributes | LabelHTMLAttributes, /** Props to apply to the field container element being labeled. */ fieldProps: AriaLabelingProps & DOMProps } diff --git a/packages/@react-aria/landmark/src/index.ts b/packages/@react-aria/landmark/src/index.ts index 113dced20b1..e3cfd8c4eb3 100644 --- a/packages/@react-aria/landmark/src/index.ts +++ b/packages/@react-aria/landmark/src/index.ts @@ -9,5 +9,5 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './useLandmark'; +export type {AriaLandmarkRole, AriaLandmarkProps} from './useLandmark'; +export {useLandmark} from './useLandmark'; diff --git a/packages/@react-aria/landmark/src/useLandmark.ts b/packages/@react-aria/landmark/src/useLandmark.ts index 2b8a04d9c46..db8ed927fbc 100644 --- a/packages/@react-aria/landmark/src/useLandmark.ts +++ b/packages/@react-aria/landmark/src/useLandmark.ts @@ -10,8 +10,8 @@ * governing permissions and limitations under the License. */ -import {AriaLabelingProps} from '@react-types/shared'; -import {HTMLAttributes, MutableRefObject, useCallback, useEffect, useState} from 'react'; +import {AriaLabelingProps, DOMAttributes, FocusableElement} from '@react-types/shared'; +import {MutableRefObject, useCallback, useEffect, useState} from 'react'; import {useLayoutEffect} from '@react-aria/utils'; export type AriaLandmarkRole = 'main' | 'region' | 'search' | 'navigation' | 'form' | 'banner' | 'contentinfo' | 'complementary'; @@ -21,14 +21,14 @@ export interface AriaLandmarkProps extends AriaLabelingProps { } interface LandmarkAria { - landmarkProps: HTMLAttributes + landmarkProps: DOMAttributes } type Landmark = { - ref: MutableRefObject, + ref: MutableRefObject, role: AriaLandmarkRole, label?: string, - lastFocused?: HTMLElement, + lastFocused?: FocusableElement, focus: () => void, blur: () => void }; @@ -66,7 +66,7 @@ class LandmarkManager { this.isListening = false; } - private focusLandmark(landmark: HTMLElement) { + private focusLandmark(landmark: Element) { this.landmarks.find(l => l.ref.current === landmark)?.focus(); } @@ -129,7 +129,7 @@ class LandmarkManager { } } - public removeLandmark(ref: MutableRefObject) { + public removeLandmark(ref: MutableRefObject) { this.landmarks = this.landmarks.filter(landmark => landmark.ref !== ref); if (this.landmarks.length === 0) { this.teardown(); @@ -169,7 +169,7 @@ class LandmarkManager { * Get the landmark that is the closest parent in the DOM. * Returns undefined if no parent is a landmark. */ - private closestLandmark(element: HTMLElement) { + private closestLandmark(element: Element) { let landmarkMap = new Map(this.landmarks.map(l => [l.ref.current, l])); let currentElement = element; while (!landmarkMap.has(currentElement) && currentElement !== document.body) { @@ -184,7 +184,7 @@ class LandmarkManager { * If not inside a landmark, will return first landmark. * Returns undefined if there are no landmarks. */ - public getNextLandmark(element: HTMLElement, {backward}: {backward?: boolean }) { + public getNextLandmark(element: Element, {backward}: {backward?: boolean }) { if (this.landmarks.length === 0) { return undefined; } @@ -216,7 +216,7 @@ class LandmarkManager { e.stopPropagation(); let backward = e.shiftKey; - let nextLandmark = this.getNextLandmark(e.target as HTMLElement, {backward}); + let nextLandmark = this.getNextLandmark(e.target as Element, {backward}); // If no landmarks, return if (!nextLandmark) { @@ -253,11 +253,11 @@ class LandmarkManager { * Lets the last focused landmark know it was blurred if something else is focused. */ public focusinHandler(e: FocusEvent) { - let currentLandmark = this.closestLandmark(e.target as HTMLElement); + let currentLandmark = this.closestLandmark(e.target as Element); if (currentLandmark && currentLandmark.ref.current !== e.target) { - this.updateLandmark({ref: currentLandmark.ref, lastFocused: e.target as HTMLElement}); + this.updateLandmark({ref: currentLandmark.ref, lastFocused: e.target as FocusableElement}); } - let previousFocusedElement = e.relatedTarget as HTMLElement; + let previousFocusedElement = e.relatedTarget as Element; if (previousFocusedElement) { let closestPreviousLandmark = this.closestLandmark(previousFocusedElement); if (closestPreviousLandmark && closestPreviousLandmark.ref.current === previousFocusedElement) { @@ -270,7 +270,7 @@ class LandmarkManager { * Track if the focus is lost to the body. If it is, do cleanup on the landmark that last had focus. */ public focusoutHandler(e: FocusEvent) { - let previousFocusedElement = e.target as HTMLElement; + let previousFocusedElement = e.target as Element; let nextFocusedElement = e.relatedTarget; // the === document seems to be a jest thing for focus to go there on generic blur event such as landmark.blur(); // browsers appear to send focus instead to document.body and the relatedTarget is null when that happens @@ -288,7 +288,7 @@ class LandmarkManager { * @param props - Props for the landmark. * @param ref - Ref to the landmark. */ -export function useLandmark(props: AriaLandmarkProps, ref: MutableRefObject): LandmarkAria { +export function useLandmark(props: AriaLandmarkProps, ref: MutableRefObject): LandmarkAria { const { role, 'aria-label': ariaLabel, diff --git a/packages/@react-aria/link/src/index.ts b/packages/@react-aria/link/src/index.ts index 706385e862b..e1f5714dc07 100644 --- a/packages/@react-aria/link/src/index.ts +++ b/packages/@react-aria/link/src/index.ts @@ -9,5 +9,5 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './useLink'; +export type {AriaLinkOptions, LinkAria} from './useLink'; +export {useLink} from './useLink'; diff --git a/packages/@react-aria/link/src/useLink.ts b/packages/@react-aria/link/src/useLink.ts index cd8fbfb5cad..f9c15b4a7a9 100644 --- a/packages/@react-aria/link/src/useLink.ts +++ b/packages/@react-aria/link/src/useLink.ts @@ -11,8 +11,9 @@ */ import {AriaLinkProps} from '@react-types/link'; +import {DOMAttributes, FocusableElement} from '@react-types/shared'; import {filterDOMProps, mergeProps} from '@react-aria/utils'; -import {HTMLAttributes, RefObject} from 'react'; +import {RefObject} from 'react'; import {useFocusable} from '@react-aria/focus'; import {usePress} from '@react-aria/interactions'; @@ -28,7 +29,7 @@ export interface AriaLinkOptions extends AriaLinkProps { export interface LinkAria { /** Props for the link element. */ - linkProps: HTMLAttributes, + linkProps: DOMAttributes, /** Whether the link is currently pressed. */ isPressed: boolean } @@ -38,7 +39,7 @@ export interface LinkAria { * A link allows a user to navigate to another page or resource within a web page * or application. */ -export function useLink(props: AriaLinkOptions, ref: RefObject): LinkAria { +export function useLink(props: AriaLinkOptions, ref: RefObject): LinkAria { let { elementType = 'a', onPress, @@ -50,7 +51,7 @@ export function useLink(props: AriaLinkOptions, ref: RefObject): Li ...otherProps } = props; - let linkProps: HTMLAttributes; + let linkProps: DOMAttributes; if (elementType !== 'a') { linkProps = { role: 'link', diff --git a/packages/@react-aria/list/src/useList.ts b/packages/@react-aria/list/src/useList.ts index 7a9618a2676..bfe3a2e92eb 100644 --- a/packages/@react-aria/list/src/useList.ts +++ b/packages/@react-aria/list/src/useList.ts @@ -11,9 +11,9 @@ */ import {AriaListProps} from '@react-types/list'; +import {DOMAttributes, KeyboardDelegate} from '@react-types/shared'; import {filterDOMProps, mergeProps, useId} from '@react-aria/utils'; -import {HTMLAttributes, Key, RefObject} from 'react'; -import {KeyboardDelegate} from '@react-types/shared'; +import {Key, RefObject} from 'react'; import {listMap} from './utils'; import {ListState} from '@react-stately/list'; import {useGridSelectionAnnouncement, useHighlightSelectionDescription} from '@react-aria/grid'; @@ -36,7 +36,7 @@ export interface AriaListOptions extends Omit, 'children'> { export interface ListViewAria { /** Props for the grid element. */ - gridProps: HTMLAttributes + gridProps: DOMAttributes } /** @@ -77,7 +77,7 @@ export function useList(props: AriaListOptions, state: ListState, ref: }); let domProps = filterDOMProps(props, {labelable: true}); - let gridProps: HTMLAttributes = mergeProps( + let gridProps: DOMAttributes = mergeProps( domProps, { role: 'grid', diff --git a/packages/@react-aria/list/src/useListItem.ts b/packages/@react-aria/list/src/useListItem.ts index e37ace8bc26..cc5709265ea 100644 --- a/packages/@react-aria/list/src/useListItem.ts +++ b/packages/@react-aria/list/src/useListItem.ts @@ -10,13 +10,13 @@ * governing permissions and limitations under the License. */ +import {DOMAttributes, FocusableElement, Node as RSNode} from '@react-types/shared'; import {focusSafely, getFocusableTreeWalker} from '@react-aria/focus'; import {getRowId, listMap} from './utils'; -import {HTMLAttributes, KeyboardEvent as ReactKeyboardEvent, RefObject} from 'react'; import {isFocusVisible} from '@react-aria/interactions'; import type {ListState} from '@react-stately/list'; import {mergeProps, useSlotId} from '@react-aria/utils'; -import {Node as RSNode} from '@react-types/shared'; +import {KeyboardEvent as ReactKeyboardEvent, RefObject} from 'react'; import {SelectableItemStates, useSelectableItem} from '@react-aria/selection'; import {useLocale} from '@react-aria/i18n'; @@ -31,11 +31,11 @@ export interface AriaListItemOptions { export interface ListItemAria extends SelectableItemStates { /** Props for the list row element. */ - rowProps: HTMLAttributes, + rowProps: DOMAttributes, /** Props for the grid cell element within the list row. */ - gridCellProps: HTMLAttributes, + gridCellProps: DOMAttributes, /** Props for the list item description element, if any. */ - descriptionProps: HTMLAttributes + descriptionProps: DOMAttributes } /** @@ -44,7 +44,7 @@ export interface ListItemAria extends SelectableItemStates { * @param state - State of the parent list, as returned by `useListState`. * @param ref - The ref attached to the row element. */ -export function useListItem(props: AriaListItemOptions, state: ListState, ref: RefObject): ListItemAria { +export function useListItem(props: AriaListItemOptions, state: ListState, ref: RefObject): ListItemAria { // Copied from useGridCell + some modifications to make it not so grid specific let { node, @@ -74,7 +74,7 @@ export function useListItem(props: AriaListItemOptions, state: ListState, }); let onKeyDown = (e: ReactKeyboardEvent) => { - if (!e.currentTarget.contains(e.target as HTMLElement)) { + if (!e.currentTarget.contains(e.target as Element)) { return; } @@ -85,8 +85,8 @@ export function useListItem(props: AriaListItemOptions, state: ListState, case 'ArrowLeft': { // Find the next focusable element within the row. let focusable = direction === 'rtl' - ? walker.nextNode() as HTMLElement - : walker.previousNode() as HTMLElement; + ? walker.nextNode() as FocusableElement + : walker.previousNode() as FocusableElement; if (focusable) { e.preventDefault(); @@ -110,8 +110,8 @@ export function useListItem(props: AriaListItemOptions, state: ListState, } case 'ArrowRight': { let focusable = direction === 'rtl' - ? walker.previousNode() as HTMLElement - : walker.nextNode() as HTMLElement; + ? walker.previousNode() as FocusableElement + : walker.nextNode() as FocusableElement; if (focusable) { e.preventDefault(); @@ -137,7 +137,7 @@ export function useListItem(props: AriaListItemOptions, state: ListState, // Prevent this event from reaching row children, e.g. menu buttons. We want arrow keys to navigate // to the row above/below instead. We need to re-dispatch the event from a higher parent so it still // bubbles and gets handled by useSelectableCollection. - if (!e.altKey && ref.current.contains(e.target as HTMLElement)) { + if (!e.altKey && ref.current.contains(e.target as Element)) { e.stopPropagation(); e.preventDefault(); ref.current.parentElement.dispatchEvent( @@ -163,7 +163,7 @@ export function useListItem(props: AriaListItemOptions, state: ListState, } }; - let rowProps: HTMLAttributes = mergeProps(itemProps, { + let rowProps: DOMAttributes = mergeProps(itemProps, { role: 'row', onKeyDownCapture: onKeyDown, onFocus, @@ -194,10 +194,10 @@ export function useListItem(props: AriaListItemOptions, state: ListState, } function last(walker: TreeWalker) { - let next: HTMLElement; - let last: HTMLElement; + let next: FocusableElement; + let last: FocusableElement; do { - last = walker.lastChild() as HTMLElement; + last = walker.lastChild() as FocusableElement; if (last) { next = last; } diff --git a/packages/@react-aria/listbox/src/index.ts b/packages/@react-aria/listbox/src/index.ts index 4e56745b97e..72edf6ec83a 100644 --- a/packages/@react-aria/listbox/src/index.ts +++ b/packages/@react-aria/listbox/src/index.ts @@ -9,8 +9,8 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './useListBox'; -export * from './useOption'; -export * from './useListBoxSection'; -export * from './utils'; +export type {AriaListBoxOptions} from './useListBox'; +export {useListBox} from './useListBox'; +export {useOption} from './useOption'; +export {useListBoxSection} from './useListBoxSection'; +export {listData, getItemId} from './utils'; diff --git a/packages/@react-aria/listbox/src/useListBox.ts b/packages/@react-aria/listbox/src/useListBox.ts index d4c4cb28b28..bb49013b680 100644 --- a/packages/@react-aria/listbox/src/useListBox.ts +++ b/packages/@react-aria/listbox/src/useListBox.ts @@ -11,11 +11,11 @@ */ import {AriaListBoxProps} from '@react-types/listbox'; +import {DOMAttributes, KeyboardDelegate} from '@react-types/shared'; import {filterDOMProps, mergeProps} from '@react-aria/utils'; -import {HTMLAttributes, ReactNode, RefObject} from 'react'; -import {KeyboardDelegate} from '@react-types/shared'; import {listData} from './utils'; import {ListState} from '@react-stately/list'; +import {ReactNode, RefObject} from 'react'; import {useFocusWithin} from '@react-aria/interactions'; import {useId} from '@react-aria/utils'; import {useLabel} from '@react-aria/label'; @@ -23,9 +23,9 @@ import {useSelectableList} from '@react-aria/selection'; interface ListBoxAria { /** Props for the listbox element. */ - listBoxProps: HTMLAttributes, + listBoxProps: DOMAttributes, /** Props for the listbox's visual label element (if any). */ - labelProps: HTMLAttributes + labelProps: DOMAttributes } export interface AriaListBoxOptions extends Omit, 'children'> { diff --git a/packages/@react-aria/listbox/src/useListBoxSection.ts b/packages/@react-aria/listbox/src/useListBoxSection.ts index 0a83dfd1cc3..c05236e53a1 100644 --- a/packages/@react-aria/listbox/src/useListBoxSection.ts +++ b/packages/@react-aria/listbox/src/useListBoxSection.ts @@ -10,7 +10,8 @@ * governing permissions and limitations under the License. */ -import {HTMLAttributes, ReactNode} from 'react'; +import {DOMAttributes} from '@react-types/shared'; +import {ReactNode} from 'react'; import {useId} from '@react-aria/utils'; interface AriaListBoxSectionProps { @@ -22,13 +23,13 @@ interface AriaListBoxSectionProps { interface ListBoxSectionAria { /** Props for the wrapper list item. */ - itemProps: HTMLAttributes, + itemProps: DOMAttributes, /** Props for the heading element, if any. */ - headingProps: HTMLAttributes, + headingProps: DOMAttributes, /** Props for the group element. */ - groupProps: HTMLAttributes + groupProps: DOMAttributes } /** diff --git a/packages/@react-aria/listbox/src/useOption.ts b/packages/@react-aria/listbox/src/useOption.ts index 1d9278756ad..c4c06bd9c1a 100644 --- a/packages/@react-aria/listbox/src/useOption.ts +++ b/packages/@react-aria/listbox/src/useOption.ts @@ -10,23 +10,24 @@ * governing permissions and limitations under the License. */ +import {DOMAttributes, FocusableElement} from '@react-types/shared'; import {getItemCount} from '@react-stately/collections'; import {getItemId, listData} from './utils'; -import {HTMLAttributes, Key, RefObject} from 'react'; import {isFocusVisible, useHover} from '@react-aria/interactions'; import {isMac, isWebKit, mergeProps, useSlotId} from '@react-aria/utils'; +import {Key, RefObject} from 'react'; import {ListState} from '@react-stately/list'; import {useSelectableItem} from '@react-aria/selection'; interface OptionAria { /** Props for the option element. */ - optionProps: HTMLAttributes, + optionProps: DOMAttributes, /** Props for the main text element inside the option. */ - labelProps: HTMLAttributes, + labelProps: DOMAttributes, /** Props for the description text element inside the option, if any. */ - descriptionProps: HTMLAttributes, + descriptionProps: DOMAttributes, /** Whether the option is currently focused. */ isFocused: boolean, @@ -88,7 +89,7 @@ interface AriaOptionProps { * @param props - Props for the option. * @param state - State for the listbox, as returned by `useListState`. */ -export function useOption(props: AriaOptionProps, state: ListState, ref: RefObject): OptionAria { +export function useOption(props: AriaOptionProps, state: ListState, ref: RefObject): OptionAria { let { key } = props; diff --git a/packages/@react-aria/live-announcer/src/index.ts b/packages/@react-aria/live-announcer/src/index.ts index 429a6538f29..51dbb8c5a46 100644 --- a/packages/@react-aria/live-announcer/src/index.ts +++ b/packages/@react-aria/live-announcer/src/index.ts @@ -9,5 +9,4 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './LiveAnnouncer'; +export {announce, clearAnnouncer, destroyAnnouncer} from './LiveAnnouncer'; diff --git a/packages/@react-aria/menu/docs/tailwind.png b/packages/@react-aria/menu/docs/tailwind.png new file mode 100644 index 0000000000000000000000000000000000000000..a73c3a1f01f88d0c01cffb24940f36680eb22eff GIT binary patch literal 23982 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 ``` +## Styled examples + + + ## Internationalization ### RTL diff --git a/packages/@react-aria/menu/src/useMenu.ts b/packages/@react-aria/menu/src/useMenu.ts index 10e69f4ad41..a5986e3dfc9 100644 --- a/packages/@react-aria/menu/src/useMenu.ts +++ b/packages/@react-aria/menu/src/useMenu.ts @@ -11,15 +11,15 @@ */ import {AriaMenuProps} from '@react-types/menu'; +import {DOMAttributes, KeyboardDelegate} from '@react-types/shared'; import {filterDOMProps, mergeProps} from '@react-aria/utils'; -import {HTMLAttributes, Key, RefObject} from 'react'; -import {KeyboardDelegate} from '@react-types/shared'; +import {Key, RefObject} from 'react'; import {TreeState} from '@react-stately/tree'; import {useSelectableList} from '@react-aria/selection'; interface MenuAria { /** Props for the menu element. */ - menuProps: HTMLAttributes + menuProps: DOMAttributes } export interface AriaMenuOptions extends Omit, 'children'> { diff --git a/packages/@react-aria/menu/src/useMenuItem.ts b/packages/@react-aria/menu/src/useMenuItem.ts index 7c12d5fb60b..3d3c3a72a03 100644 --- a/packages/@react-aria/menu/src/useMenuItem.ts +++ b/packages/@react-aria/menu/src/useMenuItem.ts @@ -10,27 +10,27 @@ * governing permissions and limitations under the License. */ +import {DOMAttributes, FocusableElement, PressEvent} from '@react-types/shared'; import {getItemCount} from '@react-stately/collections'; -import {HTMLAttributes, Key, RefObject} from 'react'; import {isFocusVisible, useHover, usePress} from '@react-aria/interactions'; +import {Key, RefObject} from 'react'; import {menuData} from './useMenu'; import {mergeProps, useSlotId} from '@react-aria/utils'; -import {PressEvent} from '@react-types/shared'; import {TreeState} from '@react-stately/tree'; import {useSelectableItem} from '@react-aria/selection'; interface MenuItemAria { /** Props for the menu item element. */ - menuItemProps: HTMLAttributes, + menuItemProps: DOMAttributes, /** Props for the main text element inside the menu item. */ - labelProps: HTMLAttributes, + labelProps: DOMAttributes, /** Props for the description text element inside the menu item, if any. */ - descriptionProps: HTMLAttributes, + descriptionProps: DOMAttributes, /** Props for the keyboard shortcut text element inside the item, if any. */ - keyboardShortcutProps: HTMLAttributes, + keyboardShortcutProps: DOMAttributes, /** Whether the item is currently focused. */ isFocused: boolean, @@ -89,7 +89,7 @@ interface AriaMenuItemProps { * @param props - Props for the item. * @param state - State for the menu, as returned by `useTreeState`. */ -export function useMenuItem(props: AriaMenuItemProps, state: TreeState, ref: RefObject): MenuItemAria { +export function useMenuItem(props: AriaMenuItemProps, state: TreeState, ref: RefObject): MenuItemAria { let { key, closeOnSelect, diff --git a/packages/@react-aria/menu/src/useMenuSection.ts b/packages/@react-aria/menu/src/useMenuSection.ts index 5db1051e19c..4a50a3a3aa7 100644 --- a/packages/@react-aria/menu/src/useMenuSection.ts +++ b/packages/@react-aria/menu/src/useMenuSection.ts @@ -10,7 +10,8 @@ * governing permissions and limitations under the License. */ -import {HTMLAttributes, ReactNode} from 'react'; +import {DOMAttributes} from '@react-types/shared'; +import {ReactNode} from 'react'; import {useId} from '@react-aria/utils'; interface AriaMenuSectionProps { @@ -22,13 +23,13 @@ interface AriaMenuSectionProps { interface MenuSectionAria { /** Props for the wrapper list item. */ - itemProps: HTMLAttributes, + itemProps: DOMAttributes, /** Props for the heading element, if any. */ - headingProps: HTMLAttributes, + headingProps: DOMAttributes, /** Props for the group element. */ - groupProps: HTMLAttributes + groupProps: DOMAttributes } /** diff --git a/packages/@react-aria/menu/src/useMenuTrigger.ts b/packages/@react-aria/menu/src/useMenuTrigger.ts index c862e9ca882..32cfadc4fb6 100644 --- a/packages/@react-aria/menu/src/useMenuTrigger.ts +++ b/packages/@react-aria/menu/src/useMenuTrigger.ts @@ -44,7 +44,7 @@ interface MenuTriggerAria { * @param props - Props for the menu trigger. * @param state - State for the menu trigger. */ -export function useMenuTrigger(props: MenuTriggerAriaProps, state: MenuTriggerState, ref: RefObject): MenuTriggerAria { +export function useMenuTrigger(props: MenuTriggerAriaProps, state: MenuTriggerState, ref: RefObject): MenuTriggerAria { let { type = 'menu' as MenuTriggerAriaProps['type'], isDisabled, diff --git a/packages/@react-aria/meter/src/index.ts b/packages/@react-aria/meter/src/index.ts index e2188f19509..ea5315b1788 100644 --- a/packages/@react-aria/meter/src/index.ts +++ b/packages/@react-aria/meter/src/index.ts @@ -9,5 +9,4 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './useMeter'; +export {useMeter} from './useMeter'; diff --git a/packages/@react-aria/meter/src/useMeter.ts b/packages/@react-aria/meter/src/useMeter.ts index 1f475499239..5ee19e5f245 100644 --- a/packages/@react-aria/meter/src/useMeter.ts +++ b/packages/@react-aria/meter/src/useMeter.ts @@ -11,14 +11,14 @@ */ import {AriaMeterProps} from '@react-types/meter'; -import {HTMLAttributes} from 'react'; +import {DOMAttributes} from '@react-types/shared'; import {useProgressBar} from '@react-aria/progress'; interface MeterAria { /** Props for the meter container element. */ - meterProps: HTMLAttributes, + meterProps: DOMAttributes, /** Props for the meter's visual label (if any). */ - labelProps: HTMLAttributes + labelProps: DOMAttributes } /** diff --git a/packages/@react-aria/numberfield/src/index.ts b/packages/@react-aria/numberfield/src/index.ts index ea1ac27a442..15eba63661b 100644 --- a/packages/@react-aria/numberfield/src/index.ts +++ b/packages/@react-aria/numberfield/src/index.ts @@ -9,5 +9,4 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './useNumberField'; +export {useNumberField} from './useNumberField'; diff --git a/packages/@react-aria/numberfield/src/useNumberField.ts b/packages/@react-aria/numberfield/src/useNumberField.ts index 183638111b6..5bd9b1915ab 100644 --- a/packages/@react-aria/numberfield/src/useNumberField.ts +++ b/packages/@react-aria/numberfield/src/useNumberField.ts @@ -12,9 +12,9 @@ import {AriaButtonProps} from '@react-types/button'; import {AriaNumberFieldProps} from '@react-types/numberfield'; +import {DOMAttributes, TextInputDOMProps} from '@react-types/shared'; import {filterDOMProps, isAndroid, isIOS, isIPhone, mergeProps, useId} from '@react-aria/utils'; import { - HTMLAttributes, InputHTMLAttributes, LabelHTMLAttributes, RefObject, @@ -25,7 +25,6 @@ import { // @ts-ignore import intlMessages from '../intl/*.json'; import {NumberFieldState} from '@react-stately/numberfield'; -import {TextInputDOMProps} from '@react-types/shared'; import {useFocus, useFocusWithin} from '@react-aria/interactions'; import {useFormattedTextField} from '@react-aria/textfield'; import { @@ -39,7 +38,7 @@ interface NumberFieldAria { /** Props for the label element. */ labelProps: LabelHTMLAttributes, /** Props for the group wrapper around the input and stepper buttons. */ - groupProps: HTMLAttributes, + groupProps: DOMAttributes, /** Props for the input element. */ inputProps: InputHTMLAttributes, /** Props for the increment button, to be passed to [useButton](useButton.html). */ @@ -47,9 +46,9 @@ interface NumberFieldAria { /** Props for the decrement button, to be passed to [useButton](useButton.html). */ decrementButtonProps: AriaButtonProps, /** Props for the number field's description element, if any. */ - descriptionProps: HTMLAttributes, + descriptionProps: DOMAttributes, /** Props for the number field's error message element, if any. */ - errorMessageProps: HTMLAttributes + errorMessageProps: DOMAttributes } /** diff --git a/packages/@react-aria/overlays/src/ariaHideOutside.ts b/packages/@react-aria/overlays/src/ariaHideOutside.ts index 6477116a0a8..703706871e6 100644 --- a/packages/@react-aria/overlays/src/ariaHideOutside.ts +++ b/packages/@react-aria/overlays/src/ariaHideOutside.ts @@ -22,7 +22,7 @@ let refCountMap = new WeakMap(); * @param root - Nothing will be hidden above this element. * @returns - A function to restore all hidden elements. */ -export function ariaHideOutside(targets: HTMLElement[], root = document.body) { +export function ariaHideOutside(targets: Element[], root = document.body) { let visibleNodes = new Set(targets); let hiddenNodes = new Set(); let walker = document.createTreeWalker( @@ -31,7 +31,7 @@ export function ariaHideOutside(targets: HTMLElement[], root = document.body) { { acceptNode(node) { // If this node is a live announcer, add it to the set of nodes to keep visible. - if ((node instanceof HTMLElement && node.dataset.liveAnnouncer === 'true')) { + if (((node instanceof HTMLElement || node instanceof SVGElement) && node.dataset.liveAnnouncer === 'true')) { visibleNodes.add(node); } @@ -46,7 +46,7 @@ export function ariaHideOutside(targets: HTMLElement[], root = document.body) { // VoiceOver on iOS has issues hiding elements with role="row". Hide the cells inside instead. // https://bugs.webkit.org/show_bug.cgi?id=222623 - if (node instanceof HTMLElement && node.getAttribute('role') === 'row') { + if (node instanceof Element && node.getAttribute('role') === 'row') { return NodeFilter.FILTER_SKIP; } @@ -93,7 +93,7 @@ export function ariaHideOutside(targets: HTMLElement[], root = document.body) { // and not already inside a hidden node, hide all of the new children. if (![...visibleNodes, ...hiddenNodes].some(node => node.contains(change.target))) { for (let node of change.addedNodes) { - if ((node instanceof HTMLElement && node.dataset.liveAnnouncer === 'true')) { + if (((node instanceof HTMLElement || node instanceof SVGElement) && node.dataset.liveAnnouncer === 'true')) { visibleNodes.add(node); } else if (node instanceof Element) { hide(node); diff --git a/packages/@react-aria/overlays/src/calculatePosition.ts b/packages/@react-aria/overlays/src/calculatePosition.ts index fa9f7ef1f7c..676b9adabce 100644 --- a/packages/@react-aria/overlays/src/calculatePosition.ts +++ b/packages/@react-aria/overlays/src/calculatePosition.ts @@ -45,12 +45,12 @@ interface Offset { interface PositionOpts { placement: Placement, - targetNode: HTMLElement, - overlayNode: HTMLElement, - scrollNode: HTMLElement, + targetNode: Element, + overlayNode: Element, + scrollNode: Element, padding: number, shouldFlip: boolean, - boundaryElement: HTMLElement, + boundaryElement: Element, offset: number, crossOffset: number, maxHeight?: number @@ -93,7 +93,7 @@ const PARSED_PLACEMENT_CACHE = {}; // @ts-ignore let visualViewport = typeof window !== 'undefined' && window.visualViewport; -function getContainerDimensions(containerNode: HTMLElement): Dimensions { +function getContainerDimensions(containerNode: Element): Dimensions { let width = 0, height = 0, top = 0, left = 0; let scroll: Position = {}; @@ -113,7 +113,7 @@ function getContainerDimensions(containerNode: HTMLElement): Dimensions { return {width, height, scroll, top, left}; } -function getScroll(node: HTMLElement): Offset { +function getScroll(node: Element): Offset { return { top: node.scrollTop, left: node.scrollLeft, @@ -144,7 +144,7 @@ function getDelta( } } -function getMargins(node: HTMLElement): Position { +function getMargins(node: Element): Position { let style = window.getComputedStyle(node); return { top: parseInt(style.marginTop, 10) || 0, @@ -364,7 +364,7 @@ export function calculatePosition(opts: PositionOpts): PositionResult { maxHeight } = opts; - let container = (overlayNode.offsetParent || document.body) as HTMLElement; + let container = ((overlayNode instanceof HTMLElement && overlayNode.offsetParent) || document.body) as Element; let isBodyContainer = container.tagName === 'BODY'; const containerPositionStyle = window.getComputedStyle(container).position; let isContainerPositioned = !!containerPositionStyle && containerPositionStyle !== 'static'; diff --git a/packages/@react-aria/overlays/src/index.ts b/packages/@react-aria/overlays/src/index.ts index 2f8e0b29b74..cb0ad52a38e 100644 --- a/packages/@react-aria/overlays/src/index.ts +++ b/packages/@react-aria/overlays/src/index.ts @@ -9,11 +9,10 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './useOverlayPosition'; -export * from './useOverlay'; -export * from './useOverlayTrigger'; -export * from './usePreventScroll'; -export * from './useModal'; -export * from './DismissButton'; -export * from './ariaHideOutside'; +export {useOverlayPosition} from './useOverlayPosition'; +export {useOverlay} from './useOverlay'; +export {useOverlayTrigger} from './useOverlayTrigger'; +export {usePreventScroll} from './usePreventScroll'; +export {ModalProvider, useModalProvider, OverlayProvider, OverlayContainer, useModal} from './useModal'; +export {DismissButton} from './DismissButton'; +export {ariaHideOutside} from './ariaHideOutside'; diff --git a/packages/@react-aria/overlays/src/useCloseOnScroll.ts b/packages/@react-aria/overlays/src/useCloseOnScroll.ts index 3e85a329aa5..231c2e99cc1 100644 --- a/packages/@react-aria/overlays/src/useCloseOnScroll.ts +++ b/packages/@react-aria/overlays/src/useCloseOnScroll.ts @@ -17,10 +17,10 @@ import {RefObject, useEffect} from 'react'; // it sets a close function here mapped from the trigger element. This way we can avoid // forcing users to pass an onClose function to useOverlayPosition which could be considered // a breaking change. -export const onCloseMap: WeakMap void> = new WeakMap(); +export const onCloseMap: WeakMap void> = new WeakMap(); interface CloseOnScrollOptions { - triggerRef: RefObject, + triggerRef: RefObject, isOpen?: boolean, onClose?: () => void } diff --git a/packages/@react-aria/overlays/src/useModal.tsx b/packages/@react-aria/overlays/src/useModal.tsx index f5f1b2bf6ff..a426e015abc 100644 --- a/packages/@react-aria/overlays/src/useModal.tsx +++ b/packages/@react-aria/overlays/src/useModal.tsx @@ -10,11 +10,12 @@ * governing permissions and limitations under the License. */ -import React, {AriaAttributes, HTMLAttributes, ReactNode, useContext, useEffect, useMemo, useState} from 'react'; +import {DOMAttributes} from '@react-types/shared'; +import React, {AriaAttributes, ReactNode, useContext, useEffect, useMemo, useState} from 'react'; import ReactDOM from 'react-dom'; import {useIsSSR} from '@react-aria/ssr'; -interface ModalProviderProps extends HTMLAttributes { +interface ModalProviderProps extends DOMAttributes { children: ReactNode } @@ -112,7 +113,7 @@ interface OverlayContainerProps extends ModalProviderProps { * The container element in which the overlay portal will be placed. * @default document.body */ - portalContainer?: HTMLElement + portalContainer?: Element } /** @@ -140,7 +141,7 @@ export function OverlayContainer(props: OverlayContainerProps): React.ReactPorta return ReactDOM.createPortal(contents, portalContainer); } -interface ModalAriaProps extends HTMLAttributes { +interface ModalAriaProps extends DOMAttributes { /** Data attribute marks the dom node as a modal for the aria-modal-polyfill. */ 'data-ismodal': boolean } diff --git a/packages/@react-aria/overlays/src/useOverlay.ts b/packages/@react-aria/overlays/src/useOverlay.ts index 5cb7a7485ab..de7889a2d19 100644 --- a/packages/@react-aria/overlays/src/useOverlay.ts +++ b/packages/@react-aria/overlays/src/useOverlay.ts @@ -10,7 +10,8 @@ * governing permissions and limitations under the License. */ -import {HTMLAttributes, RefObject, SyntheticEvent, useEffect} from 'react'; +import {DOMAttributes} from '@react-types/shared'; +import {RefObject, SyntheticEvent, useEffect} from 'react'; import {useFocusWithin, useInteractOutside} from '@react-aria/interactions'; interface OverlayProps { @@ -41,24 +42,24 @@ interface OverlayProps { * out interaction with elements that should not dismiss the overlay. * By default, onClose will always be called on interaction outside the overlay ref. */ - shouldCloseOnInteractOutside?: (element: HTMLElement) => boolean + shouldCloseOnInteractOutside?: (element: Element) => boolean } interface OverlayAria { /** Props to apply to the overlay container element. */ - overlayProps: HTMLAttributes, + overlayProps: DOMAttributes, /** Props to apply to the underlay element, if any. */ - underlayProps: HTMLAttributes + underlayProps: DOMAttributes } -const visibleOverlays: RefObject[] = []; +const visibleOverlays: RefObject[] = []; /** * Provides the behavior for overlays such as dialogs, popovers, and menus. * Hides the overlay when the user interacts outside it, when the Escape key is pressed, * or optionally, on blur. Only the top-most overlay will close at once. */ -export function useOverlay(props: OverlayProps, ref: RefObject): OverlayAria { +export function useOverlay(props: OverlayProps, ref: RefObject): OverlayAria { let { onClose, shouldCloseOnBlur, @@ -89,8 +90,8 @@ export function useOverlay(props: OverlayProps, ref: RefObject): Ov } }; - let onInteractOutsideStart = (e: SyntheticEvent) => { - if (!shouldCloseOnInteractOutside || shouldCloseOnInteractOutside(e.target as HTMLElement)) { + let onInteractOutsideStart = (e: SyntheticEvent) => { + if (!shouldCloseOnInteractOutside || shouldCloseOnInteractOutside(e.target as Element)) { if (visibleOverlays[visibleOverlays.length - 1] === ref) { e.stopPropagation(); e.preventDefault(); @@ -98,8 +99,8 @@ export function useOverlay(props: OverlayProps, ref: RefObject): Ov } }; - let onInteractOutside = (e: SyntheticEvent) => { - if (!shouldCloseOnInteractOutside || shouldCloseOnInteractOutside(e.target as HTMLElement)) { + let onInteractOutside = (e: SyntheticEvent) => { + if (!shouldCloseOnInteractOutside || shouldCloseOnInteractOutside(e.target as Element)) { if (visibleOverlays[visibleOverlays.length - 1] === ref) { e.stopPropagation(); e.preventDefault(); @@ -123,7 +124,7 @@ export function useOverlay(props: OverlayProps, ref: RefObject): Ov let {focusWithinProps} = useFocusWithin({ isDisabled: !shouldCloseOnBlur, onBlurWithin: (e) => { - if (!shouldCloseOnInteractOutside || shouldCloseOnInteractOutside(e.relatedTarget as HTMLElement)) { + if (!shouldCloseOnInteractOutside || shouldCloseOnInteractOutside(e.relatedTarget as Element)) { onClose(); } } diff --git a/packages/@react-aria/overlays/src/useOverlayPosition.ts b/packages/@react-aria/overlays/src/useOverlayPosition.ts index 6f4e4a07d5b..44813d24fa1 100644 --- a/packages/@react-aria/overlays/src/useOverlayPosition.ts +++ b/packages/@react-aria/overlays/src/useOverlayPosition.ts @@ -11,8 +11,9 @@ */ import {calculatePosition, PositionResult} from './calculatePosition'; -import {HTMLAttributes, RefObject, useCallback, useRef, useState} from 'react'; +import {DOMAttributes} from '@react-types/shared'; import {Placement, PlacementAxis, PositionProps} from '@react-types/overlays'; +import {RefObject, useCallback, useRef, useState} from 'react'; import {useCloseOnScroll} from './useCloseOnScroll'; import {useLayoutEffect} from '@react-aria/utils'; import {useLocale} from '@react-aria/i18n'; @@ -22,20 +23,20 @@ interface AriaPositionProps extends PositionProps { * Element that that serves as the positioning boundary. * @default document.body */ - boundaryElement?: HTMLElement, + boundaryElement?: Element, /** * The ref for the element which the overlay positions itself with respect to. */ - targetRef: RefObject, + targetRef: RefObject, /** * The ref for the overlay element. */ - overlayRef: RefObject, + overlayRef: RefObject, /** * A ref for the scrollable region within the overlay. * @default overlayRef */ - scrollRef?: RefObject, + scrollRef?: RefObject, /** * Whether the overlay should update its position automatically. * @default true @@ -52,9 +53,9 @@ interface AriaPositionProps extends PositionProps { interface PositionAria { /** Props for the overlay container element. */ - overlayProps: HTMLAttributes, + overlayProps: DOMAttributes, /** Props for the overlay tip arrow if any. */ - arrowProps: HTMLAttributes, + arrowProps: DOMAttributes, /** Placement of the overlay with respect to the overlay trigger. */ placement: PlacementAxis, /** Updates the position of the overlay. */ diff --git a/packages/@react-aria/overlays/src/useOverlayTrigger.ts b/packages/@react-aria/overlays/src/useOverlayTrigger.ts index f2dfd6b9599..5055bf6022e 100644 --- a/packages/@react-aria/overlays/src/useOverlayTrigger.ts +++ b/packages/@react-aria/overlays/src/useOverlayTrigger.ts @@ -11,9 +11,10 @@ */ import {AriaButtonProps} from '@react-types/button'; -import {HTMLAttributes, RefObject, useEffect} from 'react'; +import {DOMAttributes} from '@react-types/shared'; import {onCloseMap} from './useCloseOnScroll'; import {OverlayTriggerState} from '@react-stately/overlays'; +import {RefObject, useEffect} from 'react'; import {useId} from '@react-aria/utils'; interface OverlayTriggerProps { @@ -26,14 +27,14 @@ interface OverlayTriggerAria { triggerProps: AriaButtonProps, /** Props for the overlay container element. */ - overlayProps: HTMLAttributes + overlayProps: DOMAttributes } /** * Handles the behavior and accessibility for an overlay trigger, e.g. a button * that opens a popover, menu, or other overlay that is positioned relative to the trigger. */ -export function useOverlayTrigger(props: OverlayTriggerProps, state: OverlayTriggerState, ref: RefObject): OverlayTriggerAria { +export function useOverlayTrigger(props: OverlayTriggerProps, state: OverlayTriggerState, ref: RefObject): OverlayTriggerAria { let {type} = props; let {isOpen} = state; diff --git a/packages/@react-aria/pagination/src/index.ts b/packages/@react-aria/pagination/src/index.ts index 8b6a2f9937d..987f15c4a27 100644 --- a/packages/@react-aria/pagination/src/index.ts +++ b/packages/@react-aria/pagination/src/index.ts @@ -9,5 +9,4 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './usePagination'; +export {usePagination} from './usePagination'; diff --git a/packages/@react-aria/progress/src/index.ts b/packages/@react-aria/progress/src/index.ts index 630f836b6c7..a6014e56083 100644 --- a/packages/@react-aria/progress/src/index.ts +++ b/packages/@react-aria/progress/src/index.ts @@ -9,5 +9,4 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './useProgressBar'; +export {useProgressBar} from './useProgressBar'; diff --git a/packages/@react-aria/progress/src/useProgressBar.ts b/packages/@react-aria/progress/src/useProgressBar.ts index 34ddc8657ea..b5a826b6cb0 100644 --- a/packages/@react-aria/progress/src/useProgressBar.ts +++ b/packages/@react-aria/progress/src/useProgressBar.ts @@ -12,15 +12,15 @@ import {AriaProgressBarProps} from '@react-types/progress'; import {clamp, filterDOMProps, mergeProps} from '@react-aria/utils'; -import {HTMLAttributes} from 'react'; +import {DOMAttributes} from '@react-types/shared'; import {useLabel} from '@react-aria/label'; import {useNumberFormatter} from '@react-aria/i18n'; interface ProgressBarAria { /** Props for the progress bar container element. */ - progressBarProps: HTMLAttributes, + progressBarProps: DOMAttributes, /** Props for the progress bar's visual label element (if any). */ - labelProps: HTMLAttributes + labelProps: DOMAttributes } /** diff --git a/packages/@react-aria/radio/package.json b/packages/@react-aria/radio/package.json index acc0bb5687f..09457f74ef0 100644 --- a/packages/@react-aria/radio/package.json +++ b/packages/@react-aria/radio/package.json @@ -24,7 +24,8 @@ "@react-aria/label": "^3.3.1", "@react-aria/utils": "^3.13.1", "@react-stately/radio": "^3.4.1", - "@react-types/radio": "^3.2.1" + "@react-types/radio": "^3.2.1", + "@react-types/shared": "^3.13.1" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" diff --git a/packages/@react-aria/radio/src/index.ts b/packages/@react-aria/radio/src/index.ts index 641cfcc25a1..b6b4486f699 100644 --- a/packages/@react-aria/radio/src/index.ts +++ b/packages/@react-aria/radio/src/index.ts @@ -9,6 +9,5 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './useRadio'; -export * from './useRadioGroup'; +export {useRadio} from './useRadio'; +export {useRadioGroup} from './useRadioGroup'; diff --git a/packages/@react-aria/radio/src/useRadio.ts b/packages/@react-aria/radio/src/useRadio.ts index 9d012de24d4..b82253348f1 100644 --- a/packages/@react-aria/radio/src/useRadio.ts +++ b/packages/@react-aria/radio/src/useRadio.ts @@ -20,7 +20,7 @@ import {usePress} from '@react-aria/interactions'; interface RadioAria { /** Props for the input element. */ - inputProps: InputHTMLAttributes, + inputProps: InputHTMLAttributes, /** Whether the radio is disabled. */ isDisabled: boolean, /** Whether the radio is currently selected. */ @@ -34,7 +34,7 @@ interface RadioAria { * @param state - State for the radio group, as returned by `useRadioGroupState`. * @param ref - Ref to the HTML input element. */ -export function useRadio(props: AriaRadioProps, state: RadioGroupState, ref: RefObject): RadioAria { +export function useRadio(props: AriaRadioProps, state: RadioGroupState, ref: RefObject): RadioAria { let { value, children, diff --git a/packages/@react-aria/radio/src/useRadioGroup.ts b/packages/@react-aria/radio/src/useRadioGroup.ts index ba327a12aaa..0516944e62b 100644 --- a/packages/@react-aria/radio/src/useRadioGroup.ts +++ b/packages/@react-aria/radio/src/useRadioGroup.ts @@ -11,9 +11,9 @@ */ import {AriaRadioGroupProps} from '@react-types/radio'; +import {DOMAttributes} from '@react-types/shared'; import {filterDOMProps, mergeProps, useId} from '@react-aria/utils'; import {getFocusableTreeWalker} from '@react-aria/focus'; -import {HTMLAttributes} from 'react'; import {radioGroupNames} from './utils'; import {RadioGroupState} from '@react-stately/radio'; import {useFocusWithin} from '@react-aria/interactions'; @@ -22,9 +22,9 @@ import {useLocale} from '@react-aria/i18n'; interface RadioGroupAria { /** Props for the radio group wrapper element. */ - radioGroupProps: HTMLAttributes, + radioGroupProps: DOMAttributes, /** Props for the radio group's visible label (if any). */ - labelProps: HTMLAttributes + labelProps: DOMAttributes } /** diff --git a/packages/@react-aria/searchfield/src/index.ts b/packages/@react-aria/searchfield/src/index.ts index b14076e4c4e..1bfdd88a905 100644 --- a/packages/@react-aria/searchfield/src/index.ts +++ b/packages/@react-aria/searchfield/src/index.ts @@ -9,5 +9,4 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './useSearchField'; +export {useSearchField} from './useSearchField'; diff --git a/packages/@react-aria/searchfield/src/useSearchField.ts b/packages/@react-aria/searchfield/src/useSearchField.ts index dc55e658206..8c4702bd88d 100644 --- a/packages/@react-aria/searchfield/src/useSearchField.ts +++ b/packages/@react-aria/searchfield/src/useSearchField.ts @@ -13,7 +13,8 @@ import {AriaButtonProps} from '@react-types/button'; import {AriaSearchFieldProps} from '@react-types/searchfield'; import {chain} from '@react-aria/utils'; -import {HTMLAttributes, InputHTMLAttributes, LabelHTMLAttributes, RefObject} from 'react'; +import {DOMAttributes} from '@react-types/shared'; +import {InputHTMLAttributes, LabelHTMLAttributes, RefObject} from 'react'; // @ts-ignore import intlMessages from '../intl/*.json'; import {SearchFieldState} from '@react-stately/searchfield'; @@ -28,9 +29,9 @@ interface SearchFieldAria { /** Props for the clear button. */ clearButtonProps: AriaButtonProps, /** Props for the searchfield's description element, if any. */ - descriptionProps: HTMLAttributes, + descriptionProps: DOMAttributes, /** Props for the searchfield's error message element, if any. */ - errorMessageProps: HTMLAttributes + errorMessageProps: DOMAttributes } /** diff --git a/packages/@react-aria/select/docs/popup-example.png b/packages/@react-aria/select/docs/popup-example.png new file mode 100644 index 0000000000000000000000000000000000000000..6c535a36e4a5939faf1e59d9f00e0432f2b22370 GIT binary patch literal 29771 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 kw@b3WwN7BnnLQy*ykj=O5 + + ## Internationalization `useSelect` and `useListBox` handle some aspects of internationalization automatically. diff --git a/packages/@react-aria/select/src/HiddenSelect.tsx b/packages/@react-aria/select/src/HiddenSelect.tsx index 11d1b05e714..8d5febc9f0d 100644 --- a/packages/@react-aria/select/src/HiddenSelect.tsx +++ b/packages/@react-aria/select/src/HiddenSelect.tsx @@ -10,6 +10,7 @@ * governing permissions and limitations under the License. */ +import {FocusableElement} from '@react-types/shared'; import React, {ReactNode, RefObject} from 'react'; import {SelectState} from '@react-stately/select'; import {useInteractionModality} from '@react-aria/interactions'; @@ -36,7 +37,7 @@ interface HiddenSelectProps extends AriaHiddenSelectProps { state: SelectState, /** A ref to the trigger element. */ - triggerRef: RefObject + triggerRef: RefObject } /** @@ -44,7 +45,7 @@ interface HiddenSelectProps extends AriaHiddenSelectProps { * can be used in combination with `useSelect` to support browser form autofill, mobile form * navigation, and native HTML form submission. */ -export function useHiddenSelect(props: AriaHiddenSelectProps, state: SelectState, triggerRef: RefObject) { +export function useHiddenSelect(props: AriaHiddenSelectProps, state: SelectState, triggerRef: RefObject) { let {autoComplete, name, isDisabled} = props; let modality = useInteractionModality(); let {visuallyHiddenProps} = useVisuallyHidden(); diff --git a/packages/@react-aria/select/src/index.ts b/packages/@react-aria/select/src/index.ts index 9edac87ff9b..dd2de458f5d 100644 --- a/packages/@react-aria/select/src/index.ts +++ b/packages/@react-aria/select/src/index.ts @@ -9,6 +9,5 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './useSelect'; -export * from './HiddenSelect'; +export {useSelect} from './useSelect'; +export {useHiddenSelect, HiddenSelect} from './HiddenSelect'; diff --git a/packages/@react-aria/select/src/useSelect.ts b/packages/@react-aria/select/src/useSelect.ts index 7cfd13378b8..470c6834037 100644 --- a/packages/@react-aria/select/src/useSelect.ts +++ b/packages/@react-aria/select/src/useSelect.ts @@ -14,8 +14,8 @@ import {AriaButtonProps} from '@react-types/button'; import {AriaListBoxOptions} from '@react-aria/listbox'; import {AriaSelectProps} from '@react-types/select'; import {chain, filterDOMProps, mergeProps, useId} from '@react-aria/utils'; -import {FocusEvent, HTMLAttributes, RefObject, useMemo} from 'react'; -import {KeyboardDelegate} from '@react-types/shared'; +import {DOMAttributes, FocusableElement, KeyboardDelegate} from '@react-types/shared'; +import {FocusEvent, RefObject, useMemo} from 'react'; import {ListKeyboardDelegate, useTypeSelect} from '@react-aria/selection'; import {SelectState} from '@react-stately/select'; import {setInteractionModality} from '@react-aria/interactions'; @@ -33,22 +33,22 @@ interface AriaSelectOptions extends AriaSelectProps { interface SelectAria { /** Props for the label element. */ - labelProps: HTMLAttributes, + labelProps: DOMAttributes, /** Props for the popup trigger element. */ triggerProps: AriaButtonProps, /** Props for the element representing the selected value. */ - valueProps: HTMLAttributes, + valueProps: DOMAttributes, /** Props for the popup. */ menuProps: AriaListBoxOptions, /** Props for the select's description element, if any. */ - descriptionProps: HTMLAttributes, + descriptionProps: DOMAttributes, /** Props for the select's error message element, if any. */ - errorMessageProps: HTMLAttributes + errorMessageProps: DOMAttributes } /** @@ -57,7 +57,7 @@ interface SelectAria { * @param props - Props for the select. * @param state - State for the select, as returned by `useListState`. */ -export function useSelect(props: AriaSelectOptions, state: SelectState, ref: RefObject): SelectAria { +export function useSelect(props: AriaSelectOptions, state: SelectState, ref: RefObject): SelectAria { let { keyboardDelegate, isDisabled diff --git a/packages/@react-aria/selection/src/index.ts b/packages/@react-aria/selection/src/index.ts index db408eee56d..cdf52d40a1f 100644 --- a/packages/@react-aria/selection/src/index.ts +++ b/packages/@react-aria/selection/src/index.ts @@ -10,8 +10,9 @@ * governing permissions and limitations under the License. */ -export * from './useSelectableCollection'; -export * from './useSelectableItem'; -export * from './useSelectableList'; -export * from './ListKeyboardDelegate'; -export * from './useTypeSelect'; +export type {SelectableItemOptions, SelectableItemStates, SelectableItemAria} from './useSelectableItem'; +export {useSelectableCollection} from './useSelectableCollection'; +export {useSelectableItem} from './useSelectableItem'; +export {useSelectableList} from './useSelectableList'; +export {ListKeyboardDelegate} from './ListKeyboardDelegate'; +export {useTypeSelect} from './useTypeSelect'; diff --git a/packages/@react-aria/selection/src/useSelectableCollection.ts b/packages/@react-aria/selection/src/useSelectableCollection.ts index c78b28b5be6..da66ad178c5 100644 --- a/packages/@react-aria/selection/src/useSelectableCollection.ts +++ b/packages/@react-aria/selection/src/useSelectableCollection.ts @@ -10,9 +10,9 @@ * governing permissions and limitations under the License. */ -import {FocusEvent, HTMLAttributes, Key, KeyboardEvent, RefObject, useEffect, useRef} from 'react'; +import {DOMAttributes, FocusableElement, FocusStrategy, KeyboardDelegate} from '@react-types/shared'; +import {FocusEvent, Key, KeyboardEvent, RefObject, useEffect, useRef} from 'react'; import {focusSafely, getFocusableTreeWalker} from '@react-aria/focus'; -import {FocusStrategy, KeyboardDelegate} from '@react-types/shared'; import {focusWithoutScrolling, mergeProps, scrollIntoView, useEvent} from '@react-aria/utils'; import {isCtrlKeyPressed, isNonContiguousSelectionModifier} from './utils'; import {MultipleSelectionManager} from '@react-stately/selection'; @@ -83,7 +83,7 @@ interface SelectableCollectionOptions { interface SelectableCollectionAria { /** Props for the collection element. */ - collectionProps: HTMLAttributes + collectionProps: DOMAttributes } /** @@ -117,7 +117,7 @@ export function useSelectableCollection(options: SelectableCollectionOptions): S // Keyboard events bubble through portals. Don't handle keyboard events // for elements outside the collection (e.g. menus). - if (!ref.current.contains(e.target as HTMLElement)) { + if (!ref.current.contains(e.target as Element)) { return; } @@ -238,10 +238,10 @@ export function useSelectableCollection(options: SelectableCollectionOptions): S ref.current.focus(); } else { let walker = getFocusableTreeWalker(ref.current, {tabbable: true}); - let next: HTMLElement; - let last: HTMLElement; + let next: FocusableElement; + let last: FocusableElement; do { - last = walker.lastChild() as HTMLElement; + last = walker.lastChild() as FocusableElement; if (last) { next = last; } diff --git a/packages/@react-aria/selection/src/useSelectableItem.ts b/packages/@react-aria/selection/src/useSelectableItem.ts index 388db79414f..7dcd9faa885 100644 --- a/packages/@react-aria/selection/src/useSelectableItem.ts +++ b/packages/@react-aria/selection/src/useSelectableItem.ts @@ -10,10 +10,10 @@ * governing permissions and limitations under the License. */ +import {DOMAttributes, FocusableElement, LongPressEvent, PressEvent} from '@react-types/shared'; import {focusSafely} from '@react-aria/focus'; -import {HTMLAttributes, Key, RefObject, useEffect, useRef} from 'react'; import {isCtrlKeyPressed, isNonContiguousSelectionModifier} from './utils'; -import {LongPressEvent, PressEvent} from '@react-types/shared'; +import {Key, RefObject, useEffect, useRef} from 'react'; import {mergeProps} from '@react-aria/utils'; import {MultipleSelectionManager} from '@react-stately/selection'; import {PressProps, useLongPress, usePress} from '@react-aria/interactions'; @@ -30,7 +30,7 @@ export interface SelectableItemOptions { /** * Ref to the item. */ - ref: RefObject, + ref: RefObject, /** * By default, selection occurs on pointer down. This can be strange if selecting an * item causes the UI to disappear immediately (e.g. menus). @@ -89,7 +89,7 @@ export interface SelectableItemAria extends SelectableItemStates { /** * Props to be spread on the item root node. */ - itemProps: HTMLAttributes + itemProps: DOMAttributes } /** diff --git a/packages/@react-aria/selection/src/useSelectableList.ts b/packages/@react-aria/selection/src/useSelectableList.ts index 986f79a62e2..b2144856915 100644 --- a/packages/@react-aria/selection/src/useSelectableList.ts +++ b/packages/@react-aria/selection/src/useSelectableList.ts @@ -10,8 +10,8 @@ * governing permissions and limitations under the License. */ -import {Collection, FocusStrategy, KeyboardDelegate, Node} from '@react-types/shared'; -import {HTMLAttributes, Key, RefObject, useMemo} from 'react'; +import {Collection, DOMAttributes, FocusStrategy, KeyboardDelegate, Node} from '@react-types/shared'; +import {Key, RefObject, useMemo} from 'react'; import {ListKeyboardDelegate} from './ListKeyboardDelegate'; import {MultipleSelectionManager} from '@react-stately/selection'; import {useCollator} from '@react-aria/i18n'; @@ -81,7 +81,7 @@ interface SelectableListAria { /** * Props for the option element. */ - listProps: HTMLAttributes + listProps: DOMAttributes } /** diff --git a/packages/@react-aria/selection/src/useTypeSelect.ts b/packages/@react-aria/selection/src/useTypeSelect.ts index bbb32a75b08..351163c2f86 100644 --- a/packages/@react-aria/selection/src/useTypeSelect.ts +++ b/packages/@react-aria/selection/src/useTypeSelect.ts @@ -10,8 +10,8 @@ * governing permissions and limitations under the License. */ -import {HTMLAttributes, Key, KeyboardEvent, useRef} from 'react'; -import {KeyboardDelegate} from '@react-types/shared'; +import {DOMAttributes, KeyboardDelegate} from '@react-types/shared'; +import {Key, KeyboardEvent, useRef} from 'react'; import {MultipleSelectionManager} from '@react-stately/selection'; interface TypeSelectOptions { @@ -33,7 +33,7 @@ interface TypeSelectAria { /** * Props to be spread on the owner of the options. */ - typeSelectProps: HTMLAttributes + typeSelectProps: DOMAttributes } /** diff --git a/packages/@react-aria/separator/src/index.ts b/packages/@react-aria/separator/src/index.ts index e785ae697e3..97ad5024394 100644 --- a/packages/@react-aria/separator/src/index.ts +++ b/packages/@react-aria/separator/src/index.ts @@ -9,5 +9,5 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './useSeparator'; +export type {SeparatorProps, SeparatorAria} from './useSeparator'; +export {useSeparator} from './useSeparator'; diff --git a/packages/@react-aria/separator/src/useSeparator.ts b/packages/@react-aria/separator/src/useSeparator.ts index e3574fedb3f..a43660f0519 100644 --- a/packages/@react-aria/separator/src/useSeparator.ts +++ b/packages/@react-aria/separator/src/useSeparator.ts @@ -10,9 +10,8 @@ * governing permissions and limitations under the License. */ -import {AriaLabelingProps, DOMProps, Orientation} from '@react-types/shared'; +import {AriaLabelingProps, DOMAttributes, DOMProps, Orientation} from '@react-types/shared'; import {filterDOMProps} from '@react-aria/utils'; -import {HTMLAttributes} from 'react'; export interface SeparatorProps extends DOMProps, AriaLabelingProps { /** @@ -26,7 +25,7 @@ export interface SeparatorProps extends DOMProps, AriaLabelingProps { export interface SeparatorAria { /** Props for the separator element. */ - separatorProps: HTMLAttributes + separatorProps: DOMAttributes } /** diff --git a/packages/@react-aria/sidenav/src/index.ts b/packages/@react-aria/sidenav/src/index.ts index 20c19568ee6..ce93ef20bdb 100644 --- a/packages/@react-aria/sidenav/src/index.ts +++ b/packages/@react-aria/sidenav/src/index.ts @@ -9,6 +9,5 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './useSideNav'; -export * from './useSideNavItem'; +export {useSideNav} from './useSideNav'; +export {useSideNavItem} from './useSideNavItem'; diff --git a/packages/@react-aria/sidenav/src/useSideNav.ts b/packages/@react-aria/sidenav/src/useSideNav.ts index 7fb1410d3bc..349b5c15a68 100644 --- a/packages/@react-aria/sidenav/src/useSideNav.ts +++ b/packages/@react-aria/sidenav/src/useSideNav.ts @@ -11,9 +11,9 @@ */ import {AriaSideNavProps} from '@react-types/sidenav'; +import {DOMAttributes, KeyboardDelegate} from '@react-types/shared'; import {filterDOMProps, mergeProps, useId} from '@react-aria/utils'; -import {HTMLAttributes, RefObject} from 'react'; -import {KeyboardDelegate} from '@react-types/shared'; +import {RefObject} from 'react'; import {TreeState} from '@react-stately/tree'; import {useSelectableCollection} from '@react-aria/selection'; @@ -22,8 +22,8 @@ interface SideNavAriaOptions extends AriaSideNavProps { } interface SideNavAria { - navProps: HTMLAttributes, - listProps: HTMLAttributes + navProps: DOMAttributes, + listProps: DOMAttributes } export function useSideNav(props: SideNavAriaOptions, state: TreeState, ref: RefObject): SideNavAria { diff --git a/packages/@react-aria/sidenav/src/useSideNavItem.ts b/packages/@react-aria/sidenav/src/useSideNavItem.ts index 3cebbcc4e23..6e747532840 100644 --- a/packages/@react-aria/sidenav/src/useSideNavItem.ts +++ b/packages/@react-aria/sidenav/src/useSideNavItem.ts @@ -10,13 +10,14 @@ * governing permissions and limitations under the License. */ -import {AnchorHTMLAttributes, HTMLAttributes, RefObject} from 'react'; +import {AnchorHTMLAttributes, RefObject} from 'react'; +import {DOMAttributes} from '@react-types/shared'; import {SpectrumSideNavItemProps} from '@react-types/sidenav'; import {TreeState} from '@react-stately/tree'; import {useSelectableItem} from '@react-aria/selection'; interface SideNavItemAria { - listItemProps: HTMLAttributes, + listItemProps: DOMAttributes, listItemLinkProps: AnchorHTMLAttributes } diff --git a/packages/@react-aria/slider/package.json b/packages/@react-aria/slider/package.json index e1ae48bb6e4..cd56c8730fa 100644 --- a/packages/@react-aria/slider/package.json +++ b/packages/@react-aria/slider/package.json @@ -26,6 +26,7 @@ "@react-stately/radio": "^3.4.1", "@react-stately/slider": "^3.1.1", "@react-types/radio": "^3.2.1", + "@react-types/shared": "^3.13.1", "@react-types/slider": "^3.1.1" }, "peerDependencies": { diff --git a/packages/@react-aria/slider/src/index.ts b/packages/@react-aria/slider/src/index.ts index 8b4ae8d6d7b..d86a92f954f 100644 --- a/packages/@react-aria/slider/src/index.ts +++ b/packages/@react-aria/slider/src/index.ts @@ -9,6 +9,5 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './useSlider'; -export * from './useSliderThumb'; +export {useSlider} from './useSlider'; +export {useSliderThumb} from './useSliderThumb'; diff --git a/packages/@react-aria/slider/src/useSlider.ts b/packages/@react-aria/slider/src/useSlider.ts index 52cf72d9a37..f1f364db8d5 100644 --- a/packages/@react-aria/slider/src/useSlider.ts +++ b/packages/@react-aria/slider/src/useSlider.ts @@ -12,8 +12,9 @@ import {AriaSliderProps} from '@react-types/slider'; import {clamp, mergeProps, useGlobalListeners} from '@react-aria/utils'; +import {DOMAttributes} from '@react-types/shared'; import {getSliderThumbId, sliderIds} from './utils'; -import React, {HTMLAttributes, LabelHTMLAttributes, OutputHTMLAttributes, RefObject, useRef} from 'react'; +import React, {LabelHTMLAttributes, OutputHTMLAttributes, RefObject, useRef} from 'react'; import {setInteractionModality, useMove} from '@react-aria/interactions'; import {SliderState} from '@react-stately/slider'; import {useLabel} from '@react-aria/label'; @@ -24,10 +25,10 @@ interface SliderAria { labelProps: LabelHTMLAttributes, /** Props for the root element of the slider component; groups slider inputs. */ - groupProps: HTMLAttributes, + groupProps: DOMAttributes, /** Props for the track element. */ - trackProps: HTMLAttributes, + trackProps: DOMAttributes, /** Props for the output element, displaying the value of the slider thumbs. */ outputProps: OutputHTMLAttributes @@ -46,7 +47,7 @@ interface SliderAria { export function useSlider( props: AriaSliderProps, state: SliderState, - trackRef: RefObject + trackRef: RefObject ): SliderAria { let {labelProps, fieldProps} = useLabel(props); @@ -74,7 +75,8 @@ export function useSlider( currentPosition.current = null; }, onMove({deltaX, deltaY}) { - let size = isVertical ? trackRef.current.offsetHeight : trackRef.current.offsetWidth; + let {height, width} = trackRef.current.getBoundingClientRect(); + let size = isVertical ? height : width; if (currentPosition.current == null) { currentPosition.current = stateRef.current.getThumbPercent(realTimeTrackDraggingIndex.current) * size; @@ -104,9 +106,10 @@ export function useSlider( let onDownTrack = (e: React.UIEvent, id: number, clientX: number, clientY: number) => { // We only trigger track-dragging if the user clicks on the track itself and nothing is currently being dragged. if (trackRef.current && !props.isDisabled && state.values.every((_, i) => !state.isThumbDragging(i))) { - let size = isVertical ? trackRef.current.offsetHeight : trackRef.current.offsetWidth; + let {height, width, top, left} = trackRef.current.getBoundingClientRect(); + let size = isVertical ? height : width; // Find the closest thumb - const trackPosition = trackRef.current.getBoundingClientRect()[isVertical ? 'top' : 'left']; + const trackPosition = isVertical ? top : left; const clickPosition = isVertical ? clientY : clientX; const offset = clickPosition - trackPosition; let percent = offset / size; @@ -168,7 +171,7 @@ export function useSlider( } }; - if (labelProps.htmlFor) { + if ('htmlFor' in labelProps && labelProps.htmlFor) { // Ideally the `for` attribute should point to the first thumb, but VoiceOver on iOS // causes this to override the `aria-labelledby` on the thumb. This causes the first // thumb to only be announced as the slider label rather than its individual name as well. @@ -192,19 +195,19 @@ export function useSlider( ...fieldProps }, trackProps: mergeProps({ - onMouseDown(e: React.MouseEvent) { + onMouseDown(e: React.MouseEvent) { if (e.button !== 0 || e.altKey || e.ctrlKey || e.metaKey) { return; } onDownTrack(e, undefined, e.clientX, e.clientY); }, - onPointerDown(e: React.PointerEvent) { + onPointerDown(e: React.PointerEvent) { if (e.pointerType === 'mouse' && (e.button !== 0 || e.altKey || e.ctrlKey || e.metaKey)) { return; } onDownTrack(e, e.pointerId, e.clientX, e.clientY); }, - onTouchStart(e: React.TouchEvent) { onDownTrack(e, e.changedTouches[0].identifier, e.changedTouches[0].clientX, e.changedTouches[0].clientY); }, + onTouchStart(e: React.TouchEvent) { onDownTrack(e, e.changedTouches[0].identifier, e.changedTouches[0].clientX, e.changedTouches[0].clientY); }, style: { position: 'relative', touchAction: 'none' diff --git a/packages/@react-aria/slider/src/useSliderThumb.ts b/packages/@react-aria/slider/src/useSliderThumb.ts index 3c0b508cec6..92bd102bf0d 100644 --- a/packages/@react-aria/slider/src/useSliderThumb.ts +++ b/packages/@react-aria/slider/src/useSliderThumb.ts @@ -1,7 +1,8 @@ import {AriaSliderThumbProps} from '@react-types/slider'; import {clamp, focusWithoutScrolling, mergeProps, useGlobalListeners} from '@react-aria/utils'; +import {DOMAttributes} from '@react-types/shared'; import {getSliderThumbId, sliderIds} from './utils'; -import React, {ChangeEvent, HTMLAttributes, InputHTMLAttributes, LabelHTMLAttributes, RefObject, useCallback, useEffect, useRef} from 'react'; +import React, {ChangeEvent, InputHTMLAttributes, LabelHTMLAttributes, RefObject, useCallback, useEffect, useRef} from 'react'; import {SliderState} from '@react-stately/slider'; import {useFocusable} from '@react-aria/focus'; import {useKeyboard, useMove} from '@react-aria/interactions'; @@ -10,7 +11,7 @@ import {useLocale} from '@react-aria/i18n'; interface SliderThumbAria { /** Props for the root thumb element; handles the dragging motion. */ - thumbProps: HTMLAttributes, + thumbProps: DOMAttributes, /** Props for the visually hidden range input element. */ inputProps: InputHTMLAttributes, @@ -28,7 +29,7 @@ interface SliderThumbAria { interface SliderThumbOptions extends AriaSliderThumbProps { /** A ref to the track element. */ - trackRef: RefObject, + trackRef: RefObject, /** A ref to the thumb input element. */ inputRef: RefObject } @@ -138,7 +139,8 @@ export function useSliderThumb( step, pageSize } = stateRef.current; - let size = isVertical ? trackRef.current.offsetHeight : trackRef.current.offsetWidth; + let {width, height} = trackRef.current.getBoundingClientRect(); + let size = isVertical ? height : width; if (currentPosition.current == null) { currentPosition.current = getThumbPercent(index) * size; @@ -207,19 +209,19 @@ export function useSliderThumb( keyboardProps, moveProps, { - onMouseDown: (e: React.MouseEvent) => { + onMouseDown: (e: React.MouseEvent) => { if (e.button !== 0 || e.altKey || e.ctrlKey || e.metaKey) { return; } onDown(); }, - onPointerDown: (e: React.PointerEvent) => { + onPointerDown: (e: React.PointerEvent) => { if (e.button !== 0 || e.altKey || e.ctrlKey || e.metaKey) { return; } onDown(e.pointerId); }, - onTouchStart: (e: React.TouchEvent) => {onDown(e.changedTouches[0].identifier);} + onTouchStart: (e: React.TouchEvent) => {onDown(e.changedTouches[0].identifier);} } ) : {}; diff --git a/packages/@react-aria/slider/stories/StoryMultiSlider.tsx b/packages/@react-aria/slider/stories/StoryMultiSlider.tsx index 7c1f78d1cba..92ef7af92c4 100644 --- a/packages/@react-aria/slider/stories/StoryMultiSlider.tsx +++ b/packages/@react-aria/slider/stories/StoryMultiSlider.tsx @@ -79,7 +79,7 @@ interface StoryThumbProps extends Omit { interface SliderStateContext { sliderProps: StoryMultiSliderProps, state: SliderState, - trackRef: React.RefObject, + trackRef: React.RefObject, index: number } diff --git a/packages/@react-aria/slider/test/useSlider.test.js b/packages/@react-aria/slider/test/useSlider.test.js index 38fb317af36..54cce20ac77 100644 --- a/packages/@react-aria/slider/test/useSlider.test.js +++ b/packages/@react-aria/slider/test/useSlider.test.js @@ -49,14 +49,12 @@ describe('useSlider', () => { }); describe('interactions on track', () => { - let widthStub, heightStub; + let widthStub; beforeAll(() => { - widthStub = jest.spyOn(window.HTMLElement.prototype, 'offsetWidth', 'get').mockImplementation(() => 100); - heightStub = jest.spyOn(window.HTMLElement.prototype, 'offsetHeight', 'get').mockImplementation(() => 100); + widthStub = jest.spyOn(window.HTMLElement.prototype, 'getBoundingClientRect').mockImplementation(() => ({top: 0, left: 0, width: 100, height: 100})); }); afterAll(() => { widthStub.mockReset(); - heightStub.mockReset(); }); installMouseEvent(); @@ -171,7 +169,7 @@ describe('useSlider', () => { describe('interactions on track using pointerEvents', () => { let widthStub; beforeAll(() => { - widthStub = jest.spyOn(window.HTMLElement.prototype, 'offsetWidth', 'get').mockImplementation(() => 100); + widthStub = jest.spyOn(window.HTMLElement.prototype, 'getBoundingClientRect').mockImplementation(() => ({top: 0, left: 0, width: 100, height: 100})); }); afterAll(() => { widthStub.mockReset(); diff --git a/packages/@react-aria/slider/test/useSliderThumb.test.js b/packages/@react-aria/slider/test/useSliderThumb.test.js index db94a5a5798..f904b167a92 100644 --- a/packages/@react-aria/slider/test/useSliderThumb.test.js +++ b/packages/@react-aria/slider/test/useSliderThumb.test.js @@ -97,7 +97,7 @@ describe('useSliderThumb', () => { describe('interactions on thumbs, where track does not contain thumbs', () => { let widthStub; beforeAll(() => { - widthStub = jest.spyOn(window.HTMLElement.prototype, 'offsetWidth', 'get').mockImplementation(() => 100); + widthStub = jest.spyOn(window.HTMLElement.prototype, 'getBoundingClientRect').mockImplementation(() => ({top: 0, left: 0, width: 100})); }); afterAll(() => { widthStub.mockReset(); @@ -257,14 +257,12 @@ describe('useSliderThumb', () => { }); describe('interactions on thumbs, where track contains thumbs', () => { - let widthStub, heightStub; + let widthStub; beforeAll(() => { - widthStub = jest.spyOn(window.HTMLElement.prototype, 'offsetWidth', 'get').mockImplementation(() => 100); - heightStub = jest.spyOn(window.HTMLElement.prototype, 'offsetHeight', 'get').mockImplementation(() => 100); + widthStub = jest.spyOn(window.HTMLElement.prototype, 'getBoundingClientRect').mockImplementation(() => ({top: 0, left: 0, width: 100, height: 100})); }); afterAll(() => { widthStub.mockReset(); - heightStub.mockReset(); }); installMouseEvent(); diff --git a/packages/@react-aria/spinbutton/src/index.ts b/packages/@react-aria/spinbutton/src/index.ts index fe6d70ded9d..892752621f6 100644 --- a/packages/@react-aria/spinbutton/src/index.ts +++ b/packages/@react-aria/spinbutton/src/index.ts @@ -9,5 +9,5 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './useSpinButton'; +export type {SpinButtonProps, SpinbuttonAria} from './useSpinButton'; +export {useSpinButton} from './useSpinButton'; diff --git a/packages/@react-aria/spinbutton/src/useSpinButton.ts b/packages/@react-aria/spinbutton/src/useSpinButton.ts index aaf966c3f28..d180ec89c29 100644 --- a/packages/@react-aria/spinbutton/src/useSpinButton.ts +++ b/packages/@react-aria/spinbutton/src/useSpinButton.ts @@ -12,10 +12,10 @@ import {announce} from '@react-aria/live-announcer'; import {AriaButtonProps} from '@react-types/button'; -import {HTMLAttributes, useCallback, useEffect, useRef} from 'react'; -import {InputBase, RangeInputBase, Validation, ValueBase} from '@react-types/shared'; +import {DOMAttributes, InputBase, RangeInputBase, Validation, ValueBase} from '@react-types/shared'; // @ts-ignore import intlMessages from '../intl/*.json'; +import {useCallback, useEffect, useRef} from 'react'; import {useGlobalListeners} from '@react-aria/utils'; import {useMessageFormatter} from '@react-aria/i18n'; @@ -31,7 +31,7 @@ export interface SpinButtonProps extends InputBase, Validation, ValueBase, + spinButtonProps: DOMAttributes, incrementButtonProps: AriaButtonProps, decrementButtonProps: AriaButtonProps } diff --git a/packages/@react-aria/ssr/src/index.ts b/packages/@react-aria/ssr/src/index.ts index 41418afe9b8..afda2e4bfbf 100644 --- a/packages/@react-aria/ssr/src/index.ts +++ b/packages/@react-aria/ssr/src/index.ts @@ -9,5 +9,4 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './SSRProvider'; +export {SSRProvider, useSSRSafeId, useIsSSR} from './SSRProvider'; diff --git a/packages/@react-aria/switch/src/index.ts b/packages/@react-aria/switch/src/index.ts index b3fa5e93ded..5fe15eac69e 100644 --- a/packages/@react-aria/switch/src/index.ts +++ b/packages/@react-aria/switch/src/index.ts @@ -9,5 +9,5 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './useSwitch'; +export type {SwitchAria} from './useSwitch'; +export {useSwitch} from './useSwitch'; diff --git a/packages/@react-aria/table/src/index.ts b/packages/@react-aria/table/src/index.ts index c3e37cd4bab..84663de7da8 100644 --- a/packages/@react-aria/table/src/index.ts +++ b/packages/@react-aria/table/src/index.ts @@ -10,13 +10,15 @@ * governing permissions and limitations under the License. */ -export * from './useTable'; -export * from './useTableColumnHeader'; -export * from './useTableRow'; -export * from './useTableHeaderRow'; -export * from './useTableCell'; -export * from './useTableSelectionCheckbox'; -export * from './useTableColumnResize'; +export type {TableHeaderRowAria} from './useTableHeaderRow'; + +export {useTable} from './useTable'; +export {useTableColumnHeader} from './useTableColumnHeader'; +export {useTableRow} from './useTableRow'; +export {useTableHeaderRow} from './useTableHeaderRow'; +export {useTableCell} from './useTableCell'; +export {useTableSelectionCheckbox, useTableSelectAllCheckbox} from './useTableSelectionCheckbox'; +export {useTableColumnResize} from './useTableColumnResize'; // Workaround for a Parcel bug where re-exports don't work in the CommonJS output format... // export {useGridRowGroup as useTableRowGroup} from '@react-aria/grid'; diff --git a/packages/@react-aria/table/src/useTableCell.ts b/packages/@react-aria/table/src/useTableCell.ts index e983ec9f94a..8ba0285cca2 100644 --- a/packages/@react-aria/table/src/useTableCell.ts +++ b/packages/@react-aria/table/src/useTableCell.ts @@ -10,9 +10,10 @@ * governing permissions and limitations under the License. */ +import {DOMAttributes, FocusableElement} from '@react-types/shared'; import {getCellId} from './utils'; import {GridNode} from '@react-types/grid'; -import {HTMLAttributes, RefObject} from 'react'; +import {RefObject} from 'react'; import {TableState} from '@react-stately/table'; import {useGridCell} from '@react-aria/grid'; @@ -31,7 +32,7 @@ interface TableCellProps { interface TableCellAria { /** Props for the table cell element. */ - gridCellProps: HTMLAttributes, + gridCellProps: DOMAttributes, /** Whether the cell is currently in a pressed state. */ isPressed: boolean } @@ -42,7 +43,7 @@ interface TableCellAria { * @param state - State of the table, as returned by `useTableState`. * @param ref - The ref attached to the cell element. */ -export function useTableCell(props: TableCellProps, state: TableState, ref: RefObject): TableCellAria { +export function useTableCell(props: TableCellProps, state: TableState, ref: RefObject): TableCellAria { let {gridCellProps, isPressed} = useGridCell(props, state, ref); let columnKey = props.node.column.key; diff --git a/packages/@react-aria/table/src/useTableColumnHeader.ts b/packages/@react-aria/table/src/useTableColumnHeader.ts index 5f99c001f30..620d1037b20 100644 --- a/packages/@react-aria/table/src/useTableColumnHeader.ts +++ b/packages/@react-aria/table/src/useTableColumnHeader.ts @@ -10,12 +10,13 @@ * governing permissions and limitations under the License. */ +import {DOMAttributes, FocusableElement} from '@react-types/shared'; import {getColumnHeaderId} from './utils'; import {GridNode} from '@react-types/grid'; -import {HTMLAttributes, RefObject} from 'react'; // @ts-ignore import intlMessages from '../intl/*.json'; import {isAndroid, mergeProps, useDescription} from '@react-aria/utils'; +import {RefObject} from 'react'; import {TableState} from '@react-stately/table'; import {useFocusable} from '@react-aria/focus'; import {useGridCell} from '@react-aria/grid'; @@ -31,7 +32,7 @@ interface ColumnHeaderProps { interface ColumnHeaderAria { /** Props for the [column header](https://www.w3.org/TR/wai-aria-1.1/#columnheader) element. */ - columnHeaderProps: HTMLAttributes + columnHeaderProps: DOMAttributes } /** @@ -40,7 +41,7 @@ interface ColumnHeaderAria { * @param state - State of the table, as returned by `useTableState`. * @param ref - The ref attached to the column header element. */ -export function useTableColumnHeader(props: ColumnHeaderProps, state: TableState, ref: RefObject): ColumnHeaderAria { +export function useTableColumnHeader(props: ColumnHeaderProps, state: TableState, ref: RefObject): ColumnHeaderAria { let {node} = props; let allowsResizing = node.props.allowsResizing; let allowsSorting = node.props.allowsSorting; @@ -61,7 +62,7 @@ export function useTableColumnHeader(props: ColumnHeaderProps, state: TableSt // Needed to pick up the focusable context, enabling things like Tooltips for example let {focusableProps} = useFocusable({}, ref); - let ariaSort: HTMLAttributes['aria-sort'] = null; + let ariaSort: DOMAttributes['aria-sort'] = null; let isSortedColumn = state.sortDescriptor?.column === node.key; let sortDirection = state.sortDescriptor?.direction; // aria-sort not supported in Android Talkback diff --git a/packages/@react-aria/table/src/useTableColumnResize.ts b/packages/@react-aria/table/src/useTableColumnResize.ts index 71e5867126f..64b1dd574f9 100644 --- a/packages/@react-aria/table/src/useTableColumnResize.ts +++ b/packages/@react-aria/table/src/useTableColumnResize.ts @@ -11,15 +11,16 @@ */ import {ColumnResizeState, TableState} from '@react-stately/table'; +import {DOMAttributes} from '@react-types/shared'; import {focusSafely} from '@react-aria/focus'; import {GridNode} from '@react-types/grid'; -import {HTMLAttributes, RefObject, useRef} from 'react'; import {mergeProps} from '@react-aria/utils'; +import {RefObject, useRef} from 'react'; import {useKeyboard, useMove} from '@react-aria/interactions'; import {useLocale} from '@react-aria/i18n'; interface ResizerAria { - resizerProps: HTMLAttributes + resizerProps: DOMAttributes } interface ResizerProps { diff --git a/packages/@react-aria/table/src/useTableHeaderRow.ts b/packages/@react-aria/table/src/useTableHeaderRow.ts index 11cd548fc57..1643b640f29 100644 --- a/packages/@react-aria/table/src/useTableHeaderRow.ts +++ b/packages/@react-aria/table/src/useTableHeaderRow.ts @@ -10,13 +10,14 @@ * governing permissions and limitations under the License. */ +import {DOMAttributes} from '@react-types/shared'; import {GridRowProps} from '@react-aria/grid'; -import {HTMLAttributes, RefObject} from 'react'; +import {RefObject} from 'react'; import {TableState} from '@react-stately/table'; export interface TableHeaderRowAria { /** Props for the grid row element. */ - rowProps: HTMLAttributes + rowProps: DOMAttributes } /** @@ -25,7 +26,7 @@ export interface TableHeaderRowAria { * @param state - State of the table, as returned by `useTableState`. */ // eslint-disable-next-line @typescript-eslint/no-unused-vars -export function useTableHeaderRow(props: GridRowProps, state: TableState, ref: RefObject): TableHeaderRowAria { +export function useTableHeaderRow(props: GridRowProps, state: TableState, ref: RefObject): TableHeaderRowAria { let {node, isVirtualized} = props; let rowProps = { role: 'row' diff --git a/packages/@react-aria/table/src/useTableRow.ts b/packages/@react-aria/table/src/useTableRow.ts index 95f6ec8741d..131d4856aca 100644 --- a/packages/@react-aria/table/src/useTableRow.ts +++ b/packages/@react-aria/table/src/useTableRow.ts @@ -10,6 +10,7 @@ * governing permissions and limitations under the License. */ +import {FocusableElement} from '@react-types/shared'; import {getRowLabelledBy} from './utils'; import {GridRowAria, GridRowProps, useGridRow} from '@react-aria/grid'; import {RefObject} from 'react'; @@ -21,7 +22,7 @@ import {TableState} from '@react-stately/table'; * @param props - Props for the row. * @param state - State of the table, as returned by `useTableState`. */ -export function useTableRow(props: GridRowProps, state: TableState, ref: RefObject): GridRowAria { +export function useTableRow(props: GridRowProps, state: TableState, ref: RefObject): GridRowAria { let {node} = props; let {rowProps, isPressed} = useGridRow, TableState>(props, state, ref); return { diff --git a/packages/@react-aria/tabs/src/index.ts b/packages/@react-aria/tabs/src/index.ts index 6908022969f..80483fefad6 100644 --- a/packages/@react-aria/tabs/src/index.ts +++ b/packages/@react-aria/tabs/src/index.ts @@ -9,7 +9,6 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './useTab'; -export * from './useTabPanel'; -export * from './useTabList'; +export {useTab} from './useTab'; +export {useTabPanel} from './useTabPanel'; +export {useTabList} from './useTabList'; diff --git a/packages/@react-aria/tabs/src/useTab.ts b/packages/@react-aria/tabs/src/useTab.ts index 6d031877724..ad517fd695d 100644 --- a/packages/@react-aria/tabs/src/useTab.ts +++ b/packages/@react-aria/tabs/src/useTab.ts @@ -11,14 +11,15 @@ */ import {AriaTabProps} from '@react-types/tabs'; +import {DOMAttributes, FocusableElement} from '@react-types/shared'; import {generateId} from './utils'; -import {HTMLAttributes, RefObject} from 'react'; +import {RefObject} from 'react'; import {TabListState} from '@react-stately/tabs'; import {useSelectableItem} from '@react-aria/selection'; interface TabAria { /** Props for the tab element. */ - tabProps: HTMLAttributes, + tabProps: DOMAttributes, /** Whether the tab is currently selected. */ isSelected: boolean, /** Whether the tab is disabled. */ @@ -32,7 +33,7 @@ interface TabAria { export function useTab( props: AriaTabProps, state: TabListState, - ref: RefObject + ref: RefObject ): TabAria { let {key, isDisabled: propsDisabled} = props; let {selectionManager: manager, selectedKey} = state; diff --git a/packages/@react-aria/tabs/src/useTabList.ts b/packages/@react-aria/tabs/src/useTabList.ts index cdb721cd50d..d987c48d17a 100644 --- a/packages/@react-aria/tabs/src/useTabList.ts +++ b/packages/@react-aria/tabs/src/useTabList.ts @@ -11,8 +11,9 @@ */ import {AriaTabListProps} from '@react-types/tabs'; -import {HTMLAttributes, RefObject, useMemo} from 'react'; +import {DOMAttributes} from '@react-types/shared'; import {mergeProps, useId, useLabels} from '@react-aria/utils'; +import {RefObject, useMemo} from 'react'; import {TabListState} from '@react-stately/tabs'; import {tabsIds} from './utils'; import {TabsKeyboardDelegate} from './TabsKeyboardDelegate'; @@ -21,7 +22,7 @@ import {useSelectableCollection} from '@react-aria/selection'; interface TabListAria { /** Props for the tablist container. */ - tabListProps: HTMLAttributes + tabListProps: DOMAttributes } diff --git a/packages/@react-aria/tabs/src/useTabPanel.ts b/packages/@react-aria/tabs/src/useTabPanel.ts index 9bb6f54bc1e..c93c270c6de 100644 --- a/packages/@react-aria/tabs/src/useTabPanel.ts +++ b/packages/@react-aria/tabs/src/useTabPanel.ts @@ -11,15 +11,16 @@ */ import {AriaTabPanelProps} from '@react-types/tabs'; +import {DOMAttributes} from '@react-types/shared'; import {generateId} from './utils'; import {getFocusableTreeWalker} from '@react-aria/focus'; -import {HTMLAttributes, RefObject, useState} from 'react'; import {mergeProps, useLabels, useLayoutEffect} from '@react-aria/utils'; +import {RefObject, useState} from 'react'; import {TabListState} from '@react-stately/tabs'; interface TabPanelAria { /** Props for the tab panel element. */ - tabPanelProps: HTMLAttributes + tabPanelProps: DOMAttributes } @@ -27,7 +28,7 @@ interface TabPanelAria { * Provides the behavior and accessibility implementation for a tab panel. A tab panel is a container for * the contents of a tab, and is shown when the tab is selected. */ -export function useTabPanel(props: AriaTabPanelProps, state: TabListState, ref: RefObject): TabPanelAria { +export function useTabPanel(props: AriaTabPanelProps, state: TabListState, ref: RefObject): TabPanelAria { let [tabIndex, setTabIndex] = useState(0); // The tabpanel should have tabIndex=0 when there are no tabbable elements within it. diff --git a/packages/@react-aria/tag/src/index.ts b/packages/@react-aria/tag/src/index.ts index 72df016301f..6901984cb23 100644 --- a/packages/@react-aria/tag/src/index.ts +++ b/packages/@react-aria/tag/src/index.ts @@ -10,6 +10,7 @@ * governing permissions and limitations under the License. */ -export * from './TagKeyboardDelegate'; -export * from './useTag'; -export * from './useTagGroup'; +export type {TagAria} from './useTag'; +export {TagKeyboardDelegate} from './TagKeyboardDelegate'; +export {useTag} from './useTag'; +export {useTagGroup} from './useTagGroup'; diff --git a/packages/@react-aria/tag/src/useTag.ts b/packages/@react-aria/tag/src/useTag.ts index 51ad6aa4b54..92da1270860 100644 --- a/packages/@react-aria/tag/src/useTag.ts +++ b/packages/@react-aria/tag/src/useTag.ts @@ -10,7 +10,8 @@ * governing permissions and limitations under the License. */ -import {ButtonHTMLAttributes, HTMLAttributes, KeyboardEvent} from 'react'; +import {ButtonHTMLAttributes, KeyboardEvent} from 'react'; +import {DOMAttributes} from '@react-types/shared'; import {filterDOMProps, mergeProps, useId} from '@react-aria/utils'; import {GridState} from '@react-stately/grid'; // @ts-ignore @@ -21,9 +22,9 @@ import {useMessageFormatter} from '@react-aria/i18n'; export interface TagAria { - labelProps: HTMLAttributes, - tagProps: HTMLAttributes, - tagRowProps: HTMLAttributes, + labelProps: DOMAttributes, + tagProps: DOMAttributes, + tagRowProps: DOMAttributes, clearButtonProps: ButtonHTMLAttributes } @@ -55,7 +56,7 @@ export function useTag(props: TagProps, state: GridState): TagAri focusMode: 'cell' }, state, tagRef); - function onKeyDown(e: KeyboardEvent) { + function onKeyDown(e: KeyboardEvent) { if (e.key === 'Delete' || e.key === 'Backspace' || e.key === ' ') { onRemove(children, e); e.preventDefault(); diff --git a/packages/@react-aria/tag/src/useTagGroup.ts b/packages/@react-aria/tag/src/useTagGroup.ts index 2c0d95a82a6..3dc60df42e9 100644 --- a/packages/@react-aria/tag/src/useTagGroup.ts +++ b/packages/@react-aria/tag/src/useTagGroup.ts @@ -10,9 +10,9 @@ * governing permissions and limitations under the License. */ -import {DOMProps} from '@react-types/shared'; +import {DOMAttributes, DOMProps} from '@react-types/shared'; import {filterDOMProps, mergeProps} from '@react-aria/utils'; -import {HTMLAttributes, Key, ReactNode, useState} from 'react'; +import {Key, ReactNode, useState} from 'react'; import {useFocusWithin} from '@react-aria/interactions'; interface AriaTagGroupProps extends DOMProps { @@ -24,7 +24,7 @@ interface AriaTagGroupProps extends DOMProps { } interface TagGroupAria { - tagGroupProps: HTMLAttributes + tagGroupProps: DOMAttributes } export function useTagGroup(props: AriaTagGroupProps, listState): TagGroupAria { @@ -45,6 +45,6 @@ export function useTagGroup(props: AriaTagGroupProps, listState): TagGroupAria { 'aria-live': isFocusWithin ? 'polite' : 'off', 'aria-disabled': isDisabled === true, ...focusWithinProps - } as HTMLAttributes) + } as DOMAttributes) }; } diff --git a/packages/@react-aria/textfield/src/index.ts b/packages/@react-aria/textfield/src/index.ts index f6747bee547..06a30934d2c 100644 --- a/packages/@react-aria/textfield/src/index.ts +++ b/packages/@react-aria/textfield/src/index.ts @@ -9,6 +9,6 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './useTextField'; -export * from './useFormattedTextField'; +export type {TextFieldAria} from './useTextField'; +export {useTextField} from './useTextField'; +export {useFormattedTextField} from './useFormattedTextField'; diff --git a/packages/@react-aria/textfield/src/useTextField.ts b/packages/@react-aria/textfield/src/useTextField.ts index 0b8ee4349dc..a811278c412 100644 --- a/packages/@react-aria/textfield/src/useTextField.ts +++ b/packages/@react-aria/textfield/src/useTextField.ts @@ -15,10 +15,10 @@ import { ChangeEvent, DOMFactory, HTMLAttributes, - LabelHTMLAttributes, ReactDOM, RefObject } from 'react'; +import {DOMAttributes} from '@react-types/shared'; import {filterDOMProps, mergeProps} from '@react-aria/utils'; import {useField} from '@react-aria/label'; import {useFocusable} from '@react-aria/focus'; @@ -88,11 +88,11 @@ export interface TextFieldAria, /** Props for the text field's visible label element, if any. */ - labelProps: LabelHTMLAttributes, + labelProps: DOMAttributes, /** Props for the text field's description element, if any. */ - descriptionProps: HTMLAttributes, + descriptionProps: DOMAttributes, /** Props for the text field's error message element, if any. */ - errorMessageProps: HTMLAttributes + errorMessageProps: DOMAttributes } /** diff --git a/packages/@react-aria/toast/src/index.ts b/packages/@react-aria/toast/src/index.ts index 059d90d7223..44d86aedae1 100644 --- a/packages/@react-aria/toast/src/index.ts +++ b/packages/@react-aria/toast/src/index.ts @@ -9,5 +9,4 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './useToast'; +export {useToast} from './useToast'; diff --git a/packages/@react-aria/toast/src/useToast.ts b/packages/@react-aria/toast/src/useToast.ts index db10bcd052e..c68ca464322 100644 --- a/packages/@react-aria/toast/src/useToast.ts +++ b/packages/@react-aria/toast/src/useToast.ts @@ -11,8 +11,8 @@ */ import {chain, filterDOMProps, mergeProps} from '@react-aria/utils'; -import {DOMProps} from '@react-types/shared'; -import {HTMLAttributes, ImgHTMLAttributes} from 'react'; +import {DOMAttributes, DOMProps} from '@react-types/shared'; +import {ImgHTMLAttributes} from 'react'; // @ts-ignore import intlMessages from '../intl/*.json'; import {PressProps} from '@react-aria/interactions'; @@ -23,7 +23,7 @@ import {useMessageFormatter} from '@react-aria/i18n'; interface ToastAriaProps extends ToastProps {} interface ToastAria { - toastProps: HTMLAttributes, + toastProps: DOMAttributes, iconProps: ImgHTMLAttributes, actionButtonProps: PressProps, closeButtonProps: DOMProps & PressProps diff --git a/packages/@react-aria/toggle/src/index.ts b/packages/@react-aria/toggle/src/index.ts index e18c433d812..80ca2c0dbc9 100644 --- a/packages/@react-aria/toggle/src/index.ts +++ b/packages/@react-aria/toggle/src/index.ts @@ -9,5 +9,5 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './useToggle'; +export type {ToggleAria} from './useToggle'; +export {useToggle} from './useToggle'; diff --git a/packages/@react-aria/toggle/src/useToggle.ts b/packages/@react-aria/toggle/src/useToggle.ts index 1514856896b..40f1d08760d 100644 --- a/packages/@react-aria/toggle/src/useToggle.ts +++ b/packages/@react-aria/toggle/src/useToggle.ts @@ -27,7 +27,7 @@ export interface ToggleAria { /** * Handles interactions for toggle elements, e.g. Checkboxes and Switches. */ -export function useToggle(props: AriaToggleProps, state: ToggleState, ref: RefObject): ToggleAria { +export function useToggle(props: AriaToggleProps, state: ToggleState, ref: RefObject): ToggleAria { let { isDisabled = false, isRequired, diff --git a/packages/@react-aria/tooltip/src/index.ts b/packages/@react-aria/tooltip/src/index.ts index dba2c3740ee..6ff420543ef 100644 --- a/packages/@react-aria/tooltip/src/index.ts +++ b/packages/@react-aria/tooltip/src/index.ts @@ -9,6 +9,5 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './useTooltip'; -export * from './useTooltipTrigger'; +export {useTooltip} from './useTooltip'; +export {useTooltipTrigger} from './useTooltipTrigger'; diff --git a/packages/@react-aria/tooltip/src/useTooltip.ts b/packages/@react-aria/tooltip/src/useTooltip.ts index 723685a630b..00f631906a6 100644 --- a/packages/@react-aria/tooltip/src/useTooltip.ts +++ b/packages/@react-aria/tooltip/src/useTooltip.ts @@ -11,8 +11,8 @@ */ import {AriaTooltipProps} from '@react-types/tooltip'; +import {DOMAttributes} from '@react-types/shared'; import {filterDOMProps, mergeProps} from '@react-aria/utils'; -import {HTMLAttributes} from 'react'; import {TooltipTriggerState} from '@react-stately/tooltip'; import {useHover} from '@react-aria/interactions'; @@ -20,7 +20,7 @@ interface TooltipAria { /** * Props for the tooltip element. */ - tooltipProps: HTMLAttributes + tooltipProps: DOMAttributes } /** diff --git a/packages/@react-aria/tooltip/src/useTooltipTrigger.ts b/packages/@react-aria/tooltip/src/useTooltipTrigger.ts index e9e6e4769bb..a431f15f945 100644 --- a/packages/@react-aria/tooltip/src/useTooltipTrigger.ts +++ b/packages/@react-aria/tooltip/src/useTooltipTrigger.ts @@ -10,10 +10,10 @@ * governing permissions and limitations under the License. */ -import {FocusEvents} from '@react-types/shared'; +import {DOMAttributes, FocusableElement, FocusEvents} from '@react-types/shared'; import {getInteractionModality, HoverProps, isFocusVisible, PressProps, usePress} from '@react-aria/interactions'; -import {HTMLAttributes, RefObject, useEffect, useRef} from 'react'; import {mergeProps, useId} from '@react-aria/utils'; +import {RefObject, useEffect, useRef} from 'react'; import {TooltipTriggerProps} from '@react-types/tooltip'; import {TooltipTriggerState} from '@react-stately/tooltip'; import {useFocusable} from '@react-aria/focus'; @@ -23,19 +23,19 @@ interface TooltipTriggerAria { /** * Props for the trigger element. */ - triggerProps: HTMLAttributes & PressProps & HoverProps & FocusEvents, + triggerProps: DOMAttributes & PressProps & HoverProps & FocusEvents, /** * Props for the overlay container element. */ - tooltipProps: HTMLAttributes + tooltipProps: DOMAttributes } /** * Provides the behavior and accessibility implementation for a tooltip trigger, e.g. a button * that shows a description when focused or hovered. */ -export function useTooltipTrigger(props: TooltipTriggerProps, state: TooltipTriggerState, ref: RefObject) : TooltipTriggerAria { +export function useTooltipTrigger(props: TooltipTriggerProps, state: TooltipTriggerState, ref: RefObject) : TooltipTriggerAria { let { isDisabled, trigger diff --git a/packages/@react-aria/tree/src/index.ts b/packages/@react-aria/tree/src/index.ts index d761ef08f77..7d2588ee10b 100644 --- a/packages/@react-aria/tree/src/index.ts +++ b/packages/@react-aria/tree/src/index.ts @@ -9,5 +9,4 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './useTree'; +export {useTree} from './useTree'; diff --git a/packages/@react-aria/utils/src/filterDOMProps.ts b/packages/@react-aria/utils/src/filterDOMProps.ts index c6cc9d422b3..18f57179df3 100644 --- a/packages/@react-aria/utils/src/filterDOMProps.ts +++ b/packages/@react-aria/utils/src/filterDOMProps.ts @@ -11,7 +11,6 @@ */ import {AriaLabelingProps, DOMProps} from '@react-types/shared'; -import {HTMLAttributes} from 'react'; const DOMPropNames = new Set([ 'id' @@ -44,7 +43,7 @@ const propRe = /^(data-.*)$/; */ export function filterDOMProps(props: DOMProps & AriaLabelingProps, opts: Options = {}): DOMProps & AriaLabelingProps { let {labelable, propNames} = opts; - let filteredProps: HTMLAttributes = {}; + let filteredProps = {}; for (const prop in props) { if ( diff --git a/packages/@react-aria/utils/src/focusWithoutScrolling.ts b/packages/@react-aria/utils/src/focusWithoutScrolling.ts index 0ad65853c26..24eb7e8c3f9 100644 --- a/packages/@react-aria/utils/src/focusWithoutScrolling.ts +++ b/packages/@react-aria/utils/src/focusWithoutScrolling.ts @@ -10,6 +10,8 @@ * governing permissions and limitations under the License. */ +import {FocusableElement} from '@react-types/shared'; + // This is a polyfill for element.focus({preventScroll: true}); // Currently necessary for Safari and old Edge: // https://caniuse.com/#feat=mdn-api_htmlelement_focus_preventscroll_option @@ -26,7 +28,7 @@ interface ScrollableElement { scrollLeft: number } -export function focusWithoutScrolling(element: HTMLElement) { +export function focusWithoutScrolling(element: FocusableElement) { if (supportsPreventScroll()) { element.focus({preventScroll: true}); } else { @@ -56,7 +58,7 @@ function supportsPreventScroll() { return supportsPreventScrollCached; } -function getScrollableElements(element: HTMLElement): ScrollableElement[] { +function getScrollableElements(element: FocusableElement): ScrollableElement[] { var parent = element.parentNode; var scrollableElements: ScrollableElement[] = []; var rootScrollingElement = document.scrollingElement || document.documentElement; diff --git a/packages/@react-aria/utils/src/index.ts b/packages/@react-aria/utils/src/index.ts index 08f94b53f11..f3401686391 100644 --- a/packages/@react-aria/utils/src/index.ts +++ b/packages/@react-aria/utils/src/index.ts @@ -9,28 +9,27 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './useId'; -export * from './chain'; -export * from './mergeProps'; -export * from './mergeRefs'; -export * from './filterDOMProps'; -export * from './focusWithoutScrolling'; -export * from './getOffset'; -export * from './number'; -export * from './runAfterTransition'; -export * from './useDrag1D'; -export * from './useGlobalListeners'; -export * from './useLabels'; -export * from './useObjectRef'; -export * from './useUpdateEffect'; -export * from './useLayoutEffect'; -export * from './useResizeObserver'; -export * from './useSyncRef'; -export * from './getScrollParent'; -export * from './useViewportSize'; -export * from './useDescription'; -export * from './platform'; -export * from './useEvent'; -export * from './useValueEffect'; -export * from './scrollIntoView'; +export {useId, mergeIds, useSlotId} from './useId'; +export {chain} from './chain'; +export {mergeProps} from './mergeProps'; +export {mergeRefs} from './mergeRefs'; +export {filterDOMProps} from './filterDOMProps'; +export {focusWithoutScrolling} from './focusWithoutScrolling'; +export {getOffset} from './getOffset'; +export {runAfterTransition} from './runAfterTransition'; +export {useDrag1D} from './useDrag1D'; +export {useGlobalListeners} from './useGlobalListeners'; +export {useLabels} from './useLabels'; +export {useObjectRef} from './useObjectRef'; +export {useUpdateEffect} from './useUpdateEffect'; +export {useLayoutEffect} from './useLayoutEffect'; +export {useResizeObserver} from './useResizeObserver'; +export {useSyncRef} from './useSyncRef'; +export {getScrollParent} from './getScrollParent'; +export {useViewportSize} from './useViewportSize'; +export {useDescription} from './useDescription'; +export {isMac, isIPhone, isIPad, isIOS, isAppleDevice, isWebKit, isChrome, isAndroid} from './platform'; +export {useEvent} from './useEvent'; +export {useValueEffect} from './useValueEffect'; +export {scrollIntoView} from './scrollIntoView'; +export {clamp, snapValueToStep} from '@react-stately/utils'; diff --git a/packages/@react-aria/utils/src/number.ts b/packages/@react-aria/utils/src/number.ts deleted file mode 100644 index 4fa0560110b..00000000000 --- a/packages/@react-aria/utils/src/number.ts +++ /dev/null @@ -1,13 +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. - */ - -export {clamp, snapValueToStep} from '@react-stately/utils'; diff --git a/packages/@react-aria/utils/src/platform.ts b/packages/@react-aria/utils/src/platform.ts index 4ca8d7d900f..d21f1d92c3a 100644 --- a/packages/@react-aria/utils/src/platform.ts +++ b/packages/@react-aria/utils/src/platform.ts @@ -22,7 +22,7 @@ function testUserAgent(re: RegExp) { function testPlatform(re: RegExp) { return typeof window !== 'undefined' && window.navigator != null - ? re.test((window.navigator['userAgentData'] || window.navigator).platform) + ? re.test(window.navigator['userAgentData']?.platform || window.navigator.platform) : false; } diff --git a/packages/@react-aria/utils/src/useDescription.ts b/packages/@react-aria/utils/src/useDescription.ts index a0983c809a5..c22ff1bc1ff 100644 --- a/packages/@react-aria/utils/src/useDescription.ts +++ b/packages/@react-aria/utils/src/useDescription.ts @@ -15,7 +15,7 @@ import {useLayoutEffect} from './useLayoutEffect'; import {useState} from 'react'; let descriptionId = 0; -const descriptionNodes = new Map(); +const descriptionNodes = new Map(); export function useDescription(description: string): AriaLabelingProps { let [id, setId] = useState(null); diff --git a/packages/@react-aria/utils/src/useResizeObserver.ts b/packages/@react-aria/utils/src/useResizeObserver.ts index 4b24ab6d2a2..075dc88f76e 100644 --- a/packages/@react-aria/utils/src/useResizeObserver.ts +++ b/packages/@react-aria/utils/src/useResizeObserver.ts @@ -9,7 +9,7 @@ type useResizeObserverOptionsType = { onResize: () => void } -export function useResizeObserver(options: useResizeObserverOptionsType) { +export function useResizeObserver(options: useResizeObserverOptionsType) { const {ref, onResize} = options; useEffect(() => { diff --git a/packages/@react-aria/virtualizer/src/index.ts b/packages/@react-aria/virtualizer/src/index.ts index a10aa6f1a4a..f02be4c1492 100644 --- a/packages/@react-aria/virtualizer/src/index.ts +++ b/packages/@react-aria/virtualizer/src/index.ts @@ -10,8 +10,9 @@ * governing permissions and limitations under the License. */ -export * from './Virtualizer'; -export * from './useVirtualizerItem'; -export * from './VirtualizerItem'; -export * from './ScrollView'; -export * from './utils'; +export type {RTLOffsetType} from './utils'; +export {useVirtualizer, Virtualizer} from './Virtualizer'; +export {useVirtualizerItem} from './useVirtualizerItem'; +export {VirtualizerItem, layoutInfoToStyle} from './VirtualizerItem'; +export {ScrollView} from './ScrollView'; +export {getRTLOffsetType, getScrollLeft, setScrollLeft} from './utils'; diff --git a/packages/@react-aria/virtualizer/src/utils.ts b/packages/@react-aria/virtualizer/src/utils.ts index 835afffe5cd..0a1659da7d5 100644 --- a/packages/@react-aria/virtualizer/src/utils.ts +++ b/packages/@react-aria/virtualizer/src/utils.ts @@ -67,7 +67,7 @@ export function getRTLOffsetType(recalculate: boolean = false): RTLOffsetType { return cachedRTLResult; } -export function getScrollLeft(node: HTMLElement, direction: Direction): number { +export function getScrollLeft(node: Element, direction: Direction): number { let {scrollLeft} = node; // scrollLeft in rtl locales differs across browsers, so normalize. @@ -87,7 +87,7 @@ export function getScrollLeft(node: HTMLElement, direction: Direction): number { return scrollLeft; } -export function setScrollLeft(node: HTMLElement, direction: Direction, scrollLeft: number) { +export function setScrollLeft(node: Element, direction: Direction, scrollLeft: number) { if (direction === 'rtl') { switch (getRTLOffsetType()) { case 'negative': diff --git a/packages/@react-aria/visually-hidden/package.json b/packages/@react-aria/visually-hidden/package.json index 78188a93f26..42e60bb9ef4 100644 --- a/packages/@react-aria/visually-hidden/package.json +++ b/packages/@react-aria/visually-hidden/package.json @@ -22,6 +22,7 @@ "@babel/runtime": "^7.6.2", "@react-aria/interactions": "^3.9.1", "@react-aria/utils": "^3.13.1", + "@react-types/shared": "^3.13.1", "clsx": "^1.1.1" }, "peerDependencies": { diff --git a/packages/@react-aria/visually-hidden/src/VisuallyHidden.tsx b/packages/@react-aria/visually-hidden/src/VisuallyHidden.tsx index 50f5f04f066..ef4f40c0c22 100644 --- a/packages/@react-aria/visually-hidden/src/VisuallyHidden.tsx +++ b/packages/@react-aria/visually-hidden/src/VisuallyHidden.tsx @@ -10,11 +10,12 @@ * governing permissions and limitations under the License. */ +import {DOMAttributes} from '@react-types/shared'; import {mergeProps} from '@react-aria/utils'; -import React, {CSSProperties, HTMLAttributes, JSXElementConstructor, ReactNode, useMemo, useState} from 'react'; +import React, {CSSProperties, JSXElementConstructor, ReactNode, useMemo, useState} from 'react'; import {useFocus} from '@react-aria/interactions'; -interface VisuallyHiddenProps extends HTMLAttributes { +interface VisuallyHiddenProps extends DOMAttributes { /** The content to visually hide. */ children?: ReactNode, @@ -42,7 +43,7 @@ const styles: CSSProperties = { }; interface VisuallyHiddenAria { - visuallyHiddenProps: HTMLAttributes + visuallyHiddenProps: DOMAttributes } /** diff --git a/packages/@react-aria/visually-hidden/src/index.ts b/packages/@react-aria/visually-hidden/src/index.ts index df2e9e0727f..16e3f56551b 100644 --- a/packages/@react-aria/visually-hidden/src/index.ts +++ b/packages/@react-aria/visually-hidden/src/index.ts @@ -9,5 +9,4 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - -export * from './VisuallyHidden'; +export {useVisuallyHidden, VisuallyHidden} from './VisuallyHidden'; diff --git a/packages/@react-spectrum/accordion/src/index.ts b/packages/@react-spectrum/accordion/src/index.ts index 4c4d24fd628..f3bb4e0c327 100644 --- a/packages/@react-spectrum/accordion/src/index.ts +++ b/packages/@react-spectrum/accordion/src/index.ts @@ -9,8 +9,6 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - /// - -export * from './Accordion'; +export {Accordion} from './Accordion'; export {Item} from '@react-stately/collections'; diff --git a/packages/@react-spectrum/actionbar/src/index.ts b/packages/@react-spectrum/actionbar/src/index.ts index 1a173b44cdb..a5f89d69bee 100644 --- a/packages/@react-spectrum/actionbar/src/index.ts +++ b/packages/@react-spectrum/actionbar/src/index.ts @@ -9,9 +9,7 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - /// - -export * from './ActionBar'; -export * from './ActionBarContainer'; +export {ActionBar} from './ActionBar'; +export {ActionBarContainer} from './ActionBarContainer'; export {Item} from '@react-stately/collections'; diff --git a/packages/@react-spectrum/actiongroup/src/index.ts b/packages/@react-spectrum/actiongroup/src/index.ts index ba6d3453c88..5f9ea61470d 100644 --- a/packages/@react-spectrum/actiongroup/src/index.ts +++ b/packages/@react-spectrum/actiongroup/src/index.ts @@ -9,8 +9,6 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - /// - -export * from './ActionGroup'; +export {ActionGroup} from './ActionGroup'; export {Item} from '@react-stately/collections'; diff --git a/packages/@react-spectrum/alert/src/index.ts b/packages/@react-spectrum/alert/src/index.ts index 068754d2c1f..5f8d6a49afe 100644 --- a/packages/@react-spectrum/alert/src/index.ts +++ b/packages/@react-spectrum/alert/src/index.ts @@ -9,7 +9,5 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - /// - -export * from './Alert'; +export {Alert} from './Alert'; diff --git a/packages/@react-spectrum/autocomplete/src/index.ts b/packages/@react-spectrum/autocomplete/src/index.ts index ec254814ea5..58d1d994877 100644 --- a/packages/@react-spectrum/autocomplete/src/index.ts +++ b/packages/@react-spectrum/autocomplete/src/index.ts @@ -9,8 +9,6 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - /// - -export * from './SearchAutocomplete'; +export {SearchAutocomplete} from './SearchAutocomplete'; export {Item, Section} from '@react-stately/collections'; diff --git a/packages/@react-spectrum/avatar/src/index.ts b/packages/@react-spectrum/avatar/src/index.ts index c612f78a480..117caabd4f6 100644 --- a/packages/@react-spectrum/avatar/src/index.ts +++ b/packages/@react-spectrum/avatar/src/index.ts @@ -9,7 +9,5 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - /// - -export * from './Avatar'; +export {Avatar} from './Avatar'; diff --git a/packages/@react-spectrum/breadcrumbs/src/index.ts b/packages/@react-spectrum/breadcrumbs/src/index.ts index 6ae972d8e71..7777bfe6e46 100644 --- a/packages/@react-spectrum/breadcrumbs/src/index.ts +++ b/packages/@react-spectrum/breadcrumbs/src/index.ts @@ -9,8 +9,6 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - /// - -export * from './Breadcrumbs'; +export {Breadcrumbs} from './Breadcrumbs'; export {Item} from '@react-stately/collections'; diff --git a/packages/@react-spectrum/button/src/index.ts b/packages/@react-spectrum/button/src/index.ts index 06995cc7ceb..379e47a16e2 100644 --- a/packages/@react-spectrum/button/src/index.ts +++ b/packages/@react-spectrum/button/src/index.ts @@ -9,12 +9,10 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - /// - -export * from './Button'; -export * from './ActionButton'; -export * from './FieldButton'; -export * from './LogicButton'; -export * from './ClearButton'; -export * from './ToggleButton'; +export {Button} from './Button'; +export {ActionButton} from './ActionButton'; +export {FieldButton} from './FieldButton'; +export {LogicButton} from './LogicButton'; +export {ClearButton} from './ClearButton'; +export {ToggleButton} from './ToggleButton'; diff --git a/packages/@react-spectrum/buttongroup/src/index.ts b/packages/@react-spectrum/buttongroup/src/index.ts index f72fb1ed6c2..d7c155f584c 100644 --- a/packages/@react-spectrum/buttongroup/src/index.ts +++ b/packages/@react-spectrum/buttongroup/src/index.ts @@ -9,7 +9,5 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - /// - -export * from './ButtonGroup'; +export {ButtonGroup} from './ButtonGroup'; diff --git a/packages/@react-spectrum/card/src/index.ts b/packages/@react-spectrum/card/src/index.ts index a7c0611adf4..0954f904a15 100644 --- a/packages/@react-spectrum/card/src/index.ts +++ b/packages/@react-spectrum/card/src/index.ts @@ -12,8 +12,12 @@ /// -export * from './CardView'; -export * from './GalleryLayout'; -export * from './GridLayout'; -export * from './WaterfallLayout'; -export * from './Card'; +export type {GalleryLayoutOptions} from './GalleryLayout'; +export type {GridLayoutOptions} from './GridLayout'; +export type {WaterfallLayoutOptions} from './WaterfallLayout'; + +export {CardView} from './CardView'; +export {GalleryLayout} from './GalleryLayout'; +export {GridLayout} from './GridLayout'; +export {WaterfallLayout} from './WaterfallLayout'; +export {Card} from './Card'; diff --git a/packages/@react-spectrum/checkbox/src/index.ts b/packages/@react-spectrum/checkbox/src/index.ts index 111079ec916..72d776109a7 100644 --- a/packages/@react-spectrum/checkbox/src/index.ts +++ b/packages/@react-spectrum/checkbox/src/index.ts @@ -9,8 +9,6 @@ * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ - /// - -export * from './Checkbox'; -export * from './CheckboxGroup'; +export {Checkbox} from './Checkbox'; +export {CheckboxGroup} from './CheckboxGroup'; diff --git a/packages/@react-spectrum/checkbox/stories/Checkbox.stories.tsx b/packages/@react-spectrum/checkbox/stories/Checkbox.stories.tsx index e554c0e844d..06a3e213882 100644 --- a/packages/@react-spectrum/checkbox/stories/Checkbox.stories.tsx +++ b/packages/@react-spectrum/checkbox/stories/Checkbox.stories.tsx @@ -12,6 +12,8 @@ import {action} from '@storybook/addon-actions'; import {Checkbox} from '../'; +import {Flex} from '@react-spectrum/layout'; +import {Link} from '@react-spectrum/link'; import React from 'react'; import {storiesOf} from '@storybook/react'; @@ -89,6 +91,21 @@ storiesOf('Checkbox', module) .add( 'no label', () => renderNoLabel({'aria-label': 'This checkbox has no visible label'}) + ) + .add( + 'WHCM test', + () => ( + + Shows the different states from spectrum + {renderRow()} + {renderRow({isSelected: true, isEmphasized: true})} + {renderRow({isIndeterminate: true, isEmphasized: true})} + {renderRow({isSelected: true, isEmphasized: false})} + {renderRow({isIndeterminate: true, isEmphasized: false})} + {renderRow({validationState: 'invalid'})} + {renderRow({isSelected: true, validationState: 'invalid'})} + + ) ); function render(props = {}) { @@ -118,3 +135,21 @@ function renderNoLabel(props = {}) { {...props} /> ); } + +function renderRow(props = {}) { + return ( + + + Option + + + Option + + + ); +} diff --git a/packages/@react-spectrum/color/src/ColorArea.tsx b/packages/@react-spectrum/color/src/ColorArea.tsx index 7c7a427f6d8..d309f5f0406 100644 --- a/packages/@react-spectrum/color/src/ColorArea.tsx +++ b/packages/@react-spectrum/color/src/ColorArea.tsx @@ -82,5 +82,8 @@ function ColorArea(props: SpectrumColorAreaProps, ref: FocusableRef}) => ReactElement; export {_ColorArea as ColorArea}; diff --git a/packages/@react-spectrum/color/src/index.ts b/packages/@react-spectrum/color/src/index.ts index d87f973282f..cd04200cffe 100644 --- a/packages/@react-spectrum/color/src/index.ts +++ b/packages/@react-spectrum/color/src/index.ts @@ -11,8 +11,7 @@ */ /// - -export * from './ColorArea'; -export * from './ColorWheel'; -export * from './ColorSlider'; -export * from './ColorField'; +export {ColorArea} from './ColorArea'; +export {ColorWheel} from './ColorWheel'; +export {ColorSlider} from './ColorSlider'; +export {ColorField} from './ColorField'; diff --git a/packages/@react-spectrum/color/stories/ColorArea.stories.tsx b/packages/@react-spectrum/color/stories/ColorArea.stories.tsx index adbc6df9a3c..f15fd09e8a1 100644 --- a/packages/@react-spectrum/color/stories/ColorArea.stories.tsx +++ b/packages/@react-spectrum/color/stories/ColorArea.stories.tsx @@ -12,13 +12,12 @@ import {action} from '@storybook/addon-actions'; import {ColorArea, ColorField, ColorSlider, ColorWheel} from '../'; -import {Flex} from '@adobe/react-spectrum'; +import {Flex, Grid, View} from '@adobe/react-spectrum'; import {Meta, Story} from '@storybook/react'; import {parseColor} from '@react-stately/color'; import React, {useState} from 'react'; import {SpectrumColorAreaProps} from '@react-types/color'; - const meta: Meta = { title: 'ColorArea', component: ColorArea @@ -50,32 +49,48 @@ function ColorAreaExample(props: SpectrumColorAreaProps) { return (
- - - {isHue ? ( - + + + + + + + + ) : ( + + - ) : ( + isDisabled={isDisabled} /> - )} - + + )}
{ expect(ySlider).toHaveAttribute('aria-label', 'Color hue, Color picker'); expect(xSlider).not.toHaveAttribute('aria-labelledby'); expect(ySlider).not.toHaveAttribute('aria-labelledby'); + + let colorAreaGroup = xSlider.closest('[role="group"]'); + expect(colorAreaGroup).toHaveAttribute('aria-label', 'Color hue, Color picker'); + expect(colorAreaGroup).not.toHaveAttribute('aria-labelledby'); }); it('should support a custom aria-labelledby', () => { @@ -119,6 +123,10 @@ describe('ColorArea', () => { expect(ySlider).toHaveAttribute('aria-label', 'Color picker'); expect(xSlider).toHaveAttribute('aria-labelledby', `label-id ${xSlider.id}`); expect(ySlider).toHaveAttribute('aria-labelledby', `label-id ${ySlider.id}`); + + let colorAreaGroup = xSlider.closest('[role="group"]'); + expect(colorAreaGroup).toHaveAttribute('aria-labelledby', 'label-id'); + expect(colorAreaGroup).not.toHaveAttribute('aria-label'); }); }); }); diff --git a/packages/@react-spectrum/color/test/ColorSlider.test.tsx b/packages/@react-spectrum/color/test/ColorSlider.test.tsx index a6db9082dbb..f1d801e721a 100644 --- a/packages/@react-spectrum/color/test/ColorSlider.test.tsx +++ b/packages/@react-spectrum/color/test/ColorSlider.test.tsx @@ -21,8 +21,8 @@ describe('ColorSlider', () => { let onChangeEndSpy = jest.fn(); beforeAll(() => { - jest.spyOn(window.HTMLElement.prototype, 'offsetWidth', 'get').mockImplementation(() => 100); - jest.spyOn(window.HTMLElement.prototype, 'offsetHeight', 'get').mockImplementation(() => 100); + // @ts-ignore + jest.spyOn(window.HTMLElement.prototype, 'getBoundingClientRect').mockImplementation(() => ({top: 0, left: 0, width: 100, height: 100})); // @ts-ignore jest.spyOn(window, 'requestAnimationFrame').mockImplementation((cb) => cb()); // @ts-ignore diff --git a/packages/@react-spectrum/combobox/src/index.ts b/packages/@react-spectrum/combobox/src/index.ts index b66b5c80188..1cad3658da4 100644 --- a/packages/@react-spectrum/combobox/src/index.ts +++ b/packages/@react-spectrum/combobox/src/index.ts @@ -12,5 +12,5 @@ /// -export * from './ComboBox'; +export {ComboBox} from './ComboBox'; export {Item, Section} from '@react-stately/collections'; diff --git a/packages/@react-spectrum/combobox/stories/ComboBox.stories.tsx b/packages/@react-spectrum/combobox/stories/ComboBox.stories.tsx index ab8562c53df..478d1640c85 100644 --- a/packages/@react-spectrum/combobox/stories/ComboBox.stories.tsx +++ b/packages/@react-spectrum/combobox/stories/ComboBox.stories.tsx @@ -21,6 +21,7 @@ import {ComboBox, Item, Section} from '../'; import Copy from '@spectrum-icons/workflow/Copy'; import Draw from '@spectrum-icons/workflow/Draw'; import {Flex} from '@react-spectrum/layout'; +import {Link} from '@react-spectrum/link'; import {mergeProps} from '@react-aria/utils'; import React, {useRef, useState} from 'react'; import {storiesOf} from '@storybook/react'; @@ -466,6 +467,23 @@ storiesOf('ComboBox', module) ) + ) + .add( + 'WHCM test', + () => ( + + Shows the different states from spectrum + {renderRow({placeholder: 'Type here...'})} + {renderRow()} + {renderRow({labelPosition: 'side'})} + {renderRow({isQuiet: true, placeholder: 'Type here...'})} + {renderRow({isQuiet: true})} + {renderRow({isRequired: true})} + {renderRow({isRequired: true, isQuiet: true})} + {renderRow({validationState: 'invalid'})} + {renderRow({validationState: 'invalid', isQuiet: true})} + + ) ); @@ -905,6 +923,29 @@ function render(props = {}) { ); } +function renderRow(props = {}) { + return ( + + + Option 1 + + + Option 2 + + Option 3 + + + Option 1 + + + Option 2 + + Option 3 + + + ); +} + function ComboBoxWithMap(props) { let [items, setItems] = React.useState([ {name: 'The first item', id: 'one'}, diff --git a/packages/@react-spectrum/contextualhelp/src/index.ts b/packages/@react-spectrum/contextualhelp/src/index.ts index 72575dfc853..c1df7fae854 100644 --- a/packages/@react-spectrum/contextualhelp/src/index.ts +++ b/packages/@react-spectrum/contextualhelp/src/index.ts @@ -10,4 +10,4 @@ * governing permissions and limitations under the License. */ -export * from './ContextualHelp'; +export {ContextualHelp} from './ContextualHelp'; diff --git a/packages/@react-spectrum/datepicker/src/styles.css b/packages/@react-spectrum/datepicker/src/styles.css index 9ae5ac4eb81..85294221bdc 100644 --- a/packages/@react-spectrum/datepicker/src/styles.css +++ b/packages/@react-spectrum/datepicker/src/styles.css @@ -182,3 +182,23 @@ padding: 0 var(--spectrum-calendar-day-padding); box-sizing: border-box; } + +@media (forced-colors:active) { + .react-spectrum-DatePicker-cell:focus { + forced-color-adjust: none; + background-color: Highlight; + color: HighlightText; + } + .react-spectrum-DatePicker-cell.is-read-only { + color: ButtonText; + &:focus { + color: HighlightText; + } + } + .react-spectrum-DatePicker-cell.is-placeholder { + color: ButtonText; + &:focus { + color: HighlightText; + } + } +} diff --git a/packages/@react-spectrum/dialog/src/index.ts b/packages/@react-spectrum/dialog/src/index.ts index a15530d0596..bd3dc059d5d 100644 --- a/packages/@react-spectrum/dialog/src/index.ts +++ b/packages/@react-spectrum/dialog/src/index.ts @@ -12,8 +12,8 @@ /// -export * from './AlertDialog'; -export * from './Dialog'; -export * from './DialogTrigger'; -export * from './DialogContainer'; -export * from './useDialogContainer'; +export {AlertDialog} from './AlertDialog'; +export {Dialog} from './Dialog'; +export {DialogTrigger} from './DialogTrigger'; +export {DialogContainer} from './DialogContainer'; +export {useDialogContainer} from './useDialogContainer'; diff --git a/packages/@react-spectrum/divider/src/Divider.tsx b/packages/@react-spectrum/divider/src/Divider.tsx index dc7f4c10c38..deeba06684c 100644 --- a/packages/@react-spectrum/divider/src/Divider.tsx +++ b/packages/@react-spectrum/divider/src/Divider.tsx @@ -47,6 +47,7 @@ function Divider(props: SpectrumDividerProps, ref: DOMRef) { return ( -export * from './Divider'; +export {Divider} from './Divider'; diff --git a/packages/@react-spectrum/dnd/src/index.ts b/packages/@react-spectrum/dnd/src/index.ts index ac0d1c7cfa1..d704da601a8 100644 --- a/packages/@react-spectrum/dnd/src/index.ts +++ b/packages/@react-spectrum/dnd/src/index.ts @@ -12,5 +12,7 @@ /// -export * from './useDragHooks'; -export * from './useDropHooks'; +export type {DragHooks, DragHookOptions} from './useDragHooks'; +export type {DropHooks} from './useDropHooks'; +export {useDragHooks} from './useDragHooks'; +export {useDropHooks} from './useDropHooks'; diff --git a/packages/@react-spectrum/form/src/index.ts b/packages/@react-spectrum/form/src/index.ts index 090c8720c7d..a0403bafba9 100644 --- a/packages/@react-spectrum/form/src/index.ts +++ b/packages/@react-spectrum/form/src/index.ts @@ -12,4 +12,4 @@ /// -export * from './Form'; +export {useFormProps, Form} from './Form'; diff --git a/packages/@react-spectrum/icon/src/index.ts b/packages/@react-spectrum/icon/src/index.ts index 5e532b14ce0..cc44b58bc50 100644 --- a/packages/@react-spectrum/icon/src/index.ts +++ b/packages/@react-spectrum/icon/src/index.ts @@ -12,5 +12,7 @@ /// -export * from './Icon'; -export * from './UIIcon'; +export type {IconPropsWithoutChildren} from './Icon'; +export type {UIIconPropsWithoutChildren} from './UIIcon'; +export {Icon} from './Icon'; +export {UIIcon} from './UIIcon'; diff --git a/packages/@react-spectrum/illustratedmessage/src/index.ts b/packages/@react-spectrum/illustratedmessage/src/index.ts index c9d27c2d87e..20ded326019 100644 --- a/packages/@react-spectrum/illustratedmessage/src/index.ts +++ b/packages/@react-spectrum/illustratedmessage/src/index.ts @@ -12,4 +12,4 @@ /// -export * from './IllustratedMessage'; +export {IllustratedMessage} from './IllustratedMessage'; diff --git a/packages/@react-spectrum/image/src/index.ts b/packages/@react-spectrum/image/src/index.ts index abea84b2783..7cdf6d66aef 100644 --- a/packages/@react-spectrum/image/src/index.ts +++ b/packages/@react-spectrum/image/src/index.ts @@ -12,4 +12,4 @@ /// -export * from './Image'; +export {Image} from './Image'; diff --git a/packages/@react-spectrum/label/intl/ar-AE.json b/packages/@react-spectrum/label/intl/ar-AE.json index d91bd921cda..c38e7ea9669 100644 --- a/packages/@react-spectrum/label/intl/ar-AE.json +++ b/packages/@react-spectrum/label/intl/ar-AE.json @@ -1,4 +1,5 @@ { "(optional)": "(اختياري)", - "(required)": "(مطلوب)" + "(required)": "(مطلوب)", + "(None)": "(None)" } diff --git a/packages/@react-spectrum/label/intl/en-US.json b/packages/@react-spectrum/label/intl/en-US.json index 640d7139e8c..e523161b1d8 100644 --- a/packages/@react-spectrum/label/intl/en-US.json +++ b/packages/@react-spectrum/label/intl/en-US.json @@ -1,4 +1,5 @@ { "(required)": "(required)", - "(optional)": "(optional)" + "(optional)": "(optional)", + "(None)": "(None)" } diff --git a/packages/@react-spectrum/label/package.json b/packages/@react-spectrum/label/package.json index 062f9950ee9..01fe4ac0523 100644 --- a/packages/@react-spectrum/label/package.json +++ b/packages/@react-spectrum/label/package.json @@ -32,7 +32,9 @@ }, "dependencies": { "@babel/runtime": "^7.6.2", + "@react-aria/focus": "^3.6.1", "@react-aria/i18n": "^3.4.1", + "@react-aria/interactions": "^3.9.1", "@react-aria/label": "^3.3.1", "@react-aria/utils": "^3.13.1", "@react-spectrum/form": "^3.3.1", diff --git a/packages/@react-spectrum/label/src/Field.tsx b/packages/@react-spectrum/label/src/Field.tsx index d774be926b5..725abb8b986 100644 --- a/packages/@react-spectrum/label/src/Field.tsx +++ b/packages/@react-spectrum/label/src/Field.tsx @@ -13,13 +13,17 @@ import {classNames, useStyleProps} from '@react-spectrum/utils'; import {Flex} from '@react-spectrum/layout'; import {HelpText} from './HelpText'; +// @ts-ignore +import intlMessages from '../intl/*.json'; import {Label} from './Label'; import {LabelPosition} from '@react-types/shared'; import labelStyles from '@adobe/spectrum-css-temp/components/fieldlabel/vars.css'; import {mergeProps, mergeRefs} from '@react-aria/utils'; import React, {RefObject} from 'react'; +import {ReadOnlyField} from './ReadOnlyField'; import {SpectrumFieldProps} from '@react-types/label'; import {useFormProps} from '@react-spectrum/form'; +import {useMessageFormatter} from '@react-aria/i18n'; function Field(props: SpectrumFieldProps, ref: RefObject) { props = useFormProps(props); @@ -37,6 +41,10 @@ function Field(props: SpectrumFieldProps, ref: RefObject) { showErrorIcon, children, labelProps, + readOnlyText, + isReadOnly, + inputProps, + inputRef, // Not every component that uses supports help text. descriptionProps = {}, errorMessageProps = {}, @@ -47,6 +55,8 @@ function Field(props: SpectrumFieldProps, ref: RefObject) { } = props; let {styleProps} = useStyleProps(otherProps); let hasHelpText = !!description || errorMessage && validationState === 'invalid'; + let formatMessage = useMessageFormatter(intlMessages); + let displayReadOnly = isReadOnly && (readOnlyText || readOnlyText === ''); if (label || hasHelpText) { let labelWrapperClass = classNames( @@ -78,10 +88,23 @@ function Field(props: SpectrumFieldProps, ref: RefObject) { showErrorIcon={showErrorIcon} /> ); + if (displayReadOnly) { + if (readOnlyText === '') { + readOnlyText = formatMessage('(None)'); + } + children = ( + } /> + ); + } + let renderChildren = () => ( {children} - {hasHelpText && renderHelpText()} + {hasHelpText && !displayReadOnly && renderHelpText()} ); diff --git a/packages/@react-spectrum/label/src/ReadOnlyField.tsx b/packages/@react-spectrum/label/src/ReadOnlyField.tsx new file mode 100644 index 00000000000..7561bff1481 --- /dev/null +++ b/packages/@react-spectrum/label/src/ReadOnlyField.tsx @@ -0,0 +1,82 @@ +/* + * 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 {classNames} from '@react-spectrum/utils'; +import {FocusRing} from '@react-aria/focus'; +import {mergeProps} from '@react-aria/utils'; +import React, {RefObject, useCallback} from 'react'; +import {SpectrumFieldProps} from '@react-types/label'; +import styles from '@adobe/spectrum-css-temp/components/textfield/vars.css'; +import {useFormProps} from '@react-spectrum/form'; +import {useHover} from '@react-aria/interactions'; +import {useLayoutEffect} from '@react-aria/utils'; + +function ReadOnlyField(props: SpectrumFieldProps, ref: RefObject) { + props = useFormProps(props); + let { + isDisabled, + readOnlyText, + inputProps, + autoFocus + } = props; + delete inputProps.defaultValue; + let {hoverProps, isHovered} = useHover({isDisabled}); + + let onHeightChange = useCallback(() => { + let input = ref.current; + let prevAlignment = input.style.alignSelf; + input.style.alignSelf = 'start'; + input.style.height = 'auto'; + input.style.height = `${input.scrollHeight}px`; + input.style.alignSelf = prevAlignment; + }, [ref]); + + useLayoutEffect(() => { + if (ref.current) { + onHeightChange(); + } + }, [onHeightChange, readOnlyText, ref]); + + return ( +
+ +