@khanacademy/wonder-blocks-modal 8.2.3 → 8.3.1
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/.turbo/turbo-build$colon$css.log +1 -1
- package/CHANGELOG.md +25 -0
- package/dist/components/drawer-backdrop.d.ts +28 -0
- package/dist/components/drawer-dialog.d.ts +102 -0
- package/dist/components/drawer-launcher.d.ts +47 -0
- package/dist/components/flexible-dialog.d.ts +13 -8
- package/dist/components/flexible-panel.d.ts +1 -1
- package/dist/es/index.js +20 -10
- package/dist/hooks/use-direction-detection.d.ts +58 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +21 -9
- package/dist/util/drawer-context.d.ts +24 -0
- package/dist/util/types.d.ts +7 -0
- package/package.json +7 -7
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @khanacademy/wonder-blocks-modal@8.
|
|
2
|
+
> @khanacademy/wonder-blocks-modal@8.3.1 build:css /home/runner/work/wonder-blocks/wonder-blocks/packages/wonder-blocks-modal
|
|
3
3
|
> pnpm exec wonder-blocks-tokens .
|
|
4
4
|
|
|
5
5
|
CSS variables generated successfully in: /home/runner/work/wonder-blocks/wonder-blocks/packages/wonder-blocks-modal/dist/css
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# @khanacademy/wonder-blocks-modal
|
|
2
2
|
|
|
3
|
+
## 8.3.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 1feec01: Replace use of `surface` tokens in components in favour of `background` tokens
|
|
8
|
+
- Updated dependencies [1feec01]
|
|
9
|
+
- @khanacademy/wonder-blocks-styles@0.2.30
|
|
10
|
+
- @khanacademy/wonder-blocks-tokens@12.2.1
|
|
11
|
+
- @khanacademy/wonder-blocks-icon-button@10.4.1
|
|
12
|
+
- @khanacademy/wonder-blocks-breadcrumbs@3.2.2
|
|
13
|
+
- @khanacademy/wonder-blocks-layout@3.1.35
|
|
14
|
+
- @khanacademy/wonder-blocks-typography@4.2.20
|
|
15
|
+
|
|
16
|
+
## 8.3.0
|
|
17
|
+
|
|
18
|
+
### Minor Changes
|
|
19
|
+
|
|
20
|
+
- f32064b: Add DrawerLauncher and DrawerDialog components for sliding in from the left, right, or bottom.
|
|
21
|
+
|
|
22
|
+
### Patch Changes
|
|
23
|
+
|
|
24
|
+
- f32064b: Fix RTL positioning of close button in ModalPanel
|
|
25
|
+
- Updated dependencies [83620b4]
|
|
26
|
+
- @khanacademy/wonder-blocks-icon-button@10.4.0
|
|
27
|
+
|
|
3
28
|
## 8.2.3
|
|
4
29
|
|
|
5
30
|
### Patch Changes
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { ModalElement } from "../util/types";
|
|
3
|
+
type Props = {
|
|
4
|
+
children: ModalElement;
|
|
5
|
+
onCloseModal: () => unknown;
|
|
6
|
+
/**
|
|
7
|
+
* The selector for the element that will be focused when the dialog shows.
|
|
8
|
+
* When not set, the first tabbable element within the dialog will be used,
|
|
9
|
+
* which usually is the dismiss button (X).
|
|
10
|
+
*/
|
|
11
|
+
initialFocusId?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Test ID used for e2e testing.
|
|
14
|
+
*/
|
|
15
|
+
testId?: string;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* A private component used by DrawerLauncher. This is the fixed-position
|
|
19
|
+
* container element that gets mounted outside the DOM. It overlays the modal
|
|
20
|
+
* content (provided as `children`) over the content, with a gray backdrop
|
|
21
|
+
* behind it.
|
|
22
|
+
*
|
|
23
|
+
* This component is also responsible for cloning the provided modal `children`,
|
|
24
|
+
* and adding an `onClose` prop that will call `onCloseModal`. If an
|
|
25
|
+
* `onClose` prop is already provided, the two are merged.
|
|
26
|
+
*/
|
|
27
|
+
declare const DrawerBackdrop: ({ children, testId, initialFocusId, onCloseModal, }: Props) => React.JSX.Element;
|
|
28
|
+
export default DrawerBackdrop;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { StyleType } from "@khanacademy/wonder-blocks-core";
|
|
3
|
+
type AccessibleDialogProps = {
|
|
4
|
+
title: React.ReactElement | string;
|
|
5
|
+
"aria-label"?: never;
|
|
6
|
+
"aria-labelledby"?: string;
|
|
7
|
+
} | {
|
|
8
|
+
title?: never;
|
|
9
|
+
"aria-label": string;
|
|
10
|
+
"aria-labelledby"?: never;
|
|
11
|
+
} | {
|
|
12
|
+
title?: never;
|
|
13
|
+
"aria-label"?: never;
|
|
14
|
+
"aria-labelledby": string;
|
|
15
|
+
};
|
|
16
|
+
type Props = AccessibleDialogProps & {
|
|
17
|
+
/**
|
|
18
|
+
* An optional id parameter for the main heading. If one is not provided,
|
|
19
|
+
* an ID will be generated.
|
|
20
|
+
*/
|
|
21
|
+
titleId?: string;
|
|
22
|
+
/**
|
|
23
|
+
* The content of the modal. Supports a render prop for placing the title in a slot.
|
|
24
|
+
*/
|
|
25
|
+
content: React.ReactElement | ((slots: RenderProps) => React.ReactElement);
|
|
26
|
+
/**
|
|
27
|
+
* Called when the close button is clicked.
|
|
28
|
+
*
|
|
29
|
+
* If you're using `DrawerLauncher`, you probably shouldn't use this prop!
|
|
30
|
+
* Instead, to listen for when the modal closes, add an `onClose` handler
|
|
31
|
+
* to the `DrawerLauncher`.
|
|
32
|
+
*/
|
|
33
|
+
onClose?: () => unknown;
|
|
34
|
+
/**
|
|
35
|
+
* When true, the close button is shown; otherwise, the close button is not shown.
|
|
36
|
+
*/
|
|
37
|
+
closeButtonVisible?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* When set, overrides the default role value. Default role is "dialog"
|
|
40
|
+
* Roles other than dialog and alertdialog aren't appropriate for this
|
|
41
|
+
* component
|
|
42
|
+
*/
|
|
43
|
+
role?: "dialog" | "alertdialog";
|
|
44
|
+
/**
|
|
45
|
+
* Optional custom styles.
|
|
46
|
+
*/
|
|
47
|
+
styles?: DrawerDialogStyles;
|
|
48
|
+
/**
|
|
49
|
+
* Test ID used for e2e testing.
|
|
50
|
+
*/
|
|
51
|
+
testId?: string;
|
|
52
|
+
/**
|
|
53
|
+
* The ID of the content describing this dialog, if applicable.
|
|
54
|
+
*/
|
|
55
|
+
"aria-describedby"?: string;
|
|
56
|
+
};
|
|
57
|
+
export type DrawerDialogStyles = {
|
|
58
|
+
root?: StyleType;
|
|
59
|
+
dialog?: StyleType;
|
|
60
|
+
panel?: StyleType;
|
|
61
|
+
closeButton?: StyleType;
|
|
62
|
+
};
|
|
63
|
+
type RenderProps = {
|
|
64
|
+
title: React.ReactNode | string;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* A dialog to be used with DrawerLauncher that builds on top of FlexibleDialog.
|
|
68
|
+
* It can receive a custom background (image or color), a title for the main
|
|
69
|
+
* heading, and that title can optionally render in the content area through
|
|
70
|
+
* a render prop.
|
|
71
|
+
*
|
|
72
|
+
* One of the following is required for labeling the dialog:
|
|
73
|
+
* - title content (React element or string)
|
|
74
|
+
* - aria-label (string)
|
|
75
|
+
* - aria-labelledby (string ID reference)
|
|
76
|
+
*
|
|
77
|
+
* ### Usage
|
|
78
|
+
*
|
|
79
|
+
* ```jsx
|
|
80
|
+
* import {DrawerDialog} from "@khanacademy/wonder-blocks-modal";
|
|
81
|
+
* import {BodyText} from "@khanacademy/wonder-blocks-typography";
|
|
82
|
+
*
|
|
83
|
+
* <DrawerDialog
|
|
84
|
+
* title={<Heading size="xxlarge" id="main-heading">Select mission</Heading>}
|
|
85
|
+
* content={
|
|
86
|
+
* <BodyText>
|
|
87
|
+
* {`Lorem ipsum dolor sit amet, consectetur adipiscing
|
|
88
|
+
* elit, sed do eiusmod tempor incididunt ut labore et
|
|
89
|
+
* dolore magna aliqua. Ut enim ad minim veniam,
|
|
90
|
+
* quis nostrud exercitation ullamco laboris nisi ut
|
|
91
|
+
* aliquip ex ea commodo consequat. Duis aute irure
|
|
92
|
+
* dolor in reprehenderit in voluptate velit esse
|
|
93
|
+
* cillum dolore eu fugiat nulla pariatur. Excepteur
|
|
94
|
+
* sint occaecat cupidatat non proident, sunt in culpa
|
|
95
|
+
* qui officia deserunt mollit anim id est.`}
|
|
96
|
+
* </BodyText>
|
|
97
|
+
* }
|
|
98
|
+
* />
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
declare const DrawerDialog: React.ForwardRefExoticComponent<Props & React.RefAttributes<HTMLDivElement>>;
|
|
102
|
+
export default DrawerDialog;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { StyleType } from "@khanacademy/wonder-blocks-core";
|
|
3
|
+
import type { DrawerAlignment, ModalElement } from "../util/types";
|
|
4
|
+
import { DEFAULT_DRAWER_TIMING_DURATION_MS, DEFAULT_DRAWER_ANIMATED, DEFAULT_DRAWER_BACKDROP_DISMISS_ENABLED, DEFAULT_DRAWER_IS_EXITING } from "../util/drawer-context";
|
|
5
|
+
import type { DrawerDialogStyles } from "./drawer-dialog";
|
|
6
|
+
/**
|
|
7
|
+
* Re-exported centralized default values for the drawer system.
|
|
8
|
+
*
|
|
9
|
+
* These constants provide the default behavior for all drawer components
|
|
10
|
+
* and can be imported by consumers who need to reference or override defaults.
|
|
11
|
+
*/
|
|
12
|
+
export { DEFAULT_DRAWER_TIMING_DURATION_MS, DEFAULT_DRAWER_ANIMATED, DEFAULT_DRAWER_BACKDROP_DISMISS_ENABLED, DEFAULT_DRAWER_IS_EXITING, };
|
|
13
|
+
/** @deprecated Use DEFAULT_DRAWER_TIMING_DURATION_MS instead. */
|
|
14
|
+
export declare const DEFAULT_TIMING_DURATION_MS = 400;
|
|
15
|
+
/**
|
|
16
|
+
* A more restrictive type for DrawerLauncher that encourages the use of DrawerDialog.
|
|
17
|
+
*/
|
|
18
|
+
type DrawerModalElement = ModalElement;
|
|
19
|
+
/**
|
|
20
|
+
* Function type that should return a DrawerDialog for proper drawer functionality
|
|
21
|
+
*/
|
|
22
|
+
type DrawerModalFunction = (props: {
|
|
23
|
+
closeModal: () => void;
|
|
24
|
+
styles?: DrawerDialogStyles;
|
|
25
|
+
}) => DrawerModalElement;
|
|
26
|
+
declare const _default: {
|
|
27
|
+
(props: {
|
|
28
|
+
children?: ((arg1: {
|
|
29
|
+
openModal: () => unknown;
|
|
30
|
+
}) => React.ReactNode) | undefined;
|
|
31
|
+
readonly testId?: string | undefined;
|
|
32
|
+
readonly initialFocusId?: string | undefined;
|
|
33
|
+
readonly backdropDismissEnabled?: boolean | undefined;
|
|
34
|
+
readonly modal: DrawerModalElement | DrawerModalFunction;
|
|
35
|
+
readonly closedFocusId?: string | undefined;
|
|
36
|
+
opened?: boolean | undefined;
|
|
37
|
+
onClose?: (() => unknown) | (() => unknown) | undefined;
|
|
38
|
+
readonly styles?: {
|
|
39
|
+
container?: StyleType;
|
|
40
|
+
} | undefined;
|
|
41
|
+
readonly alignment: DrawerAlignment;
|
|
42
|
+
readonly animated?: boolean | undefined;
|
|
43
|
+
readonly timingDuration?: number | undefined;
|
|
44
|
+
}): React.JSX.Element;
|
|
45
|
+
displayName: string;
|
|
46
|
+
};
|
|
47
|
+
export default _default;
|
|
@@ -44,13 +44,9 @@ type Props = AccessibleDialogProps & {
|
|
|
44
44
|
/**
|
|
45
45
|
* Optional custom styles.
|
|
46
46
|
*/
|
|
47
|
-
styles?:
|
|
48
|
-
root?: StyleType;
|
|
49
|
-
panel?: StyleType;
|
|
50
|
-
closeButton?: StyleType;
|
|
51
|
-
};
|
|
47
|
+
styles?: FlexibleDialogStyles;
|
|
52
48
|
/**
|
|
53
|
-
* Test ID used for e2e testing.
|
|
49
|
+
* Test ID used for e2e testing.
|
|
54
50
|
*/
|
|
55
51
|
testId?: string;
|
|
56
52
|
/**
|
|
@@ -58,14 +54,23 @@ type Props = AccessibleDialogProps & {
|
|
|
58
54
|
*/
|
|
59
55
|
"aria-describedby"?: string;
|
|
60
56
|
};
|
|
57
|
+
export type FlexibleDialogStyles = {
|
|
58
|
+
root?: StyleType;
|
|
59
|
+
dialog?: StyleType;
|
|
60
|
+
panel?: StyleType;
|
|
61
|
+
closeButton?: StyleType;
|
|
62
|
+
};
|
|
61
63
|
type RenderProps = {
|
|
62
64
|
title: React.ReactNode | string;
|
|
63
65
|
};
|
|
64
66
|
/**
|
|
65
|
-
* A
|
|
67
|
+
* A flexible modal variant with fewer layout constraints. It can receive
|
|
66
68
|
* a custom background (image or color), a title for the main heading, and that
|
|
67
69
|
* title can optionally render in the content area through a render prop.
|
|
68
70
|
*
|
|
71
|
+
* It can be used directly with `ModalLauncher`. In a `DrawerLauncher`, use
|
|
72
|
+
* `DrawerDialog` instead, which is a wrapper around `FlexibleDialog`.
|
|
73
|
+
*
|
|
69
74
|
* One of the following is required for labeling the dialog:
|
|
70
75
|
* - title content (React element or string)
|
|
71
76
|
* - aria-label (string)
|
|
@@ -95,5 +100,5 @@ type RenderProps = {
|
|
|
95
100
|
* />
|
|
96
101
|
* ```
|
|
97
102
|
*/
|
|
98
|
-
declare const FlexibleDialog:
|
|
103
|
+
declare const FlexibleDialog: React.ForwardRefExoticComponent<Props & React.RefAttributes<HTMLDivElement>>;
|
|
99
104
|
export default FlexibleDialog;
|
package/dist/es/index.js
CHANGED
|
@@ -15,11 +15,11 @@ const theme$1={root:{border:{radius:border.radius.radius_040}},dialog:{layout:{p
|
|
|
15
15
|
|
|
16
16
|
var theme = mapValuesToCssVars(theme$1,"--wb-c-modal-");
|
|
17
17
|
|
|
18
|
-
const ModalDialog=React.forwardRef(function ModalDialog(props,ref){const{above,below,role="dialog",style,children,testId,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledBy,"aria-describedby":ariaDescribedBy}=props;return jsxs(View,{style:[styles$
|
|
18
|
+
const ModalDialog=React.forwardRef(function ModalDialog(props,ref){const{above,below,role="dialog",style,children,testId,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledBy,"aria-describedby":ariaDescribedBy}=props;return jsxs(View,{style:[styles$7.wrapper,style],children:[below&&jsx(View,{style:styles$7.below,children:below}),jsx(View,{role:role,"aria-modal":"true","aria-label":ariaLabel,"aria-labelledby":ariaLabelledBy,"aria-describedby":ariaDescribedBy,ref:ref,style:styles$7.dialog,testId:testId,children:children}),above&&jsx(View,{style:styles$7.above,children:above})]})});const small$2="@media (max-width: 767px)";const styles$7=StyleSheet.create({wrapper:{color:semanticColor.core.foreground.neutral.strong,flexDirection:"row",alignItems:"stretch",width:"100%",height:"100%",position:"relative",boxShadow:theme.dialog.shadow.default,borderRadius:theme.root.border.radius,[small$2]:{padding:theme.dialog.layout.padding,flexDirection:"column"}},dialog:{width:"100%",height:"100%",borderRadius:theme.root.border.radius,overflow:"hidden"},above:{pointerEvents:"none",position:"absolute",top:0,left:0,bottom:0,right:0,zIndex:1},below:{pointerEvents:"none",position:"absolute",top:0,left:0,bottom:0,right:0,zIndex:-1}});ModalDialog.displayName="ModalDialog";
|
|
19
19
|
|
|
20
|
-
function ModalFooter({children}){return jsx(View,{style:styles$
|
|
20
|
+
function ModalFooter({children}){return jsx(View,{style:styles$6.footer,children:children})}ModalFooter.__IS_MODAL_FOOTER__=true;ModalFooter.isComponentOf=instance=>{return instance&&instance.type&&instance.type.__IS_MODAL_FOOTER__};const styles$6=StyleSheet.create({footer:{flex:"0 0 auto",boxSizing:"border-box",minHeight:sizing.size_640,paddingInline:theme.footer.layout.padding.inline,paddingBlock:theme.footer.layout.padding.block,display:"flex",flexDirection:"row",alignItems:"center",justifyContent:"flex-end",boxShadow:`0px -1px 0px ${semanticColor.core.border.neutral.subtle}`}});
|
|
21
21
|
|
|
22
|
-
function ModalHeader(props){const{breadcrumbs=undefined,subtitle=undefined,testId,title,titleId}=props;if(subtitle&&breadcrumbs){throw new Error("'subtitle' and 'breadcrumbs' can't be used together")}return jsxs(View,{style:[styles$
|
|
22
|
+
function ModalHeader(props){const{breadcrumbs=undefined,subtitle=undefined,testId,title,titleId}=props;if(subtitle&&breadcrumbs){throw new Error("'subtitle' and 'breadcrumbs' can't be used together")}return jsxs(View,{style:[styles$5.header],testId:testId,children:[breadcrumbs&&jsx(View,{style:styles$5.breadcrumbs,children:breadcrumbs}),jsx(Heading,{size:"large",tag:"h2",style:styles$5.title,id:titleId,testId:testId&&`${testId}-title`,children:title}),subtitle&&jsx(BodyText,{size:"small",style:styles$5.subtitle,testId:testId&&`${testId}-subtitle`,children:subtitle})]})}const small$1="@media (max-width: 767px)";const styles$5=StyleSheet.create({header:{boxShadow:`0px 1px 0px ${semanticColor.core.border.neutral.subtle}`,display:"flex",flexDirection:"column",minHeight:66,paddingBlock:theme.header.layout.padding.block,paddingInline:theme.header.layout.padding.inline.default,position:"relative",width:"100%",[small$1]:{paddingInline:theme.header.layout.padding.inline.small}},breadcrumbs:{color:semanticColor.core.foreground.neutral.default,marginBlockEnd:theme.header.layout.gap.default},title:{paddingInlineEnd:theme.header.layout.gap.title.default,[small$1]:{paddingInlineEnd:theme.header.layout.gap.title.small}},subtitle:{color:semanticColor.core.foreground.neutral.default,marginBlockStart:theme.header.layout.gap.default}});
|
|
23
23
|
|
|
24
24
|
const FOCUSABLE_ELEMENTS$1='button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';class FocusTrap extends React.Component{tryToFocus(node){if(node instanceof HTMLElement){try{node.focus();}catch(e){}return document.activeElement===node}}focusElementIn(isLast){const modalRootAsHtmlEl=this.modalRoot;const focusableNodes=Array.from(modalRootAsHtmlEl.querySelectorAll(FOCUSABLE_ELEMENTS$1)).filter(element=>{const style=window.getComputedStyle(element);return style.display!=="none"&&style.visibility!=="hidden"});const nodeIndex=!isLast?focusableNodes.length-1:0;const focusableNode=focusableNodes[nodeIndex];this.tryToFocus(focusableNode);}render(){const{style}=this.props;return jsxs(React.Fragment,{children:[jsx("div",{tabIndex:0,className:"modal-focus-trap-first",onFocus:this.handleFocusMoveToLast,style:{position:"fixed"}}),jsx(View,{style:style,ref:this.getModalRoot,children:this.props.children}),jsx("div",{tabIndex:0,className:"modal-focus-trap-last",onFocus:this.handleFocusMoveToFirst,style:{position:"fixed"}})]})}constructor(...args){super(...args),this.getModalRoot=node=>{if(!node){return}const modalRoot=ReactDOM.findDOMNode(node);if(!modalRoot){throw new Error("Assertion error: modal root should exist after mount")}this.modalRoot=modalRoot;},this.handleFocusMoveToLast=()=>{this.focusElementIn(false);},this.handleFocusMoveToFirst=()=>{this.focusElementIn(true);};}}
|
|
25
25
|
|
|
@@ -27,26 +27,36 @@ const ModalLauncherPortalAttributeName="data-modal-launcher-portal";
|
|
|
27
27
|
|
|
28
28
|
const FOCUSABLE_ELEMENTS="a[href], details, input, textarea, select, button";function findFocusableNodes(root){return Array.from(root.querySelectorAll(FOCUSABLE_ELEMENTS))}
|
|
29
29
|
|
|
30
|
-
class ModalBackdrop extends React.Component{componentDidMount(){const node=ReactDOM.findDOMNode(this);if(!node){return}const firstFocusableElement=this._getInitialFocusElement(node)||this._getFirstFocusableElement(node)||this._getDialogElement(node);setTimeout(()=>{firstFocusableElement.focus();},0);}_getInitialFocusElement(node){const{initialFocusId}=this.props;if(!initialFocusId){return null}return ReactDOM.findDOMNode(node.querySelector(`#${initialFocusId}`))}_getFirstFocusableElement(node){const focusableElements=findFocusableNodes(node);if(!focusableElements){return null}return focusableElements[0]}_getDialogElement(node){const dialogElement=ReactDOM.findDOMNode(node.querySelector('[role="dialog"]'));dialogElement.tabIndex=-1;return dialogElement}render(){const{children,testId}=this.props;const backdropProps={[ModalLauncherPortalAttributeName]:true};return jsx(View,{style:styles$
|
|
30
|
+
class ModalBackdrop extends React.Component{componentDidMount(){const node=ReactDOM.findDOMNode(this);if(!node){return}const firstFocusableElement=this._getInitialFocusElement(node)||this._getFirstFocusableElement(node)||this._getDialogElement(node);setTimeout(()=>{firstFocusableElement.focus();},0);}_getInitialFocusElement(node){const{initialFocusId}=this.props;if(!initialFocusId){return null}return ReactDOM.findDOMNode(node.querySelector(`#${initialFocusId}`))}_getFirstFocusableElement(node){const focusableElements=findFocusableNodes(node);if(!focusableElements){return null}return focusableElements[0]}_getDialogElement(node){const dialogElement=ReactDOM.findDOMNode(node.querySelector('[role="dialog"]'));dialogElement.tabIndex=-1;return dialogElement}render(){const{children,testId}=this.props;const backdropProps={[ModalLauncherPortalAttributeName]:true};return jsx(View,{style:styles$4.modalPositioner,onMouseDown:this.handleMouseDown,onMouseUp:this.handleMouseUp,testId:testId,...backdropProps,children:children})}constructor(...args){super(...args),this._mousePressedOutside=false,this.handleMouseDown=e=>{this._mousePressedOutside=e.target===e.currentTarget;},this.handleMouseUp=e=>{if(e.target===e.currentTarget&&this._mousePressedOutside){this.props.onCloseModal();}this._mousePressedOutside=false;};}}const styles$4=StyleSheet.create({modalPositioner:{position:"fixed",left:0,top:0,width:"100%",height:"100%",alignItems:"center",justifyContent:"center",overflow:"auto",background:semanticColor.core.background.overlay.default}});
|
|
31
31
|
|
|
32
32
|
const needsHackyMobileSafariScrollDisabler=(()=>{if(typeof window==="undefined"){return false}const userAgent=window.navigator.userAgent;return userAgent.indexOf("iPad")>-1||userAgent.indexOf("iPhone")>-1})();class ScrollDisabler extends React.Component{componentDidMount(){if(ScrollDisabler.numModalsOpened===0){const body=document.body;if(!body){throw new Error("couldn't find document.body")}ScrollDisabler.oldOverflow=body.style.overflow;ScrollDisabler.oldScrollY=window.scrollY;if(needsHackyMobileSafariScrollDisabler){ScrollDisabler.oldPosition=body.style.position;ScrollDisabler.oldWidth=body.style.width;ScrollDisabler.oldTop=body.style.top;}body.style.overflow="hidden";if(needsHackyMobileSafariScrollDisabler){body.style.position="fixed";body.style.width="100%";body.style.top=`${-ScrollDisabler.oldScrollY}px`;}}ScrollDisabler.numModalsOpened++;}componentWillUnmount(){ScrollDisabler.numModalsOpened--;if(ScrollDisabler.numModalsOpened===0){const body=document.body;if(!body){throw new Error("couldn't find document.body")}body.style.overflow=ScrollDisabler.oldOverflow;if(needsHackyMobileSafariScrollDisabler){body.style.position=ScrollDisabler.oldPosition;body.style.width=ScrollDisabler.oldWidth;body.style.top=ScrollDisabler.oldTop;}if(typeof window!=="undefined"&&window.scrollTo){window.scrollTo(0,ScrollDisabler.oldScrollY);}}}render(){return null}}ScrollDisabler.numModalsOpened=0;
|
|
33
33
|
|
|
34
34
|
const defaultContext={closeModal:undefined};const ModalContext=React.createContext(defaultContext);ModalContext.displayName="ModalContext";
|
|
35
35
|
|
|
36
|
-
class ModalLauncher extends React.Component{static getDerivedStateFromProps(props,state){if(typeof props.opened==="boolean"&&props.children){console.warn("'children' and 'opened' can't be used together");}if(typeof props.opened==="boolean"&&!props.onClose){console.warn("'onClose' should be used with 'opened'");}if(typeof props.opened!=="boolean"&&!props.children){console.warn("either 'children' or 'opened' must be set");}return {opened:typeof props.opened==="boolean"?props.opened:state.opened}}componentDidUpdate(prevProps){if(!prevProps.opened&&this.props.opened){this._saveLastElementFocused();}}_renderModal(){if(typeof this.props.modal==="function"){return this.props.modal({closeModal:this.handleCloseModal})}else {return this.props.modal}}render(){const renderedChildren=this.props.children?this.props.children({openModal:this._openModal}):null;const{body}=document;if(!body){return null}return jsxs(ModalContext.Provider,{value:{closeModal:this.handleCloseModal},children:[renderedChildren,this.state.opened&&ReactDOM.createPortal(jsx(FocusTrap,{style:styles$
|
|
36
|
+
class ModalLauncher extends React.Component{static getDerivedStateFromProps(props,state){if(typeof props.opened==="boolean"&&props.children){console.warn("'children' and 'opened' can't be used together");}if(typeof props.opened==="boolean"&&!props.onClose){console.warn("'onClose' should be used with 'opened'");}if(typeof props.opened!=="boolean"&&!props.children){console.warn("either 'children' or 'opened' must be set");}return {opened:typeof props.opened==="boolean"?props.opened:state.opened}}componentDidUpdate(prevProps){if(!prevProps.opened&&this.props.opened){this._saveLastElementFocused();}}_renderModal(){if(typeof this.props.modal==="function"){return this.props.modal({closeModal:this.handleCloseModal})}else {return this.props.modal}}render(){const renderedChildren=this.props.children?this.props.children({openModal:this._openModal}):null;const{body}=document;if(!body){return null}return jsxs(ModalContext.Provider,{value:{closeModal:this.handleCloseModal},children:[renderedChildren,this.state.opened&&ReactDOM.createPortal(jsx(FocusTrap,{style:styles$3.container,children:jsx(ModalBackdrop,{initialFocusId:this.props.initialFocusId,testId:this.props.testId,onCloseModal:this.props.backdropDismissEnabled?this.handleCloseModal:()=>{},children:this._renderModal()})}),body),this.state.opened&&jsx(ModalLauncherKeypressListener,{onClose:this.handleCloseModal}),this.state.opened&&jsx(ScrollDisabler,{})]})}constructor(...args){super(...args),this.state={opened:false},this._saveLastElementFocused=()=>{this.lastElementFocusedOutsideModal=document.activeElement;},this._openModal=()=>{this._saveLastElementFocused();this.setState({opened:true});},this._returnFocus=()=>{const{closedFocusId,schedule}=this.props;const lastElement=this.lastElementFocusedOutsideModal;if(closedFocusId){const focusElement=ReactDOM.findDOMNode(document.getElementById(closedFocusId));if(focusElement){schedule.animationFrame(()=>{focusElement.focus();});return}}if(lastElement!=null){schedule.animationFrame(()=>{lastElement.focus();});}},this.handleCloseModal=()=>{this.setState({opened:false},()=>{const{onClose}=this.props;onClose?.();this._returnFocus();});};}}ModalLauncher.defaultProps={backdropDismissEnabled:true};class ModalLauncherKeypressListener extends React.Component{componentDidMount(){window.addEventListener("keyup",this._handleKeyup);}componentWillUnmount(){window.removeEventListener("keyup",this._handleKeyup);}render(){return null}constructor(...args){super(...args),this._handleKeyup=e=>{if(e.key==="Escape"){e.preventDefault();e.stopPropagation();this.props.onClose();}};}}const styles$3=StyleSheet.create({container:{zIndex:1080}});var modalLauncher = withActionScheduler(ModalLauncher);
|
|
37
37
|
|
|
38
|
-
function ModalContent(props){const{scrollOverflow,style,children}=props;return jsx(View,{style:[styles$
|
|
38
|
+
function ModalContent(props){const{scrollOverflow,style,children}=props;return jsx(View,{style:[styles$2.wrapper,scrollOverflow&&styles$2.scrollOverflow],children:jsx(View,{style:[styles$2.content,style],children:children})})}ModalContent.__IS_MODAL_CONTENT__=true;ModalContent.isComponentOf=instance=>{return instance&&instance.type&&instance.type.__IS_MODAL_CONTENT__};const small="@media (max-width: 767px)";const styles$2=StyleSheet.create({wrapper:{flex:1,display:"block"},scrollOverflow:{overflow:"auto"},content:{flex:1,minHeight:"100%",padding:theme.panel.layout.gap.default,boxSizing:"border-box",[small]:{paddingInline:theme.panel.layout.gap.small}}});ModalContent.defaultProps={scrollOverflow:true};
|
|
39
39
|
|
|
40
40
|
class CloseButton extends React.Component{render(){const{onClick,style,testId}=this.props;return jsx(ModalContext.Consumer,{children:({closeModal})=>{if(closeModal&&onClick){throw new Error("You've specified 'onClose' on a modal when using ModalLauncher. Please specify 'onClose' on the ModalLauncher instead")}return jsx(IconButton,{icon:xIcon,"aria-label":"Close modal",onClick:onClick||closeModal,kind:"tertiary",actionType:"neutral",style:style,testId:testId})}})}}
|
|
41
41
|
|
|
42
|
-
function ModalPanel({closeButtonVisible=true,scrollOverflow=true,content,footer,header,onClose,style,testId}){const renderMainContent=React.useCallback(()=>{const mainContent=ModalContent.isComponentOf(content)?content:jsx(ModalContent,{children:content});if(!mainContent){return mainContent}return React.cloneElement(mainContent,{scrollOverflow,style:[!!footer&&styles.hasFooter,mainContent.props.style]})},[content,footer,scrollOverflow]);const mainContent=renderMainContent();return jsxs(View,{style:[styles.wrapper,style],testId:testId&&`${testId}-panel`,children:[closeButtonVisible&&jsx(CloseButton,{onClick:onClose,style:[styles.closeButton],testId:testId&&`${testId}-close`}),header,mainContent,!footer||ModalFooter.isComponentOf(footer)?footer:jsx(ModalFooter,{children:footer})]})}ModalPanel.defaultProps={closeButtonVisible:true,scrollOverflow:true};const styles=StyleSheet.create({wrapper:{flex:"1 1 auto",flexDirection:"column",background:semanticColor.
|
|
42
|
+
function ModalPanel({closeButtonVisible=true,scrollOverflow=true,content,footer,header,onClose,style,testId}){const renderMainContent=React.useCallback(()=>{const mainContent=ModalContent.isComponentOf(content)?content:jsx(ModalContent,{children:content});if(!mainContent){return mainContent}return React.cloneElement(mainContent,{scrollOverflow,style:[!!footer&&styles$1.hasFooter,mainContent.props.style]})},[content,footer,scrollOverflow]);const mainContent=renderMainContent();return jsxs(View,{style:[styles$1.wrapper,style],testId:testId&&`${testId}-panel`,children:[closeButtonVisible&&jsx(CloseButton,{onClick:onClose,style:[styles$1.closeButton],testId:testId&&`${testId}-close`}),header,mainContent,!footer||ModalFooter.isComponentOf(footer)?footer:jsx(ModalFooter,{children:footer})]})}ModalPanel.defaultProps={closeButtonVisible:true,scrollOverflow:true};const styles$1=StyleSheet.create({wrapper:{flex:"1 1 auto",flexDirection:"column",background:semanticColor.core.background.base.default,boxSizing:"border-box",overflow:"hidden",height:"100%",width:"100%"},closeButton:{position:"absolute",insetInlineEnd:theme.closeButton.layout.gapRight,top:theme.closeButton.layout.gapTop,zIndex:1,":focus":focusStyles.focus[":focus-visible"]},hasFooter:{paddingBlockEnd:theme.panel.layout.gap.default}});
|
|
43
43
|
|
|
44
44
|
class OnePaneDialog extends React.Component{renderHeader(uniqueId){const{title,breadcrumbs=undefined,subtitle=undefined,testId}=this.props;if(breadcrumbs){return jsx(ModalHeader,{title:title,breadcrumbs:breadcrumbs,titleId:uniqueId,testId:testId&&`${testId}-header`})}else if(subtitle){return jsx(ModalHeader,{title:title,subtitle:subtitle,titleId:uniqueId,testId:testId&&`${testId}-header`})}else {return jsx(ModalHeader,{title:title,titleId:uniqueId,testId:testId&&`${testId}-header`})}}render(){const{onClose,footer,content,above,below,style,closeButtonVisible,testId,titleId,role,"aria-describedby":ariaDescribedBy}=this.props;return jsx(MediaLayout,{styleSheets:styleSheets,children:({styles})=>jsx(Id,{id:titleId,children:uniqueId=>jsx(ModalDialog,{style:[styles.dialog,style],above:above,below:below,testId:testId,"aria-labelledby":uniqueId,"aria-describedby":ariaDescribedBy,role:role,children:jsx(ModalPanel,{onClose:onClose,header:this.renderHeader(uniqueId),content:content,footer:footer,closeButtonVisible:closeButtonVisible,testId:testId})})})})}}OnePaneDialog.defaultProps={closeButtonVisible:true};const styleSheets={small:StyleSheet.create({dialog:{width:"100%",height:"100%",overflow:"hidden"}}),mdOrLarger:StyleSheet.create({dialog:{width:"93.75%",maxWidth:576,height:"81.25%",maxHeight:624}})};
|
|
45
45
|
|
|
46
|
-
function FlexiblePanel({closeButtonVisible=true,content,title,onClose,styles,testId}){const renderMainContent=React.useCallback(()=>{const contentNode=typeof content==="function"?content({title}):jsxs(Fragment,{children:[title,content]});const mainContent=ModalContent.isComponentOf(contentNode)?contentNode:jsx(ModalContent,{children:contentNode});if(!mainContent){return mainContent}return React.cloneElement(mainContent,{style:[mainContent.props.style]})},[title,content]);const mainContent=renderMainContent();const defaultBackgroundStyle={backgroundColor:semanticColor.
|
|
46
|
+
function FlexiblePanel({closeButtonVisible=true,content,title,onClose,styles,testId}){const panelRef=React.useRef(null);const renderMainContent=React.useCallback(()=>{const contentNode=typeof content==="function"?content({title}):jsxs(Fragment,{children:[title,content]});const mainContent=ModalContent.isComponentOf(contentNode)?contentNode:jsx(ModalContent,{children:contentNode});if(!mainContent){return mainContent}return React.cloneElement(mainContent,{style:[mainContent.props.style]})},[title,content]);const mainContent=renderMainContent();const defaultBackgroundStyle={backgroundColor:semanticColor.core.background.base.default};const combinedBackgroundStyles={...defaultBackgroundStyle,...styles?.panel};return jsxs(View,{style:[componentStyles$1.wrapper,combinedBackgroundStyles],testId:testId&&`${testId}-panel`,ref:panelRef,children:[closeButtonVisible&&jsx(CloseButton,{onClick:onClose,style:[componentStyles$1.closeButton,styles?.closeButton],testId:testId&&`${testId}-close`}),mainContent]})}FlexiblePanel.defaultProps={closeButtonVisible:true,scrollOverflow:true};const componentStyles$1=StyleSheet.create({wrapper:{flex:"1 1 auto",flexDirection:"column",boxSizing:"border-box",overflow:"hidden",height:"100%",width:"100%"},closeButton:{position:"absolute",insetInlineEnd:theme.closeButton.layout.gapRight,top:theme.closeButton.layout.gapTop,zIndex:1,":focus":focusStyles.focus[":focus-visible"]}});
|
|
47
47
|
|
|
48
|
-
const FlexibleDialog=({onClose,title,content,styles,closeButtonVisible=true,testId,titleId,role,...accessibilityProps}
|
|
48
|
+
const FlexibleDialog=React.forwardRef(function FlexibleDialog(props,ref){const{onClose,title,content,styles,closeButtonVisible=true,testId,titleId,role="dialog",...accessibilityProps}=props;const uniqueId=React.useId();const headingId=titleId??uniqueId;const renderedTitle=title==null?null:typeof title==="string"?jsx(Heading,{id:headingId,children:title}):React.cloneElement(title,{id:headingId,testId:"title-heading-wrapper"});return jsx(View,{style:[componentStyles.root,styles?.root],children:jsx(View,{role:role,"aria-modal":"true","aria-label":accessibilityProps["aria-label"],"aria-labelledby":headingId,"aria-describedby":accessibilityProps["aria-describedby"],ref:ref,testId:testId,style:[componentStyles.dialog,styles?.dialog],children:jsx(FlexiblePanel,{styles:{panel:styles?.panel,closeButton:styles?.closeButton},onClose:onClose,title:renderedTitle,content:content,closeButtonVisible:closeButtonVisible,testId:testId})})})});const componentStyles=StyleSheet.create({root:{boxShadow:theme.dialog.shadow.default,color:semanticColor.core.foreground.neutral.strong,overflow:"auto",position:"relative",willChange:"transform, opacity",height:"auto",maxHeight:"100vh",maxWidth:576,width:"93.75%",[breakpoint.mediaQuery.sm]:{height:"100vh",maxHeight:"100vh",width:"100%"}}});
|
|
49
|
+
|
|
50
|
+
const DEFAULT_DRAWER_TIMING_DURATION_MS=400;const DEFAULT_DRAWER_ANIMATED=true;const DEFAULT_DRAWER_BACKDROP_DISMISS_ENABLED=true;const DEFAULT_DRAWER_IS_EXITING=false;const defaultDrawerContextValue={animated:DEFAULT_DRAWER_ANIMATED,isExiting:DEFAULT_DRAWER_IS_EXITING,timingDuration:DEFAULT_DRAWER_TIMING_DURATION_MS};const DrawerContext=React.createContext(defaultDrawerContextValue);const useDrawerContext=()=>{return React.useContext(DrawerContext)};
|
|
51
|
+
|
|
52
|
+
const DrawerBackdrop=({children,testId,initialFocusId,onCloseModal})=>{const{alignment,animated,timingDuration,isExiting}=useDrawerContext();const[mousePressedOutside,setMousePressedOutside]=React.useState(false);const backdropRef=React.useRef(null);const computedTimingDuration=animated?timingDuration:0;const getInitialFocusElement=React.useCallback(container=>{if(!initialFocusId){return null}return container.querySelector(`#${initialFocusId}`)},[initialFocusId]);const getFirstFocusableElement=React.useCallback(container=>{const focusableElements=findFocusableNodes(container);if(!focusableElements){return null}return focusableElements[0]},[]);const getDialogElement=React.useCallback(container=>{const dialogElement=container.querySelector('[role="dialog"]');if(dialogElement){dialogElement.tabIndex=-1;}return dialogElement},[]);React.useEffect(()=>{const container=backdropRef.current;if(!container){return}const firstFocusableElement=getInitialFocusElement(container)||getFirstFocusableElement(container)||getDialogElement(container);if(firstFocusableElement){setTimeout(()=>{firstFocusableElement.focus();},computedTimingDuration);}},[getInitialFocusElement,getFirstFocusableElement,getDialogElement,computedTimingDuration]);const handleMouseDown=e=>{if(e.target===e.currentTarget){setMousePressedOutside(true);}};const handleMouseUp=e=>{if(e.target===e.currentTarget&&mousePressedOutside){onCloseModal();}setMousePressedOutside(false);};const backdropProps={[ModalLauncherPortalAttributeName]:true};return jsx(View,{ref:backdropRef,style:[styles.drawerPositioner,alignment&&styles[alignment],animated&&(isExiting?styles.fadeOut:styles.fadeIn)].filter(Boolean),onMouseDown:handleMouseDown,onMouseUp:handleMouseUp,testId:testId,...backdropProps,children:children})};const keyframes={fadeIn:{"0%":{opacity:0},"100%":{opacity:1}},fadeOut:{"0%":{opacity:1},"100%":{opacity:0}}};const styles=StyleSheet.create({drawerPositioner:{position:"fixed",left:0,top:0,width:"100%",height:"100%",display:"flex",overflow:"hidden",background:semanticColor.core.background.overlay.default},inlineStart:{alignItems:"flex-start",justifyContent:"flex-start"},inlineEnd:{alignItems:"flex-end",justifyContent:"flex-start"},blockEnd:{alignItems:"center",justifyContent:"flex-end"},fadeIn:{animationName:keyframes.fadeIn,animationDuration:"400ms",animationTimingFunction:"linear",animationFillMode:"forwards"},fadeOut:{animationName:keyframes.fadeOut,animationDuration:"400ms",animationTimingFunction:"linear",animationFillMode:"forwards"}});
|
|
53
|
+
|
|
54
|
+
const defaultProps={backdropDismissEnabled:DEFAULT_DRAWER_BACKDROP_DISMISS_ENABLED,defaultTimingDuration:DEFAULT_DRAWER_TIMING_DURATION_MS};const DrawerLauncher=props=>{const{modal,backdropDismissEnabled=defaultProps.backdropDismissEnabled,initialFocusId,closedFocusId,testId,opened:controlledOpened,onClose,children,schedule,alignment,styles,animated=DEFAULT_DRAWER_ANIMATED,timingDuration=defaultProps.defaultTimingDuration}=props;const[uncontrolledOpened,setUncontrolledOpened]=React.useState(false);const[isExiting,setIsExiting]=React.useState(false);const lastElementFocusedOutsideModalRef=React.useRef(null);const opened=typeof controlledOpened==="boolean"?controlledOpened:uncontrolledOpened;const saveLastElementFocused=React.useCallback(()=>{lastElementFocusedOutsideModalRef.current=document.activeElement;},[]);React.useEffect(()=>{if(controlledOpened&&!prevControlledOpened.current){saveLastElementFocused();}prevControlledOpened.current=controlledOpened;},[controlledOpened,saveLastElementFocused]);const prevControlledOpened=React.useRef(controlledOpened);const returnFocus=React.useCallback(()=>{const focusElement=closedFocusId?document.getElementById(closedFocusId):null;schedule.animationFrame(()=>{if(focusElement){focusElement.focus();}else if(lastElementFocusedOutsideModalRef.current){lastElementFocusedOutsideModalRef.current.focus();}});},[closedFocusId,schedule]);const handleCloseModal=React.useCallback(()=>{if(animated){setIsExiting(true);setTimeout(()=>{setIsExiting(false);if(typeof controlledOpened==="boolean"){onClose?.();}else {setUncontrolledOpened(false);onClose?.();}returnFocus();},timingDuration);}else {if(typeof controlledOpened==="boolean"){onClose?.();}else {setUncontrolledOpened(false);onClose?.();}returnFocus();}},[controlledOpened,onClose,returnFocus,animated,timingDuration]);const openModal=React.useCallback(()=>{saveLastElementFocused();setUncontrolledOpened(true);},[saveLastElementFocused]);const drawerDialogProps=React.useMemo(()=>({alignment,animated,isExiting,timingDuration}),[alignment,animated,isExiting,timingDuration]);const renderModal=React.useCallback(()=>{if(typeof modal==="function"){const renderedModal=modal({closeModal:handleCloseModal});if(!renderedModal){return null}return renderedModal}if(!modal){return null}return modal},[modal,handleCloseModal]);const renderedChildren=children?children({openModal}):null;const body=document.body;if(!body){return null}return jsxs(ModalContext.Provider,{value:{closeModal:handleCloseModal},children:[renderedChildren,opened&&!isExiting||opened&&isExiting&&animated?ReactDOM.createPortal(jsx(DrawerContext.Provider,{value:drawerDialogProps,children:jsx(FocusTrap,{style:styles?.container,children:jsx(DrawerBackdrop,{initialFocusId:initialFocusId,testId:testId,onCloseModal:backdropDismissEnabled?handleCloseModal:()=>{},children:renderModal()})})}),body):null,opened&&!isExiting&&jsxs(Fragment,{children:[jsx(DrawerLauncherKeypressListener,{onClose:handleCloseModal}),jsx(ScrollDisabler,{})]})]})};function DrawerLauncherKeypressListener({onClose}){React.useEffect(()=>{const handleKeyup=e=>{if(e.key==="Escape"){e.preventDefault();e.stopPropagation();onClose();}};window.addEventListener("keyup",handleKeyup);return ()=>window.removeEventListener("keyup",handleKeyup)},[onClose]);return null}DrawerLauncher.displayName="DrawerLauncher";var drawerLauncher = withActionScheduler(DrawerLauncher);
|
|
55
|
+
|
|
56
|
+
function useDirectionDetection(elementRef,options={}){const{direction:explicitDirection,defaultDirection="ltr"}=options;if(explicitDirection){return explicitDirection}if(elementRef?.current){const elementWithDir=elementRef.current.closest("[dir]");if(elementWithDir){const dirValue=elementWithDir.getAttribute("dir");return dirValue==="rtl"||dirValue==="ltr"?dirValue:defaultDirection}}const documentDir=document.documentElement.getAttribute("dir")||document.body.getAttribute("dir");if(documentDir==="rtl"||documentDir==="ltr"){return documentDir}return defaultDirection}
|
|
57
|
+
|
|
58
|
+
const DrawerDialog=React.forwardRef(function DrawerDialog(props,ref){const contextProps=useDrawerContext();const alignment=contextProps.alignment;const animated=contextProps.animated??DEFAULT_DRAWER_ANIMATED;const isExiting=contextProps.isExiting??DEFAULT_DRAWER_IS_EXITING;const timingDuration=contextProps.timingDuration??DEFAULT_DRAWER_TIMING_DURATION_MS;const{styles}=props;const direction=useDirectionDetection();const isRtl=direction==="rtl";const componentStyles=getComponentStyles({alignment,isRtl,animated,isExiting,timingDuration});const alignmentStyles=alignment&&componentStyles[alignment]||componentStyles.inlineEnd;return jsx(FlexibleDialog,{...props,ref:ref,styles:{root:[componentStyles.root,alignmentStyles,styles?.root].filter(Boolean),dialog:[componentStyles.dialog,styles?.dialog].filter(Boolean),panel:styles?.panel,closeButton:styles?.closeButton}})});const getTransformValue=(isRtl,alignment,percentage)=>{if(alignment==="blockEnd"){return `translate3d(0, ${percentage}%, 0)`}const directionMultiplier=isRtl?alignment==="inlineEnd"?-1:1:alignment==="inlineEnd"?1:-1;return `translate3d(${directionMultiplier*percentage}%, 0, 0)`};const createKeyframes=(isRtl,alignment)=>({slideIn:{"0%":{transform:getTransformValue(isRtl,alignment,100),opacity:0},"100%":{transform:getTransformValue(isRtl,alignment,0),opacity:1}},slideOut:{"0%":{transform:getTransformValue(isRtl,alignment,0),opacity:1},"100%":{transform:getTransformValue(isRtl,alignment,100),opacity:0}}});const getComponentStyles=({alignment,isRtl,animated,isExiting,timingDuration})=>{const alignmentKeyframes=alignment?createKeyframes(isRtl,alignment):null;return StyleSheet.create({root:{boxShadow:theme.dialog.shadow.default,color:semanticColor.core.foreground.neutral.strong,overflow:"auto",position:"relative",willChange:"transform, opacity",height:"100%",minHeight:"100vh",minWidth:breakpoint.width.xsMax,maxWidth:breakpoint.width.smMax,width:"100%",[breakpoint.mediaQuery.smOrSmaller]:{minWidth:"unset",maxWidth:"unset"}},dialog:{minHeight:alignment==="blockEnd"?"unset":"100vh",minWidth:"unset"},inlineStart:{animationName:animated&&alignmentKeyframes&&(isExiting?alignmentKeyframes.slideOut:alignmentKeyframes.slideIn),animationDuration:`${timingDuration}ms`,animationTimingFunction:"linear",animationFillMode:"forwards"},inlineEnd:{animationName:animated&&alignmentKeyframes&&(isExiting?alignmentKeyframes.slideOut:alignmentKeyframes.slideIn),animationDuration:`${timingDuration}ms`,animationTimingFunction:"linear",animationFillMode:"forwards"},blockEnd:{animationName:animated&&alignmentKeyframes&&(isExiting?alignmentKeyframes.slideOut:alignmentKeyframes.slideIn),animationDuration:`${timingDuration}ms`,animationTimingFunction:"linear",animationFillMode:"forwards",height:"auto",minHeight:"unset",maxWidth:"unset",[breakpoint.mediaQuery.smOrSmaller]:{height:"auto"}}})};DrawerDialog.displayName="DrawerDialog";
|
|
49
59
|
|
|
50
60
|
function maybeGetNextAncestorModalLauncherPortal(element){let candidateElement=element&&element.parentElement;while(candidateElement&&!candidateElement.hasAttribute(ModalLauncherPortalAttributeName)){candidateElement=candidateElement.parentElement;}return candidateElement}function maybeGetPortalMountedModalHostElement(element){return maybeGetNextAncestorModalLauncherPortal(element)}
|
|
51
61
|
|
|
52
|
-
export { FlexibleDialog, ModalDialog, ModalFooter, ModalHeader, modalLauncher as ModalLauncher, ModalPanel, OnePaneDialog, maybeGetPortalMountedModalHostElement };
|
|
62
|
+
export { DrawerDialog, drawerLauncher as DrawerLauncher, FlexibleDialog, ModalDialog, ModalFooter, ModalHeader, modalLauncher as ModalLauncher, ModalPanel, OnePaneDialog, maybeGetPortalMountedModalHostElement };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
type Direction = "ltr" | "rtl";
|
|
3
|
+
interface DirectionDetectionOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Explicit direction to use, bypassing DOM detection.
|
|
6
|
+
* Useful when direction is known from external sources like RequestInfo.
|
|
7
|
+
*/
|
|
8
|
+
direction?: Direction;
|
|
9
|
+
/**
|
|
10
|
+
* The default direction to use if no dir attribute is found.
|
|
11
|
+
* Defaults to "ltr".
|
|
12
|
+
*/
|
|
13
|
+
defaultDirection?: Direction;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Hook for detecting the text direction (LTR/RTL). Supports both element-specific
|
|
17
|
+
* and document-level direction detection.
|
|
18
|
+
*
|
|
19
|
+
* This hook performs DOM queries on each render and does not use state or
|
|
20
|
+
* observers, making it stable and predictable.
|
|
21
|
+
*
|
|
22
|
+
* @param elementRef - Optional ref to the element to start searching from.
|
|
23
|
+
* If provided, searches up the DOM tree from this element.
|
|
24
|
+
* If not provided, uses document-level detection.
|
|
25
|
+
* @param options - Configuration options for direction detection
|
|
26
|
+
* @returns The detected direction ("ltr" or "rtl")
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```tsx
|
|
30
|
+
* // Element-specific detection - searches up DOM tree from the element
|
|
31
|
+
* const MyComponent = () => {
|
|
32
|
+
* const ref = React.useRef<HTMLDivElement>(null);
|
|
33
|
+
* const direction = useDirectionDetection(ref);
|
|
34
|
+
*
|
|
35
|
+
* return (
|
|
36
|
+
* <div ref={ref}>
|
|
37
|
+
* Direction is: {direction}
|
|
38
|
+
* </div>
|
|
39
|
+
* );
|
|
40
|
+
* };
|
|
41
|
+
*
|
|
42
|
+
* // Document-level detection - uses document.documentElement.dir
|
|
43
|
+
* const MyOtherComponent = () => {
|
|
44
|
+
* const direction = useDirectionDetection();
|
|
45
|
+
* return <div>Page direction is: {direction}</div>;
|
|
46
|
+
* };
|
|
47
|
+
*
|
|
48
|
+
* // With explicit direction (e.g., from RequestInfo)
|
|
49
|
+
* const MyRTLComponent = ({requestInfo}) => {
|
|
50
|
+
* const direction = useDirectionDetection(undefined, {
|
|
51
|
+
* direction: requestInfo.isRTL ? "rtl" : "ltr"
|
|
52
|
+
* });
|
|
53
|
+
* return <div>Explicit direction: {direction}</div>;
|
|
54
|
+
* };
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export declare function useDirectionDetection(elementRef?: React.RefObject<HTMLElement | null>, options?: DirectionDetectionOptions): Direction;
|
|
58
|
+
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -5,5 +5,9 @@ import ModalLauncher from "./components/modal-launcher";
|
|
|
5
5
|
import ModalPanel from "./components/modal-panel";
|
|
6
6
|
import OnePaneDialog from "./components/one-pane-dialog";
|
|
7
7
|
import FlexibleDialog from "./components/flexible-dialog";
|
|
8
|
+
import DrawerLauncher from "./components/drawer-launcher";
|
|
9
|
+
import DrawerDialog, { type DrawerDialogStyles } from "./components/drawer-dialog";
|
|
8
10
|
import maybeGetPortalMountedModalHostElement from "./util/maybe-get-portal-mounted-modal-host-element";
|
|
9
|
-
|
|
11
|
+
import type { DrawerAlignment } from "./util/types";
|
|
12
|
+
export { ModalHeader, ModalFooter, ModalDialog, ModalPanel, ModalLauncher, OnePaneDialog, FlexibleDialog, DrawerLauncher, DrawerDialog, maybeGetPortalMountedModalHostElement, };
|
|
13
|
+
export type { DrawerAlignment, DrawerDialogStyles };
|
package/dist/index.js
CHANGED
|
@@ -44,11 +44,11 @@ const theme$1={root:{border:{radius:wonderBlocksTokens.border.radius.radius_040}
|
|
|
44
44
|
|
|
45
45
|
var theme = wonderBlocksTokens.mapValuesToCssVars(theme$1,"--wb-c-modal-");
|
|
46
46
|
|
|
47
|
-
const ModalDialog=React__namespace.forwardRef(function ModalDialog(props,ref){const{above,below,role="dialog",style,children,testId,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledBy,"aria-describedby":ariaDescribedBy}=props;return jsxRuntime.jsxs(wonderBlocksCore.View,{style:[styles$
|
|
47
|
+
const ModalDialog=React__namespace.forwardRef(function ModalDialog(props,ref){const{above,below,role="dialog",style,children,testId,"aria-label":ariaLabel,"aria-labelledby":ariaLabelledBy,"aria-describedby":ariaDescribedBy}=props;return jsxRuntime.jsxs(wonderBlocksCore.View,{style:[styles$7.wrapper,style],children:[below&&jsxRuntime.jsx(wonderBlocksCore.View,{style:styles$7.below,children:below}),jsxRuntime.jsx(wonderBlocksCore.View,{role:role,"aria-modal":"true","aria-label":ariaLabel,"aria-labelledby":ariaLabelledBy,"aria-describedby":ariaDescribedBy,ref:ref,style:styles$7.dialog,testId:testId,children:children}),above&&jsxRuntime.jsx(wonderBlocksCore.View,{style:styles$7.above,children:above})]})});const small$2="@media (max-width: 767px)";const styles$7=aphrodite.StyleSheet.create({wrapper:{color:wonderBlocksTokens.semanticColor.core.foreground.neutral.strong,flexDirection:"row",alignItems:"stretch",width:"100%",height:"100%",position:"relative",boxShadow:theme.dialog.shadow.default,borderRadius:theme.root.border.radius,[small$2]:{padding:theme.dialog.layout.padding,flexDirection:"column"}},dialog:{width:"100%",height:"100%",borderRadius:theme.root.border.radius,overflow:"hidden"},above:{pointerEvents:"none",position:"absolute",top:0,left:0,bottom:0,right:0,zIndex:1},below:{pointerEvents:"none",position:"absolute",top:0,left:0,bottom:0,right:0,zIndex:-1}});ModalDialog.displayName="ModalDialog";
|
|
48
48
|
|
|
49
|
-
function ModalFooter({children}){return jsxRuntime.jsx(wonderBlocksCore.View,{style:styles$
|
|
49
|
+
function ModalFooter({children}){return jsxRuntime.jsx(wonderBlocksCore.View,{style:styles$6.footer,children:children})}ModalFooter.__IS_MODAL_FOOTER__=true;ModalFooter.isComponentOf=instance=>{return instance&&instance.type&&instance.type.__IS_MODAL_FOOTER__};const styles$6=aphrodite.StyleSheet.create({footer:{flex:"0 0 auto",boxSizing:"border-box",minHeight:wonderBlocksTokens.sizing.size_640,paddingInline:theme.footer.layout.padding.inline,paddingBlock:theme.footer.layout.padding.block,display:"flex",flexDirection:"row",alignItems:"center",justifyContent:"flex-end",boxShadow:`0px -1px 0px ${wonderBlocksTokens.semanticColor.core.border.neutral.subtle}`}});
|
|
50
50
|
|
|
51
|
-
function ModalHeader(props){const{breadcrumbs=undefined,subtitle=undefined,testId,title,titleId}=props;if(subtitle&&breadcrumbs){throw new Error("'subtitle' and 'breadcrumbs' can't be used together")}return jsxRuntime.jsxs(wonderBlocksCore.View,{style:[styles$
|
|
51
|
+
function ModalHeader(props){const{breadcrumbs=undefined,subtitle=undefined,testId,title,titleId}=props;if(subtitle&&breadcrumbs){throw new Error("'subtitle' and 'breadcrumbs' can't be used together")}return jsxRuntime.jsxs(wonderBlocksCore.View,{style:[styles$5.header],testId:testId,children:[breadcrumbs&&jsxRuntime.jsx(wonderBlocksCore.View,{style:styles$5.breadcrumbs,children:breadcrumbs}),jsxRuntime.jsx(wonderBlocksTypography.Heading,{size:"large",tag:"h2",style:styles$5.title,id:titleId,testId:testId&&`${testId}-title`,children:title}),subtitle&&jsxRuntime.jsx(wonderBlocksTypography.BodyText,{size:"small",style:styles$5.subtitle,testId:testId&&`${testId}-subtitle`,children:subtitle})]})}const small$1="@media (max-width: 767px)";const styles$5=aphrodite.StyleSheet.create({header:{boxShadow:`0px 1px 0px ${wonderBlocksTokens.semanticColor.core.border.neutral.subtle}`,display:"flex",flexDirection:"column",minHeight:66,paddingBlock:theme.header.layout.padding.block,paddingInline:theme.header.layout.padding.inline.default,position:"relative",width:"100%",[small$1]:{paddingInline:theme.header.layout.padding.inline.small}},breadcrumbs:{color:wonderBlocksTokens.semanticColor.core.foreground.neutral.default,marginBlockEnd:theme.header.layout.gap.default},title:{paddingInlineEnd:theme.header.layout.gap.title.default,[small$1]:{paddingInlineEnd:theme.header.layout.gap.title.small}},subtitle:{color:wonderBlocksTokens.semanticColor.core.foreground.neutral.default,marginBlockStart:theme.header.layout.gap.default}});
|
|
52
52
|
|
|
53
53
|
const FOCUSABLE_ELEMENTS$1='button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';class FocusTrap extends React__namespace.Component{tryToFocus(node){if(node instanceof HTMLElement){try{node.focus();}catch(e){}return document.activeElement===node}}focusElementIn(isLast){const modalRootAsHtmlEl=this.modalRoot;const focusableNodes=Array.from(modalRootAsHtmlEl.querySelectorAll(FOCUSABLE_ELEMENTS$1)).filter(element=>{const style=window.getComputedStyle(element);return style.display!=="none"&&style.visibility!=="hidden"});const nodeIndex=!isLast?focusableNodes.length-1:0;const focusableNode=focusableNodes[nodeIndex];this.tryToFocus(focusableNode);}render(){const{style}=this.props;return jsxRuntime.jsxs(React__namespace.Fragment,{children:[jsxRuntime.jsx("div",{tabIndex:0,className:"modal-focus-trap-first",onFocus:this.handleFocusMoveToLast,style:{position:"fixed"}}),jsxRuntime.jsx(wonderBlocksCore.View,{style:style,ref:this.getModalRoot,children:this.props.children}),jsxRuntime.jsx("div",{tabIndex:0,className:"modal-focus-trap-last",onFocus:this.handleFocusMoveToFirst,style:{position:"fixed"}})]})}constructor(...args){super(...args),this.getModalRoot=node=>{if(!node){return}const modalRoot=ReactDOM__namespace.findDOMNode(node);if(!modalRoot){throw new Error("Assertion error: modal root should exist after mount")}this.modalRoot=modalRoot;},this.handleFocusMoveToLast=()=>{this.focusElementIn(false);},this.handleFocusMoveToFirst=()=>{this.focusElementIn(true);};}}
|
|
54
54
|
|
|
@@ -56,28 +56,40 @@ const ModalLauncherPortalAttributeName="data-modal-launcher-portal";
|
|
|
56
56
|
|
|
57
57
|
const FOCUSABLE_ELEMENTS="a[href], details, input, textarea, select, button";function findFocusableNodes(root){return Array.from(root.querySelectorAll(FOCUSABLE_ELEMENTS))}
|
|
58
58
|
|
|
59
|
-
class ModalBackdrop extends React__namespace.Component{componentDidMount(){const node=ReactDOM__namespace.findDOMNode(this);if(!node){return}const firstFocusableElement=this._getInitialFocusElement(node)||this._getFirstFocusableElement(node)||this._getDialogElement(node);setTimeout(()=>{firstFocusableElement.focus();},0);}_getInitialFocusElement(node){const{initialFocusId}=this.props;if(!initialFocusId){return null}return ReactDOM__namespace.findDOMNode(node.querySelector(`#${initialFocusId}`))}_getFirstFocusableElement(node){const focusableElements=findFocusableNodes(node);if(!focusableElements){return null}return focusableElements[0]}_getDialogElement(node){const dialogElement=ReactDOM__namespace.findDOMNode(node.querySelector('[role="dialog"]'));dialogElement.tabIndex=-1;return dialogElement}render(){const{children,testId}=this.props;const backdropProps={[ModalLauncherPortalAttributeName]:true};return jsxRuntime.jsx(wonderBlocksCore.View,{style:styles$
|
|
59
|
+
class ModalBackdrop extends React__namespace.Component{componentDidMount(){const node=ReactDOM__namespace.findDOMNode(this);if(!node){return}const firstFocusableElement=this._getInitialFocusElement(node)||this._getFirstFocusableElement(node)||this._getDialogElement(node);setTimeout(()=>{firstFocusableElement.focus();},0);}_getInitialFocusElement(node){const{initialFocusId}=this.props;if(!initialFocusId){return null}return ReactDOM__namespace.findDOMNode(node.querySelector(`#${initialFocusId}`))}_getFirstFocusableElement(node){const focusableElements=findFocusableNodes(node);if(!focusableElements){return null}return focusableElements[0]}_getDialogElement(node){const dialogElement=ReactDOM__namespace.findDOMNode(node.querySelector('[role="dialog"]'));dialogElement.tabIndex=-1;return dialogElement}render(){const{children,testId}=this.props;const backdropProps={[ModalLauncherPortalAttributeName]:true};return jsxRuntime.jsx(wonderBlocksCore.View,{style:styles$4.modalPositioner,onMouseDown:this.handleMouseDown,onMouseUp:this.handleMouseUp,testId:testId,...backdropProps,children:children})}constructor(...args){super(...args),this._mousePressedOutside=false,this.handleMouseDown=e=>{this._mousePressedOutside=e.target===e.currentTarget;},this.handleMouseUp=e=>{if(e.target===e.currentTarget&&this._mousePressedOutside){this.props.onCloseModal();}this._mousePressedOutside=false;};}}const styles$4=aphrodite.StyleSheet.create({modalPositioner:{position:"fixed",left:0,top:0,width:"100%",height:"100%",alignItems:"center",justifyContent:"center",overflow:"auto",background:wonderBlocksTokens.semanticColor.core.background.overlay.default}});
|
|
60
60
|
|
|
61
61
|
const needsHackyMobileSafariScrollDisabler=(()=>{if(typeof window==="undefined"){return false}const userAgent=window.navigator.userAgent;return userAgent.indexOf("iPad")>-1||userAgent.indexOf("iPhone")>-1})();class ScrollDisabler extends React__namespace.Component{componentDidMount(){if(ScrollDisabler.numModalsOpened===0){const body=document.body;if(!body){throw new Error("couldn't find document.body")}ScrollDisabler.oldOverflow=body.style.overflow;ScrollDisabler.oldScrollY=window.scrollY;if(needsHackyMobileSafariScrollDisabler){ScrollDisabler.oldPosition=body.style.position;ScrollDisabler.oldWidth=body.style.width;ScrollDisabler.oldTop=body.style.top;}body.style.overflow="hidden";if(needsHackyMobileSafariScrollDisabler){body.style.position="fixed";body.style.width="100%";body.style.top=`${-ScrollDisabler.oldScrollY}px`;}}ScrollDisabler.numModalsOpened++;}componentWillUnmount(){ScrollDisabler.numModalsOpened--;if(ScrollDisabler.numModalsOpened===0){const body=document.body;if(!body){throw new Error("couldn't find document.body")}body.style.overflow=ScrollDisabler.oldOverflow;if(needsHackyMobileSafariScrollDisabler){body.style.position=ScrollDisabler.oldPosition;body.style.width=ScrollDisabler.oldWidth;body.style.top=ScrollDisabler.oldTop;}if(typeof window!=="undefined"&&window.scrollTo){window.scrollTo(0,ScrollDisabler.oldScrollY);}}}render(){return null}}ScrollDisabler.numModalsOpened=0;
|
|
62
62
|
|
|
63
63
|
const defaultContext={closeModal:undefined};const ModalContext=React__namespace.createContext(defaultContext);ModalContext.displayName="ModalContext";
|
|
64
64
|
|
|
65
|
-
class ModalLauncher extends React__namespace.Component{static getDerivedStateFromProps(props,state){if(typeof props.opened==="boolean"&&props.children){console.warn("'children' and 'opened' can't be used together");}if(typeof props.opened==="boolean"&&!props.onClose){console.warn("'onClose' should be used with 'opened'");}if(typeof props.opened!=="boolean"&&!props.children){console.warn("either 'children' or 'opened' must be set");}return {opened:typeof props.opened==="boolean"?props.opened:state.opened}}componentDidUpdate(prevProps){if(!prevProps.opened&&this.props.opened){this._saveLastElementFocused();}}_renderModal(){if(typeof this.props.modal==="function"){return this.props.modal({closeModal:this.handleCloseModal})}else {return this.props.modal}}render(){const renderedChildren=this.props.children?this.props.children({openModal:this._openModal}):null;const{body}=document;if(!body){return null}return jsxRuntime.jsxs(ModalContext.Provider,{value:{closeModal:this.handleCloseModal},children:[renderedChildren,this.state.opened&&ReactDOM__namespace.createPortal(jsxRuntime.jsx(FocusTrap,{style:styles$
|
|
65
|
+
class ModalLauncher extends React__namespace.Component{static getDerivedStateFromProps(props,state){if(typeof props.opened==="boolean"&&props.children){console.warn("'children' and 'opened' can't be used together");}if(typeof props.opened==="boolean"&&!props.onClose){console.warn("'onClose' should be used with 'opened'");}if(typeof props.opened!=="boolean"&&!props.children){console.warn("either 'children' or 'opened' must be set");}return {opened:typeof props.opened==="boolean"?props.opened:state.opened}}componentDidUpdate(prevProps){if(!prevProps.opened&&this.props.opened){this._saveLastElementFocused();}}_renderModal(){if(typeof this.props.modal==="function"){return this.props.modal({closeModal:this.handleCloseModal})}else {return this.props.modal}}render(){const renderedChildren=this.props.children?this.props.children({openModal:this._openModal}):null;const{body}=document;if(!body){return null}return jsxRuntime.jsxs(ModalContext.Provider,{value:{closeModal:this.handleCloseModal},children:[renderedChildren,this.state.opened&&ReactDOM__namespace.createPortal(jsxRuntime.jsx(FocusTrap,{style:styles$3.container,children:jsxRuntime.jsx(ModalBackdrop,{initialFocusId:this.props.initialFocusId,testId:this.props.testId,onCloseModal:this.props.backdropDismissEnabled?this.handleCloseModal:()=>{},children:this._renderModal()})}),body),this.state.opened&&jsxRuntime.jsx(ModalLauncherKeypressListener,{onClose:this.handleCloseModal}),this.state.opened&&jsxRuntime.jsx(ScrollDisabler,{})]})}constructor(...args){super(...args),this.state={opened:false},this._saveLastElementFocused=()=>{this.lastElementFocusedOutsideModal=document.activeElement;},this._openModal=()=>{this._saveLastElementFocused();this.setState({opened:true});},this._returnFocus=()=>{const{closedFocusId,schedule}=this.props;const lastElement=this.lastElementFocusedOutsideModal;if(closedFocusId){const focusElement=ReactDOM__namespace.findDOMNode(document.getElementById(closedFocusId));if(focusElement){schedule.animationFrame(()=>{focusElement.focus();});return}}if(lastElement!=null){schedule.animationFrame(()=>{lastElement.focus();});}},this.handleCloseModal=()=>{this.setState({opened:false},()=>{const{onClose}=this.props;onClose?.();this._returnFocus();});};}}ModalLauncher.defaultProps={backdropDismissEnabled:true};class ModalLauncherKeypressListener extends React__namespace.Component{componentDidMount(){window.addEventListener("keyup",this._handleKeyup);}componentWillUnmount(){window.removeEventListener("keyup",this._handleKeyup);}render(){return null}constructor(...args){super(...args),this._handleKeyup=e=>{if(e.key==="Escape"){e.preventDefault();e.stopPropagation();this.props.onClose();}};}}const styles$3=aphrodite.StyleSheet.create({container:{zIndex:1080}});var modalLauncher = wonderBlocksTiming.withActionScheduler(ModalLauncher);
|
|
66
66
|
|
|
67
|
-
function ModalContent(props){const{scrollOverflow,style,children}=props;return jsxRuntime.jsx(wonderBlocksCore.View,{style:[styles$
|
|
67
|
+
function ModalContent(props){const{scrollOverflow,style,children}=props;return jsxRuntime.jsx(wonderBlocksCore.View,{style:[styles$2.wrapper,scrollOverflow&&styles$2.scrollOverflow],children:jsxRuntime.jsx(wonderBlocksCore.View,{style:[styles$2.content,style],children:children})})}ModalContent.__IS_MODAL_CONTENT__=true;ModalContent.isComponentOf=instance=>{return instance&&instance.type&&instance.type.__IS_MODAL_CONTENT__};const small="@media (max-width: 767px)";const styles$2=aphrodite.StyleSheet.create({wrapper:{flex:1,display:"block"},scrollOverflow:{overflow:"auto"},content:{flex:1,minHeight:"100%",padding:theme.panel.layout.gap.default,boxSizing:"border-box",[small]:{paddingInline:theme.panel.layout.gap.small}}});ModalContent.defaultProps={scrollOverflow:true};
|
|
68
68
|
|
|
69
69
|
class CloseButton extends React__namespace.Component{render(){const{onClick,style,testId}=this.props;return jsxRuntime.jsx(ModalContext.Consumer,{children:({closeModal})=>{if(closeModal&&onClick){throw new Error("You've specified 'onClose' on a modal when using ModalLauncher. Please specify 'onClose' on the ModalLauncher instead")}return jsxRuntime.jsx(IconButton__default["default"],{icon:xIcon__default["default"],"aria-label":"Close modal",onClick:onClick||closeModal,kind:"tertiary",actionType:"neutral",style:style,testId:testId})}})}}
|
|
70
70
|
|
|
71
|
-
function ModalPanel({closeButtonVisible=true,scrollOverflow=true,content,footer,header,onClose,style,testId}){const renderMainContent=React__namespace.useCallback(()=>{const mainContent=ModalContent.isComponentOf(content)?content:jsxRuntime.jsx(ModalContent,{children:content});if(!mainContent){return mainContent}return React__namespace.cloneElement(mainContent,{scrollOverflow,style:[!!footer&&styles.hasFooter,mainContent.props.style]})},[content,footer,scrollOverflow]);const mainContent=renderMainContent();return jsxRuntime.jsxs(wonderBlocksCore.View,{style:[styles.wrapper,style],testId:testId&&`${testId}-panel`,children:[closeButtonVisible&&jsxRuntime.jsx(CloseButton,{onClick:onClose,style:[styles.closeButton],testId:testId&&`${testId}-close`}),header,mainContent,!footer||ModalFooter.isComponentOf(footer)?footer:jsxRuntime.jsx(ModalFooter,{children:footer})]})}ModalPanel.defaultProps={closeButtonVisible:true,scrollOverflow:true};const styles=aphrodite.StyleSheet.create({wrapper:{flex:"1 1 auto",flexDirection:"column",background:wonderBlocksTokens.semanticColor.
|
|
71
|
+
function ModalPanel({closeButtonVisible=true,scrollOverflow=true,content,footer,header,onClose,style,testId}){const renderMainContent=React__namespace.useCallback(()=>{const mainContent=ModalContent.isComponentOf(content)?content:jsxRuntime.jsx(ModalContent,{children:content});if(!mainContent){return mainContent}return React__namespace.cloneElement(mainContent,{scrollOverflow,style:[!!footer&&styles$1.hasFooter,mainContent.props.style]})},[content,footer,scrollOverflow]);const mainContent=renderMainContent();return jsxRuntime.jsxs(wonderBlocksCore.View,{style:[styles$1.wrapper,style],testId:testId&&`${testId}-panel`,children:[closeButtonVisible&&jsxRuntime.jsx(CloseButton,{onClick:onClose,style:[styles$1.closeButton],testId:testId&&`${testId}-close`}),header,mainContent,!footer||ModalFooter.isComponentOf(footer)?footer:jsxRuntime.jsx(ModalFooter,{children:footer})]})}ModalPanel.defaultProps={closeButtonVisible:true,scrollOverflow:true};const styles$1=aphrodite.StyleSheet.create({wrapper:{flex:"1 1 auto",flexDirection:"column",background:wonderBlocksTokens.semanticColor.core.background.base.default,boxSizing:"border-box",overflow:"hidden",height:"100%",width:"100%"},closeButton:{position:"absolute",insetInlineEnd:theme.closeButton.layout.gapRight,top:theme.closeButton.layout.gapTop,zIndex:1,":focus":wonderBlocksStyles.focusStyles.focus[":focus-visible"]},hasFooter:{paddingBlockEnd:theme.panel.layout.gap.default}});
|
|
72
72
|
|
|
73
73
|
class OnePaneDialog extends React__namespace.Component{renderHeader(uniqueId){const{title,breadcrumbs=undefined,subtitle=undefined,testId}=this.props;if(breadcrumbs){return jsxRuntime.jsx(ModalHeader,{title:title,breadcrumbs:breadcrumbs,titleId:uniqueId,testId:testId&&`${testId}-header`})}else if(subtitle){return jsxRuntime.jsx(ModalHeader,{title:title,subtitle:subtitle,titleId:uniqueId,testId:testId&&`${testId}-header`})}else {return jsxRuntime.jsx(ModalHeader,{title:title,titleId:uniqueId,testId:testId&&`${testId}-header`})}}render(){const{onClose,footer,content,above,below,style,closeButtonVisible,testId,titleId,role,"aria-describedby":ariaDescribedBy}=this.props;return jsxRuntime.jsx(wonderBlocksLayout.MediaLayout,{styleSheets:styleSheets,children:({styles})=>jsxRuntime.jsx(wonderBlocksCore.Id,{id:titleId,children:uniqueId=>jsxRuntime.jsx(ModalDialog,{style:[styles.dialog,style],above:above,below:below,testId:testId,"aria-labelledby":uniqueId,"aria-describedby":ariaDescribedBy,role:role,children:jsxRuntime.jsx(ModalPanel,{onClose:onClose,header:this.renderHeader(uniqueId),content:content,footer:footer,closeButtonVisible:closeButtonVisible,testId:testId})})})})}}OnePaneDialog.defaultProps={closeButtonVisible:true};const styleSheets={small:aphrodite.StyleSheet.create({dialog:{width:"100%",height:"100%",overflow:"hidden"}}),mdOrLarger:aphrodite.StyleSheet.create({dialog:{width:"93.75%",maxWidth:576,height:"81.25%",maxHeight:624}})};
|
|
74
74
|
|
|
75
|
-
function FlexiblePanel({closeButtonVisible=true,content,title,onClose,styles,testId}){const renderMainContent=React__namespace.useCallback(()=>{const contentNode=typeof content==="function"?content({title}):jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[title,content]});const mainContent=ModalContent.isComponentOf(contentNode)?contentNode:jsxRuntime.jsx(ModalContent,{children:contentNode});if(!mainContent){return mainContent}return React__namespace.cloneElement(mainContent,{style:[mainContent.props.style]})},[title,content]);const mainContent=renderMainContent();const defaultBackgroundStyle={backgroundColor:wonderBlocksTokens.semanticColor.
|
|
75
|
+
function FlexiblePanel({closeButtonVisible=true,content,title,onClose,styles,testId}){const panelRef=React__namespace.useRef(null);const renderMainContent=React__namespace.useCallback(()=>{const contentNode=typeof content==="function"?content({title}):jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[title,content]});const mainContent=ModalContent.isComponentOf(contentNode)?contentNode:jsxRuntime.jsx(ModalContent,{children:contentNode});if(!mainContent){return mainContent}return React__namespace.cloneElement(mainContent,{style:[mainContent.props.style]})},[title,content]);const mainContent=renderMainContent();const defaultBackgroundStyle={backgroundColor:wonderBlocksTokens.semanticColor.core.background.base.default};const combinedBackgroundStyles={...defaultBackgroundStyle,...styles?.panel};return jsxRuntime.jsxs(wonderBlocksCore.View,{style:[componentStyles$1.wrapper,combinedBackgroundStyles],testId:testId&&`${testId}-panel`,ref:panelRef,children:[closeButtonVisible&&jsxRuntime.jsx(CloseButton,{onClick:onClose,style:[componentStyles$1.closeButton,styles?.closeButton],testId:testId&&`${testId}-close`}),mainContent]})}FlexiblePanel.defaultProps={closeButtonVisible:true,scrollOverflow:true};const componentStyles$1=aphrodite.StyleSheet.create({wrapper:{flex:"1 1 auto",flexDirection:"column",boxSizing:"border-box",overflow:"hidden",height:"100%",width:"100%"},closeButton:{position:"absolute",insetInlineEnd:theme.closeButton.layout.gapRight,top:theme.closeButton.layout.gapTop,zIndex:1,":focus":wonderBlocksStyles.focusStyles.focus[":focus-visible"]}});
|
|
76
76
|
|
|
77
|
-
const FlexibleDialog=({onClose,title,content,styles,closeButtonVisible=true,testId,titleId,role,...accessibilityProps}
|
|
77
|
+
const FlexibleDialog=React__namespace.forwardRef(function FlexibleDialog(props,ref){const{onClose,title,content,styles,closeButtonVisible=true,testId,titleId,role="dialog",...accessibilityProps}=props;const uniqueId=React__namespace.useId();const headingId=titleId??uniqueId;const renderedTitle=title==null?null:typeof title==="string"?jsxRuntime.jsx(wonderBlocksTypography.Heading,{id:headingId,children:title}):React__namespace.cloneElement(title,{id:headingId,testId:"title-heading-wrapper"});return jsxRuntime.jsx(wonderBlocksCore.View,{style:[componentStyles.root,styles?.root],children:jsxRuntime.jsx(wonderBlocksCore.View,{role:role,"aria-modal":"true","aria-label":accessibilityProps["aria-label"],"aria-labelledby":headingId,"aria-describedby":accessibilityProps["aria-describedby"],ref:ref,testId:testId,style:[componentStyles.dialog,styles?.dialog],children:jsxRuntime.jsx(FlexiblePanel,{styles:{panel:styles?.panel,closeButton:styles?.closeButton},onClose:onClose,title:renderedTitle,content:content,closeButtonVisible:closeButtonVisible,testId:testId})})})});const componentStyles=aphrodite.StyleSheet.create({root:{boxShadow:theme.dialog.shadow.default,color:wonderBlocksTokens.semanticColor.core.foreground.neutral.strong,overflow:"auto",position:"relative",willChange:"transform, opacity",height:"auto",maxHeight:"100vh",maxWidth:576,width:"93.75%",[wonderBlocksTokens.breakpoint.mediaQuery.sm]:{height:"100vh",maxHeight:"100vh",width:"100%"}}});
|
|
78
|
+
|
|
79
|
+
const DEFAULT_DRAWER_TIMING_DURATION_MS=400;const DEFAULT_DRAWER_ANIMATED=true;const DEFAULT_DRAWER_BACKDROP_DISMISS_ENABLED=true;const DEFAULT_DRAWER_IS_EXITING=false;const defaultDrawerContextValue={animated:DEFAULT_DRAWER_ANIMATED,isExiting:DEFAULT_DRAWER_IS_EXITING,timingDuration:DEFAULT_DRAWER_TIMING_DURATION_MS};const DrawerContext=React__namespace.createContext(defaultDrawerContextValue);const useDrawerContext=()=>{return React__namespace.useContext(DrawerContext)};
|
|
80
|
+
|
|
81
|
+
const DrawerBackdrop=({children,testId,initialFocusId,onCloseModal})=>{const{alignment,animated,timingDuration,isExiting}=useDrawerContext();const[mousePressedOutside,setMousePressedOutside]=React__namespace.useState(false);const backdropRef=React__namespace.useRef(null);const computedTimingDuration=animated?timingDuration:0;const getInitialFocusElement=React__namespace.useCallback(container=>{if(!initialFocusId){return null}return container.querySelector(`#${initialFocusId}`)},[initialFocusId]);const getFirstFocusableElement=React__namespace.useCallback(container=>{const focusableElements=findFocusableNodes(container);if(!focusableElements){return null}return focusableElements[0]},[]);const getDialogElement=React__namespace.useCallback(container=>{const dialogElement=container.querySelector('[role="dialog"]');if(dialogElement){dialogElement.tabIndex=-1;}return dialogElement},[]);React__namespace.useEffect(()=>{const container=backdropRef.current;if(!container){return}const firstFocusableElement=getInitialFocusElement(container)||getFirstFocusableElement(container)||getDialogElement(container);if(firstFocusableElement){setTimeout(()=>{firstFocusableElement.focus();},computedTimingDuration);}},[getInitialFocusElement,getFirstFocusableElement,getDialogElement,computedTimingDuration]);const handleMouseDown=e=>{if(e.target===e.currentTarget){setMousePressedOutside(true);}};const handleMouseUp=e=>{if(e.target===e.currentTarget&&mousePressedOutside){onCloseModal();}setMousePressedOutside(false);};const backdropProps={[ModalLauncherPortalAttributeName]:true};return jsxRuntime.jsx(wonderBlocksCore.View,{ref:backdropRef,style:[styles.drawerPositioner,alignment&&styles[alignment],animated&&(isExiting?styles.fadeOut:styles.fadeIn)].filter(Boolean),onMouseDown:handleMouseDown,onMouseUp:handleMouseUp,testId:testId,...backdropProps,children:children})};const keyframes={fadeIn:{"0%":{opacity:0},"100%":{opacity:1}},fadeOut:{"0%":{opacity:1},"100%":{opacity:0}}};const styles=aphrodite.StyleSheet.create({drawerPositioner:{position:"fixed",left:0,top:0,width:"100%",height:"100%",display:"flex",overflow:"hidden",background:wonderBlocksTokens.semanticColor.core.background.overlay.default},inlineStart:{alignItems:"flex-start",justifyContent:"flex-start"},inlineEnd:{alignItems:"flex-end",justifyContent:"flex-start"},blockEnd:{alignItems:"center",justifyContent:"flex-end"},fadeIn:{animationName:keyframes.fadeIn,animationDuration:"400ms",animationTimingFunction:"linear",animationFillMode:"forwards"},fadeOut:{animationName:keyframes.fadeOut,animationDuration:"400ms",animationTimingFunction:"linear",animationFillMode:"forwards"}});
|
|
82
|
+
|
|
83
|
+
const defaultProps={backdropDismissEnabled:DEFAULT_DRAWER_BACKDROP_DISMISS_ENABLED,defaultTimingDuration:DEFAULT_DRAWER_TIMING_DURATION_MS};const DrawerLauncher=props=>{const{modal,backdropDismissEnabled=defaultProps.backdropDismissEnabled,initialFocusId,closedFocusId,testId,opened:controlledOpened,onClose,children,schedule,alignment,styles,animated=DEFAULT_DRAWER_ANIMATED,timingDuration=defaultProps.defaultTimingDuration}=props;const[uncontrolledOpened,setUncontrolledOpened]=React__namespace.useState(false);const[isExiting,setIsExiting]=React__namespace.useState(false);const lastElementFocusedOutsideModalRef=React__namespace.useRef(null);const opened=typeof controlledOpened==="boolean"?controlledOpened:uncontrolledOpened;const saveLastElementFocused=React__namespace.useCallback(()=>{lastElementFocusedOutsideModalRef.current=document.activeElement;},[]);React__namespace.useEffect(()=>{if(controlledOpened&&!prevControlledOpened.current){saveLastElementFocused();}prevControlledOpened.current=controlledOpened;},[controlledOpened,saveLastElementFocused]);const prevControlledOpened=React__namespace.useRef(controlledOpened);const returnFocus=React__namespace.useCallback(()=>{const focusElement=closedFocusId?document.getElementById(closedFocusId):null;schedule.animationFrame(()=>{if(focusElement){focusElement.focus();}else if(lastElementFocusedOutsideModalRef.current){lastElementFocusedOutsideModalRef.current.focus();}});},[closedFocusId,schedule]);const handleCloseModal=React__namespace.useCallback(()=>{if(animated){setIsExiting(true);setTimeout(()=>{setIsExiting(false);if(typeof controlledOpened==="boolean"){onClose?.();}else {setUncontrolledOpened(false);onClose?.();}returnFocus();},timingDuration);}else {if(typeof controlledOpened==="boolean"){onClose?.();}else {setUncontrolledOpened(false);onClose?.();}returnFocus();}},[controlledOpened,onClose,returnFocus,animated,timingDuration]);const openModal=React__namespace.useCallback(()=>{saveLastElementFocused();setUncontrolledOpened(true);},[saveLastElementFocused]);const drawerDialogProps=React__namespace.useMemo(()=>({alignment,animated,isExiting,timingDuration}),[alignment,animated,isExiting,timingDuration]);const renderModal=React__namespace.useCallback(()=>{if(typeof modal==="function"){const renderedModal=modal({closeModal:handleCloseModal});if(!renderedModal){return null}return renderedModal}if(!modal){return null}return modal},[modal,handleCloseModal]);const renderedChildren=children?children({openModal}):null;const body=document.body;if(!body){return null}return jsxRuntime.jsxs(ModalContext.Provider,{value:{closeModal:handleCloseModal},children:[renderedChildren,opened&&!isExiting||opened&&isExiting&&animated?ReactDOM__namespace.createPortal(jsxRuntime.jsx(DrawerContext.Provider,{value:drawerDialogProps,children:jsxRuntime.jsx(FocusTrap,{style:styles?.container,children:jsxRuntime.jsx(DrawerBackdrop,{initialFocusId:initialFocusId,testId:testId,onCloseModal:backdropDismissEnabled?handleCloseModal:()=>{},children:renderModal()})})}),body):null,opened&&!isExiting&&jsxRuntime.jsxs(jsxRuntime.Fragment,{children:[jsxRuntime.jsx(DrawerLauncherKeypressListener,{onClose:handleCloseModal}),jsxRuntime.jsx(ScrollDisabler,{})]})]})};function DrawerLauncherKeypressListener({onClose}){React__namespace.useEffect(()=>{const handleKeyup=e=>{if(e.key==="Escape"){e.preventDefault();e.stopPropagation();onClose();}};window.addEventListener("keyup",handleKeyup);return ()=>window.removeEventListener("keyup",handleKeyup)},[onClose]);return null}DrawerLauncher.displayName="DrawerLauncher";var drawerLauncher = wonderBlocksTiming.withActionScheduler(DrawerLauncher);
|
|
84
|
+
|
|
85
|
+
function useDirectionDetection(elementRef,options={}){const{direction:explicitDirection,defaultDirection="ltr"}=options;if(explicitDirection){return explicitDirection}if(elementRef?.current){const elementWithDir=elementRef.current.closest("[dir]");if(elementWithDir){const dirValue=elementWithDir.getAttribute("dir");return dirValue==="rtl"||dirValue==="ltr"?dirValue:defaultDirection}}const documentDir=document.documentElement.getAttribute("dir")||document.body.getAttribute("dir");if(documentDir==="rtl"||documentDir==="ltr"){return documentDir}return defaultDirection}
|
|
86
|
+
|
|
87
|
+
const DrawerDialog=React__namespace.forwardRef(function DrawerDialog(props,ref){const contextProps=useDrawerContext();const alignment=contextProps.alignment;const animated=contextProps.animated??DEFAULT_DRAWER_ANIMATED;const isExiting=contextProps.isExiting??DEFAULT_DRAWER_IS_EXITING;const timingDuration=contextProps.timingDuration??DEFAULT_DRAWER_TIMING_DURATION_MS;const{styles}=props;const direction=useDirectionDetection();const isRtl=direction==="rtl";const componentStyles=getComponentStyles({alignment,isRtl,animated,isExiting,timingDuration});const alignmentStyles=alignment&&componentStyles[alignment]||componentStyles.inlineEnd;return jsxRuntime.jsx(FlexibleDialog,{...props,ref:ref,styles:{root:[componentStyles.root,alignmentStyles,styles?.root].filter(Boolean),dialog:[componentStyles.dialog,styles?.dialog].filter(Boolean),panel:styles?.panel,closeButton:styles?.closeButton}})});const getTransformValue=(isRtl,alignment,percentage)=>{if(alignment==="blockEnd"){return `translate3d(0, ${percentage}%, 0)`}const directionMultiplier=isRtl?alignment==="inlineEnd"?-1:1:alignment==="inlineEnd"?1:-1;return `translate3d(${directionMultiplier*percentage}%, 0, 0)`};const createKeyframes=(isRtl,alignment)=>({slideIn:{"0%":{transform:getTransformValue(isRtl,alignment,100),opacity:0},"100%":{transform:getTransformValue(isRtl,alignment,0),opacity:1}},slideOut:{"0%":{transform:getTransformValue(isRtl,alignment,0),opacity:1},"100%":{transform:getTransformValue(isRtl,alignment,100),opacity:0}}});const getComponentStyles=({alignment,isRtl,animated,isExiting,timingDuration})=>{const alignmentKeyframes=alignment?createKeyframes(isRtl,alignment):null;return aphrodite.StyleSheet.create({root:{boxShadow:theme.dialog.shadow.default,color:wonderBlocksTokens.semanticColor.core.foreground.neutral.strong,overflow:"auto",position:"relative",willChange:"transform, opacity",height:"100%",minHeight:"100vh",minWidth:wonderBlocksTokens.breakpoint.width.xsMax,maxWidth:wonderBlocksTokens.breakpoint.width.smMax,width:"100%",[wonderBlocksTokens.breakpoint.mediaQuery.smOrSmaller]:{minWidth:"unset",maxWidth:"unset"}},dialog:{minHeight:alignment==="blockEnd"?"unset":"100vh",minWidth:"unset"},inlineStart:{animationName:animated&&alignmentKeyframes&&(isExiting?alignmentKeyframes.slideOut:alignmentKeyframes.slideIn),animationDuration:`${timingDuration}ms`,animationTimingFunction:"linear",animationFillMode:"forwards"},inlineEnd:{animationName:animated&&alignmentKeyframes&&(isExiting?alignmentKeyframes.slideOut:alignmentKeyframes.slideIn),animationDuration:`${timingDuration}ms`,animationTimingFunction:"linear",animationFillMode:"forwards"},blockEnd:{animationName:animated&&alignmentKeyframes&&(isExiting?alignmentKeyframes.slideOut:alignmentKeyframes.slideIn),animationDuration:`${timingDuration}ms`,animationTimingFunction:"linear",animationFillMode:"forwards",height:"auto",minHeight:"unset",maxWidth:"unset",[wonderBlocksTokens.breakpoint.mediaQuery.smOrSmaller]:{height:"auto"}}})};DrawerDialog.displayName="DrawerDialog";
|
|
78
88
|
|
|
79
89
|
function maybeGetNextAncestorModalLauncherPortal(element){let candidateElement=element&&element.parentElement;while(candidateElement&&!candidateElement.hasAttribute(ModalLauncherPortalAttributeName)){candidateElement=candidateElement.parentElement;}return candidateElement}function maybeGetPortalMountedModalHostElement(element){return maybeGetNextAncestorModalLauncherPortal(element)}
|
|
80
90
|
|
|
91
|
+
exports.DrawerDialog = DrawerDialog;
|
|
92
|
+
exports.DrawerLauncher = drawerLauncher;
|
|
81
93
|
exports.FlexibleDialog = FlexibleDialog;
|
|
82
94
|
exports.ModalDialog = ModalDialog;
|
|
83
95
|
exports.ModalFooter = ModalFooter;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { DrawerAlignment } from "./types";
|
|
3
|
+
/**
|
|
4
|
+
* Centralized default values for the drawer system.
|
|
5
|
+
*
|
|
6
|
+
* These constants provide the default behavior for all drawer components
|
|
7
|
+
* and can be imported by consumers who need to reference or override defaults.
|
|
8
|
+
*/
|
|
9
|
+
/** Default duration in milliseconds for drawer slide animations and focus timing. */
|
|
10
|
+
export declare const DEFAULT_DRAWER_TIMING_DURATION_MS = 400;
|
|
11
|
+
/** Default setting for whether drawer animations are enabled. */
|
|
12
|
+
export declare const DEFAULT_DRAWER_ANIMATED = true;
|
|
13
|
+
/** Default setting for whether clicking the backdrop dismisses the drawer. */
|
|
14
|
+
export declare const DEFAULT_DRAWER_BACKDROP_DISMISS_ENABLED = true;
|
|
15
|
+
/** Default value for internal exit animation state. */
|
|
16
|
+
export declare const DEFAULT_DRAWER_IS_EXITING = false;
|
|
17
|
+
export interface DrawerContextProps {
|
|
18
|
+
alignment?: DrawerAlignment;
|
|
19
|
+
animated?: boolean;
|
|
20
|
+
isExiting?: boolean;
|
|
21
|
+
timingDuration?: number;
|
|
22
|
+
}
|
|
23
|
+
export declare const DrawerContext: React.Context<DrawerContextProps>;
|
|
24
|
+
export declare const useDrawerContext: () => DrawerContextProps;
|
package/dist/util/types.d.ts
CHANGED
|
@@ -10,3 +10,10 @@ import * as React from "react";
|
|
|
10
10
|
* NOTE(kevinb): we include `| null` here because that's what React.FC<> returns.
|
|
11
11
|
*/
|
|
12
12
|
export type ModalElement = React.ReactElement | null;
|
|
13
|
+
/**
|
|
14
|
+
* The position of the modal, with logical support for RTL (Right-to-Left).
|
|
15
|
+
* `inlineStart` is left-aligned in LTR,
|
|
16
|
+
* `inlineEnd` is right-aligned in LTR,
|
|
17
|
+
* `blockEnd` is bottom-aligned.
|
|
18
|
+
*/
|
|
19
|
+
export type DrawerAlignment = "inlineStart" | "inlineEnd" | "blockEnd";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@khanacademy/wonder-blocks-modal",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.3.1",
|
|
4
4
|
"design": "v2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -20,14 +20,14 @@
|
|
|
20
20
|
"author": "",
|
|
21
21
|
"license": "MIT",
|
|
22
22
|
"dependencies": {
|
|
23
|
+
"@khanacademy/wonder-blocks-breadcrumbs": "3.2.2",
|
|
23
24
|
"@khanacademy/wonder-blocks-core": "12.4.0",
|
|
24
|
-
"@khanacademy/wonder-blocks-icon-button": "10.
|
|
25
|
-
"@khanacademy/wonder-blocks-layout": "3.1.
|
|
26
|
-
"@khanacademy/wonder-blocks-styles": "0.2.
|
|
25
|
+
"@khanacademy/wonder-blocks-icon-button": "10.4.1",
|
|
26
|
+
"@khanacademy/wonder-blocks-layout": "3.1.35",
|
|
27
|
+
"@khanacademy/wonder-blocks-styles": "0.2.30",
|
|
27
28
|
"@khanacademy/wonder-blocks-timing": "7.0.2",
|
|
28
|
-
"@khanacademy/wonder-blocks-tokens": "12.2.
|
|
29
|
-
"@khanacademy/wonder-blocks-typography": "4.2.
|
|
30
|
-
"@khanacademy/wonder-blocks-breadcrumbs": "3.2.1"
|
|
29
|
+
"@khanacademy/wonder-blocks-tokens": "12.2.1",
|
|
30
|
+
"@khanacademy/wonder-blocks-typography": "4.2.20"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
33
33
|
"@phosphor-icons/core": "^2.0.2",
|