@wordpress/components 25.4.0 → 25.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/build/border-control/border-control-dropdown/component.js +8 -10
- package/build/border-control/border-control-dropdown/component.js.map +1 -1
- package/build/color-palette/index.js +2 -2
- package/build/color-palette/index.js.map +1 -1
- package/build/focal-point-picker/index.native.js +6 -4
- package/build/focal-point-picker/index.native.js.map +1 -1
- package/build/menu-items-choice/index.js +1 -0
- package/build/menu-items-choice/index.js.map +1 -1
- package/build/mobile/bottom-sheet/bottom-sheet-navigation/bottom-sheet-navigation-context.native.js +3 -1
- package/build/mobile/bottom-sheet/bottom-sheet-navigation/bottom-sheet-navigation-context.native.js.map +1 -1
- package/build/mobile/bottom-sheet/bottom-sheet-navigation/navigation-container.native.js +50 -44
- package/build/mobile/bottom-sheet/bottom-sheet-navigation/navigation-container.native.js.map +1 -1
- package/build/mobile/bottom-sheet/bottom-sheet-navigation/navigation-screen.native.js +13 -20
- package/build/mobile/bottom-sheet/bottom-sheet-navigation/navigation-screen.native.js.map +1 -1
- package/build/mobile/bottom-sheet/index.native.js +3 -1
- package/build/mobile/bottom-sheet/index.native.js.map +1 -1
- package/build/mobile/link-picker/link-picker-results.native.js +2 -1
- package/build/mobile/link-picker/link-picker-results.native.js.map +1 -1
- package/build/mobile/segmented-control/index.native.js +7 -7
- package/build/mobile/segmented-control/index.native.js.map +1 -1
- package/build/modal/index.js +14 -1
- package/build/modal/index.js.map +1 -1
- package/build/private-apis.js +4 -1
- package/build/private-apis.js.map +1 -1
- package/build/progress-bar/index.js +54 -0
- package/build/progress-bar/index.js.map +1 -0
- package/build/progress-bar/styles.js +69 -0
- package/build/progress-bar/styles.js.map +1 -0
- package/build/progress-bar/types.js +6 -0
- package/build/progress-bar/types.js.map +1 -0
- package/build/tab-panel/index.js +91 -58
- package/build/tab-panel/index.js.map +1 -1
- package/build-module/border-control/border-control-dropdown/component.js +8 -10
- package/build-module/border-control/border-control-dropdown/component.js.map +1 -1
- package/build-module/color-palette/index.js +2 -2
- package/build-module/color-palette/index.js.map +1 -1
- package/build-module/focal-point-picker/index.native.js +6 -5
- package/build-module/focal-point-picker/index.native.js.map +1 -1
- package/build-module/menu-items-choice/index.js +1 -0
- package/build-module/menu-items-choice/index.js.map +1 -1
- package/build-module/mobile/bottom-sheet/bottom-sheet-navigation/bottom-sheet-navigation-context.native.js +3 -1
- package/build-module/mobile/bottom-sheet/bottom-sheet-navigation/bottom-sheet-navigation-context.native.js.map +1 -1
- package/build-module/mobile/bottom-sheet/bottom-sheet-navigation/navigation-container.native.js +43 -41
- package/build-module/mobile/bottom-sheet/bottom-sheet-navigation/navigation-container.native.js.map +1 -1
- package/build-module/mobile/bottom-sheet/bottom-sheet-navigation/navigation-screen.native.js +14 -20
- package/build-module/mobile/bottom-sheet/bottom-sheet-navigation/navigation-screen.native.js.map +1 -1
- package/build-module/mobile/bottom-sheet/index.native.js +3 -1
- package/build-module/mobile/bottom-sheet/index.native.js.map +1 -1
- package/build-module/mobile/link-picker/link-picker-results.native.js +2 -1
- package/build-module/mobile/link-picker/link-picker-results.native.js.map +1 -1
- package/build-module/mobile/segmented-control/index.native.js +7 -7
- package/build-module/mobile/segmented-control/index.native.js.map +1 -1
- package/build-module/modal/index.js +14 -1
- package/build-module/modal/index.js.map +1 -1
- package/build-module/private-apis.js +3 -1
- package/build-module/private-apis.js.map +1 -1
- package/build-module/progress-bar/index.js +41 -0
- package/build-module/progress-bar/index.js.map +1 -0
- package/build-module/progress-bar/styles.js +61 -0
- package/build-module/progress-bar/styles.js.map +1 -0
- package/build-module/progress-bar/types.js +2 -0
- package/build-module/progress-bar/types.js.map +1 -0
- package/build-module/tab-panel/index.js +88 -59
- package/build-module/tab-panel/index.js.map +1 -1
- package/build-types/border-control/border-control-dropdown/component.d.ts.map +1 -1
- package/build-types/menu-items-choice/index.d.ts.map +1 -1
- package/build-types/menu-items-choice/types.d.ts +5 -0
- package/build-types/menu-items-choice/types.d.ts.map +1 -1
- package/build-types/modal/index.d.ts.map +1 -1
- package/build-types/private-apis.d.ts.map +1 -1
- package/build-types/progress-bar/index.d.ts +5 -0
- package/build-types/progress-bar/index.d.ts.map +1 -0
- package/build-types/progress-bar/stories/index.d.ts +12 -0
- package/build-types/progress-bar/stories/index.d.ts.map +1 -0
- package/build-types/progress-bar/styles.d.ts +18 -0
- package/build-types/progress-bar/styles.d.ts.map +1 -0
- package/build-types/progress-bar/test/index.d.ts +2 -0
- package/build-types/progress-bar/test/index.d.ts.map +1 -0
- package/build-types/progress-bar/types.d.ts +11 -0
- package/build-types/progress-bar/types.d.ts.map +1 -0
- package/build-types/tab-panel/index.d.ts.map +1 -1
- package/build-types/tab-panel/stories/index.d.ts +1 -0
- package/build-types/tab-panel/stories/index.d.ts.map +1 -1
- package/build-types/tab-panel/types.d.ts +1 -9
- package/build-types/tab-panel/types.d.ts.map +1 -1
- package/package.json +20 -20
- package/src/border-control/border-control-dropdown/component.tsx +7 -11
- package/src/border-control/test/index.js +6 -6
- package/src/color-palette/index.tsx +2 -2
- package/src/color-palette/test/__snapshots__/index.tsx.snap +1 -1
- package/src/color-palette/test/index.tsx +1 -5
- package/src/draggable/test/index.native.js +4 -0
- package/src/focal-point-picker/index.native.js +6 -5
- package/src/menu-item/README.md +7 -0
- package/src/menu-items-choice/index.tsx +1 -0
- package/src/menu-items-choice/types.ts +5 -0
- package/src/mobile/bottom-sheet/bottom-sheet-navigation/bottom-sheet-navigation-context.native.js +1 -1
- package/src/mobile/bottom-sheet/bottom-sheet-navigation/navigation-container.native.js +72 -53
- package/src/mobile/bottom-sheet/bottom-sheet-navigation/navigation-screen.native.js +15 -21
- package/src/mobile/bottom-sheet/bottom-sheet-navigation/test/navigation-container.native.js +165 -119
- package/src/mobile/bottom-sheet/index.native.js +2 -0
- package/src/mobile/link-picker/link-picker-results.native.js +1 -1
- package/src/mobile/link-settings/test/edit.native.js +37 -23
- package/src/mobile/segmented-control/index.native.js +11 -11
- package/src/modal/index.tsx +16 -0
- package/src/modal/test/index.tsx +33 -0
- package/src/private-apis.ts +2 -0
- package/src/progress-bar/README.md +30 -0
- package/src/progress-bar/index.tsx +45 -0
- package/src/progress-bar/stories/index.tsx +33 -0
- package/src/progress-bar/styles.ts +67 -0
- package/src/progress-bar/test/index.tsx +79 -0
- package/src/progress-bar/types.ts +11 -0
- package/src/tab-panel/index.tsx +121 -84
- package/src/tab-panel/stories/index.tsx +6 -0
- package/src/tab-panel/test/index.tsx +128 -109
- package/src/tab-panel/types.ts +1 -10
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { ForwardedRef } from 'react';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* WordPress dependencies
|
|
8
|
+
*/
|
|
9
|
+
import { __ } from '@wordpress/i18n';
|
|
10
|
+
import { forwardRef } from '@wordpress/element';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Internal dependencies
|
|
14
|
+
*/
|
|
15
|
+
import * as ProgressBarStyled from './styles';
|
|
16
|
+
import type { ProgressBarProps } from './types';
|
|
17
|
+
import type { WordPressComponentProps } from '../ui/context';
|
|
18
|
+
|
|
19
|
+
function UnforwardedProgressBar(
|
|
20
|
+
props: WordPressComponentProps< ProgressBarProps, 'progress', false >,
|
|
21
|
+
ref: ForwardedRef< HTMLProgressElement >
|
|
22
|
+
) {
|
|
23
|
+
const { className, value, ...progressProps } = props;
|
|
24
|
+
const isIndeterminate = ! Number.isFinite( value );
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<ProgressBarStyled.Track className={ className }>
|
|
28
|
+
<ProgressBarStyled.Indicator
|
|
29
|
+
isIndeterminate={ isIndeterminate }
|
|
30
|
+
value={ value }
|
|
31
|
+
/>
|
|
32
|
+
<ProgressBarStyled.ProgressElement
|
|
33
|
+
max={ 100 }
|
|
34
|
+
value={ value }
|
|
35
|
+
aria-label={ __( 'Loading …' ) }
|
|
36
|
+
ref={ ref }
|
|
37
|
+
{ ...progressProps }
|
|
38
|
+
/>
|
|
39
|
+
</ProgressBarStyled.Track>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const ProgressBar = forwardRef( UnforwardedProgressBar );
|
|
44
|
+
|
|
45
|
+
export default ProgressBar;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import type { ComponentMeta, ComponentStory } from '@storybook/react';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Internal dependencies
|
|
8
|
+
*/
|
|
9
|
+
import { ProgressBar } from '..';
|
|
10
|
+
|
|
11
|
+
const meta: ComponentMeta< typeof ProgressBar > = {
|
|
12
|
+
component: ProgressBar,
|
|
13
|
+
title: 'Components (Experimental)/ProgressBar',
|
|
14
|
+
argTypes: {
|
|
15
|
+
value: { control: { type: 'number', min: 0, max: 100, step: 1 } },
|
|
16
|
+
},
|
|
17
|
+
parameters: {
|
|
18
|
+
controls: {
|
|
19
|
+
expanded: true,
|
|
20
|
+
},
|
|
21
|
+
docs: { source: { state: 'open' } },
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
export default meta;
|
|
25
|
+
|
|
26
|
+
const Template: ComponentStory< typeof ProgressBar > = ( { ...args } ) => {
|
|
27
|
+
return <ProgressBar { ...args } />;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const Default: ComponentStory< typeof ProgressBar > = Template.bind(
|
|
31
|
+
{}
|
|
32
|
+
);
|
|
33
|
+
Default.args = {};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import styled from '@emotion/styled';
|
|
5
|
+
import { css, keyframes } from '@emotion/react';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Internal dependencies
|
|
9
|
+
*/
|
|
10
|
+
import { COLORS, CONFIG } from '../utils';
|
|
11
|
+
|
|
12
|
+
const animateProgressBar = keyframes( {
|
|
13
|
+
'0%': {
|
|
14
|
+
left: '-50%',
|
|
15
|
+
},
|
|
16
|
+
'100%': {
|
|
17
|
+
left: '100%',
|
|
18
|
+
},
|
|
19
|
+
} );
|
|
20
|
+
|
|
21
|
+
// Width of the indicator for the indeterminate progress bar
|
|
22
|
+
export const INDETERMINATE_TRACK_WIDTH = 50;
|
|
23
|
+
|
|
24
|
+
export const Track = styled.div`
|
|
25
|
+
position: relative;
|
|
26
|
+
overflow: hidden;
|
|
27
|
+
width: 100%;
|
|
28
|
+
max-width: 160px;
|
|
29
|
+
height: ${ CONFIG.borderWidthFocus };
|
|
30
|
+
background-color: var(
|
|
31
|
+
--wp-components-color-gray-100,
|
|
32
|
+
${ COLORS.gray[ 100 ] }
|
|
33
|
+
);
|
|
34
|
+
border-radius: ${ CONFIG.radiusBlockUi };
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
export const Indicator = styled.div< {
|
|
38
|
+
isIndeterminate: boolean;
|
|
39
|
+
value?: number;
|
|
40
|
+
} >`
|
|
41
|
+
display: inline-block;
|
|
42
|
+
position: absolute;
|
|
43
|
+
top: 0;
|
|
44
|
+
height: 100%;
|
|
45
|
+
border-radius: ${ CONFIG.radiusBlockUi };
|
|
46
|
+
background-color: ${ COLORS.ui.theme };
|
|
47
|
+
|
|
48
|
+
${ ( { isIndeterminate, value } ) =>
|
|
49
|
+
isIndeterminate
|
|
50
|
+
? css( {
|
|
51
|
+
animationDuration: '1.5s',
|
|
52
|
+
animationTimingFunction: 'ease-in-out',
|
|
53
|
+
animationIterationCount: 'infinite',
|
|
54
|
+
animationName: animateProgressBar,
|
|
55
|
+
width: `${ INDETERMINATE_TRACK_WIDTH }%`,
|
|
56
|
+
} )
|
|
57
|
+
: css( { width: `${ value }%` } ) };
|
|
58
|
+
`;
|
|
59
|
+
|
|
60
|
+
export const ProgressElement = styled.progress`
|
|
61
|
+
position: absolute;
|
|
62
|
+
top: 0;
|
|
63
|
+
left: 0;
|
|
64
|
+
opacity: 0;
|
|
65
|
+
width: 100%;
|
|
66
|
+
height: 100%;
|
|
67
|
+
`;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { render, screen } from '@testing-library/react';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Internal dependencies
|
|
8
|
+
*/
|
|
9
|
+
import { ProgressBar } from '..';
|
|
10
|
+
import { INDETERMINATE_TRACK_WIDTH } from '../styles';
|
|
11
|
+
|
|
12
|
+
describe( 'ProgressBar', () => {
|
|
13
|
+
it( 'should render an indeterminate semantic progress bar element', () => {
|
|
14
|
+
render( <ProgressBar /> );
|
|
15
|
+
|
|
16
|
+
const progressBar = screen.getByRole( 'progressbar' );
|
|
17
|
+
|
|
18
|
+
expect( progressBar ).toBeInTheDocument();
|
|
19
|
+
expect( progressBar ).not.toBeVisible();
|
|
20
|
+
expect( progressBar ).not.toHaveValue();
|
|
21
|
+
} );
|
|
22
|
+
|
|
23
|
+
it( 'should render a determinate semantic progress bar element', () => {
|
|
24
|
+
render( <ProgressBar value={ 55 } /> );
|
|
25
|
+
|
|
26
|
+
const progressBar = screen.getByRole( 'progressbar' );
|
|
27
|
+
|
|
28
|
+
expect( progressBar ).toBeInTheDocument();
|
|
29
|
+
expect( progressBar ).not.toBeVisible();
|
|
30
|
+
expect( progressBar ).toHaveValue( 55 );
|
|
31
|
+
} );
|
|
32
|
+
|
|
33
|
+
it( 'should use `INDETERMINATE_TRACK_WIDTH`% as track width for indeterminate progress bar', () => {
|
|
34
|
+
const { container } = render( <ProgressBar /> );
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* We're intentionally not using an accessible selector, because
|
|
38
|
+
* the track is an intentionally non-interactive presentation element.
|
|
39
|
+
*/
|
|
40
|
+
// eslint-disable-next-line testing-library/no-container, testing-library/no-node-access
|
|
41
|
+
const indicator = container.firstChild?.firstChild;
|
|
42
|
+
|
|
43
|
+
expect( indicator ).toHaveStyle( {
|
|
44
|
+
width: `${ INDETERMINATE_TRACK_WIDTH }%`,
|
|
45
|
+
} );
|
|
46
|
+
} );
|
|
47
|
+
|
|
48
|
+
it( 'should use `value`% as width for determinate progress bar', () => {
|
|
49
|
+
const { container } = render( <ProgressBar value={ 55 } /> );
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* We're intentionally not using an accessible selector, because
|
|
53
|
+
* the track is an intentionally non-interactive presentation element.
|
|
54
|
+
*/
|
|
55
|
+
// eslint-disable-next-line testing-library/no-container, testing-library/no-node-access
|
|
56
|
+
const indicator = container.firstChild?.firstChild;
|
|
57
|
+
|
|
58
|
+
expect( indicator ).toHaveStyle( {
|
|
59
|
+
width: '55%',
|
|
60
|
+
} );
|
|
61
|
+
} );
|
|
62
|
+
|
|
63
|
+
it( 'should pass any additional props down to the underlying `progress` element', () => {
|
|
64
|
+
const id = 'foo-bar-123';
|
|
65
|
+
const ariaLabel = 'in progress...';
|
|
66
|
+
const style = { opacity: 1 };
|
|
67
|
+
|
|
68
|
+
render(
|
|
69
|
+
<ProgressBar id={ id } aria-label={ ariaLabel } style={ style } />
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
expect( screen.getByRole( 'progressbar' ) ).toHaveAttribute( 'id', id );
|
|
73
|
+
expect( screen.getByRole( 'progressbar' ) ).toHaveAttribute(
|
|
74
|
+
'aria-label',
|
|
75
|
+
ariaLabel
|
|
76
|
+
);
|
|
77
|
+
expect( screen.getByRole( 'progressbar' ) ).toHaveStyle( style );
|
|
78
|
+
} );
|
|
79
|
+
} );
|
package/src/tab-panel/index.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
|
+
import * as Ariakit from '@ariakit/react';
|
|
4
5
|
import classnames from 'classnames';
|
|
5
6
|
import type { ForwardedRef } from 'react';
|
|
6
7
|
|
|
@@ -9,38 +10,30 @@ import type { ForwardedRef } from 'react';
|
|
|
9
10
|
*/
|
|
10
11
|
import {
|
|
11
12
|
forwardRef,
|
|
12
|
-
useState,
|
|
13
13
|
useEffect,
|
|
14
14
|
useLayoutEffect,
|
|
15
15
|
useCallback,
|
|
16
16
|
} from '@wordpress/element';
|
|
17
|
-
import { useInstanceId } from '@wordpress/compose';
|
|
17
|
+
import { useInstanceId, usePrevious } from '@wordpress/compose';
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Internal dependencies
|
|
21
21
|
*/
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
import Button from '../button';
|
|
24
|
-
import type {
|
|
24
|
+
import type { TabPanelProps } from './types';
|
|
25
25
|
import type { WordPressComponentProps } from '../ui/context';
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
id={ tabId }
|
|
38
|
-
__experimentalIsFocusable
|
|
39
|
-
{ ...rest }
|
|
40
|
-
>
|
|
41
|
-
{ children }
|
|
42
|
-
</Button>
|
|
43
|
-
);
|
|
27
|
+
// Separate the actual tab name from the instance ID. This is
|
|
28
|
+
// necessary because Ariakit internally uses the element ID when
|
|
29
|
+
// a new tab is selected, but our implementation looks specifically
|
|
30
|
+
// for the tab name to be passed to the `onSelect` callback.
|
|
31
|
+
const extractTabName = ( id: string | undefined | null ) => {
|
|
32
|
+
if ( typeof id === 'undefined' || id === null ) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
return id.match( /^tab-panel-[0-9]*-(.*)/ )?.[ 1 ];
|
|
36
|
+
};
|
|
44
37
|
|
|
45
38
|
/**
|
|
46
39
|
* TabPanel is an ARIA-compliant tabpanel.
|
|
@@ -92,26 +85,65 @@ const UnforwardedTabPanel = (
|
|
|
92
85
|
ref: ForwardedRef< any >
|
|
93
86
|
) => {
|
|
94
87
|
const instanceId = useInstanceId( TabPanel, 'tab-panel' );
|
|
95
|
-
const [ selected, setSelected ] = useState< string >();
|
|
96
88
|
|
|
97
|
-
const
|
|
98
|
-
(
|
|
99
|
-
|
|
100
|
-
|
|
89
|
+
const prependInstanceId = useCallback(
|
|
90
|
+
( tabName: string | undefined ) => {
|
|
91
|
+
if ( typeof tabName === 'undefined' ) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
return `${ instanceId }-${ tabName }`;
|
|
95
|
+
},
|
|
96
|
+
[ instanceId ]
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const tabStore = Ariakit.useTabStore( {
|
|
100
|
+
setSelectedId: ( newTabValue ) => {
|
|
101
|
+
if ( typeof newTabValue === 'undefined' || newTabValue === null ) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const newTab = tabs.find(
|
|
106
|
+
( t ) => prependInstanceId( t.name ) === newTabValue
|
|
107
|
+
);
|
|
108
|
+
if ( newTab?.disabled || newTab === selectedTab ) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const simplifiedTabName = extractTabName( newTabValue );
|
|
113
|
+
if ( typeof simplifiedTabName === 'undefined' ) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
onSelect?.( simplifiedTabName );
|
|
118
|
+
},
|
|
119
|
+
orientation,
|
|
120
|
+
selectOnMove,
|
|
121
|
+
defaultSelectedId: prependInstanceId( initialTabName ),
|
|
122
|
+
} );
|
|
123
|
+
|
|
124
|
+
const selectedTabName = extractTabName( tabStore.useState( 'selectedId' ) );
|
|
125
|
+
|
|
126
|
+
const setTabStoreSelectedId = useCallback(
|
|
127
|
+
( tabName: string ) => {
|
|
128
|
+
tabStore.setState( 'selectedId', prependInstanceId( tabName ) );
|
|
101
129
|
},
|
|
102
|
-
[
|
|
130
|
+
[ prependInstanceId, tabStore ]
|
|
103
131
|
);
|
|
104
132
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
) => {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
133
|
+
const selectedTab = tabs.find( ( { name } ) => name === selectedTabName );
|
|
134
|
+
|
|
135
|
+
const previousSelectedTabName = usePrevious( selectedTabName );
|
|
136
|
+
|
|
137
|
+
// Ensure `onSelect` is called when the initial tab is selected.
|
|
138
|
+
useEffect( () => {
|
|
139
|
+
if (
|
|
140
|
+
previousSelectedTabName !== selectedTabName &&
|
|
141
|
+
selectedTabName === initialTabName &&
|
|
142
|
+
!! selectedTabName
|
|
143
|
+
) {
|
|
144
|
+
onSelect?.( selectedTabName );
|
|
145
|
+
}
|
|
146
|
+
}, [ selectedTabName, initialTabName, onSelect, previousSelectedTabName ] );
|
|
115
147
|
|
|
116
148
|
// Handle selecting the initial tab.
|
|
117
149
|
useLayoutEffect( () => {
|
|
@@ -119,25 +151,31 @@ const UnforwardedTabPanel = (
|
|
|
119
151
|
if ( selectedTab ) {
|
|
120
152
|
return;
|
|
121
153
|
}
|
|
122
|
-
|
|
123
154
|
const initialTab = tabs.find( ( tab ) => tab.name === initialTabName );
|
|
124
|
-
|
|
125
155
|
// Wait for the denoted initial tab to be declared before making a
|
|
126
156
|
// selection. This ensures that if a tab is declared lazily it can
|
|
127
157
|
// still receive initial selection.
|
|
128
158
|
if ( initialTabName && ! initialTab ) {
|
|
129
159
|
return;
|
|
130
160
|
}
|
|
131
|
-
|
|
132
161
|
if ( initialTab && ! initialTab.disabled ) {
|
|
133
162
|
// Select the initial tab if it's not disabled.
|
|
134
|
-
|
|
163
|
+
setTabStoreSelectedId( initialTab.name );
|
|
135
164
|
} else {
|
|
136
|
-
// Fallback to the first enabled tab when the initial is
|
|
165
|
+
// Fallback to the first enabled tab when the initial tab is
|
|
166
|
+
// disabled or it can't be found.
|
|
137
167
|
const firstEnabledTab = tabs.find( ( tab ) => ! tab.disabled );
|
|
138
|
-
if ( firstEnabledTab )
|
|
168
|
+
if ( firstEnabledTab ) {
|
|
169
|
+
setTabStoreSelectedId( firstEnabledTab.name );
|
|
170
|
+
}
|
|
139
171
|
}
|
|
140
|
-
}, [
|
|
172
|
+
}, [
|
|
173
|
+
tabs,
|
|
174
|
+
selectedTab,
|
|
175
|
+
initialTabName,
|
|
176
|
+
instanceId,
|
|
177
|
+
setTabStoreSelectedId,
|
|
178
|
+
] );
|
|
141
179
|
|
|
142
180
|
// Handle the currently selected tab becoming disabled.
|
|
143
181
|
useEffect( () => {
|
|
@@ -145,59 +183,58 @@ const UnforwardedTabPanel = (
|
|
|
145
183
|
if ( ! selectedTab?.disabled ) {
|
|
146
184
|
return;
|
|
147
185
|
}
|
|
148
|
-
|
|
149
186
|
const firstEnabledTab = tabs.find( ( tab ) => ! tab.disabled );
|
|
150
|
-
|
|
151
187
|
// If the currently selected tab becomes disabled, select the first enabled tab.
|
|
152
188
|
// (if there is one).
|
|
153
189
|
if ( firstEnabledTab ) {
|
|
154
|
-
|
|
190
|
+
setTabStoreSelectedId( firstEnabledTab.name );
|
|
155
191
|
}
|
|
156
|
-
}, [ tabs, selectedTab?.disabled,
|
|
157
|
-
|
|
192
|
+
}, [ tabs, selectedTab?.disabled, setTabStoreSelectedId, instanceId ] );
|
|
158
193
|
return (
|
|
159
194
|
<div className={ className } ref={ ref }>
|
|
160
|
-
<
|
|
161
|
-
|
|
162
|
-
orientation={ orientation }
|
|
163
|
-
onNavigate={
|
|
164
|
-
selectOnMove ? activateTabAutomatically : undefined
|
|
165
|
-
}
|
|
195
|
+
<Ariakit.TabList
|
|
196
|
+
store={ tabStore }
|
|
166
197
|
className="components-tab-panel__tabs"
|
|
167
198
|
>
|
|
168
|
-
{ tabs.map( ( tab ) =>
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
tab.
|
|
173
|
-
{
|
|
174
|
-
|
|
199
|
+
{ tabs.map( ( tab ) => {
|
|
200
|
+
return (
|
|
201
|
+
<Ariakit.Tab
|
|
202
|
+
key={ tab.name }
|
|
203
|
+
id={ prependInstanceId( tab.name ) }
|
|
204
|
+
className={ classnames(
|
|
205
|
+
'components-tab-panel__tabs-item',
|
|
206
|
+
tab.className,
|
|
207
|
+
{
|
|
208
|
+
[ activeClass ]:
|
|
209
|
+
tab.name === selectedTabName,
|
|
210
|
+
}
|
|
211
|
+
) }
|
|
212
|
+
disabled={ tab.disabled }
|
|
213
|
+
aria-controls={ `${ prependInstanceId(
|
|
214
|
+
tab.name
|
|
215
|
+
) }-view` }
|
|
216
|
+
render={
|
|
217
|
+
<Button
|
|
218
|
+
icon={ tab.icon }
|
|
219
|
+
label={ tab.icon && tab.title }
|
|
220
|
+
showTooltip={ !! tab.icon }
|
|
221
|
+
/>
|
|
175
222
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
disabled={ tab.disabled }
|
|
183
|
-
label={ tab.icon && tab.title }
|
|
184
|
-
icon={ tab.icon }
|
|
185
|
-
showTooltip={ !! tab.icon }
|
|
186
|
-
>
|
|
187
|
-
{ ! tab.icon && tab.title }
|
|
188
|
-
</TabButton>
|
|
189
|
-
) ) }
|
|
190
|
-
</NavigableMenu>
|
|
223
|
+
>
|
|
224
|
+
{ ! tab.icon && tab.title }
|
|
225
|
+
</Ariakit.Tab>
|
|
226
|
+
);
|
|
227
|
+
} ) }
|
|
228
|
+
</Ariakit.TabList>
|
|
191
229
|
{ selectedTab && (
|
|
192
|
-
<
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
className="components-tab-panel__tab-content"
|
|
230
|
+
<Ariakit.TabPanel
|
|
231
|
+
id={ `${ prependInstanceId( selectedTab.name ) }-view` }
|
|
232
|
+
store={ tabStore }
|
|
233
|
+
tabId={ prependInstanceId( selectedTab.name ) }
|
|
234
|
+
className={ 'components-tab-panel__tab-content' }
|
|
198
235
|
>
|
|
199
236
|
{ children( selectedTab ) }
|
|
200
|
-
</
|
|
237
|
+
</Ariakit.TabPanel>
|
|
201
238
|
) }
|
|
202
239
|
</div>
|
|
203
240
|
);
|