@mskcc/carbon-react
Version:
Carbon react components for the MSKCC DSM
255 lines (249 loc) • 7.43 kB
JavaScript
/**
* MSKCC 2021, 2024
*/
import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js';
import cx from 'classnames';
import PropTypes from 'prop-types';
import React__default, { useRef, useState, useContext } from 'react';
import { Popover, PopoverContent } from '../Popover/index.js';
import { useWindowEvent } from '../../internal/useEvent.js';
import { useId } from '../../internal/useId.js';
import { usePrefix } from '../../internal/usePrefix.js';
import { match } from '../../internal/keyboard/match.js';
import { Escape } from '../../internal/keyboard/keys.js';
/**
* Used to render the label for a Toggletip
*/
function ToggletipLabel(_ref) {
let {
as: BaseComponent = 'span',
children,
className: customClassName
} = _ref;
const prefix = usePrefix();
const className = cx(`${prefix}--toggletip-label`, customClassName);
const BaseComponentAsAny = BaseComponent;
return /*#__PURE__*/React__default.createElement(BaseComponentAsAny, {
className: className
}, children);
}
ToggletipLabel.propTypes = {
/**
* Provide a custom element or component to render the top-level node for the
* component.
*/
as: PropTypes.elementType,
/**
* Custom children to be rendered as the content of the label
*/
children: PropTypes.node,
/**
* Provide a custom class name to be added to the outermost node in the
* component
*/
className: PropTypes.string
};
// Used to coordinate accessibility props between button and content along with
// the actions to open and close the toggletip
const ToggletipContext = /*#__PURE__*/React__default.createContext(undefined);
function useToggletip() {
return useContext(ToggletipContext);
}
/**
* Used as a container for the button and content of a toggletip. This component
* is responsible for coordinating between interactions with the button and the
* visibility of the content
*/
function Toggletip(_ref2) {
let {
align,
as,
className: customClassName,
children,
defaultOpen = false
} = _ref2;
const ref = useRef(null);
const [open, setOpen] = useState(defaultOpen);
const prefix = usePrefix();
const id = useId();
const className = cx(`${prefix}--toggletip`, customClassName, {
[`${prefix}--toggletip--open`]: open
});
const actions = {
toggle: () => {
setOpen(!open);
},
close: () => {
setOpen(false);
}
};
const value = {
buttonProps: {
'aria-expanded': open,
'aria-controls': id,
onClick: actions.toggle
},
contentProps: {
id
}
};
const onKeyDown = event => {
if (open && match(event, Escape)) {
actions.close();
// If the menu is closed while focus is still inside the menu, it should return to the trigger button (#12922)
const button = ref.current?.children[0];
if (button instanceof HTMLButtonElement) {
button.focus();
}
}
};
const handleBlur = event => {
// Do not close if the menu itself is clicked, should only close on focus out
if (open && event.relatedTarget === null) {
return;
}
if (!event.currentTarget.contains(event.relatedTarget)) {
// The menu should be closed when focus leaves the `Toggletip` (#12922)
actions.close();
}
};
// If the `Toggletip` is the last focusable item in the tab order, it shoudl also close when the browser window loses focus (#12922)
useWindowEvent('blur', () => {
if (open) {
actions.close();
}
});
useWindowEvent('click', event => {
if (open && ref.current && !ref.current.contains(event.target)) {
actions.close();
}
});
return /*#__PURE__*/React__default.createElement(ToggletipContext.Provider, {
value: value
}, /*#__PURE__*/React__default.createElement(Popover, {
align: align,
as: as,
caret: true,
className: className,
dropShadow: false,
highContrast: true,
open: open,
onKeyDown: onKeyDown,
onBlur: handleBlur,
ref: ref
}, children));
}
Toggletip.propTypes = {
/**
* Specify how the toggletip should align with the button
*/
align: PropTypes.oneOf(['top', 'top-left', 'top-right', 'bottom', 'bottom-left', 'bottom-right', 'left', 'left-bottom', 'left-top', 'right', 'right-bottom', 'right-top']),
/**
* Provide a custom element or component to render the top-level node for the
* component.
*/
as: PropTypes.elementType,
/**
* Custom children to be rendered as the content of the label
*/
children: PropTypes.node,
/**
* Provide a custom class name to be added to the outermost node in the
* component
*/
className: PropTypes.string,
/**
* Specify if the toggletip should be open by default
*/
defaultOpen: PropTypes.bool
};
/**
* `ToggletipButton` controls the visibility of the Toggletip through mouse
* clicks and keyboard interactions.
*/
function ToggletipButton(_ref3) {
let {
children,
className: customClassName,
label = 'Show information'
} = _ref3;
const toggletip = useToggletip();
const prefix = usePrefix();
const className = cx(`${prefix}--toggletip-button`, customClassName);
return /*#__PURE__*/React__default.createElement("button", _extends({}, toggletip?.buttonProps, {
"aria-label": label,
type: "button",
className: className
}), children);
}
ToggletipButton.propTypes = {
/**
* Custom children to be rendered as the content of the label
*/
children: PropTypes.node,
/**
* Provide a custom class name to be added to the outermost node in the
* component
*/
className: PropTypes.string,
/**
* Provide an accessible label for this button
*/
label: PropTypes.string
};
/**
* `ToggletipContent` is a wrapper around `PopoverContent`. It places the
* `children` passed in as a prop inside of `PopoverContent` so that they will
* be rendered inside of the popover for this component.
*/
function ToggletipContent(_ref4) {
let {
children,
className: customClassName
} = _ref4;
const toggletip = useToggletip();
const prefix = usePrefix();
return /*#__PURE__*/React__default.createElement(PopoverContent, _extends({
className: customClassName
}, toggletip?.contentProps), /*#__PURE__*/React__default.createElement("div", {
className: `${prefix}--toggletip-content`
}, children));
}
ToggletipContent.propTypes = {
/**
* Custom children to be rendered as the content of the label
*/
children: PropTypes.node,
/**
* Provide a custom class name to be added to the outermost node in the
* component
*/
className: PropTypes.string
};
/**
* `ToggletipActions` is a container for one or two actions present at the base
* of a toggletip. It is used for layout of these items.
*/
function ToggletipActions(_ref5) {
let {
children,
className: customClassName
} = _ref5;
const prefix = usePrefix();
const className = cx(`${prefix}--toggletip-actions`, customClassName);
return /*#__PURE__*/React__default.createElement("div", {
className: className
}, children);
}
ToggletipActions.propTypes = {
/**
* Custom children to be rendered as the content of the label
*/
children: PropTypes.node,
/**
* Provide a custom class name to be added to the outermost node in the
* component
*/
className: PropTypes.string
};
export { Toggletip, ToggletipActions, ToggletipButton, ToggletipContent, ToggletipLabel };