Skip to content

Commit 08ba4d5

Browse files
committed
Merge branch 'main' of github.com:adobe/react-spectrum into selection-indicator
2 parents 1b46eeb + f1a021e commit 08ba4d5

File tree

50 files changed

+843
-463
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+843
-463
lines changed

packages/@adobe/spectrum-css-temp/components/tray/index.css

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,13 @@
2020

2121
.spectrum-Tray-wrapper {
2222
inset-inline-start: 0;
23-
/* Positioned at the top of the window */
24-
position: fixed;
23+
position: absolute;
2524
top: 0;
2625

2726
display: flex;
2827
justify-content: center;
2928
width: 100%;
30-
height: 100vh;
29+
height: 100dvh;
3130

3231
/* Don't catch clicks */
3332
pointer-events: none;
@@ -55,10 +54,12 @@
5554
max-height: calc(var(--spectrum-visual-viewport-height) - var(--spectrum-tray-margin-top));
5655
/* Add padding at the bottom to account for the rest of the viewport height behind the keyboard.
5756
* This is necessary so that there isn't a visible gap that appears while the keyboard is animating
58-
* in and out. Fall back to the safe area inset to account for things like iOS home indicator. */
59-
padding-bottom: max(calc(100vh - var(--spectrum-visual-viewport-height)), env(safe-area-inset-bottom));
57+
* in and out. Fall back to the safe area inset to account for things like iOS home indicator.
58+
We also add an additional 100vh of padding (offset by the bottom position below) so the tray
59+
extends behind Safari's address bar and keyboard in iOS 26. */
60+
padding-bottom: calc(max(calc(100dvh - var(--spectrum-visual-viewport-height)), env(safe-area-inset-bottom)) + 100vh);
6061
position: absolute;
61-
bottom: 0;
62+
bottom: -100vh;
6263
outline: none;
6364
display: flex;
6465
flex-direction: column;

packages/@adobe/spectrum-css-temp/components/underlay/index.css

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ governing permissions and limitations under the License.
2323
.spectrum-Underlay {
2424
composes: spectrum-overlay;
2525

26-
position: fixed;
26+
/* Use position: absolute instead of fixed to avoid being clipped to the "inner" viewport in iOS 26 */
27+
position: absolute;
2728
top: 0;
2829
right: 0;
2930
bottom: 0;

packages/@internationalized/date/src/queries.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,9 @@ function getWeekStart(locale: string): number {
268268
}
269269
let region = getRegion(locale);
270270
if (locale.includes('-fw-')) {
271-
let day = locale.split('-fw-')[1];
271+
// pull the value for the attribute fw from strings such as en-US-u-ca-iso8601-fw-tue or en-US-u-ca-iso8601-fw-mon-nu-thai
272+
// where the fw attribute could be followed by another unicode locale extension or not
273+
let day = locale.split('-fw-')[1].split('-')[0];
272274
if (day === 'mon') {
273275
weekInfo = {firstDay: 1};
274276
} else if (day === 'tue') {
@@ -284,7 +286,7 @@ function getWeekStart(locale: string): number {
284286
} else {
285287
weekInfo = {firstDay: 0};
286288
}
287-
} else if (locale.includes('u-ca-iso8601')) {
289+
} else if (locale.includes('-ca-iso8601')) {
288290
weekInfo = {firstDay: 1};
289291
} else {
290292
weekInfo = {firstDay: region ? weekStartData[region] || 0 : 0};

packages/@internationalized/date/tests/queries.test.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,11 @@ describe('queries', function () {
281281

282282
// override first day of week
283283
expect(startOfWeek(new CalendarDate(2021, 8, 4), 'en-US-u-ca-iso8601-fw-tue')).toEqual(new CalendarDate(2021, 8, 3));
284+
285+
// override applied if extension appears in the middle of other extensions
286+
expect(startOfWeek(new CalendarDate(2021, 8, 4), 'en-US-u-nu-thai-ca-iso8601')).toEqual(new CalendarDate(2021, 8, 2));
287+
expect(startOfWeek(new CalendarDate(2021, 8, 4), 'en-US-u-nu-thai-ca-iso8601-fw-tue')).toEqual(new CalendarDate(2021, 8, 3));
288+
expect(startOfWeek(new CalendarDate(2021, 8, 4), 'en-US-u-ca-iso8601-fw-tue-nu-thai')).toEqual(new CalendarDate(2021, 8, 3));
284289
});
285290
});
286291

packages/@react-aria/autocomplete/src/useAutocomplete.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import {AriaLabelingProps, BaseEvent, DOMProps, FocusableElement, FocusEvents, KeyboardEvents, Node, RefObject, ValueBase} from '@react-types/shared';
1414
import {AriaTextFieldProps} from '@react-aria/textfield';
1515
import {AutocompleteProps, AutocompleteState} from '@react-stately/autocomplete';
16-
import {CLEAR_FOCUS_EVENT, FOCUS_EVENT, getActiveElement, getOwnerDocument, isAndroid, isCtrlKeyPressed, isIOS, mergeProps, mergeRefs, useEffectEvent, useEvent, useLabels, useObjectRef, useSlotId} from '@react-aria/utils';
16+
import {CLEAR_FOCUS_EVENT, FOCUS_EVENT, getActiveElement, getOwnerDocument, getOwnerWindow, isAndroid, isCtrlKeyPressed, isIOS, mergeProps, mergeRefs, useEffectEvent, useEvent, useLabels, useObjectRef, useSlotId} from '@react-aria/utils';
1717
import {dispatchVirtualBlur, dispatchVirtualFocus, getVirtuallyFocusedElement, moveVirtualFocus} from '@react-aria/focus';
1818
import {getInteractionModality} from '@react-aria/interactions';
1919
// @ts-ignore
@@ -106,6 +106,9 @@ export function useAutocomplete<T>(props: AriaAutocompleteOptions<T>, state: Aut
106106
// Ensure input is focused if the user clicks on the collection directly.
107107
if (!e.isTrusted && shouldUseVirtualFocus && inputRef.current && getActiveElement(getOwnerDocument(inputRef.current)) !== inputRef.current) {
108108
inputRef.current.focus();
109+
if (inputRef.current instanceof getOwnerWindow(inputRef.current).HTMLInputElement) {
110+
inputRef.current.select();
111+
}
109112
}
110113

111114
let target = e.target as Element | null;

packages/@react-aria/checkbox/docs/useCheckbox.mdx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,10 @@ function Checkbox(props) {
8585
let {children} = props;
8686
let state = useToggleState(props);
8787
let ref = React.useRef(null);
88-
let {inputProps} = useCheckbox(props, state, ref);
88+
let {inputProps, labelProps} = useCheckbox(props, state, ref);
8989

9090
return (
91-
<label style={{display: 'block'}}>
91+
<label {...labelProps} style={{display: 'block'}}>
9292
<input {...inputProps} ref={ref} />
9393
{children}
9494
</label>
@@ -120,12 +120,12 @@ import {mergeProps} from '@react-aria/utils';
120120
function Checkbox(props) {
121121
let state = useToggleState(props);
122122
let ref = React.useRef(null);
123-
let {inputProps} = useCheckbox(props, state, ref);
123+
let {inputProps, labelProps} = useCheckbox(props, state, ref);
124124
let {isFocusVisible, focusProps} = useFocusRing();
125125
let isSelected = state.isSelected && !props.isIndeterminate;
126126

127127
return (
128-
<label style={{display: 'flex', alignItems: 'center', opacity: props.isDisabled ? 0.4 : 1}}>
128+
<label {...labelProps} style={{display: 'flex', alignItems: 'center', opacity: props.isDisabled ? 0.4 : 1}}>
129129
<VisuallyHidden>
130130
<input {...mergeProps(inputProps, focusProps)} ref={ref} />
131131
</VisuallyHidden>

packages/@react-aria/checkbox/docs/useCheckboxGroup.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,13 +127,14 @@ function Checkbox(props) {
127127
let {children} = props;
128128
let state = React.useContext(CheckboxGroupContext);
129129
let ref = React.useRef(null);
130-
let {inputProps} = useCheckboxGroupItem(props, state, ref);
130+
let {inputProps, labelProps} = useCheckboxGroupItem(props, state, ref);
131131

132132
let isDisabled = state.isDisabled || props.isDisabled;
133133
let isSelected = state.isSelected(props.value);
134134

135135
return (
136136
<label
137+
{...labelProps}
137138
style={{
138139
display: 'block',
139140
color: (isDisabled && 'var(--gray)') || (isSelected && 'var(--blue)'),

packages/@react-aria/checkbox/src/useCheckbox.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
*/
1212

1313
import {AriaCheckboxProps} from '@react-types/checkbox';
14-
import {InputHTMLAttributes, LabelHTMLAttributes, useEffect} from 'react';
14+
import {InputHTMLAttributes, LabelHTMLAttributes, useEffect, useMemo} from 'react';
1515
import {mergeProps} from '@react-aria/utils';
1616
import {privateValidationStateProp, useFormValidationState} from '@react-stately/form';
1717
import {RefObject, ValidationResult} from '@react-types/shared';
@@ -69,17 +69,24 @@ export function useCheckbox(props: AriaCheckboxProps, state: ToggleState, inputR
6969
onPress() {
7070
// @ts-expect-error
7171
let {[privateValidationStateProp]: groupValidationState} = props;
72-
72+
7373
let {commitValidation} = groupValidationState
7474
? groupValidationState
7575
: validationState;
76-
76+
7777
commitValidation();
7878
}
7979
});
8080

8181
return {
82-
labelProps: mergeProps(labelProps, pressProps),
82+
labelProps: mergeProps(
83+
labelProps,
84+
pressProps,
85+
useMemo(() => ({
86+
// Prevent label from being focused when mouse down on it.
87+
// Note, this does not prevent the input from being focused in the `click` event.
88+
onMouseDown: e => e.preventDefault()
89+
}), [])),
8390
inputProps: {
8491
...inputProps,
8592
checked: isSelected,
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright 2025 Adobe. All rights reserved.
3+
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License. You may obtain a copy
5+
* of the License at http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software distributed under
8+
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9+
* OF ANY KIND, either express or implied. See the License for the specific language
10+
* governing permissions and limitations under the License.
11+
*/
12+
13+
import {action} from '@storybook/addon-actions';
14+
import {AriaCheckboxProps, useCheckbox} from '../';
15+
import React from 'react';
16+
import {StoryObj} from '@storybook/react';
17+
import {useToggleState} from '@react-stately/toggle';
18+
19+
export default {
20+
title: 'useCheckbox'
21+
};
22+
23+
export type CheckboxStory = StoryObj<typeof Checkbox>;
24+
25+
function Checkbox(props: AriaCheckboxProps) {
26+
let {children} = props;
27+
let state = useToggleState(props);
28+
let ref = React.useRef(null);
29+
let {inputProps, labelProps} = useCheckbox(props, state, ref);
30+
31+
return (
32+
<>
33+
<label {...labelProps} style={{display: 'block'}}>
34+
{children}
35+
</label>
36+
<input {...inputProps} ref={ref} />
37+
</>
38+
);
39+
}
40+
41+
export const Example: CheckboxStory = {
42+
render: (args) => <Checkbox {...args}>Unsubscribe</Checkbox>,
43+
args: {
44+
onFocus: action('onFocus'),
45+
onBlur: action('onBlur')
46+
}
47+
};

packages/@react-aria/collections/src/Document.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -461,16 +461,14 @@ export class Document<T, C extends BaseCollection<T> = BaseCollection<T>> extend
461461
}
462462

463463
private removeNode(node: ElementNode<T>): void {
464-
if (node.node == null) {
465-
return;
466-
}
467-
468464
for (let child of node) {
469465
this.removeNode(child);
470466
}
471467

472-
let collection = this.getMutableCollection();
473-
collection.removeNode(node.node.key);
468+
if (node.node) {
469+
let collection = this.getMutableCollection();
470+
collection.removeNode(node.node.key);
471+
}
474472
}
475473

476474
/** Finalizes the collection update, updating all nodes and freezing the collection. */
@@ -508,12 +506,16 @@ export class Document<T, C extends BaseCollection<T> = BaseCollection<T>> extend
508506
this.addNode(element);
509507
}
510508

509+
if (element.node) {
510+
this.dirtyNodes.delete(element);
511+
}
512+
511513
element.isMutated = false;
514+
} else {
515+
this.dirtyNodes.delete(element);
512516
}
513517
}
514518

515-
this.dirtyNodes.clear();
516-
517519
// Finally, update the collection.
518520
if (this.nextCollection) {
519521
this.nextCollection.commit(this.firstVisibleChild?.node?.key ?? null, this.lastVisibleChild?.node?.key ?? null, this.isSSR);

0 commit comments

Comments
 (0)