@automattic/jetpack-components 1.3.11 → 1.3.13
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 +14 -2
- package/build/components/navigator-modal/context.d.ts +2 -0
- package/build/components/navigator-modal/context.js +6 -0
- package/build/components/navigator-modal/footer.d.ts +14 -0
- package/build/components/navigator-modal/footer.js +29 -0
- package/build/components/navigator-modal/header.d.ts +17 -0
- package/build/components/navigator-modal/header.js +21 -0
- package/build/components/navigator-modal/index.d.ts +16 -0
- package/build/components/navigator-modal/index.js +33 -0
- package/build/components/navigator-modal/screen.d.ts +51 -0
- package/build/components/navigator-modal/screen.js +18 -0
- package/build/components/navigator-modal/types.d.ts +24 -0
- package/build/components/navigator-modal/types.js +1 -0
- package/build/index.d.ts +1 -0
- package/build/index.js +1 -0
- package/components/navigator-modal/context.ts +10 -0
- package/components/navigator-modal/footer.tsx +50 -0
- package/components/navigator-modal/header.tsx +58 -0
- package/components/navigator-modal/index.tsx +57 -0
- package/components/navigator-modal/screen.tsx +97 -0
- package/components/navigator-modal/styles.module.scss +77 -0
- package/components/navigator-modal/types.ts +27 -0
- package/index.ts +1 -0
- package/package.json +16 -16
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
### This is a list detailing changes for the Jetpack RNA Components package releases.
|
|
4
4
|
|
|
5
|
+
## [1.3.13] - 2025-12-01
|
|
6
|
+
### Added
|
|
7
|
+
- Add NavigatorModal component. [#46101]
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
- Update package dependencies. [#46143]
|
|
11
|
+
|
|
12
|
+
## [1.3.12] - 2025-11-20
|
|
13
|
+
### Changed
|
|
14
|
+
- Update package dependencies. [#46022]
|
|
15
|
+
|
|
5
16
|
## [1.3.11] - 2025-11-18
|
|
6
17
|
### Changed
|
|
7
18
|
- Update dependencies.
|
|
@@ -29,8 +40,7 @@
|
|
|
29
40
|
|
|
30
41
|
## [1.3.5] - 2025-10-02
|
|
31
42
|
### Changed
|
|
32
|
-
- Update package dependencies. [#45334]
|
|
33
|
-
- Update package dependencies. [#45335]
|
|
43
|
+
- Update package dependencies. [#45334] [#45335]
|
|
34
44
|
|
|
35
45
|
## [1.3.4] - 2025-09-22
|
|
36
46
|
### Changed
|
|
@@ -1566,6 +1576,8 @@
|
|
|
1566
1576
|
### Changed
|
|
1567
1577
|
- Update node version requirement to 14.16.1
|
|
1568
1578
|
|
|
1579
|
+
[1.3.13]: https://github.com/Automattic/jetpack-components/compare/1.3.12...1.3.13
|
|
1580
|
+
[1.3.12]: https://github.com/Automattic/jetpack-components/compare/1.3.11...1.3.12
|
|
1569
1581
|
[1.3.11]: https://github.com/Automattic/jetpack-components/compare/1.3.10...1.3.11
|
|
1570
1582
|
[1.3.10]: https://github.com/Automattic/jetpack-components/compare/1.3.9...1.3.10
|
|
1571
1583
|
[1.3.9]: https://github.com/Automattic/jetpack-components/compare/1.3.8...1.3.9
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Button } from '@wordpress/components';
|
|
2
|
+
import { SharedProps } from './types.ts';
|
|
3
|
+
export type FooterProps = SharedProps & {
|
|
4
|
+
actions?: Array<React.ComponentProps<typeof Button>>;
|
|
5
|
+
isScreenLocked?: boolean;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Renders a footer.
|
|
9
|
+
*
|
|
10
|
+
* @param {FooterProps} props - Props
|
|
11
|
+
*
|
|
12
|
+
* @return The rendered footer.
|
|
13
|
+
*/
|
|
14
|
+
export declare function Footer({ children, actions, isScreenLocked }: FooterProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button, Flex, FlexBlock, FlexItem, useNavigator } from '@wordpress/components';
|
|
3
|
+
import { useContext } from 'react';
|
|
4
|
+
import { NavigatorModalContext } from "./context.js";
|
|
5
|
+
import styles from './styles.module.scss';
|
|
6
|
+
/**
|
|
7
|
+
* Renders a footer.
|
|
8
|
+
*
|
|
9
|
+
* @param {FooterProps} props - Props
|
|
10
|
+
*
|
|
11
|
+
* @return The rendered footer.
|
|
12
|
+
*/
|
|
13
|
+
export function Footer({ children, actions, isScreenLocked }) {
|
|
14
|
+
const navigator = useNavigator();
|
|
15
|
+
const context = useContext(NavigatorModalContext);
|
|
16
|
+
return (_jsxs(Flex, { className: styles.footer, children: [_jsx(FlexBlock, { children: children }), actions ? (_jsx(FlexItem, { children: _jsx(Flex, { children: actions.map(({ onClick, ...actionProps }, index) => (_jsx(Button
|
|
17
|
+
// eslint-disable-next-line react/jsx-no-bind
|
|
18
|
+
, {
|
|
19
|
+
// eslint-disable-next-line react/jsx-no-bind
|
|
20
|
+
onClick: event => {
|
|
21
|
+
onClick?.(event);
|
|
22
|
+
if (!isScreenLocked) {
|
|
23
|
+
navigator.goBack();
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
context.onClose?.();
|
|
27
|
+
}
|
|
28
|
+
}, ...actionProps }, index))) }) })) : null] }));
|
|
29
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type HeaderProps = {
|
|
2
|
+
/**
|
|
3
|
+
* The title of the header.
|
|
4
|
+
*/
|
|
5
|
+
title: string;
|
|
6
|
+
/**
|
|
7
|
+
* Whether the screen is locked, in which case to hide the back button.
|
|
8
|
+
*/
|
|
9
|
+
isScreenLocked?: boolean;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Renders a header for the NavigatorModal.
|
|
13
|
+
* @param {HeaderProps} props - Props
|
|
14
|
+
*
|
|
15
|
+
* @return component
|
|
16
|
+
*/
|
|
17
|
+
export declare function Header({ title, isScreenLocked }: HeaderProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Button, useNavigator } from '@wordpress/components';
|
|
3
|
+
import { __, isRTL } from '@wordpress/i18n';
|
|
4
|
+
import { chevronLeft, chevronRight, close } from '@wordpress/icons';
|
|
5
|
+
import { useCallback, useContext } from 'react';
|
|
6
|
+
import { NavigatorModalContext } from "./context.js";
|
|
7
|
+
import styles from './styles.module.scss';
|
|
8
|
+
/**
|
|
9
|
+
* Renders a header for the NavigatorModal.
|
|
10
|
+
* @param {HeaderProps} props - Props
|
|
11
|
+
*
|
|
12
|
+
* @return component
|
|
13
|
+
*/
|
|
14
|
+
export function Header({ title, isScreenLocked }) {
|
|
15
|
+
const context = useContext(NavigatorModalContext);
|
|
16
|
+
const navigator = useNavigator();
|
|
17
|
+
const onGoBack = useCallback(() => {
|
|
18
|
+
navigator.goBack();
|
|
19
|
+
}, [navigator]);
|
|
20
|
+
return (_jsxs("div", { className: styles.header, children: [_jsxs("div", { className: styles['title-wrap'], children: [!isScreenLocked ? (_jsx(Button, { label: __('Go back', 'jetpack-components'), icon: isRTL() ? chevronRight : chevronLeft, onClick: onGoBack, variant: "tertiary", size: "compact" })) : null, _jsx("h1", { children: title })] }), context.isDismissible ? (_jsx(Button, { size: "compact", onClick: context.onClose, icon: close, label: __('Close', 'jetpack-components'), variant: "tertiary" })) : null] }));
|
|
21
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Screen } from './screen.tsx';
|
|
2
|
+
import { TNavigatorModalContext, SharedProps } from './types.ts';
|
|
3
|
+
/**
|
|
4
|
+
* Renders a modal with navigator capabilities.
|
|
5
|
+
*
|
|
6
|
+
* @param {SharedProps & TNavigatorModalContext} props - Props
|
|
7
|
+
*
|
|
8
|
+
* @return Component
|
|
9
|
+
*/
|
|
10
|
+
declare function NavigatorModalMain({ children, className, initialPath, onClose, isDismissible, }: SharedProps & TNavigatorModalContext): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
export declare const NavigatorModal: typeof NavigatorModalMain & {
|
|
12
|
+
Screen: typeof Screen & {
|
|
13
|
+
displayName: string;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Modal, Navigator } from '@wordpress/components';
|
|
3
|
+
import clsx from 'clsx';
|
|
4
|
+
import { useContext } from 'react';
|
|
5
|
+
import { NavigatorModalContext } from "./context.js";
|
|
6
|
+
import { Screen } from "./screen.js";
|
|
7
|
+
import styles from './styles.module.scss';
|
|
8
|
+
/**
|
|
9
|
+
* Renders the internal NavigatorModal component.
|
|
10
|
+
*
|
|
11
|
+
* @param { SharedProps } props - Props
|
|
12
|
+
*
|
|
13
|
+
* @return Component
|
|
14
|
+
*/
|
|
15
|
+
function InternalNavigatorModal({ children, className }) {
|
|
16
|
+
const context = useContext(NavigatorModalContext);
|
|
17
|
+
return (_jsx(Modal, { __experimentalHideHeader: true, onRequestClose: context.onClose, className: clsx(styles.modal, className), children: _jsx(Navigator, { initialPath: context.initialPath, className: styles.navigator, children: children }) }));
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Renders a modal with navigator capabilities.
|
|
21
|
+
*
|
|
22
|
+
* @param {SharedProps & TNavigatorModalContext} props - Props
|
|
23
|
+
*
|
|
24
|
+
* @return Component
|
|
25
|
+
*/
|
|
26
|
+
function NavigatorModalMain({ children, className, initialPath = '/', onClose, isDismissible = true, }) {
|
|
27
|
+
return (_jsx(NavigatorModalContext.Provider, { value: { onClose, initialPath, isDismissible }, children: _jsx(InternalNavigatorModal, { className: className, children: children }) }));
|
|
28
|
+
}
|
|
29
|
+
export const NavigatorModal = Object.assign(NavigatorModalMain, {
|
|
30
|
+
Screen: Object.assign(Screen, {
|
|
31
|
+
displayName: 'NavigatorModal.Screen',
|
|
32
|
+
}),
|
|
33
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { FooterProps } from './footer.tsx';
|
|
2
|
+
export type ScreenProps = {
|
|
3
|
+
/**
|
|
4
|
+
* The title of the screen.
|
|
5
|
+
*/
|
|
6
|
+
title?: string;
|
|
7
|
+
/**
|
|
8
|
+
* The path of the screen.
|
|
9
|
+
*/
|
|
10
|
+
path: string;
|
|
11
|
+
/**
|
|
12
|
+
* The sidebar content
|
|
13
|
+
*/
|
|
14
|
+
sidebar?: React.ReactNode;
|
|
15
|
+
/**
|
|
16
|
+
* Whether the screen is locked or has a parent screen.
|
|
17
|
+
*
|
|
18
|
+
* When it's locked, it means there will be no navigation back to a previous screen.
|
|
19
|
+
*/
|
|
20
|
+
isScreenLocked?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* The footer content
|
|
23
|
+
*/
|
|
24
|
+
footerContent?: React.ReactNode;
|
|
25
|
+
/**
|
|
26
|
+
* The footer actions
|
|
27
|
+
*/
|
|
28
|
+
footerActions?: FooterProps['actions'];
|
|
29
|
+
/**
|
|
30
|
+
* className to be applied to the modal.
|
|
31
|
+
*/
|
|
32
|
+
className?: string;
|
|
33
|
+
} & ({
|
|
34
|
+
/**
|
|
35
|
+
* The content of the screen.
|
|
36
|
+
*/
|
|
37
|
+
children: React.ReactNode;
|
|
38
|
+
} | {
|
|
39
|
+
/**
|
|
40
|
+
* The content of the screen.
|
|
41
|
+
*/
|
|
42
|
+
content: React.ReactNode;
|
|
43
|
+
});
|
|
44
|
+
/**
|
|
45
|
+
* Renders a screen.
|
|
46
|
+
*
|
|
47
|
+
* @param {ScreenProps} props - Props
|
|
48
|
+
*
|
|
49
|
+
* @return The rendered screen.
|
|
50
|
+
*/
|
|
51
|
+
export declare function Screen({ path, className, title, sidebar, isScreenLocked, footerContent, footerActions, ...props }: ScreenProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Flex, Navigator } from '@wordpress/components';
|
|
3
|
+
import clsx from 'clsx';
|
|
4
|
+
import { Footer } from "./footer.js";
|
|
5
|
+
import { Header } from "./header.js";
|
|
6
|
+
import styles from './styles.module.scss';
|
|
7
|
+
/**
|
|
8
|
+
* Renders a screen.
|
|
9
|
+
*
|
|
10
|
+
* @param {ScreenProps} props - Props
|
|
11
|
+
*
|
|
12
|
+
* @return The rendered screen.
|
|
13
|
+
*/
|
|
14
|
+
export function Screen({ path, className, title, sidebar, isScreenLocked, footerContent, footerActions, ...props }) {
|
|
15
|
+
const hasFooter = Boolean(footerContent || (footerActions && footerActions.length));
|
|
16
|
+
return (_jsx(Navigator.Screen, { path: path, className: clsx(styles.screen, className), children: _jsxs(Flex, { direction: "column", gap: 0, children: [_jsx(Header, { title: title, isScreenLocked: isScreenLocked }), _jsx("div", { className: styles.body, children: _jsxs(Flex, { gap: 0, align: "start", children: [sidebar ? _jsx("div", { className: styles.sidebar, children: sidebar }) : null, _jsx("div", { className: styles.content, children: ('children' in props && props.children) ||
|
|
17
|
+
('content' in props && props.content) })] }) }), hasFooter ? (_jsx(Footer, { actions: footerActions, isScreenLocked: isScreenLocked, children: footerContent })) : null] }) }));
|
|
18
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export type TNavigatorModalContext = {
|
|
2
|
+
/**
|
|
3
|
+
* Whether the modal is dismissible.
|
|
4
|
+
*/
|
|
5
|
+
isDismissible?: boolean;
|
|
6
|
+
/**
|
|
7
|
+
* The initial active path.
|
|
8
|
+
*/
|
|
9
|
+
initialPath?: string;
|
|
10
|
+
/**
|
|
11
|
+
* Callback fired when the close button is clicked.
|
|
12
|
+
*/
|
|
13
|
+
onClose?: VoidFunction;
|
|
14
|
+
};
|
|
15
|
+
export type SharedProps = {
|
|
16
|
+
/**
|
|
17
|
+
* The content of the component.
|
|
18
|
+
*/
|
|
19
|
+
children: React.ReactNode;
|
|
20
|
+
/**
|
|
21
|
+
* className to be applied to the modal.
|
|
22
|
+
*/
|
|
23
|
+
className?: string;
|
|
24
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/build/index.d.ts
CHANGED
|
@@ -64,3 +64,4 @@ export { default as DiffViewer } from './components/diff-viewer/index.tsx';
|
|
|
64
64
|
export { default as MarkedLines } from './components/marked-lines/index.tsx';
|
|
65
65
|
export { default as DetailsViewer } from './components/details-viewer/index.tsx';
|
|
66
66
|
export * from './components/global-notices/index.ts';
|
|
67
|
+
export * from './components/navigator-modal/index.tsx';
|
package/build/index.js
CHANGED
|
@@ -78,3 +78,4 @@ export { default as DiffViewer } from "./components/diff-viewer/index.js";
|
|
|
78
78
|
export { default as MarkedLines } from "./components/marked-lines/index.js";
|
|
79
79
|
export { default as DetailsViewer } from "./components/details-viewer/index.js";
|
|
80
80
|
export * from "./components/global-notices/index.js";
|
|
81
|
+
export * from "./components/navigator-modal/index.js";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createContext } from '@wordpress/element';
|
|
2
|
+
import type { TNavigatorModalContext } from './types.ts';
|
|
3
|
+
|
|
4
|
+
const initialContextValue: TNavigatorModalContext = {
|
|
5
|
+
onClose: () => {},
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export const NavigatorModalContext = createContext( initialContextValue );
|
|
9
|
+
|
|
10
|
+
NavigatorModalContext.displayName = 'NavigatorModalContext';
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Button, Flex, FlexBlock, FlexItem, useNavigator } from '@wordpress/components';
|
|
2
|
+
import { useContext } from 'react';
|
|
3
|
+
import { NavigatorModalContext } from './context.ts';
|
|
4
|
+
import styles from './styles.module.scss';
|
|
5
|
+
import { SharedProps } from './types.ts';
|
|
6
|
+
|
|
7
|
+
export type FooterProps = SharedProps & {
|
|
8
|
+
actions?: Array< React.ComponentProps< typeof Button > >;
|
|
9
|
+
isScreenLocked?: boolean;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Renders a footer.
|
|
14
|
+
*
|
|
15
|
+
* @param {FooterProps} props - Props
|
|
16
|
+
*
|
|
17
|
+
* @return The rendered footer.
|
|
18
|
+
*/
|
|
19
|
+
export function Footer( { children, actions, isScreenLocked }: FooterProps ) {
|
|
20
|
+
const navigator = useNavigator();
|
|
21
|
+
const context = useContext( NavigatorModalContext );
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<Flex className={ styles.footer }>
|
|
25
|
+
<FlexBlock>{ children }</FlexBlock>
|
|
26
|
+
{ actions ? (
|
|
27
|
+
<FlexItem>
|
|
28
|
+
<Flex>
|
|
29
|
+
{ actions.map( ( { onClick, ...actionProps }, index ) => (
|
|
30
|
+
<Button
|
|
31
|
+
// eslint-disable-next-line react/jsx-no-bind
|
|
32
|
+
onClick={ event => {
|
|
33
|
+
onClick?.( event );
|
|
34
|
+
|
|
35
|
+
if ( ! isScreenLocked ) {
|
|
36
|
+
navigator.goBack();
|
|
37
|
+
} else {
|
|
38
|
+
context.onClose?.();
|
|
39
|
+
}
|
|
40
|
+
} }
|
|
41
|
+
key={ index }
|
|
42
|
+
{ ...actionProps }
|
|
43
|
+
/>
|
|
44
|
+
) ) }
|
|
45
|
+
</Flex>
|
|
46
|
+
</FlexItem>
|
|
47
|
+
) : null }
|
|
48
|
+
</Flex>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Button, useNavigator } from '@wordpress/components';
|
|
2
|
+
import { __, isRTL } from '@wordpress/i18n';
|
|
3
|
+
import { chevronLeft, chevronRight, close } from '@wordpress/icons';
|
|
4
|
+
import { useCallback, useContext } from 'react';
|
|
5
|
+
import { NavigatorModalContext } from './context.ts';
|
|
6
|
+
import styles from './styles.module.scss';
|
|
7
|
+
|
|
8
|
+
export type HeaderProps = {
|
|
9
|
+
/**
|
|
10
|
+
* The title of the header.
|
|
11
|
+
*/
|
|
12
|
+
title: string;
|
|
13
|
+
/**
|
|
14
|
+
* Whether the screen is locked, in which case to hide the back button.
|
|
15
|
+
*/
|
|
16
|
+
isScreenLocked?: boolean;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Renders a header for the NavigatorModal.
|
|
21
|
+
* @param {HeaderProps} props - Props
|
|
22
|
+
*
|
|
23
|
+
* @return component
|
|
24
|
+
*/
|
|
25
|
+
export function Header( { title, isScreenLocked }: HeaderProps ) {
|
|
26
|
+
const context = useContext( NavigatorModalContext );
|
|
27
|
+
const navigator = useNavigator();
|
|
28
|
+
|
|
29
|
+
const onGoBack = useCallback( () => {
|
|
30
|
+
navigator.goBack();
|
|
31
|
+
}, [ navigator ] );
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div className={ styles.header }>
|
|
35
|
+
<div className={ styles[ 'title-wrap' ] }>
|
|
36
|
+
{ ! isScreenLocked ? (
|
|
37
|
+
<Button
|
|
38
|
+
label={ __( 'Go back', 'jetpack-components' ) }
|
|
39
|
+
icon={ isRTL() ? chevronRight : chevronLeft }
|
|
40
|
+
onClick={ onGoBack }
|
|
41
|
+
variant="tertiary"
|
|
42
|
+
size="compact"
|
|
43
|
+
/>
|
|
44
|
+
) : null }
|
|
45
|
+
<h1>{ title }</h1>
|
|
46
|
+
</div>
|
|
47
|
+
{ context.isDismissible ? (
|
|
48
|
+
<Button
|
|
49
|
+
size="compact"
|
|
50
|
+
onClick={ context.onClose }
|
|
51
|
+
icon={ close }
|
|
52
|
+
label={ __( 'Close', 'jetpack-components' ) }
|
|
53
|
+
variant="tertiary"
|
|
54
|
+
/>
|
|
55
|
+
) : null }
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Modal, Navigator } from '@wordpress/components';
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
import { useContext } from 'react';
|
|
4
|
+
import { NavigatorModalContext } from './context.ts';
|
|
5
|
+
import { Screen } from './screen.tsx';
|
|
6
|
+
import styles from './styles.module.scss';
|
|
7
|
+
import { TNavigatorModalContext, SharedProps } from './types.ts';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Renders the internal NavigatorModal component.
|
|
11
|
+
*
|
|
12
|
+
* @param { SharedProps } props - Props
|
|
13
|
+
*
|
|
14
|
+
* @return Component
|
|
15
|
+
*/
|
|
16
|
+
function InternalNavigatorModal( { children, className }: SharedProps ) {
|
|
17
|
+
const context = useContext( NavigatorModalContext );
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<Modal
|
|
21
|
+
__experimentalHideHeader
|
|
22
|
+
onRequestClose={ context.onClose }
|
|
23
|
+
className={ clsx( styles.modal, className ) }
|
|
24
|
+
>
|
|
25
|
+
<Navigator initialPath={ context.initialPath } className={ styles.navigator }>
|
|
26
|
+
{ children }
|
|
27
|
+
</Navigator>
|
|
28
|
+
</Modal>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Renders a modal with navigator capabilities.
|
|
34
|
+
*
|
|
35
|
+
* @param {SharedProps & TNavigatorModalContext} props - Props
|
|
36
|
+
*
|
|
37
|
+
* @return Component
|
|
38
|
+
*/
|
|
39
|
+
function NavigatorModalMain( {
|
|
40
|
+
children,
|
|
41
|
+
className,
|
|
42
|
+
initialPath = '/',
|
|
43
|
+
onClose,
|
|
44
|
+
isDismissible = true,
|
|
45
|
+
}: SharedProps & TNavigatorModalContext ) {
|
|
46
|
+
return (
|
|
47
|
+
<NavigatorModalContext.Provider value={ { onClose, initialPath, isDismissible } }>
|
|
48
|
+
<InternalNavigatorModal className={ className }>{ children }</InternalNavigatorModal>
|
|
49
|
+
</NavigatorModalContext.Provider>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const NavigatorModal = Object.assign( NavigatorModalMain, {
|
|
54
|
+
Screen: Object.assign( Screen, {
|
|
55
|
+
displayName: 'NavigatorModal.Screen',
|
|
56
|
+
} ),
|
|
57
|
+
} );
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Flex, Navigator } from '@wordpress/components';
|
|
2
|
+
import clsx from 'clsx';
|
|
3
|
+
import { Footer, FooterProps } from './footer.tsx';
|
|
4
|
+
import { Header } from './header.tsx';
|
|
5
|
+
import styles from './styles.module.scss';
|
|
6
|
+
|
|
7
|
+
export type ScreenProps = {
|
|
8
|
+
/**
|
|
9
|
+
* The title of the screen.
|
|
10
|
+
*/
|
|
11
|
+
title?: string;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The path of the screen.
|
|
15
|
+
*/
|
|
16
|
+
path: string;
|
|
17
|
+
/**
|
|
18
|
+
* The sidebar content
|
|
19
|
+
*/
|
|
20
|
+
sidebar?: React.ReactNode;
|
|
21
|
+
/**
|
|
22
|
+
* Whether the screen is locked or has a parent screen.
|
|
23
|
+
*
|
|
24
|
+
* When it's locked, it means there will be no navigation back to a previous screen.
|
|
25
|
+
*/
|
|
26
|
+
isScreenLocked?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* The footer content
|
|
29
|
+
*/
|
|
30
|
+
footerContent?: React.ReactNode;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The footer actions
|
|
34
|
+
*/
|
|
35
|
+
footerActions?: FooterProps[ 'actions' ];
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* className to be applied to the modal.
|
|
39
|
+
*/
|
|
40
|
+
className?: string;
|
|
41
|
+
} & (
|
|
42
|
+
| {
|
|
43
|
+
/**
|
|
44
|
+
* The content of the screen.
|
|
45
|
+
*/
|
|
46
|
+
children: React.ReactNode;
|
|
47
|
+
}
|
|
48
|
+
| {
|
|
49
|
+
/**
|
|
50
|
+
* The content of the screen.
|
|
51
|
+
*/
|
|
52
|
+
content: React.ReactNode;
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Renders a screen.
|
|
58
|
+
*
|
|
59
|
+
* @param {ScreenProps} props - Props
|
|
60
|
+
*
|
|
61
|
+
* @return The rendered screen.
|
|
62
|
+
*/
|
|
63
|
+
export function Screen( {
|
|
64
|
+
path,
|
|
65
|
+
className,
|
|
66
|
+
title,
|
|
67
|
+
sidebar,
|
|
68
|
+
isScreenLocked,
|
|
69
|
+
footerContent,
|
|
70
|
+
footerActions,
|
|
71
|
+
...props
|
|
72
|
+
}: ScreenProps ) {
|
|
73
|
+
const hasFooter = Boolean( footerContent || ( footerActions && footerActions.length ) );
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<Navigator.Screen path={ path } className={ clsx( styles.screen, className ) }>
|
|
77
|
+
<Flex direction="column" gap={ 0 }>
|
|
78
|
+
<Header title={ title } isScreenLocked={ isScreenLocked } />
|
|
79
|
+
|
|
80
|
+
<div className={ styles.body }>
|
|
81
|
+
<Flex gap={ 0 } align="start">
|
|
82
|
+
{ sidebar ? <div className={ styles.sidebar }>{ sidebar }</div> : null }
|
|
83
|
+
<div className={ styles.content }>
|
|
84
|
+
{ ( 'children' in props && props.children ) ||
|
|
85
|
+
( 'content' in props && props.content ) }
|
|
86
|
+
</div>
|
|
87
|
+
</Flex>
|
|
88
|
+
</div>
|
|
89
|
+
{ hasFooter ? (
|
|
90
|
+
<Footer actions={ footerActions } isScreenLocked={ isScreenLocked }>
|
|
91
|
+
{ footerContent }
|
|
92
|
+
</Footer>
|
|
93
|
+
) : null }
|
|
94
|
+
</Flex>
|
|
95
|
+
</Navigator.Screen>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
@use "@automattic/jetpack-base-styles/gutenberg-base-styles" as gb;
|
|
2
|
+
|
|
3
|
+
.modal {
|
|
4
|
+
width: 80rem;
|
|
5
|
+
height: 40rem;
|
|
6
|
+
max-height: 90%;
|
|
7
|
+
|
|
8
|
+
:global(.components-modal__content) {
|
|
9
|
+
padding: 0;
|
|
10
|
+
|
|
11
|
+
> div {
|
|
12
|
+
height: 100%;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.navigator {
|
|
18
|
+
height: 100%;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.screen {
|
|
22
|
+
height: 100%;
|
|
23
|
+
|
|
24
|
+
.header {
|
|
25
|
+
display: flex;
|
|
26
|
+
align-items: center;
|
|
27
|
+
justify-content: space-between;
|
|
28
|
+
padding: 1.5rem;
|
|
29
|
+
height: 4.5rem;
|
|
30
|
+
border-bottom: 1px solid gb.$gray-200;
|
|
31
|
+
flex-shrink: 0;
|
|
32
|
+
|
|
33
|
+
.title-wrap {
|
|
34
|
+
display: flex;
|
|
35
|
+
align-items: center;
|
|
36
|
+
gap: 1rem;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
h1 {
|
|
40
|
+
margin: 0;
|
|
41
|
+
font-size: 15px;
|
|
42
|
+
font-weight: 500;
|
|
43
|
+
line-height: 24px;
|
|
44
|
+
color: gb.$gray-900;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.body {
|
|
49
|
+
display: flex;
|
|
50
|
+
height: 100%;
|
|
51
|
+
overflow: auto;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.sidebar {
|
|
55
|
+
height: 100%;
|
|
56
|
+
display: flex;
|
|
57
|
+
flex-direction: column;
|
|
58
|
+
gap: 1.5rem;
|
|
59
|
+
max-width: 22rem;
|
|
60
|
+
flex-shrink: 0;
|
|
61
|
+
border-inline-end: 1px solid gb.$gray-300;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.content {
|
|
65
|
+
height: 100%;
|
|
66
|
+
flex: 1;
|
|
67
|
+
overflow: auto;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.footer {
|
|
71
|
+
padding: 1rem 1.5rem;
|
|
72
|
+
border-top: 1px solid gb.$gray-300;
|
|
73
|
+
height: 4.5rem;
|
|
74
|
+
flex-shrink: 0;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type TNavigatorModalContext = {
|
|
2
|
+
/**
|
|
3
|
+
* Whether the modal is dismissible.
|
|
4
|
+
*/
|
|
5
|
+
isDismissible?: boolean;
|
|
6
|
+
/**
|
|
7
|
+
* The initial active path.
|
|
8
|
+
*/
|
|
9
|
+
initialPath?: string;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Callback fired when the close button is clicked.
|
|
13
|
+
*/
|
|
14
|
+
onClose?: VoidFunction;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type SharedProps = {
|
|
18
|
+
/**
|
|
19
|
+
* The content of the component.
|
|
20
|
+
*/
|
|
21
|
+
children: React.ReactNode;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* className to be applied to the modal.
|
|
25
|
+
*/
|
|
26
|
+
className?: string;
|
|
27
|
+
};
|
package/index.ts
CHANGED
|
@@ -85,3 +85,4 @@ export { default as DiffViewer } from './components/diff-viewer/index.tsx';
|
|
|
85
85
|
export { default as MarkedLines } from './components/marked-lines/index.tsx';
|
|
86
86
|
export { default as DetailsViewer } from './components/details-viewer/index.tsx';
|
|
87
87
|
export * from './components/global-notices/index.ts';
|
|
88
|
+
export * from './components/navigator-modal/index.tsx';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@automattic/jetpack-components",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.13",
|
|
4
4
|
"description": "Jetpack Components Package",
|
|
5
5
|
"homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/components/#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -41,20 +41,20 @@
|
|
|
41
41
|
],
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@automattic/format-currency": "1.0.1",
|
|
44
|
-
"@automattic/jetpack-api": "^1.0.
|
|
45
|
-
"@automattic/jetpack-boost-score-api": "^1.0.
|
|
44
|
+
"@automattic/jetpack-api": "^1.0.12",
|
|
45
|
+
"@automattic/jetpack-boost-score-api": "^1.0.19",
|
|
46
46
|
"@automattic/jetpack-script-data": "^0.5.4",
|
|
47
47
|
"@automattic/number-formatters": "^1.0.15",
|
|
48
48
|
"@babel/runtime": "^7",
|
|
49
|
-
"@wordpress/browserslist-config": "6.
|
|
50
|
-
"@wordpress/components": "30.
|
|
51
|
-
"@wordpress/compose": "7.
|
|
52
|
-
"@wordpress/data": "10.
|
|
53
|
-
"@wordpress/date": "5.
|
|
54
|
-
"@wordpress/element": "6.
|
|
55
|
-
"@wordpress/i18n": "6.
|
|
56
|
-
"@wordpress/icons": "11.
|
|
57
|
-
"@wordpress/notices": "5.
|
|
49
|
+
"@wordpress/browserslist-config": "6.36.0",
|
|
50
|
+
"@wordpress/components": "30.9.0",
|
|
51
|
+
"@wordpress/compose": "7.36.0",
|
|
52
|
+
"@wordpress/data": "10.36.0",
|
|
53
|
+
"@wordpress/date": "5.36.0",
|
|
54
|
+
"@wordpress/element": "6.36.0",
|
|
55
|
+
"@wordpress/i18n": "6.9.0",
|
|
56
|
+
"@wordpress/icons": "11.3.0",
|
|
57
|
+
"@wordpress/notices": "5.36.0",
|
|
58
58
|
"clsx": "2.1.1",
|
|
59
59
|
"prop-types": "^15.7.2",
|
|
60
60
|
"qrcode.react": "4.2.0",
|
|
@@ -64,12 +64,12 @@
|
|
|
64
64
|
"uplot-react": "1.1.4"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
|
-
"@automattic/jetpack-base-styles": "^1.0.
|
|
67
|
+
"@automattic/jetpack-base-styles": "^1.0.12",
|
|
68
68
|
"@babel/core": "7.28.4",
|
|
69
69
|
"@babel/preset-react": "7.27.1",
|
|
70
70
|
"@jest/globals": "30.2.0",
|
|
71
|
-
"@storybook/addon-docs": "
|
|
72
|
-
"@storybook/react": "
|
|
71
|
+
"@storybook/addon-docs": "10.0.8",
|
|
72
|
+
"@storybook/react": "10.0.8",
|
|
73
73
|
"@testing-library/dom": "10.4.1",
|
|
74
74
|
"@testing-library/react": "16.3.0",
|
|
75
75
|
"@testing-library/user-event": "14.6.1",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"react": "18.3.1",
|
|
83
83
|
"react-dom": "18.3.1",
|
|
84
84
|
"require-from-string": "2.0.2",
|
|
85
|
-
"storybook": "
|
|
85
|
+
"storybook": "10.0.8",
|
|
86
86
|
"ts-dedent": "2.2.0",
|
|
87
87
|
"typescript": "5.9.3",
|
|
88
88
|
"webpack": "5.101.3",
|