@atlaskit/editor-common
Version:
A package that contains common classes and components for editor and renderer
158 lines (155 loc) • 5.7 kB
JavaScript
import _defineProperty from "@babel/runtime/helpers/defineProperty";
/** @jsx jsx */
import { Component } from 'react';
import { css, jsx } from '@emotion/react';
import { createAndFireEvent, withAnalyticsContext, withAnalyticsEvents } from '@atlaskit/analytics-next';
import { N0, N50A, N60A, N900 } from '@atlaskit/theme/colors';
import Layer from '../Layer';
const packageName = "@atlaskit/editor-common";
const packageVersion = "78.23.1";
const halfFocusRing = 1;
const dropOffset = '0, 8';
class DropList extends Component {
constructor(...args) {
super(...args);
_defineProperty(this, "wrapperStyles", css`
${this.props.shouldFitContainer ? 'display: block; flex: 1 1 auto;' : 'display: inline-flex;'}
transition-duration: 0.2s;
transition: box-shadow 0.15s cubic-bezier(0.47, 0.03, 0.49, 1.38);
`);
_defineProperty(this, "triggerStyles", css`
transition-duration: 0.2s;
transition: box-shadow 0.15s cubic-bezier(0.47, 0.03, 0.49, 1.38);
${this.props.shouldFitContainer ? 'display: block; box-sizing: border-box;' : 'display: inline-flex;'}
`);
/* eslint-disable @atlaskit/design-system/ensure-design-token-usage */
_defineProperty(this, "menuWrapper", () => {
return css`
color: ${`var(--ds-text, ${N900})`};
background-color: ${`var(--ds-surface-overlay, ${N0})`};
border-radius: ${"var(--ds-border-radius, 3px)"};
box-shadow: ${`var(--ds-shadow-overlay, ${`0 4px 8px calc(-1 * 2px) ${N50A}, 0 0 1px ${N60A}`})`};
box-sizing: border-box;
overflow: auto;
padding: ${"var(--ds-space-050, 4px)"} 0;
max-height: 90vh;
`;
});
/* eslint-enable @atlaskit/design-system/ensure-design-token-usage */
_defineProperty(this, "componentDidMount", () => {
this.setContentWidth();
// We use a captured event here to avoid a radio or checkbox dropdown item firing its
// click event first, which would cause a re-render of the element and prevent DropList
// from detecting the actual source of this original click event.
document.addEventListener('click', this.handleClickOutside, true);
document.addEventListener('keydown', this.handleEsc);
});
_defineProperty(this, "componentDidUpdate", () => {
if (this.props.isOpen) {
this.setContentWidth();
}
});
_defineProperty(this, "componentWillUnmount", () => {
document.removeEventListener('click', this.handleClickOutside, true);
document.removeEventListener('keydown', this.handleEsc);
});
_defineProperty(this, "setContentWidth", () => {
const {
dropContentRef,
triggerRef
} = this;
const {
shouldFitContainer
} = this.props;
// We need to manually set the content width to match the trigger width
if (shouldFitContainer && dropContentRef && triggerRef) {
dropContentRef.style.width = `${triggerRef.offsetWidth - halfFocusRing * 2}px`;
}
});
_defineProperty(this, "handleEsc", event => {
if ((event.key === 'Escape' || event.key === 'Esc') && this.props.isOpen) {
this.close(event);
}
});
_defineProperty(this, "handleClickOutside", event => {
if (this.props.isOpen) {
if (event.target instanceof Node) {
// Rather than check for the target within the entire DropList, we specify the trigger/content.
// This aids with future effort in scroll-locking DropList when isMenuFixed is enabled; the scroll
// blanket which stretches to the viewport should not stop 'close' from being triggered.
const withinTrigger = this.triggerRef && this.triggerRef.contains(event.target);
const withinContent = this.dropContentRef && this.dropContentRef.contains(event.target);
if (!withinTrigger && !withinContent) {
this.close(event);
}
}
}
});
_defineProperty(this, "close", event => {
if (this.props.onOpenChange) {
this.props.onOpenChange({
isOpen: false,
event
});
}
});
_defineProperty(this, "handleContentRef", ref => {
this.dropContentRef = ref;
// If the dropdown has just been opened, we focus on the containing element so the
// user can tab to the first dropdown item. We will only receive this ref if isOpen
// is true or null, so no need to check for truthiness here.
if (ref) {
ref.focus();
}
});
_defineProperty(this, "handleTriggerRef", ref => {
this.triggerRef = ref;
});
}
render() {
const {
children,
isOpen,
position,
trigger,
onPositioned,
testId,
id
} = this.props;
let layerContent = isOpen ? jsx("div", {
css: this.menuWrapper,
"data-role": "droplistContent",
"data-testid": testId && `${testId}--content`,
ref: this.handleContentRef,
id: id,
role: "presentation"
}, children) : null;
return jsx("div", {
css: this.wrapperStyles
}, jsx(Layer, {
content: layerContent,
offset: dropOffset,
position: position,
onPositioned: onPositioned
}, jsx("div", {
css: this.triggerStyles,
ref: this.handleTriggerRef
}, trigger)));
}
}
const createAndFireEventOnAtlaskit = createAndFireEvent('atlaskit');
export default withAnalyticsContext({
componentName: 'droplist',
packageName,
packageVersion
})(withAnalyticsEvents({
onOpenChange: createAndFireEventOnAtlaskit({
action: 'toggled',
actionSubject: 'droplist',
attributes: {
componentName: 'droplist',
packageName,
packageVersion
}
})
})(DropList));