no-frills-ui 0.0.14-alpha.11 → 0.0.14-alpha.12
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/README.md +2 -3
- package/dist/index.js +327 -163
- package/dist/index.js.map +1 -1
- package/lib-esm/components/Accordion/Accordion.js +3 -2
- package/lib-esm/components/Accordion/Accordion.js.map +1 -1
- package/lib-esm/components/Accordion/AccordionStep.js +13 -9
- package/lib-esm/components/Accordion/AccordionStep.js.map +1 -1
- package/lib-esm/components/Badge/Badge.js +6 -2
- package/lib-esm/components/Badge/Badge.js.map +1 -1
- package/lib-esm/components/Button/ActionButton.js +6 -2
- package/lib-esm/components/Button/ActionButton.js.map +1 -1
- package/lib-esm/components/Button/Button.js +6 -2
- package/lib-esm/components/Button/Button.js.map +1 -1
- package/lib-esm/components/Button/IconButton.js +6 -2
- package/lib-esm/components/Button/IconButton.js.map +1 -1
- package/lib-esm/components/Button/LinkButton.js +6 -2
- package/lib-esm/components/Button/LinkButton.js.map +1 -1
- package/lib-esm/components/Button/RaisedButton.js +6 -2
- package/lib-esm/components/Button/RaisedButton.js.map +1 -1
- package/lib-esm/components/Card/Card.js +6 -2
- package/lib-esm/components/Card/Card.js.map +1 -1
- package/lib-esm/components/Chip/Chip.js +7 -3
- package/lib-esm/components/Chip/Chip.js.map +1 -1
- package/lib-esm/components/ChipInput/ChipInput.js +10 -4
- package/lib-esm/components/ChipInput/ChipInput.js.map +1 -1
- package/lib-esm/components/Dialog/Dialog.d.ts +2 -0
- package/lib-esm/components/Dialog/Dialog.js +9 -2
- package/lib-esm/components/Dialog/Dialog.js.map +1 -1
- package/lib-esm/components/Dialog/PromptDialog.js +5 -3
- package/lib-esm/components/Dialog/PromptDialog.js.map +1 -1
- package/lib-esm/components/DragAndDrop/DragAndDrop.js +6 -2
- package/lib-esm/components/DragAndDrop/DragAndDrop.js.map +1 -1
- package/lib-esm/components/Drawer/Drawer.d.ts +6 -2
- package/lib-esm/components/Drawer/Drawer.js +32 -24
- package/lib-esm/components/Drawer/Drawer.js.map +1 -1
- package/lib-esm/components/Groups/Group.d.ts +4 -3
- package/lib-esm/components/Groups/Group.js +7 -3
- package/lib-esm/components/Groups/Group.js.map +1 -1
- package/lib-esm/components/Input/Checkbox.js +8 -4
- package/lib-esm/components/Input/Checkbox.js.map +1 -1
- package/lib-esm/components/Input/Dropdown.d.ts +5 -6
- package/lib-esm/components/Input/Dropdown.js +6 -1
- package/lib-esm/components/Input/Dropdown.js.map +1 -1
- package/lib-esm/components/Input/Input.d.ts +5 -0
- package/lib-esm/components/Input/Input.js +12 -6
- package/lib-esm/components/Input/Input.js.map +1 -1
- package/lib-esm/components/Input/Radio.js +8 -4
- package/lib-esm/components/Input/Radio.js.map +1 -1
- package/lib-esm/components/Input/RadioButton.js +8 -4
- package/lib-esm/components/Input/RadioButton.js.map +1 -1
- package/lib-esm/components/Input/Select.js +13 -7
- package/lib-esm/components/Input/Select.js.map +1 -1
- package/lib-esm/components/Input/TextArea.js +12 -6
- package/lib-esm/components/Input/TextArea.js.map +1 -1
- package/lib-esm/components/Input/Toggle.js +7 -3
- package/lib-esm/components/Input/Toggle.js.map +1 -1
- package/lib-esm/components/Menu/Menu.js +6 -1
- package/lib-esm/components/Menu/Menu.js.map +1 -1
- package/lib-esm/components/Menu/MenuItem.d.ts +6 -0
- package/lib-esm/components/Menu/MenuItem.js +7 -2
- package/lib-esm/components/Menu/MenuItem.js.map +1 -1
- package/lib-esm/components/Modal/Modal.d.ts +6 -2
- package/lib-esm/components/Modal/Modal.js +30 -22
- package/lib-esm/components/Modal/Modal.js.map +1 -1
- package/lib-esm/components/Notification/Notification.d.ts +2 -0
- package/lib-esm/components/Notification/Notification.js +13 -7
- package/lib-esm/components/Notification/Notification.js.map +1 -1
- package/lib-esm/components/Notification/NotificationManager.js +1 -0
- package/lib-esm/components/Notification/NotificationManager.js.map +1 -1
- package/lib-esm/components/Notification/index.d.ts +1 -0
- package/lib-esm/components/Popover/Popover.js +7 -3
- package/lib-esm/components/Popover/Popover.js.map +1 -1
- package/lib-esm/components/Spinner/Spinner.js +6 -2
- package/lib-esm/components/Spinner/Spinner.js.map +1 -1
- package/lib-esm/components/Stepper/Step.js +6 -2
- package/lib-esm/components/Stepper/Step.js.map +1 -1
- package/lib-esm/components/Stepper/Stepper.js +17 -10
- package/lib-esm/components/Stepper/Stepper.js.map +1 -1
- package/lib-esm/components/Tabs/Tabs.js +21 -12
- package/lib-esm/components/Tabs/Tabs.js.map +1 -1
- package/lib-esm/components/Tooltip/Tooltip.js +7 -3
- package/lib-esm/components/Tooltip/Tooltip.js.map +1 -1
- package/lib-esm/shared/LayerManager.d.ts +2 -2
- package/lib-esm/shared/LayerManager.js +1 -1
- package/lib-esm/shared/LayerManager.js.map +1 -1
- package/package.json +20 -10
|
@@ -10,6 +10,8 @@ interface DialogOptions {
|
|
|
10
10
|
closeOnEsc?: boolean;
|
|
11
11
|
/** Close layer overlay is clicked. Default value is true. */
|
|
12
12
|
closeOnOverlayClick?: boolean;
|
|
13
|
+
/** Ref forwarded to the underlying HTMLDivElement of the dialog container */
|
|
14
|
+
forwardRef?: React.Ref<HTMLDivElement>;
|
|
13
15
|
}
|
|
14
16
|
interface DialogState {
|
|
15
17
|
show: boolean;
|
|
@@ -5,7 +5,7 @@ import LayerManager, { LAYER_POSITION } from '../../shared/LayerManager.js';
|
|
|
5
5
|
import Card from '../Card/Card.js';
|
|
6
6
|
|
|
7
7
|
const DialogContainer = /*#__PURE__*/ styled(Card, {
|
|
8
|
-
target: "
|
|
8
|
+
target: "e1sxvvof0",
|
|
9
9
|
label: "DialogContainer"
|
|
10
10
|
})("max-width:768px;max-height:80vh;transform:scale(0);opacity:0;transition:all 0.3s ease;.nf-layer-enter &{opacity:1;transform:scale(1);}.nf-layer-exit &{opacity:0;transform:scale(0);}");
|
|
11
11
|
class Dialog extends React.Component {
|
|
@@ -79,6 +79,13 @@ class Dialog extends React.Component {
|
|
|
79
79
|
if (node) {
|
|
80
80
|
this.setInitialFocus(node);
|
|
81
81
|
}
|
|
82
|
+
if (this.props.forwardRef) {
|
|
83
|
+
if (typeof this.props.forwardRef === 'function') {
|
|
84
|
+
this.props.forwardRef(node);
|
|
85
|
+
} else {
|
|
86
|
+
this.props.forwardRef.current = node;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
82
89
|
}, /**
|
|
83
90
|
* Sets initial focus within the dialog.
|
|
84
91
|
* Tries to focus the header first, then the first interactive element, or falls back to the container.
|
|
@@ -109,9 +116,9 @@ class Dialog extends React.Component {
|
|
|
109
116
|
closeOnOverlayClick,
|
|
110
117
|
position: LAYER_POSITION.DIALOG,
|
|
111
118
|
component: /*#__PURE__*/ jsx(DialogContainer, {
|
|
119
|
+
role: "dialog",
|
|
112
120
|
...rest,
|
|
113
121
|
ref: this.setDialogRef,
|
|
114
|
-
role: "dialog",
|
|
115
122
|
"aria-modal": "true",
|
|
116
123
|
tabIndex: -1,
|
|
117
124
|
onKeyDown: this.handleKeyDown,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Dialog.js","sources":["../../../src/components/Dialog/Dialog.tsx"],"sourcesContent":["import React from 'react';\nimport styled from '@emotion/styled';\nimport LayerManager, { LAYER_POSITION } from '../../shared/LayerManager';\nimport { Card } from '../Card';\n\nexport const DialogContainer = styled(Card)`\n max-width: 768px;\n max-height: 80vh;\n transform: scale(0);\n opacity: 0;\n transition: all 0.3s ease;\n\n .nf-layer-enter & {\n opacity: 1;\n transform: scale(1);\n }\n\n .nf-layer-exit & {\n opacity: 0;\n transform: scale(0);\n }\n`;\n\nexport {\n Header as DialogHeader,\n Body as DialogBody,\n Footer as DialogFooter,\n} from '../../shared/styles';\n\ninterface DialogOptions {\n /** Flag to close dialog on `esc` click. Default value is true. */\n closeOnEsc?: boolean;\n /** Close layer overlay is clicked. Default value is true. */\n closeOnOverlayClick?: boolean;\n}\n\ninterface DialogState {\n show: boolean;\n LayerComponent?: React.ComponentType | null;\n}\n\nclass Dialog extends React.Component<\n React.PropsWithChildren<DialogOptions> & React.HTMLAttributes<HTMLDivElement>,\n DialogState\n> {\n static defaultProps = {\n closeOnEsc: true,\n closeOnOverlayClick: true,\n };\n\n private closeDialog: ((resp?: unknown) => void) | null = null;\n private onCloseFn: ((resp?: unknown) => void) | null = null;\n private lastFocusedElement: HTMLElement | null = null;\n private dialogRef = React.createRef<HTMLDivElement>();\n\n state: DialogState = {\n show: false,\n LayerComponent: undefined,\n };\n\n /**\n * Retrieves all focusable elements within the dialog.\n */\n private getFocusableElements = (): HTMLElement[] => {\n if (!this.dialogRef.current) return [];\n return Array.from(\n this.dialogRef.current.querySelectorAll(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])',\n ),\n ) as HTMLElement[];\n };\n\n /**\n * Handles keydown events to implement the focus trap.\n * Traps Tab and Shift+Tab within the dialog.\n */\n private handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Tab') {\n const focusableElements = this.getFocusableElements();\n if (focusableElements.length === 0) return;\n\n const firstElement = focusableElements[0];\n const lastElement = focusableElements[focusableElements.length - 1];\n\n if (e.shiftKey) {\n if (document.activeElement === firstElement) {\n lastElement.focus();\n e.preventDefault();\n }\n } else {\n if (document.activeElement === lastElement) {\n firstElement.focus();\n e.preventDefault();\n }\n }\n }\n };\n\n /**\n * Restores focus to the element that was focused before the dialog opened.\n */\n private restoreFocus = () => {\n if (this.lastFocusedElement) {\n const elementToBeFocused = this.lastFocusedElement;\n this.lastFocusedElement = null;\n setTimeout(() => {\n if (document.body.contains(elementToBeFocused)) {\n elementToBeFocused.focus();\n }\n }, 100);\n }\n };\n\n /**\n * Callback ref to capture the Dialog DOM element.\n * Triggers initial focus setting when the element mounts.\n */\n private setDialogRef = (node: HTMLDivElement | null) => {\n (this.dialogRef as React.MutableRefObject<HTMLDivElement | null>).current = node;\n\n if (node) {\n this.setInitialFocus(node);\n }\n };\n\n /**\n * Sets initial focus within the dialog.\n * Tries to focus the header first, then the first interactive element, or falls back to the container.\n */\n private setInitialFocus = (root: HTMLElement) => {\n const firstChild = root.firstElementChild as HTMLElement;\n if (firstChild) {\n if (firstChild.getAttribute('tabindex') === null) {\n firstChild.setAttribute('tabindex', '-1');\n }\n firstChild.focus();\n return;\n }\n\n const focusableElements = this.getFocusableElements();\n if (focusableElements.length > 0) {\n focusableElements[0].focus();\n } else {\n root.focus();\n }\n };\n\n shouldComponentUpdate(nextProps: DialogOptions, nextState: DialogState) {\n return this.state.show !== nextState.show;\n }\n\n componentWillUnmount() {\n // Clean up if component unmounts while dialog is open\n if (this.state.show && this.closeDialog) {\n this.closeDialog();\n }\n this.restoreFocus();\n this.closeDialog = null;\n this.onCloseFn = null;\n }\n\n public open = (closeCallback?: (resp?: unknown) => void) => {\n const { closeOnEsc, closeOnOverlayClick, children, ...rest } = this.props;\n\n // Save current focus\n this.lastFocusedElement = document.activeElement as HTMLElement;\n\n const [Component, closeFn] = LayerManager.renderLayer({\n exitDelay: 300,\n overlay: true,\n closeOnEsc,\n closeCallback: this.closeCallback,\n closeOnOverlayClick,\n position: LAYER_POSITION.DIALOG,\n component: (\n <DialogContainer\n {...rest}\n ref={this.setDialogRef}\n role=\"dialog\"\n aria-modal=\"true\"\n tabIndex={-1}\n onKeyDown={this.handleKeyDown}\n onClick={(e) => e.stopPropagation()}\n elevated\n >\n {children}\n </DialogContainer>\n ),\n });\n\n this.closeDialog = closeFn;\n\n this.setState({\n show: true,\n LayerComponent: Component,\n });\n this.onCloseFn = closeCallback ?? null;\n };\n\n public close = (resp?: unknown) => {\n this.closeDialog?.(resp);\n };\n\n private closeCallback = (resp?: unknown) => {\n this.restoreFocus();\n this.setState({\n show: false,\n LayerComponent: undefined,\n });\n this.onCloseFn?.(resp);\n };\n\n render() {\n const { LayerComponent } = this.state;\n\n if (this.state.show && LayerComponent) {\n return <LayerComponent />;\n } else {\n return null;\n }\n }\n}\n\nexport default Dialog;\n"],"names":["DialogContainer","styled","Card","Dialog","React","Component","shouldComponentUpdate","nextProps","nextState","state","show","componentWillUnmount","closeDialog","restoreFocus","onCloseFn","render","LayerComponent","_jsx","lastFocusedElement","dialogRef","createRef","undefined","getFocusableElements","current","Array","from","querySelectorAll","handleKeyDown","e","key","focusableElements","length","firstElement","lastElement","shiftKey","document","activeElement","focus","preventDefault","elementToBeFocused","setTimeout","body","contains","setDialogRef","node","setInitialFocus","root","firstChild","firstElementChild","getAttribute","setAttribute","open","closeCallback","closeOnEsc","closeOnOverlayClick","children","rest","props","closeFn","LayerManager","renderLayer","exitDelay","overlay","position","LAYER_POSITION","DIALOG","component","ref","role","aria-modal","tabIndex","onKeyDown","onClick","stopPropagation","elevated","setState","close","resp","defaultProps"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Dialog.js","sources":["../../../src/components/Dialog/Dialog.tsx"],"sourcesContent":["import React from 'react';\nimport styled from '@emotion/styled';\nimport LayerManager, { LAYER_POSITION } from '../../shared/LayerManager';\nimport { Card } from '../Card';\n\nexport const DialogContainer = styled(Card)`\n max-width: 768px;\n max-height: 80vh;\n transform: scale(0);\n opacity: 0;\n transition: all 0.3s ease;\n\n .nf-layer-enter & {\n opacity: 1;\n transform: scale(1);\n }\n\n .nf-layer-exit & {\n opacity: 0;\n transform: scale(0);\n }\n`;\n\nexport {\n Header as DialogHeader,\n Body as DialogBody,\n Footer as DialogFooter,\n} from '../../shared/styles';\n\ninterface DialogOptions {\n /** Flag to close dialog on `esc` click. Default value is true. */\n closeOnEsc?: boolean;\n /** Close layer overlay is clicked. Default value is true. */\n closeOnOverlayClick?: boolean;\n /** Ref forwarded to the underlying HTMLDivElement of the dialog container */\n forwardRef?: React.Ref<HTMLDivElement>;\n}\n\ninterface DialogState {\n show: boolean;\n LayerComponent?: React.ComponentType | null;\n}\n\nclass Dialog extends React.Component<\n React.PropsWithChildren<DialogOptions> & React.HTMLAttributes<HTMLDivElement>,\n DialogState\n> {\n static defaultProps = {\n closeOnEsc: true,\n closeOnOverlayClick: true,\n };\n\n private closeDialog: ((resp?: unknown) => void) | null = null;\n private onCloseFn: ((resp?: unknown) => void) | null = null;\n private lastFocusedElement: HTMLElement | null = null;\n private dialogRef = React.createRef<HTMLDivElement>();\n\n state: DialogState = {\n show: false,\n LayerComponent: undefined,\n };\n\n /**\n * Retrieves all focusable elements within the dialog.\n */\n private getFocusableElements = (): HTMLElement[] => {\n if (!this.dialogRef.current) return [];\n return Array.from(\n this.dialogRef.current.querySelectorAll(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])',\n ),\n ) as HTMLElement[];\n };\n\n /**\n * Handles keydown events to implement the focus trap.\n * Traps Tab and Shift+Tab within the dialog.\n */\n private handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Tab') {\n const focusableElements = this.getFocusableElements();\n if (focusableElements.length === 0) return;\n\n const firstElement = focusableElements[0];\n const lastElement = focusableElements[focusableElements.length - 1];\n\n if (e.shiftKey) {\n if (document.activeElement === firstElement) {\n lastElement.focus();\n e.preventDefault();\n }\n } else {\n if (document.activeElement === lastElement) {\n firstElement.focus();\n e.preventDefault();\n }\n }\n }\n };\n\n /**\n * Restores focus to the element that was focused before the dialog opened.\n */\n private restoreFocus = () => {\n if (this.lastFocusedElement) {\n const elementToBeFocused = this.lastFocusedElement;\n this.lastFocusedElement = null;\n setTimeout(() => {\n if (document.body.contains(elementToBeFocused)) {\n elementToBeFocused.focus();\n }\n }, 100);\n }\n };\n\n /**\n * Callback ref to capture the Dialog DOM element.\n * Triggers initial focus setting when the element mounts.\n */\n private setDialogRef = (node: HTMLDivElement | null) => {\n (this.dialogRef as React.MutableRefObject<HTMLDivElement | null>).current = node;\n\n if (node) {\n this.setInitialFocus(node);\n }\n\n if (this.props.forwardRef) {\n if (typeof this.props.forwardRef === 'function') {\n this.props.forwardRef(node);\n } else {\n (this.props.forwardRef as React.MutableRefObject<HTMLDivElement | null>).current =\n node;\n }\n }\n };\n\n /**\n * Sets initial focus within the dialog.\n * Tries to focus the header first, then the first interactive element, or falls back to the container.\n */\n private setInitialFocus = (root: HTMLElement) => {\n const firstChild = root.firstElementChild as HTMLElement;\n if (firstChild) {\n if (firstChild.getAttribute('tabindex') === null) {\n firstChild.setAttribute('tabindex', '-1');\n }\n firstChild.focus();\n return;\n }\n\n const focusableElements = this.getFocusableElements();\n if (focusableElements.length > 0) {\n focusableElements[0].focus();\n } else {\n root.focus();\n }\n };\n\n shouldComponentUpdate(nextProps: DialogOptions, nextState: DialogState) {\n return this.state.show !== nextState.show;\n }\n\n componentWillUnmount() {\n // Clean up if component unmounts while dialog is open\n if (this.state.show && this.closeDialog) {\n this.closeDialog();\n }\n this.restoreFocus();\n this.closeDialog = null;\n this.onCloseFn = null;\n }\n\n public open = (closeCallback?: (resp?: unknown) => void) => {\n const { closeOnEsc, closeOnOverlayClick, children, ...rest } = this.props;\n\n // Save current focus\n this.lastFocusedElement = document.activeElement as HTMLElement;\n\n const [Component, closeFn] = LayerManager.renderLayer({\n exitDelay: 300,\n overlay: true,\n closeOnEsc,\n closeCallback: this.closeCallback,\n closeOnOverlayClick,\n position: LAYER_POSITION.DIALOG,\n component: (\n <DialogContainer\n role=\"dialog\"\n {...rest}\n ref={this.setDialogRef}\n aria-modal=\"true\"\n tabIndex={-1}\n onKeyDown={this.handleKeyDown}\n onClick={(e) => e.stopPropagation()}\n elevated\n >\n {children}\n </DialogContainer>\n ),\n });\n\n this.closeDialog = closeFn;\n\n this.setState({\n show: true,\n LayerComponent: Component,\n });\n this.onCloseFn = closeCallback ?? null;\n };\n\n public close = (resp?: unknown) => {\n this.closeDialog?.(resp);\n };\n\n private closeCallback = (resp?: unknown) => {\n this.restoreFocus();\n this.setState({\n show: false,\n LayerComponent: undefined,\n });\n this.onCloseFn?.(resp);\n };\n\n render() {\n const { LayerComponent } = this.state;\n\n if (this.state.show && LayerComponent) {\n return <LayerComponent />;\n } else {\n return null;\n }\n }\n}\n\nexport default Dialog;\n"],"names":["DialogContainer","styled","Card","Dialog","React","Component","shouldComponentUpdate","nextProps","nextState","state","show","componentWillUnmount","closeDialog","restoreFocus","onCloseFn","render","LayerComponent","_jsx","lastFocusedElement","dialogRef","createRef","undefined","getFocusableElements","current","Array","from","querySelectorAll","handleKeyDown","e","key","focusableElements","length","firstElement","lastElement","shiftKey","document","activeElement","focus","preventDefault","elementToBeFocused","setTimeout","body","contains","setDialogRef","node","setInitialFocus","props","forwardRef","root","firstChild","firstElementChild","getAttribute","setAttribute","open","closeCallback","closeOnEsc","closeOnOverlayClick","children","rest","closeFn","LayerManager","renderLayer","exitDelay","overlay","position","LAYER_POSITION","DIALOG","component","role","ref","aria-modal","tabIndex","onKeyDown","onClick","stopPropagation","elevated","setState","close","resp","defaultProps"],"mappings":"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"}
|
|
@@ -8,15 +8,15 @@ import ActionButton from '../Button/ActionButton.js';
|
|
|
8
8
|
import Input from '../Input/Input.js';
|
|
9
9
|
|
|
10
10
|
const BodyText = /*#__PURE__*/ styled("p", {
|
|
11
|
-
target: "
|
|
11
|
+
target: "e18k0mlw0",
|
|
12
12
|
label: "BodyText"
|
|
13
13
|
})("margin-top:0;");
|
|
14
14
|
const InputContainer = /*#__PURE__*/ styled("div", {
|
|
15
|
-
target: "
|
|
15
|
+
target: "e18k0mlw1",
|
|
16
16
|
label: "InputContainer"
|
|
17
17
|
})("display:flex;flex:1;margin-top:10px;& > label{flex:1;width:100%;padding:0;& > input{width:100%;padding:0 8px;box-sizing:border-box;}}");
|
|
18
18
|
const StyledInput = /*#__PURE__*/ styled(Input, {
|
|
19
|
-
target: "
|
|
19
|
+
target: "e18k0mlw2",
|
|
20
20
|
label: "StyledInput"
|
|
21
21
|
})("flex:1;padding:0;");
|
|
22
22
|
class PromptDialog extends React.Component {
|
|
@@ -55,6 +55,7 @@ class PromptDialog extends React.Component {
|
|
|
55
55
|
children: cancelText
|
|
56
56
|
}),
|
|
57
57
|
/*#__PURE__*/ jsx(ActionButton, {
|
|
58
|
+
onClick: this.submit,
|
|
58
59
|
children: submitText
|
|
59
60
|
})
|
|
60
61
|
]
|
|
@@ -70,6 +71,7 @@ class PromptDialog extends React.Component {
|
|
|
70
71
|
});
|
|
71
72
|
}, this.cancel = ()=>this.dialog.current?.close(), this.submit = (e)=>{
|
|
72
73
|
e.preventDefault();
|
|
74
|
+
if (!this.state.value) return;
|
|
73
75
|
this.dialog.current?.close(this.state.value);
|
|
74
76
|
}, this.show = ()=>{
|
|
75
77
|
return new Promise((resolve, reject)=>{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PromptDialog.js","sources":["../../../src/components/Dialog/PromptDialog.tsx"],"sourcesContent":["import React, { createRef } from 'react';\nimport styled from '@emotion/styled';\nimport { Button, ActionButton } from '../Button';\nimport { Input } from '../Input';\nimport Dialog, { DialogHeader, DialogBody, DialogFooter } from './Dialog';\n\ntype PromptOption = {\n /** Shown as header of the dialog */\n header: string;\n /** Rendered as the body of the dialog */\n body: string;\n /** Default value for the input. */\n defaultValue?: string;\n /** Submit button text. Default value is 'Submit' */\n submitText?: string;\n /** Cancel button text. Default value is 'Cancel' */\n cancelText?: string;\n /** Props for the input. */\n inputProps?: React.HTMLProps<HTMLInputElement>;\n /** Additional props for the dialog. */\n dialogProps?: React.ComponentProps<typeof Dialog>;\n};\n\nconst BodyText = styled.p`\n margin-top: 0;\n`;\n\nconst InputContainer = styled.div`\n display: flex;\n flex: 1;\n margin-top: 10px;\n\n & > label {\n flex: 1;\n width: 100%;\n padding: 0;\n\n & > input {\n width: 100%;\n padding: 0 8px;\n box-sizing: border-box;\n }\n }\n`;\n\nconst StyledInput = styled(Input)`\n flex: 1;\n padding: 0;\n`;\n\nexport default class PromptDialog extends React.Component<PromptOption, { value?: string }> {\n static defaultProps = {\n cancelText: 'Cancel',\n submitText: 'Submit',\n defaultValue: '',\n };\n\n constructor(props: PromptOption) {\n super(props);\n this.state = {\n value: props.defaultValue,\n };\n }\n\n private dialog = createRef<Dialog>();\n\n private valueChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n this.setState({\n value: e.target.value,\n });\n };\n\n private cancel = () => this.dialog.current?.close();\n\n private submit = (e: React.
|
|
1
|
+
{"version":3,"file":"PromptDialog.js","sources":["../../../src/components/Dialog/PromptDialog.tsx"],"sourcesContent":["import React, { createRef } from 'react';\nimport styled from '@emotion/styled';\nimport { Button, ActionButton } from '../Button';\nimport { Input } from '../Input';\nimport Dialog, { DialogHeader, DialogBody, DialogFooter } from './Dialog';\n\ntype PromptOption = {\n /** Shown as header of the dialog */\n header: string;\n /** Rendered as the body of the dialog */\n body: string;\n /** Default value for the input. */\n defaultValue?: string;\n /** Submit button text. Default value is 'Submit' */\n submitText?: string;\n /** Cancel button text. Default value is 'Cancel' */\n cancelText?: string;\n /** Props for the input. */\n inputProps?: React.HTMLProps<HTMLInputElement>;\n /** Additional props for the dialog. */\n dialogProps?: React.ComponentProps<typeof Dialog>;\n};\n\nconst BodyText = styled.p`\n margin-top: 0;\n`;\n\nconst InputContainer = styled.div`\n display: flex;\n flex: 1;\n margin-top: 10px;\n\n & > label {\n flex: 1;\n width: 100%;\n padding: 0;\n\n & > input {\n width: 100%;\n padding: 0 8px;\n box-sizing: border-box;\n }\n }\n`;\n\nconst StyledInput = styled(Input)`\n flex: 1;\n padding: 0;\n`;\n\nexport default class PromptDialog extends React.Component<PromptOption, { value?: string }> {\n static defaultProps = {\n cancelText: 'Cancel',\n submitText: 'Submit',\n defaultValue: '',\n };\n\n constructor(props: PromptOption) {\n super(props);\n this.state = {\n value: props.defaultValue,\n };\n }\n\n private dialog = createRef<Dialog>();\n\n private valueChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n this.setState({\n value: e.target.value,\n });\n };\n\n private cancel = () => this.dialog.current?.close();\n\n private submit = (e: React.SyntheticEvent) => {\n e.preventDefault();\n if (!this.state.value) return;\n this.dialog.current?.close(this.state.value);\n };\n\n public show = () => {\n return new Promise((resolve, reject) => {\n const onClose = (value: unknown) => {\n if (value) {\n resolve(value);\n } else {\n reject();\n }\n this.setState({\n value: this.props.defaultValue,\n });\n };\n this.dialog.current?.open(onClose);\n });\n };\n\n render() {\n const { header, body, inputProps, submitText, cancelText, dialogProps } = this.props;\n\n return (\n <Dialog\n {...dialogProps}\n ref={this.dialog}\n closeOnEsc={false}\n closeOnOverlayClick={false}\n >\n <form onSubmit={this.submit}>\n {header && <DialogHeader>{header}</DialogHeader>}\n <DialogBody>\n <BodyText>{body}</BodyText>\n <InputContainer>\n <StyledInput\n value={this.state.value}\n onChange={this.valueChange}\n {...inputProps}\n />\n </InputContainer>\n </DialogBody>\n <DialogFooter>\n <Button type=\"button\" onClick={this.cancel}>\n {cancelText}\n </Button>\n <ActionButton onClick={this.submit}>{submitText}</ActionButton>\n </DialogFooter>\n </form>\n </Dialog>\n );\n }\n}\n"],"names":["BodyText","styled","InputContainer","StyledInput","Input","PromptDialog","React","Component","render","header","body","inputProps","submitText","cancelText","dialogProps","props","_jsx","Dialog","ref","dialog","closeOnEsc","closeOnOverlayClick","_jsxs","form","onSubmit","submit","DialogHeader","DialogBody","value","state","onChange","valueChange","DialogFooter","Button","type","onClick","cancel","ActionButton","createRef","e","setState","target","current","close","preventDefault","show","Promise","resolve","reject","onClose","defaultValue","open","defaultProps"],"mappings":"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"}
|
|
@@ -5,11 +5,11 @@ import DragItem from './DragItem.js';
|
|
|
5
5
|
import { ORIENTATION, DragContext } from './types.js';
|
|
6
6
|
|
|
7
7
|
/** Container Component */ const Container$6 = /*#__PURE__*/ styled("div", {
|
|
8
|
-
target: "
|
|
8
|
+
target: "e18d6tqk0",
|
|
9
9
|
label: "Container"
|
|
10
10
|
})("flex:1;display:flex;position:relative;flex-wrap:wrap;flex-direction:", (props)=>props.orientation === ORIENTATION.HORIZONTAL ? 'row' : 'column', ";");
|
|
11
11
|
/** Visually hidden but accessible to screen readers */ const VisuallyHidden$2 = /*#__PURE__*/ styled("div", {
|
|
12
|
-
target: "
|
|
12
|
+
target: "e18d6tqk1",
|
|
13
13
|
label: "VisuallyHidden"
|
|
14
14
|
})("position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0;");
|
|
15
15
|
/**
|
|
@@ -36,6 +36,10 @@ import { ORIENTATION, DragContext } from './types.js';
|
|
|
36
36
|
* @param props.children - Child elements to be rendered as draggable items
|
|
37
37
|
*
|
|
38
38
|
* @returns A draggable container with reorderable items
|
|
39
|
+
*/ /**
|
|
40
|
+
* DragAndDrop Component
|
|
41
|
+
* @param props - Component props
|
|
42
|
+
* @param ref - Ref forwarded to the underlying HTMLDivElement
|
|
39
43
|
*/ function DragAndDropComponent(props, ref) {
|
|
40
44
|
const { orientation = ORIENTATION.VERTICAL, children, onDrop, showIndicator = false, itemAriaLabelTemplate = 'Item {:position}. Press {:grabKey} to grab, {:moveKeys} to move, {:dropKey} or {:altDropKey} to drop', dragHandleAriaLabel = 'Drag to reorder', grabbedAnnouncementTemplate = 'Item {:position} grabbed. Use {:moveKeys} to move, {:dropKey} or {:altDropKey} to drop, {:cancelKey} to cancel', movedAnnouncementTemplate = 'Item moved to position {:position}', droppedAnnouncementTemplate = 'Item dropped at position {:position}', cancelledAnnouncementTemplate = 'Drag cancelled, item restored to original position', ...rest } = props;
|
|
41
45
|
const [startIndex, setStartIndex] = useState(null);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DragAndDrop.js","sources":["../../../src/components/DragAndDrop/DragAndDrop.tsx"],"sourcesContent":["import React, { PropsWithChildren, useState } from 'react';\nimport styled from '@emotion/styled';\nimport DragItem from './DragItem';\nimport { ORIENTATION, DragContext } from './types';\n\ntype DragAndDropProps = {\n /**\n * Orientation of the list layout\n * @default ORIENTATION.VERTICAL\n */\n orientation?: ORIENTATION;\n /** Drop event handler */\n onDrop: (start: number, end: number) => void;\n /** Shows drag indicator against each list item\n * @default false\n */\n showIndicator?: boolean;\n /**\n * i18n: Template for item aria-label. Placeholders: {:position}, {:grabKey}, {:moveKeys}, {:dropKey}, {:altDropKey}\n * @default 'Item {:position}. Press {:grabKey} to grab, {:moveKeys} to move, {:dropKey} or {:altDropKey} to drop'\n */\n itemAriaLabelTemplate?: string;\n /** i18n: Aria label for drag handle\n * @default 'Drag to reorder'\n */\n dragHandleAriaLabel?: string;\n /**\n * i18n: Template for grabbed announcement. Placeholders: {:position}, {:moveKeys}, {:dropKey}, {:altDropKey}, {:cancelKey}\n * @default 'Item {:position} grabbed. Use {:moveKeys} to move, {:dropKey} or {:altDropKey} to drop, {:cancelKey} to cancel'\n */\n grabbedAnnouncementTemplate?: string;\n /**\n * i18n: Template for moved announcement. Placeholders: {:position}\n * @default 'Item moved to position {:position}'\n */\n movedAnnouncementTemplate?: string;\n /**\n * i18n: Template for dropped announcement. Placeholders: {:position}\n * @default 'Item dropped at position {:position}'\n */\n droppedAnnouncementTemplate?: string;\n /**\n * i18n: Template for cancelled announcement\n * @default 'Drag cancelled, item restored to original position'\n */\n cancelledAnnouncementTemplate?: string;\n} & PropsWithChildren<unknown>;\n\n/** Container Component */\nconst Container = styled.div<{ orientation: ORIENTATION }>`\n flex: 1;\n display: flex;\n position: relative;\n flex-wrap: wrap;\n flex-direction: ${(props) => (props.orientation === ORIENTATION.HORIZONTAL ? 'row' : 'column')};\n`;\n\n/** Visually hidden but accessible to screen readers */\nconst VisuallyHidden = styled.div`\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border-width: 0;\n`;\n\n/**\n * A drag and drop container component that enables reordering of child elements.\n *\n * @component\n * @example\n * ```tsx\n * <DragAndDrop\n * orientation={ORIENTATION.VERTICAL}\n * onDrop={(start, end) => handleReorder(start, end)}\n * showIndicator={true}\n * >\n * <div>Item 1</div>\n * <div>Item 2</div>\n * <div>Item 3</div>\n * </DragAndDrop>\n * ```\n *\n * @param props - The component props\n * @param props.orientation - Determines the layout direction (horizontal or vertical). Defaults to VERTICAL.\n * @param props.onDrop - Callback fired when an item is dropped, receives the start and end indices\n * @param props.showIndicator - Whether to display drag indicators for each list item. Defaults to false.\n * @param props.children - Child elements to be rendered as draggable items\n *\n * @returns A draggable container with reorderable items\n */\nfunction DragAndDropComponent(props: DragAndDropProps, ref: React.Ref<HTMLDivElement>) {\n const {\n orientation = ORIENTATION.VERTICAL,\n children,\n onDrop,\n showIndicator = false,\n itemAriaLabelTemplate = 'Item {:position}. Press {:grabKey} to grab, {:moveKeys} to move, {:dropKey} or {:altDropKey} to drop',\n dragHandleAriaLabel = 'Drag to reorder',\n grabbedAnnouncementTemplate = 'Item {:position} grabbed. Use {:moveKeys} to move, {:dropKey} or {:altDropKey} to drop, {:cancelKey} to cancel',\n movedAnnouncementTemplate = 'Item moved to position {:position}',\n droppedAnnouncementTemplate = 'Item dropped at position {:position}',\n cancelledAnnouncementTemplate = 'Drag cancelled, item restored to original position',\n ...rest\n } = props;\n const [startIndex, setStartIndex] = useState<number | null>(null);\n const [originalIndex, setOriginalIndex] = useState<number | null>(null);\n const [isDragging, setIsDragging] = useState<boolean>(false);\n const [dragOver, setDragOver] = useState<number | null>(null);\n const [announcement, setAnnouncement] = useState<string | undefined>('');\n const childrenArray = React.Children.toArray(children);\n const totalItems = childrenArray.length;\n\n /**\n * Replace placeholders in i18n templates\n */\n const replacePlaceholders = (\n template: string,\n data: {\n position?: number;\n grabKey?: string;\n dropKey?: string;\n altDropKey?: string;\n cancelKey?: string;\n moveKeys?: string;\n },\n ): string => {\n return template\n .replace(/\\{:position\\}/g, String(data.position ?? ''))\n .replace(/\\{:grabKey\\}/g, data.grabKey ?? 'Space')\n .replace(/\\{:dropKey\\}/g, data.dropKey ?? 'Space')\n .replace(/\\{:altDropKey\\}/g, data.altDropKey ?? 'Enter')\n .replace(/\\{:cancelKey\\}/g, data.cancelKey ?? 'Escape')\n .replace(\n /\\{:moveKeys\\}/g,\n data.moveKeys ??\n (orientation === ORIENTATION.VERTICAL ? 'Arrow Up/Down' : 'Arrow Left/Right'),\n );\n };\n\n // i18n configuration object\n const i18n = {\n itemAriaLabelTemplate:\n itemAriaLabelTemplate ??\n 'Item {:position}. Press {:grabKey} to grab, {:moveKeys} to move, {:dropKey} or {:altDropKey} to drop',\n dragHandleAriaLabel: dragHandleAriaLabel ?? 'Drag to reorder',\n grabbedAnnouncementTemplate:\n grabbedAnnouncementTemplate ??\n 'Item {:position} grabbed. Use {:moveKeys} to move, {:dropKey} or {:altDropKey} to drop, {:cancelKey} to cancel',\n movedAnnouncementTemplate:\n movedAnnouncementTemplate ?? 'Item moved to position {:position}',\n droppedAnnouncementTemplate:\n droppedAnnouncementTemplate ?? 'Item dropped at position {:position}',\n cancelledAnnouncementTemplate:\n cancelledAnnouncementTemplate ?? 'Drag cancelled, item restored to original position',\n replacePlaceholders,\n };\n\n /**\n * Drop handler invoked when a draggable item is released.\n * @param index\n */\n const drop = (index: number | null) => {\n if (startIndex !== null && index !== null) {\n onDrop?.(startIndex, index);\n }\n setStartIndex(null);\n setOriginalIndex(null);\n setIsDragging(false);\n };\n\n /**\n * Cancel handler to restore item to original position\n */\n const cancel = () => {\n if (originalIndex !== null && startIndex !== null && startIndex !== originalIndex) {\n onDrop?.(startIndex, originalIndex);\n }\n setStartIndex(null);\n setOriginalIndex(null);\n setIsDragging(false);\n };\n\n /**\n * Start grab handler to track original position\n */\n const startGrab = (index: number) => {\n setStartIndex(index);\n setOriginalIndex(index);\n setIsDragging(true);\n };\n\n return (\n <>\n <DragContext.Provider\n value={{\n startIndex,\n setStartIndex,\n drop,\n onDrop,\n cancel,\n startGrab,\n isDragging,\n setIsDragging,\n setDragOver,\n i18n,\n }}\n >\n <Container {...rest} ref={ref} orientation={orientation} role=\"list\">\n {React.Children.map(childrenArray, (child, index) => (\n <DragItem\n index={index}\n orientation={orientation}\n showIndicator={showIndicator}\n dragOver={dragOver}\n totalItems={totalItems}\n setAnnouncement={setAnnouncement}\n >\n {child}\n </DragItem>\n ))}\n </Container>\n </DragContext.Provider>\n <VisuallyHidden role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">\n {announcement}\n </VisuallyHidden>\n </>\n );\n}\n\nconst DragAndDrop = React.forwardRef(DragAndDropComponent);\nexport default DragAndDrop;\n"],"names":["Container","styled","props","orientation","ORIENTATION","HORIZONTAL","VisuallyHidden","DragAndDropComponent","ref","VERTICAL","children","onDrop","showIndicator","itemAriaLabelTemplate","dragHandleAriaLabel","grabbedAnnouncementTemplate","movedAnnouncementTemplate","droppedAnnouncementTemplate","cancelledAnnouncementTemplate","rest","startIndex","setStartIndex","useState","originalIndex","setOriginalIndex","isDragging","setIsDragging","dragOver","setDragOver","announcement","setAnnouncement","childrenArray","React","Children","toArray","totalItems","length","replacePlaceholders","template","data","replace","String","position","grabKey","dropKey","altDropKey","cancelKey","moveKeys","i18n","drop","index","cancel","startGrab","_jsxs","_Fragment","_jsx","DragContext","Provider","value","role","map","child","DragItem","aria-live","aria-atomic","DragAndDrop","forwardRef"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DragAndDrop.js","sources":["../../../src/components/DragAndDrop/DragAndDrop.tsx"],"sourcesContent":["import React, { PropsWithChildren, useState } from 'react';\nimport styled from '@emotion/styled';\nimport DragItem from './DragItem';\nimport { ORIENTATION, DragContext } from './types';\n\ntype DragAndDropProps = {\n /**\n * Orientation of the list layout\n * @default ORIENTATION.VERTICAL\n */\n orientation?: ORIENTATION;\n /** Drop event handler */\n onDrop: (start: number, end: number) => void;\n /** Shows drag indicator against each list item\n * @default false\n */\n showIndicator?: boolean;\n /**\n * i18n: Template for item aria-label. Placeholders: {:position}, {:grabKey}, {:moveKeys}, {:dropKey}, {:altDropKey}\n * @default 'Item {:position}. Press {:grabKey} to grab, {:moveKeys} to move, {:dropKey} or {:altDropKey} to drop'\n */\n itemAriaLabelTemplate?: string;\n /** i18n: Aria label for drag handle\n * @default 'Drag to reorder'\n */\n dragHandleAriaLabel?: string;\n /**\n * i18n: Template for grabbed announcement. Placeholders: {:position}, {:moveKeys}, {:dropKey}, {:altDropKey}, {:cancelKey}\n * @default 'Item {:position} grabbed. Use {:moveKeys} to move, {:dropKey} or {:altDropKey} to drop, {:cancelKey} to cancel'\n */\n grabbedAnnouncementTemplate?: string;\n /**\n * i18n: Template for moved announcement. Placeholders: {:position}\n * @default 'Item moved to position {:position}'\n */\n movedAnnouncementTemplate?: string;\n /**\n * i18n: Template for dropped announcement. Placeholders: {:position}\n * @default 'Item dropped at position {:position}'\n */\n droppedAnnouncementTemplate?: string;\n /**\n * i18n: Template for cancelled announcement\n * @default 'Drag cancelled, item restored to original position'\n */\n cancelledAnnouncementTemplate?: string;\n} & PropsWithChildren<unknown>;\n\n/** Container Component */\nconst Container = styled.div<{ orientation: ORIENTATION }>`\n flex: 1;\n display: flex;\n position: relative;\n flex-wrap: wrap;\n flex-direction: ${(props) => (props.orientation === ORIENTATION.HORIZONTAL ? 'row' : 'column')};\n`;\n\n/** Visually hidden but accessible to screen readers */\nconst VisuallyHidden = styled.div`\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border-width: 0;\n`;\n\n/**\n * A drag and drop container component that enables reordering of child elements.\n *\n * @component\n * @example\n * ```tsx\n * <DragAndDrop\n * orientation={ORIENTATION.VERTICAL}\n * onDrop={(start, end) => handleReorder(start, end)}\n * showIndicator={true}\n * >\n * <div>Item 1</div>\n * <div>Item 2</div>\n * <div>Item 3</div>\n * </DragAndDrop>\n * ```\n *\n * @param props - The component props\n * @param props.orientation - Determines the layout direction (horizontal or vertical). Defaults to VERTICAL.\n * @param props.onDrop - Callback fired when an item is dropped, receives the start and end indices\n * @param props.showIndicator - Whether to display drag indicators for each list item. Defaults to false.\n * @param props.children - Child elements to be rendered as draggable items\n *\n * @returns A draggable container with reorderable items\n */\n/**\n * DragAndDrop Component\n * @param props - Component props\n * @param ref - Ref forwarded to the underlying HTMLDivElement\n */\nfunction DragAndDropComponent(props: DragAndDropProps, ref: React.Ref<HTMLDivElement>) {\n const {\n orientation = ORIENTATION.VERTICAL,\n children,\n onDrop,\n showIndicator = false,\n itemAriaLabelTemplate = 'Item {:position}. Press {:grabKey} to grab, {:moveKeys} to move, {:dropKey} or {:altDropKey} to drop',\n dragHandleAriaLabel = 'Drag to reorder',\n grabbedAnnouncementTemplate = 'Item {:position} grabbed. Use {:moveKeys} to move, {:dropKey} or {:altDropKey} to drop, {:cancelKey} to cancel',\n movedAnnouncementTemplate = 'Item moved to position {:position}',\n droppedAnnouncementTemplate = 'Item dropped at position {:position}',\n cancelledAnnouncementTemplate = 'Drag cancelled, item restored to original position',\n ...rest\n } = props;\n const [startIndex, setStartIndex] = useState<number | null>(null);\n const [originalIndex, setOriginalIndex] = useState<number | null>(null);\n const [isDragging, setIsDragging] = useState<boolean>(false);\n const [dragOver, setDragOver] = useState<number | null>(null);\n const [announcement, setAnnouncement] = useState<string | undefined>('');\n const childrenArray = React.Children.toArray(children);\n const totalItems = childrenArray.length;\n\n /**\n * Replace placeholders in i18n templates\n */\n const replacePlaceholders = (\n template: string,\n data: {\n position?: number;\n grabKey?: string;\n dropKey?: string;\n altDropKey?: string;\n cancelKey?: string;\n moveKeys?: string;\n },\n ): string => {\n return template\n .replace(/\\{:position\\}/g, String(data.position ?? ''))\n .replace(/\\{:grabKey\\}/g, data.grabKey ?? 'Space')\n .replace(/\\{:dropKey\\}/g, data.dropKey ?? 'Space')\n .replace(/\\{:altDropKey\\}/g, data.altDropKey ?? 'Enter')\n .replace(/\\{:cancelKey\\}/g, data.cancelKey ?? 'Escape')\n .replace(\n /\\{:moveKeys\\}/g,\n data.moveKeys ??\n (orientation === ORIENTATION.VERTICAL ? 'Arrow Up/Down' : 'Arrow Left/Right'),\n );\n };\n\n // i18n configuration object\n const i18n = {\n itemAriaLabelTemplate:\n itemAriaLabelTemplate ??\n 'Item {:position}. Press {:grabKey} to grab, {:moveKeys} to move, {:dropKey} or {:altDropKey} to drop',\n dragHandleAriaLabel: dragHandleAriaLabel ?? 'Drag to reorder',\n grabbedAnnouncementTemplate:\n grabbedAnnouncementTemplate ??\n 'Item {:position} grabbed. Use {:moveKeys} to move, {:dropKey} or {:altDropKey} to drop, {:cancelKey} to cancel',\n movedAnnouncementTemplate:\n movedAnnouncementTemplate ?? 'Item moved to position {:position}',\n droppedAnnouncementTemplate:\n droppedAnnouncementTemplate ?? 'Item dropped at position {:position}',\n cancelledAnnouncementTemplate:\n cancelledAnnouncementTemplate ?? 'Drag cancelled, item restored to original position',\n replacePlaceholders,\n };\n\n /**\n * Drop handler invoked when a draggable item is released.\n * @param index\n */\n const drop = (index: number | null) => {\n if (startIndex !== null && index !== null) {\n onDrop?.(startIndex, index);\n }\n setStartIndex(null);\n setOriginalIndex(null);\n setIsDragging(false);\n };\n\n /**\n * Cancel handler to restore item to original position\n */\n const cancel = () => {\n if (originalIndex !== null && startIndex !== null && startIndex !== originalIndex) {\n onDrop?.(startIndex, originalIndex);\n }\n setStartIndex(null);\n setOriginalIndex(null);\n setIsDragging(false);\n };\n\n /**\n * Start grab handler to track original position\n */\n const startGrab = (index: number) => {\n setStartIndex(index);\n setOriginalIndex(index);\n setIsDragging(true);\n };\n\n return (\n <>\n <DragContext.Provider\n value={{\n startIndex,\n setStartIndex,\n drop,\n onDrop,\n cancel,\n startGrab,\n isDragging,\n setIsDragging,\n setDragOver,\n i18n,\n }}\n >\n <Container {...rest} ref={ref} orientation={orientation} role=\"list\">\n {React.Children.map(childrenArray, (child, index) => (\n <DragItem\n index={index}\n orientation={orientation}\n showIndicator={showIndicator}\n dragOver={dragOver}\n totalItems={totalItems}\n setAnnouncement={setAnnouncement}\n >\n {child}\n </DragItem>\n ))}\n </Container>\n </DragContext.Provider>\n <VisuallyHidden role=\"status\" aria-live=\"polite\" aria-atomic=\"true\">\n {announcement}\n </VisuallyHidden>\n </>\n );\n}\n\nconst DragAndDrop = React.forwardRef(DragAndDropComponent);\nexport default DragAndDrop;\n"],"names":["Container","styled","props","orientation","ORIENTATION","HORIZONTAL","VisuallyHidden","DragAndDropComponent","ref","VERTICAL","children","onDrop","showIndicator","itemAriaLabelTemplate","dragHandleAriaLabel","grabbedAnnouncementTemplate","movedAnnouncementTemplate","droppedAnnouncementTemplate","cancelledAnnouncementTemplate","rest","startIndex","setStartIndex","useState","originalIndex","setOriginalIndex","isDragging","setIsDragging","dragOver","setDragOver","announcement","setAnnouncement","childrenArray","React","Children","toArray","totalItems","length","replacePlaceholders","template","data","replace","String","position","grabKey","dropKey","altDropKey","cancelKey","moveKeys","i18n","drop","index","cancel","startGrab","_jsxs","_Fragment","_jsx","DragContext","Provider","value","role","map","child","DragItem","aria-live","aria-atomic","DragAndDrop","forwardRef"],"mappings":"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"}
|
|
@@ -20,7 +20,7 @@ type DrawerProps = {
|
|
|
20
20
|
closeOnOverlayClick?: boolean;
|
|
21
21
|
/** Call back function called when the drawer closes. */
|
|
22
22
|
onClose?: () => void;
|
|
23
|
-
/** Ref to the drawer
|
|
23
|
+
/** Ref forwarded to the underlying HTMLDivElement of the drawer container */
|
|
24
24
|
forwardRef?: React.Ref<HTMLDivElement> | React.MutableRefObject<HTMLDivElement | null>;
|
|
25
25
|
};
|
|
26
26
|
interface DrawerState {
|
|
@@ -76,6 +76,10 @@ export default class Drawer extends React.Component<React.PropsWithChildren<Draw
|
|
|
76
76
|
* Lifecycle method to save the currently focused element when the drawer mounts while open.
|
|
77
77
|
*/
|
|
78
78
|
componentDidMount(): void;
|
|
79
|
+
/**
|
|
80
|
+
* Handles opening the drawer by creating the layer.
|
|
81
|
+
*/
|
|
82
|
+
private handleOpen;
|
|
79
83
|
/**
|
|
80
84
|
* Lifecycle method to restore focus when the drawer unmounts.
|
|
81
85
|
*/
|
|
@@ -98,7 +102,7 @@ export default class Drawer extends React.Component<React.PropsWithChildren<Draw
|
|
|
98
102
|
* Lifecycle method to handle Drawer updates.
|
|
99
103
|
* Manages opening/closing logic via LayerManager and focus preservation.
|
|
100
104
|
*/
|
|
101
|
-
getSnapshotBeforeUpdate(prevProps: DrawerProps):
|
|
105
|
+
getSnapshotBeforeUpdate(prevProps: DrawerProps): null;
|
|
102
106
|
/**
|
|
103
107
|
* Sets the ref prop passed to the Drawer Container component.
|
|
104
108
|
* @param node
|
|
@@ -32,7 +32,7 @@ const positionStyle$1 = (size)=>({
|
|
|
32
32
|
}
|
|
33
33
|
});
|
|
34
34
|
const DrawerDiv = /*#__PURE__*/ styled("div", {
|
|
35
|
-
target: "
|
|
35
|
+
target: "e1topccf0",
|
|
36
36
|
label: "DrawerDiv"
|
|
37
37
|
})("display:flex;flex-direction:column;background-color:", getThemeValue(THEME_NAME.BACKGROUND), ";transition:transform 0.3s ease;box-shadow:", getThemeValue(THEME_NAME.MODAL_SHADOW), ";", (props)=>positionStyle$1(props.size)[props.position].before, " .nf-layer-enter &{transform:translateX(0%);", (props)=>positionStyle$1(props.size)[props.position].after, "}");
|
|
38
38
|
const positionMap$1 = {
|
|
@@ -56,6 +56,8 @@ class Drawer extends React.Component {
|
|
|
56
56
|
*/ componentDidMount() {
|
|
57
57
|
if (this.props.open) {
|
|
58
58
|
this.lastFocusedElement = document.activeElement;
|
|
59
|
+
// Handle initial open state
|
|
60
|
+
this.handleOpen();
|
|
59
61
|
}
|
|
60
62
|
}
|
|
61
63
|
/**
|
|
@@ -69,7 +71,7 @@ class Drawer extends React.Component {
|
|
|
69
71
|
* Lifecycle method to handle Drawer updates.
|
|
70
72
|
* Manages opening/closing logic via LayerManager and focus preservation.
|
|
71
73
|
*/ getSnapshotBeforeUpdate(prevProps) {
|
|
72
|
-
const { open
|
|
74
|
+
const { open } = this.props;
|
|
73
75
|
if (prevProps.open && !open) {
|
|
74
76
|
this.closeCallback?.();
|
|
75
77
|
this.restoreFocus();
|
|
@@ -77,29 +79,9 @@ class Drawer extends React.Component {
|
|
|
77
79
|
if (!prevProps.open && open) {
|
|
78
80
|
// Save current focus
|
|
79
81
|
this.lastFocusedElement = document.activeElement;
|
|
80
|
-
this.
|
|
81
|
-
overlay,
|
|
82
|
-
exitDelay: 300,
|
|
83
|
-
position: positionMap$1[position],
|
|
84
|
-
closeCallback: this.onClose,
|
|
85
|
-
closeOnEsc,
|
|
86
|
-
closeOnOverlayClick,
|
|
87
|
-
component: /*#__PURE__*/ jsx(DrawerDiv, {
|
|
88
|
-
...rest,
|
|
89
|
-
ref: this.setDrawerRef,
|
|
90
|
-
role: "dialog",
|
|
91
|
-
"aria-modal": "true",
|
|
92
|
-
tabIndex: -1,
|
|
93
|
-
onKeyDown: this.handleKeyDown,
|
|
94
|
-
position: position,
|
|
95
|
-
size: size,
|
|
96
|
-
onClick: (e)=>e.stopPropagation(),
|
|
97
|
-
children: children
|
|
98
|
-
})
|
|
99
|
-
});
|
|
100
|
-
this.closeCallback = this.layer[1];
|
|
101
|
-
this.forceUpdate();
|
|
82
|
+
this.handleOpen();
|
|
102
83
|
}
|
|
84
|
+
return null;
|
|
103
85
|
}
|
|
104
86
|
/**
|
|
105
87
|
* Renders the Drawer component via the LayerManager portal.
|
|
@@ -153,6 +135,32 @@ class Drawer extends React.Component {
|
|
|
153
135
|
}
|
|
154
136
|
}
|
|
155
137
|
}, /**
|
|
138
|
+
* Handles opening the drawer by creating the layer.
|
|
139
|
+
*/ this.handleOpen = ()=>{
|
|
140
|
+
const { closeOnEsc, closeOnOverlayClick, position, size, overlay, children, ...rest } = this.props;
|
|
141
|
+
this.layer = LayerManager.renderLayer({
|
|
142
|
+
overlay,
|
|
143
|
+
exitDelay: 300,
|
|
144
|
+
position: positionMap$1[position],
|
|
145
|
+
closeCallback: this.onClose,
|
|
146
|
+
closeOnEsc,
|
|
147
|
+
closeOnOverlayClick,
|
|
148
|
+
component: /*#__PURE__*/ jsx(DrawerDiv, {
|
|
149
|
+
...rest,
|
|
150
|
+
ref: this.setDrawerRef,
|
|
151
|
+
role: "dialog",
|
|
152
|
+
"aria-modal": "true",
|
|
153
|
+
tabIndex: -1,
|
|
154
|
+
onKeyDown: this.handleKeyDown,
|
|
155
|
+
position: position,
|
|
156
|
+
size: size,
|
|
157
|
+
onClick: (e)=>e.stopPropagation(),
|
|
158
|
+
children: children
|
|
159
|
+
})
|
|
160
|
+
});
|
|
161
|
+
this.closeCallback = this.layer[1];
|
|
162
|
+
this.forceUpdate();
|
|
163
|
+
}, /**
|
|
156
164
|
* Restores focus to the element that was focused before the drawer opened.
|
|
157
165
|
*/ this.restoreFocus = ()=>{
|
|
158
166
|
if (this.lastFocusedElement) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Drawer.js","sources":["../../../src/components/Drawer/Drawer.tsx"],"sourcesContent":["import React from 'react';\nimport styled from '@emotion/styled';\nimport { getThemeValue, THEME_NAME } from '../../shared/constants';\nimport LayerManager, { LAYER_POSITION } from '../../shared/LayerManager';\n\nexport {\n Header as DrawerHeader,\n Body as DrawerBody,\n Footer as DrawerFooter,\n} from '../../shared/styles';\n\nexport enum DRAWER_POSITION {\n LEFT = 'LEFT',\n RIGHT = 'RIGHT',\n BOTTOM = 'BOTTOM',\n}\n\nconst positionStyle = (size?: string) => ({\n [DRAWER_POSITION.LEFT]: {\n before: `height: 100vh; min-width: ${size || '300px'}; transform: translateX(-100%);`,\n after: 'transform: translateX(0%);',\n },\n [DRAWER_POSITION.RIGHT]: {\n before: `height: 100vh; min-width: ${size || '300px'}; transform: translateX(100%);`,\n after: 'transform: translateX(0%);',\n },\n [DRAWER_POSITION.BOTTOM]: {\n before: `\n position: absolute;\n bottom: 0;\n width: 100%;\n height: ${size || '90vh'};\n transform: translateY(100%);\n border-radius: 15px 15px 0 0; \n `,\n after: 'transform: translateX(0%);',\n },\n});\n\nconst DrawerDiv = styled.div<{ position: DRAWER_POSITION; size?: string }>`\n display: flex;\n flex-direction: column;\n background-color: ${getThemeValue(THEME_NAME.BACKGROUND)};\n transition: transform 0.3s ease;\n box-shadow: ${getThemeValue(THEME_NAME.MODAL_SHADOW)};\n ${(props) => positionStyle(props.size)[props.position].before}\n\n .nf-layer-enter & {\n transform: translateX(0%);\n ${(props) => positionStyle(props.size)[props.position].after}\n }\n`;\n\ntype DrawerProps = {\n /** Opens the drawer */\n open: boolean;\n /** position of the drawer */\n position: DRAWER_POSITION;\n /** size of the drawer */\n size?: string;\n /** Shows an overlay behind the drawer. */\n overlay?: boolean;\n /** Closes the drawer on esc */\n closeOnEsc?: boolean;\n /** Closes the drawer on overlay click */\n closeOnOverlayClick?: boolean;\n /** Call back function called when the drawer closes. */\n onClose?: () => void;\n /** Ref to the drawer element */\n forwardRef?: React.Ref<HTMLDivElement> | React.MutableRefObject<HTMLDivElement | null>;\n};\n\ninterface DrawerState {\n open: boolean;\n}\n\nconst positionMap = {\n [DRAWER_POSITION.LEFT]: LAYER_POSITION.TOP_LEFT,\n [DRAWER_POSITION.RIGHT]: LAYER_POSITION.TOP_RIGHT,\n [DRAWER_POSITION.BOTTOM]: LAYER_POSITION.BOTTOM_LEFT,\n};\n\n/**\n * Drawer component\n *\n * A panel that slides in from the edge of the screen.\n * It sits on top of the application content and is often used for navigation or details.\n *\n * Accessibility:\n * - Implements ARIA `role=\"dialog\"` and `aria-modal=\"true\"`.\n * - Traps focus effectively within the drawer while open.\n * - Restores focus to the triggering element upon closure.\n * - Supports closing via ESC key and overlay click.\n */\nexport default class Drawer extends React.Component<\n React.PropsWithChildren<DrawerProps>,\n DrawerState\n> {\n state = {\n open: false,\n };\n\n static defaultProps = {\n overlay: true,\n position: DRAWER_POSITION.LEFT,\n closeOnEsc: true,\n closeOnOverlayClick: true,\n };\n\n /**\n * Syncs state with props.\n */\n static getDerivedStateFromProps(props: DrawerProps) {\n if (props.open) {\n return {\n open: true,\n };\n }\n return null;\n }\n\n private layer?: ReturnType<typeof LayerManager.renderLayer>;\n\n private closeCallback?: (resp?: unknown) => void;\n\n /**\n * Internal close handler.\n * Restores focus and calls the external onClose callback.\n */\n private onClose = () => {\n this.restoreFocus();\n this.setState({\n open: false,\n });\n this.props.onClose?.();\n this.closeCallback = undefined;\n this.layer = undefined;\n };\n\n private lastFocusedElement: HTMLElement | null = null;\n private drawerRef = React.createRef<HTMLDivElement>();\n\n /**\n * Retrieves all focusable elements within the drawer.\n */\n private getFocusableElements = (): HTMLElement[] => {\n if (!this.drawerRef.current) return [];\n return Array.from(\n this.drawerRef.current.querySelectorAll(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])',\n ),\n ) as HTMLElement[];\n };\n\n /**\n * Handles keydown events to implement the focus trap.\n * Traps Tab and Shift+Tab within the drawer.\n */\n private handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Tab') {\n const focusableElements = this.getFocusableElements();\n if (focusableElements.length === 0) return;\n\n const firstElement = focusableElements[0];\n const lastElement = focusableElements[focusableElements.length - 1];\n\n if (e.shiftKey) {\n if (document.activeElement === firstElement) {\n lastElement.focus();\n e.preventDefault();\n }\n } else {\n if (document.activeElement === lastElement) {\n firstElement.focus();\n e.preventDefault();\n }\n }\n }\n };\n\n /**\n * Lifecycle method to save the currently focused element when the drawer mounts while open.\n */\n componentDidMount() {\n if (this.props.open) {\n this.lastFocusedElement = document.activeElement as HTMLElement;\n }\n }\n\n /**\n * Lifecycle method to restore focus when the drawer unmounts.\n */\n componentWillUnmount() {\n if (this.props.open) {\n this.restoreFocus();\n }\n }\n\n /**\n * Restores focus to the element that was focused before the drawer opened.\n */\n private restoreFocus = () => {\n if (this.lastFocusedElement) {\n // Check if the element is still in the document\n const elementToBeFocused = this.lastFocusedElement;\n this.lastFocusedElement = null;\n setTimeout(() => {\n if (document.body.contains(elementToBeFocused)) {\n elementToBeFocused.focus();\n }\n }, 100);\n }\n };\n\n /**\n * Callback ref to capture the Drawer DOM element.\n * Triggers initial focus setting when the element mounts.\n */\n private setDrawerRef = (node: HTMLDivElement | null) => {\n // Update ref\n (this.drawerRef as React.MutableRefObject<HTMLDivElement | null>).current = node;\n\n if (node) {\n // Set initial focus when the node is mounted\n this.setInitialFocus(node);\n }\n };\n\n /**\n * Sets initial focus within the drawer.\n * Tries to focus the header first, then the first interactive element, or falls back to the container.\n */\n private setInitialFocus = (root: HTMLElement) => {\n // Try to find the header (assumed to be the first child)\n const firstChild = root.firstElementChild as HTMLElement;\n if (firstChild) {\n // Ensure it's focusable\n if (firstChild.getAttribute('tabindex') === null) {\n firstChild.setAttribute('tabindex', '-1');\n }\n firstChild.focus();\n return;\n }\n\n // Fallback to focusable elements\n const focusableElements = this.getFocusableElements();\n if (focusableElements.length > 0) {\n focusableElements[0].focus();\n } else {\n // Fallback to container\n root.focus();\n }\n };\n\n /**\n * Lifecycle method to handle Drawer updates.\n * Manages opening/closing logic via LayerManager and focus preservation.\n */\n getSnapshotBeforeUpdate(prevProps: DrawerProps) {\n const {\n open,\n closeOnEsc,\n closeOnOverlayClick,\n overlay,\n position,\n children,\n size,\n ...rest\n } = this.props;\n\n if (prevProps.open && !open) {\n this.closeCallback?.();\n this.restoreFocus();\n }\n\n if (!prevProps.open && open) {\n // Save current focus\n this.lastFocusedElement = document.activeElement as HTMLElement;\n\n this.layer = LayerManager.renderLayer({\n overlay,\n exitDelay: 300,\n position: positionMap[position],\n closeCallback: this.onClose,\n closeOnEsc,\n closeOnOverlayClick,\n component: (\n <DrawerDiv\n {...rest}\n ref={this.setDrawerRef}\n role=\"dialog\"\n aria-modal=\"true\"\n tabIndex={-1}\n onKeyDown={this.handleKeyDown}\n position={position}\n size={size}\n onClick={(e) => e.stopPropagation()}\n >\n {children}\n </DrawerDiv>\n ),\n });\n this.closeCallback = this.layer[1];\n this.forceUpdate();\n }\n }\n\n /**\n * Sets the ref prop passed to the Drawer Container component.\n * @param node\n */\n setRefProp = (node: HTMLDivElement | null) => {\n if (this.props.forwardRef && typeof this.props.forwardRef === 'function') {\n this.props.forwardRef(node);\n } else if (this.props.forwardRef && typeof this.props.forwardRef === 'object') {\n (this.props.forwardRef as React.MutableRefObject<HTMLDivElement | null>).current = node;\n }\n };\n\n /**\n * Renders the Drawer component via the LayerManager portal.\n */\n render() {\n if (this.state.open && this.layer) {\n const [Component] = this.layer;\n return <Component ref={this.setRefProp} />;\n }\n\n return null;\n }\n}\n"],"names":["DRAWER_POSITION","positionStyle","size","before","after","DrawerDiv","styled","getThemeValue","THEME_NAME","BACKGROUND","MODAL_SHADOW","props","position","positionMap","LAYER_POSITION","TOP_LEFT","TOP_RIGHT","BOTTOM_LEFT","Drawer","React","Component","getDerivedStateFromProps","open","componentDidMount","lastFocusedElement","document","activeElement","componentWillUnmount","restoreFocus","getSnapshotBeforeUpdate","prevProps","closeOnEsc","closeOnOverlayClick","overlay","children","rest","closeCallback","layer","LayerManager","renderLayer","exitDelay","onClose","component","_jsx","ref","setDrawerRef","role","aria-modal","tabIndex","onKeyDown","handleKeyDown","onClick","e","stopPropagation","forceUpdate","render","state","setRefProp","setState","undefined","drawerRef","createRef","getFocusableElements","current","Array","from","querySelectorAll","key","focusableElements","length","firstElement","lastElement","shiftKey","focus","preventDefault","elementToBeFocused","setTimeout","body","contains","node","setInitialFocus","root","firstChild","firstElementChild","getAttribute","setAttribute","forwardRef","defaultProps"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Drawer.js","sources":["../../../src/components/Drawer/Drawer.tsx"],"sourcesContent":["import React from 'react';\nimport styled from '@emotion/styled';\nimport { getThemeValue, THEME_NAME } from '../../shared/constants';\nimport LayerManager, { LAYER_POSITION } from '../../shared/LayerManager';\n\nexport {\n Header as DrawerHeader,\n Body as DrawerBody,\n Footer as DrawerFooter,\n} from '../../shared/styles';\n\nexport enum DRAWER_POSITION {\n LEFT = 'LEFT',\n RIGHT = 'RIGHT',\n BOTTOM = 'BOTTOM',\n}\n\nconst positionStyle = (size?: string) => ({\n [DRAWER_POSITION.LEFT]: {\n before: `height: 100vh; min-width: ${size || '300px'}; transform: translateX(-100%);`,\n after: 'transform: translateX(0%);',\n },\n [DRAWER_POSITION.RIGHT]: {\n before: `height: 100vh; min-width: ${size || '300px'}; transform: translateX(100%);`,\n after: 'transform: translateX(0%);',\n },\n [DRAWER_POSITION.BOTTOM]: {\n before: `\n position: absolute;\n bottom: 0;\n width: 100%;\n height: ${size || '90vh'};\n transform: translateY(100%);\n border-radius: 15px 15px 0 0; \n `,\n after: 'transform: translateX(0%);',\n },\n});\n\nconst DrawerDiv = styled.div<{ position: DRAWER_POSITION; size?: string }>`\n display: flex;\n flex-direction: column;\n background-color: ${getThemeValue(THEME_NAME.BACKGROUND)};\n transition: transform 0.3s ease;\n box-shadow: ${getThemeValue(THEME_NAME.MODAL_SHADOW)};\n ${(props) => positionStyle(props.size)[props.position].before}\n\n .nf-layer-enter & {\n transform: translateX(0%);\n ${(props) => positionStyle(props.size)[props.position].after}\n }\n`;\n\ntype DrawerProps = {\n /** Opens the drawer */\n open: boolean;\n /** position of the drawer */\n position: DRAWER_POSITION;\n /** size of the drawer */\n size?: string;\n /** Shows an overlay behind the drawer. */\n overlay?: boolean;\n /** Closes the drawer on esc */\n closeOnEsc?: boolean;\n /** Closes the drawer on overlay click */\n closeOnOverlayClick?: boolean;\n /** Call back function called when the drawer closes. */\n onClose?: () => void;\n /** Ref forwarded to the underlying HTMLDivElement of the drawer container */\n forwardRef?: React.Ref<HTMLDivElement> | React.MutableRefObject<HTMLDivElement | null>;\n};\n\ninterface DrawerState {\n open: boolean;\n}\n\nconst positionMap = {\n [DRAWER_POSITION.LEFT]: LAYER_POSITION.TOP_LEFT,\n [DRAWER_POSITION.RIGHT]: LAYER_POSITION.TOP_RIGHT,\n [DRAWER_POSITION.BOTTOM]: LAYER_POSITION.BOTTOM_LEFT,\n};\n\n/**\n * Drawer component\n *\n * A panel that slides in from the edge of the screen.\n * It sits on top of the application content and is often used for navigation or details.\n *\n * Accessibility:\n * - Implements ARIA `role=\"dialog\"` and `aria-modal=\"true\"`.\n * - Traps focus effectively within the drawer while open.\n * - Restores focus to the triggering element upon closure.\n * - Supports closing via ESC key and overlay click.\n */\nexport default class Drawer extends React.Component<\n React.PropsWithChildren<DrawerProps>,\n DrawerState\n> {\n state = {\n open: false,\n };\n\n static defaultProps = {\n overlay: true,\n position: DRAWER_POSITION.LEFT,\n closeOnEsc: true,\n closeOnOverlayClick: true,\n };\n\n /**\n * Syncs state with props.\n */\n static getDerivedStateFromProps(props: DrawerProps) {\n if (props.open) {\n return {\n open: true,\n };\n }\n return null;\n }\n\n private layer?: ReturnType<typeof LayerManager.renderLayer>;\n\n private closeCallback?: (resp?: unknown) => void;\n\n /**\n * Internal close handler.\n * Restores focus and calls the external onClose callback.\n */\n private onClose = () => {\n this.restoreFocus();\n this.setState({\n open: false,\n });\n this.props.onClose?.();\n this.closeCallback = undefined;\n this.layer = undefined;\n };\n\n private lastFocusedElement: HTMLElement | null = null;\n private drawerRef = React.createRef<HTMLDivElement>();\n\n /**\n * Retrieves all focusable elements within the drawer.\n */\n private getFocusableElements = (): HTMLElement[] => {\n if (!this.drawerRef.current) return [];\n return Array.from(\n this.drawerRef.current.querySelectorAll(\n 'button, [href], input, select, textarea, [tabindex]:not([tabindex=\"-1\"])',\n ),\n ) as HTMLElement[];\n };\n\n /**\n * Handles keydown events to implement the focus trap.\n * Traps Tab and Shift+Tab within the drawer.\n */\n private handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Tab') {\n const focusableElements = this.getFocusableElements();\n if (focusableElements.length === 0) return;\n\n const firstElement = focusableElements[0];\n const lastElement = focusableElements[focusableElements.length - 1];\n\n if (e.shiftKey) {\n if (document.activeElement === firstElement) {\n lastElement.focus();\n e.preventDefault();\n }\n } else {\n if (document.activeElement === lastElement) {\n firstElement.focus();\n e.preventDefault();\n }\n }\n }\n };\n\n /**\n * Lifecycle method to save the currently focused element when the drawer mounts while open.\n */\n componentDidMount() {\n if (this.props.open) {\n this.lastFocusedElement = document.activeElement as HTMLElement;\n // Handle initial open state\n this.handleOpen();\n }\n }\n\n /**\n * Handles opening the drawer by creating the layer.\n */\n private handleOpen = () => {\n const { closeOnEsc, closeOnOverlayClick, position, size, overlay, children, ...rest } =\n this.props;\n\n this.layer = LayerManager.renderLayer({\n overlay,\n exitDelay: 300,\n position: positionMap[position],\n closeCallback: this.onClose,\n closeOnEsc,\n closeOnOverlayClick,\n component: (\n <DrawerDiv\n {...rest}\n ref={this.setDrawerRef}\n role=\"dialog\"\n aria-modal=\"true\"\n tabIndex={-1}\n onKeyDown={this.handleKeyDown}\n position={position}\n size={size}\n onClick={(e) => e.stopPropagation()}\n >\n {children}\n </DrawerDiv>\n ),\n });\n this.closeCallback = this.layer[1];\n this.forceUpdate();\n };\n\n /**\n * Lifecycle method to restore focus when the drawer unmounts.\n */\n componentWillUnmount() {\n if (this.props.open) {\n this.restoreFocus();\n }\n }\n\n /**\n * Restores focus to the element that was focused before the drawer opened.\n */\n private restoreFocus = () => {\n if (this.lastFocusedElement) {\n // Check if the element is still in the document\n const elementToBeFocused = this.lastFocusedElement;\n this.lastFocusedElement = null;\n setTimeout(() => {\n if (document.body.contains(elementToBeFocused)) {\n elementToBeFocused.focus();\n }\n }, 100);\n }\n };\n\n /**\n * Callback ref to capture the Drawer DOM element.\n * Triggers initial focus setting when the element mounts.\n */\n private setDrawerRef = (node: HTMLDivElement | null) => {\n // Update ref\n (this.drawerRef as React.MutableRefObject<HTMLDivElement | null>).current = node;\n\n if (node) {\n // Set initial focus when the node is mounted\n this.setInitialFocus(node);\n }\n };\n\n /**\n * Sets initial focus within the drawer.\n * Tries to focus the header first, then the first interactive element, or falls back to the container.\n */\n private setInitialFocus = (root: HTMLElement) => {\n // Try to find the header (assumed to be the first child)\n const firstChild = root.firstElementChild as HTMLElement;\n if (firstChild) {\n // Ensure it's focusable\n if (firstChild.getAttribute('tabindex') === null) {\n firstChild.setAttribute('tabindex', '-1');\n }\n firstChild.focus();\n return;\n }\n\n // Fallback to focusable elements\n const focusableElements = this.getFocusableElements();\n if (focusableElements.length > 0) {\n focusableElements[0].focus();\n } else {\n // Fallback to container\n root.focus();\n }\n };\n\n /**\n * Lifecycle method to handle Drawer updates.\n * Manages opening/closing logic via LayerManager and focus preservation.\n */\n getSnapshotBeforeUpdate(prevProps: DrawerProps) {\n const { open } = this.props;\n\n if (prevProps.open && !open) {\n this.closeCallback?.();\n this.restoreFocus();\n }\n\n if (!prevProps.open && open) {\n // Save current focus\n this.lastFocusedElement = document.activeElement as HTMLElement;\n this.handleOpen();\n }\n\n return null;\n }\n\n /**\n * Sets the ref prop passed to the Drawer Container component.\n * @param node\n */\n setRefProp = (node: HTMLDivElement | null) => {\n if (this.props.forwardRef && typeof this.props.forwardRef === 'function') {\n this.props.forwardRef(node);\n } else if (this.props.forwardRef && typeof this.props.forwardRef === 'object') {\n (this.props.forwardRef as React.MutableRefObject<HTMLDivElement | null>).current = node;\n }\n };\n\n /**\n * Renders the Drawer component via the LayerManager portal.\n */\n render() {\n if (this.state.open && this.layer) {\n const [Component] = this.layer;\n return <Component ref={this.setRefProp} />;\n }\n\n return null;\n }\n}\n"],"names":["DRAWER_POSITION","positionStyle","size","before","after","DrawerDiv","styled","getThemeValue","THEME_NAME","BACKGROUND","MODAL_SHADOW","props","position","positionMap","LAYER_POSITION","TOP_LEFT","TOP_RIGHT","BOTTOM_LEFT","Drawer","React","Component","getDerivedStateFromProps","open","componentDidMount","lastFocusedElement","document","activeElement","handleOpen","componentWillUnmount","restoreFocus","getSnapshotBeforeUpdate","prevProps","closeCallback","render","state","layer","_jsx","ref","setRefProp","onClose","setState","undefined","drawerRef","createRef","getFocusableElements","current","Array","from","querySelectorAll","handleKeyDown","e","key","focusableElements","length","firstElement","lastElement","shiftKey","focus","preventDefault","closeOnEsc","closeOnOverlayClick","overlay","children","rest","LayerManager","renderLayer","exitDelay","component","setDrawerRef","role","aria-modal","tabIndex","onKeyDown","onClick","stopPropagation","forceUpdate","elementToBeFocused","setTimeout","body","contains","node","setInitialFocus","root","firstChild","firstElementChild","getAttribute","setAttribute","forwardRef","defaultProps"],"mappings":"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"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
|
|
2
|
+
declare const Group: React.ForwardRefExoticComponent<{
|
|
3
3
|
/** Error Message for the group */
|
|
4
4
|
errorText?: string;
|
|
5
|
-
}
|
|
6
|
-
|
|
5
|
+
} & {
|
|
6
|
+
children?: React.ReactNode | undefined;
|
|
7
|
+
} & React.RefAttributes<HTMLDivElement>>;
|
|
7
8
|
export default Group;
|
|
@@ -4,7 +4,7 @@ import styled from '@emotion/styled';
|
|
|
4
4
|
import { getThemeValue, THEME_NAME } from '../../shared/constants.js';
|
|
5
5
|
|
|
6
6
|
const Container$3 = /*#__PURE__*/ styled("div", {
|
|
7
|
-
target: "
|
|
7
|
+
target: "eahc3qe0",
|
|
8
8
|
label: "Container"
|
|
9
9
|
})("display:inline-flex;border:1px solid ", getThemeValue(THEME_NAME.BORDER_COLOR), ";border-radius:3px;margin:5px;& button,& label{margin:0;border:none;border-radius:0;border-left:1px solid ", getThemeValue(THEME_NAME.BORDER_COLOR), ";box-shadow:none;height:32px;}& > div button{border-left:none;}& input,& select{border:none;height:32px;}& input,& select{border-radius:0;}& input:active,& select:active{box-shadow:none;}& > div > span{top:8px;}& > *:first-child,& > label:first-child input,& > label:first-child select,& > *:first-child label,& > *:first-child input{border-left:none;border-radius:2px 0 0 2px;}& > *:last-child,& > label:last-child input,& > label:last-child select,& > *:last-child label,& > *:last-child input{border-radius:0 2px 2px 0;}& *:focus,& *:focus + span{z-index:1;}&:focus-within,&:hover{box-shadow:", getThemeValue(THEME_NAME.HOVER_SHADOW), ";}", (props)=>props.errorText ? `
|
|
10
10
|
border-color: ${getThemeValue(THEME_NAME.ERROR)};
|
|
@@ -14,10 +14,14 @@ const Container$3 = /*#__PURE__*/ styled("div", {
|
|
|
14
14
|
}
|
|
15
15
|
` : '');
|
|
16
16
|
const ErrorContainer = /*#__PURE__*/ styled("div", {
|
|
17
|
-
target: "
|
|
17
|
+
target: "eahc3qe1",
|
|
18
18
|
label: "ErrorContainer"
|
|
19
19
|
})("color:", getThemeValue(THEME_NAME.ERROR), ";margin-left:8px;font-size:12px;");
|
|
20
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Group Component
|
|
22
|
+
* @param props - Component props
|
|
23
|
+
* @param ref - Ref forwarded to the underlying HTMLDivElement
|
|
24
|
+
*/ function GroupComponent(props, ref) {
|
|
21
25
|
const errorId = useId();
|
|
22
26
|
return /*#__PURE__*/ jsxs(Fragment, {
|
|
23
27
|
children: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Group.js","sources":["../../../src/components/Groups/Group.tsx"],"sourcesContent":["import React, { useId } from 'react';\nimport styled from '@emotion/styled';\nimport { getThemeValue, THEME_NAME } from '../../shared/constants';\n\nconst Container = styled.div<GroupProps>`\n display: inline-flex;\n border: 1px solid ${getThemeValue(THEME_NAME.BORDER_COLOR)};\n border-radius: 3px;\n margin: 5px;\n\n /* overrides */\n & button,\n & label {\n margin: 0;\n border: none;\n border-radius: 0;\n border-left: 1px solid ${getThemeValue(THEME_NAME.BORDER_COLOR)};\n box-shadow: none;\n height: 32px;\n }\n\n & > div button {\n border-left: none;\n }\n\n & input,\n & select {\n border: none;\n height: 32px;\n }\n\n & input,\n & select {\n border-radius: 0;\n }\n\n & input:active,\n & select:active {\n box-shadow: none;\n }\n\n & > div > span {\n top: 8px;\n }\n\n /* Handling for first and last child */\n & > *:first-child,\n & > label:first-child input,\n & > label:first-child select,\n & > *:first-child label,\n & > *:first-child input {\n border-left: none;\n border-radius: 2px 0 0 2px;\n }\n\n & > *:last-child,\n & > label:last-child input,\n & > label:last-child select,\n & > *:last-child label,\n & > *:last-child input {\n border-radius: 0 2px 2px 0;\n }\n\n /* focus */\n & *:focus,\n & *:focus + span {\n z-index: 1;\n }\n\n &:focus-within,\n &:hover {\n box-shadow: ${getThemeValue(THEME_NAME.HOVER_SHADOW)};\n }\n\n ${(props) =>\n props.errorText\n ? `\n border-color: ${getThemeValue(THEME_NAME.ERROR)};\n\n & > button, & > label {\n border-color: ${getThemeValue(THEME_NAME.ERROR)};\n }\n `\n : ''}\n`;\n\nconst ErrorContainer = styled.div`\n color: ${getThemeValue(THEME_NAME.ERROR)};\n margin-left: 8px;\n font-size: 12px;\n`;\n\ntype GroupProps = {\n /** Error Message for the group */\n errorText?: string;\n}
|
|
1
|
+
{"version":3,"file":"Group.js","sources":["../../../src/components/Groups/Group.tsx"],"sourcesContent":["import React, { useId } from 'react';\nimport styled from '@emotion/styled';\nimport { getThemeValue, THEME_NAME } from '../../shared/constants';\n\nconst Container = styled.div<GroupProps>`\n display: inline-flex;\n border: 1px solid ${getThemeValue(THEME_NAME.BORDER_COLOR)};\n border-radius: 3px;\n margin: 5px;\n\n /* overrides */\n & button,\n & label {\n margin: 0;\n border: none;\n border-radius: 0;\n border-left: 1px solid ${getThemeValue(THEME_NAME.BORDER_COLOR)};\n box-shadow: none;\n height: 32px;\n }\n\n & > div button {\n border-left: none;\n }\n\n & input,\n & select {\n border: none;\n height: 32px;\n }\n\n & input,\n & select {\n border-radius: 0;\n }\n\n & input:active,\n & select:active {\n box-shadow: none;\n }\n\n & > div > span {\n top: 8px;\n }\n\n /* Handling for first and last child */\n & > *:first-child,\n & > label:first-child input,\n & > label:first-child select,\n & > *:first-child label,\n & > *:first-child input {\n border-left: none;\n border-radius: 2px 0 0 2px;\n }\n\n & > *:last-child,\n & > label:last-child input,\n & > label:last-child select,\n & > *:last-child label,\n & > *:last-child input {\n border-radius: 0 2px 2px 0;\n }\n\n /* focus */\n & *:focus,\n & *:focus + span {\n z-index: 1;\n }\n\n &:focus-within,\n &:hover {\n box-shadow: ${getThemeValue(THEME_NAME.HOVER_SHADOW)};\n }\n\n ${(props) =>\n props.errorText\n ? `\n border-color: ${getThemeValue(THEME_NAME.ERROR)};\n\n & > button, & > label {\n border-color: ${getThemeValue(THEME_NAME.ERROR)};\n }\n `\n : ''}\n`;\n\nconst ErrorContainer = styled.div`\n color: ${getThemeValue(THEME_NAME.ERROR)};\n margin-left: 8px;\n font-size: 12px;\n`;\n\ntype GroupProps = React.PropsWithChildren<{\n /** Error Message for the group */\n errorText?: string;\n}>;\n\n/**\n * Group Component\n * @param props - Component props\n * @param ref - Ref forwarded to the underlying HTMLDivElement\n */\nfunction GroupComponent(\n props: React.PropsWithChildren<GroupProps>,\n ref: React.Ref<HTMLDivElement>,\n) {\n const errorId = useId();\n\n return (\n <>\n <Container\n {...props}\n ref={ref}\n aria-describedby={props.errorText ? errorId : undefined}\n >\n {props.children}\n </Container>\n {props.errorText && <ErrorContainer id={errorId}>{props.errorText}</ErrorContainer>}\n </>\n );\n}\n\nconst Group = React.forwardRef<HTMLDivElement, GroupProps>(GroupComponent);\nexport default Group;\n"],"names":["Container","styled","getThemeValue","THEME_NAME","BORDER_COLOR","HOVER_SHADOW","props","errorText","ERROR","ErrorContainer","GroupComponent","ref","errorId","useId","_jsxs","_Fragment","_jsx","aria-describedby","undefined","children","id","Group","React","forwardRef"],"mappings":";;;;;AAIA,MAAMA,WAAAA,iBAAYC,MAAAA,CAAAA,KAAAA,EAAAA;;;AAEMC,CAAAA,CAAAA,CAAAA,uCAAAA,EAAAA,aAAAA,CAAcC,WAAWC,YAAY,CAAA,EAAA,4GAAA,EAU5BF,aAAAA,CAAcC,UAAAA,CAAWC,YAAY,CAAA,EAAA,qlBAAA,EAuDhDF,aAAAA,CAAcC,UAAAA,CAAWE,YAAY,SAGrD,CAACC,KAAAA,GACCA,KAAAA,CAAMC,SAAS,GACT;sBACQ,EAAEL,aAAAA,CAAcC,UAAAA,CAAWK,KAAK,CAAA,CAAE;;;0BAG9B,EAAEN,aAAAA,CAAcC,UAAAA,CAAWK,KAAK,CAAA,CAAE;;AAExD,IAAA,CAAC,GACS,EAAA,CAAA;AAGd,MAAMC,cAAAA,iBAAiBR,MAAAA,CAAAA,KAAAA,EAAAA;;;AACVC,CAAAA,CAAAA,CAAAA,QAAAA,EAAAA,aAAAA,CAAcC,WAAWK,KAAK,CAAA,EAAA,kCAAA,CAAA;AAU3C;;;;AAIC,IACD,SAASE,cAAAA,CACLJ,KAA0C,EAC1CK,GAA8B,EAAA;AAE9B,IAAA,MAAMC,OAAAA,GAAUC,KAAAA,EAAAA;IAEhB,qBACIC,IAAA,CAAAC,QAAA,EAAA;;0BACIC,GAAA,CAAChB,WAAAA,EAAAA;AACI,gBAAA,GAAGM,KAAK;gBACTK,GAAAA,EAAKA,GAAAA;gBACLM,kBAAAA,EAAkBX,KAAAA,CAAMC,SAAS,GAAGK,OAAAA,GAAUM,SAAAA;AAE7CZ,gBAAAA,QAAAA,EAAAA,KAAAA,CAAMa;;YAEVb,KAAAA,CAAMC,SAAS,kBAAIS,GAAA,CAACP,cAAAA,EAAAA;gBAAeW,EAAAA,EAAIR,OAAAA;AAAUN,gBAAAA,QAAAA,EAAAA,KAAAA,CAAMC;;;;AAGpE;AAEA,MAAMc,KAAAA,iBAAQC,KAAAA,CAAMC,UAAU,CAA6Bb,cAAAA;;;;"}
|
|
@@ -4,18 +4,22 @@ import styled from '@emotion/styled';
|
|
|
4
4
|
import { getThemeValue, THEME_NAME } from '../../shared/constants.js';
|
|
5
5
|
|
|
6
6
|
const Label$2 = /*#__PURE__*/ styled("label", {
|
|
7
|
-
target: "
|
|
7
|
+
target: "e10ud8wb0",
|
|
8
8
|
label: "Label"
|
|
9
9
|
})("margin:5px 0;position:relative;display:inline-flex;align-items:center;cursor:pointer;");
|
|
10
10
|
const StyledCheckmark = /*#__PURE__*/ styled("span", {
|
|
11
|
-
target: "
|
|
11
|
+
target: "e10ud8wb1",
|
|
12
12
|
label: "StyledCheckmark"
|
|
13
13
|
})("width:16px;height:16px;border:1px solid ", getThemeValue(THEME_NAME.BORDER_COLOR), ";display:inline-block;border-radius:3px;margin-right:5px;background-color:", getThemeValue(THEME_NAME.BACKGROUND), ";transition:all 0.3s ease;position:relative;flex-shrink:0;&::after{content:'';width:3px;height:10px;border-right:2px solid ", getThemeValue(THEME_NAME.TEXT_COLOR_LIGHT), ";border-bottom:2px solid ", getThemeValue(THEME_NAME.TEXT_COLOR_LIGHT), ";position:absolute;top:1px;left:6px;opacity:0;transform:rotate(45deg) scale(0);transition:all 0.2s ease;}");
|
|
14
14
|
const HiddenInput$1 = /*#__PURE__*/ styled("input", {
|
|
15
|
-
target: "
|
|
15
|
+
target: "e10ud8wb2",
|
|
16
16
|
label: "HiddenInput"
|
|
17
17
|
})("opacity:0;width:0;height:0;position:absolute;margin:0;&:checked + ", StyledCheckmark, "{background-color:", getThemeValue(THEME_NAME.PRIMARY), ";border-color:", getThemeValue(THEME_NAME.PRIMARY), ";}&:checked + ", StyledCheckmark, "::after{opacity:1;transform:rotate(45deg) scale(1);}&:indeterminate + ", StyledCheckmark, "{background-color:", getThemeValue(THEME_NAME.PRIMARY), ";border-color:", getThemeValue(THEME_NAME.PRIMARY), ";}&:indeterminate + ", StyledCheckmark, "::after{opacity:1;height:0;width:8px;border-right:none;border-bottom:2px solid ", getThemeValue(THEME_NAME.TEXT_COLOR_LIGHT), ";transform:rotate(0deg) scale(1);top:7px;left:4px;}&:enabled:active + ", StyledCheckmark, ",&:focus + ", StyledCheckmark, "{border-color:", getThemeValue(THEME_NAME.PRIMARY), ";box-shadow:0 0 0 3px ", getThemeValue(THEME_NAME.PRIMARY_LIGHT), ";}&:enabled:active ~ span,&:focus ~ span{color:", getThemeValue(THEME_NAME.PRIMARY), ";}&:enabled:hover + ", StyledCheckmark, "{border-color:", getThemeValue(THEME_NAME.PRIMARY), ";}&:enabled:hover ~ span{color:", getThemeValue(THEME_NAME.PRIMARY), ";}&:disabled + ", StyledCheckmark, "{border-color:", getThemeValue(THEME_NAME.DISABLED_BORDER), ";cursor:not-allowed;}&:disabled ~ span{color:", getThemeValue(THEME_NAME.DISABLED), ";cursor:not-allowed;}&:checked:disabled + ", StyledCheckmark, ",&:indeterminate:disabled + ", StyledCheckmark, "{background-color:", getThemeValue(THEME_NAME.DISABLED), ";}");
|
|
18
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Checkbox Component
|
|
20
|
+
* @param props - Component props
|
|
21
|
+
* @param fwdRef - Ref forwarded to the underlying HTMLInputElement
|
|
22
|
+
*/ function CheckboxComponent(props, fwdRef) {
|
|
19
23
|
const { label = '', indeterminate = false, checked, ...rest } = props;
|
|
20
24
|
const ref = useCallback((node)=>{
|
|
21
25
|
// Ensure the DOM `indeterminate` flag always matches the prop
|