@atlaskit/editor-plugin-card
Version:
Card plugin for @atlaskit/editor-core
267 lines (264 loc) • 10.7 kB
JavaScript
import _extends from "@babel/runtime/helpers/extends";
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
import _defineProperty from "@babel/runtime/helpers/defineProperty";
var _excluded = ["children", "isSelected", "isVisible", "testId", "url"];
/* eslint-disable @atlaskit/design-system/no-nested-styles */
/* eslint-disable @atlaskit/design-system/prefer-primitives */
/**
* @jsxRuntime classic
* @jsx jsx
*/
import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
// eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled, @typescript-eslint/consistent-type-imports
import { css, jsx } from '@emotion/react';
import debounce from 'lodash/debounce';
import { useIntl } from 'react-intl-next';
import { cardMessages as messages } from '@atlaskit/editor-common/messages';
import { ZERO_WIDTH_JOINER } from '@atlaskit/editor-common/whitespace';
import CustomizeIcon from '@atlaskit/icon/core/customize';
import { getChildElement, getInlineCardAvailableWidth, getOverlayWidths, isOneLine } from './utils';
var DEBOUNCE_IN_MS = 5;
var ESTIMATED_MIN_WIDTH_IN_PX = 16;
var PADDING_IN_PX = 4;
var ICON_WIDTH_IN_PX = 14;
var ICON_AND_LABEL_CLASSNAME = 'ak-editor-card-overlay-icon-and-label';
var OVERLAY_LABEL_CLASSNAME = 'ak-editor-card-overlay-label';
var OVERLAY_GRADIENT_CLASSNAME = 'ak-editor-card-overlay-gradient';
var OVERLAY_MARKER_CLASSNAME = 'ak-editor-card-overlay-marker';
var TEXT_NODE_SELECTOR = ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'].join(',');
var SMART_LINK_BACKGROUND_COLOR = "var(--ds-surface-raised, #FFFFFF)";
var SMART_LINK_ACTIVE_COLOR = "var(--ds-background-selected, #E9F2FE)";
var getGradientWithColor = function getGradientWithColor(color) {
return "linear-gradient(270deg, ".concat(color, " 0%, rgba(255, 255, 255, 0.00) 100%)");
};
var containerStyles = css({
position: 'relative',
// eslint-disable-next-line @atlaskit/design-system/use-tokens-typography
lineHeight: 'normal',
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-unsafe-selectors -- Ignored via go/DSP-18766
':active': _defineProperty(_defineProperty({}, ".".concat(ICON_AND_LABEL_CLASSNAME), {
background: SMART_LINK_ACTIVE_COLOR
}), ".".concat(OVERLAY_GRADIENT_CLASSNAME), {
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-unsafe-values -- Ignored via go/DSP-18766
background: getGradientWithColor(SMART_LINK_ACTIVE_COLOR)
})
});
var overlayStyles = css({
// Set default styling to be invisible but available in dom for width calculation.
visibility: 'hidden',
position: 'absolute',
display: 'inline-flex',
justifyContent: 'flex-end',
alignItems: 'center',
verticalAlign: 'text-top',
height: '1lh',
'@supports not (height: 1lh)': {
height: '1.2em'
},
overflow: 'hidden',
// EDM-1717: box-shadow Safari fix bring load wrapper zIndex to 1
zIndex: 2,
pointerEvents: 'none'
});
var showOverlayStyles = css({
position: 'relative',
visibility: 'visible'
});
var iconStyles = css({
// Position icon in the middle
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-nested-selectors -- Ignored via go/DSP-18766
span: {
display: 'flex'
}
});
var labelStyles = css({
font: "var(--ds-font-body, normal 400 14px/20px \"Atlassian Sans\", ui-sans-serif, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Ubuntu, \"Helvetica Neue\", sans-serif)",
fontWeight: "var(--ds-font-weight-semibold, 600)",
width: 'max-content'
});
var iconAndLabelStyles = css({
display: 'flex',
alignItems: 'center',
height: '100%',
gap: "var(--ds-space-050, 4px)",
paddingRight: "var(--ds-space-050, 4px)",
// Margin to avoid the background covering the link border
marginRight: "var(--ds-space-025, 2px)",
background: SMART_LINK_BACKGROUND_COLOR,
color: "var(--ds-text-subtlest, #6B6E76)"
});
var overflowingContainerStyles = css({
display: 'flex',
flexDirection: 'row-reverse',
alignItems: 'center',
width: 'max-content',
height: '100%'
});
var gradientStyles = css({
width: '2.5rem',
height: '100%',
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-unsafe-values -- Ignored via go/DSP-18766
background: getGradientWithColor(SMART_LINK_BACKGROUND_COLOR)
});
var InlineCardOverlay = function InlineCardOverlay(_ref) {
var children = _ref.children,
_ref$isSelected = _ref.isSelected,
isSelected = _ref$isSelected === void 0 ? false : _ref$isSelected,
_ref$isVisible = _ref.isVisible,
isVisible = _ref$isVisible === void 0 ? false : _ref$isVisible,
_ref$testId = _ref.testId,
testId = _ref$testId === void 0 ? 'inline-card-overlay' : _ref$testId,
url = _ref.url,
props = _objectWithoutProperties(_ref, _excluded);
var _useState = useState(false),
_useState2 = _slicedToArray(_useState, 2),
showOverlay = _useState2[0],
setShowOverlay = _useState2[1];
var _useState3 = useState(true),
_useState4 = _slicedToArray(_useState3, 2),
showLabel = _useState4[0],
setShowLabel = _useState4[1];
var _useState5 = useState(undefined),
_useState6 = _slicedToArray(_useState5, 2),
availableWidth = _useState6[0],
setAvailableWidth = _useState6[1];
var maxOverlayWidth = useRef(0);
var minOverlayWidth = useRef(ESTIMATED_MIN_WIDTH_IN_PX);
var parentWidth = useRef(0);
var containerRef = useRef(null);
var setVisibility = useCallback(function () {
if (!containerRef.current || !maxOverlayWidth.current) {
return;
}
var marker = getChildElement(containerRef, ".".concat(OVERLAY_MARKER_CLASSNAME));
if (!marker) {
return;
}
try {
var oneLine = isOneLine(containerRef.current, marker);
// Get the width of the available space to display overlay.
// This is the width of the inline link itself. If the inline
// is wrapped to the next line, this is width of the last line.
var _availableWidth = getInlineCardAvailableWidth(containerRef.current, marker) - PADDING_IN_PX - (
// Always leave at least the icon visible
oneLine ? ICON_WIDTH_IN_PX + PADDING_IN_PX : 0);
setAvailableWidth(_availableWidth);
var canShowLabel = _availableWidth > maxOverlayWidth.current;
setShowLabel(canShowLabel);
var canShowOverlay = _availableWidth > minOverlayWidth.current && !isSelected;
setShowOverlay(canShowOverlay);
} catch (_unused) {
// If something goes wrong, hide the overlay all together.
setShowOverlay(false);
}
}, [isSelected]);
useLayoutEffect(function () {
// Using useLayoutEffect here.
// 1) We want all to be able to determine whether to display label before
// the overlay becomes visible.
// 2) We need to wait for the refs to be assigned to be able to do determine
// the width of the overlay.
if (!containerRef.current) {
return;
}
// This should run only once
if (!maxOverlayWidth.current) {
var iconAndLabel = getChildElement(containerRef, ".".concat(ICON_AND_LABEL_CLASSNAME));
var _label = getChildElement(containerRef, ".".concat(OVERLAY_LABEL_CLASSNAME));
if (iconAndLabel && _label) {
// Set overlay max (label + icon) and min (icon only) width.
var _getOverlayWidths = getOverlayWidths(iconAndLabel, _label),
max = _getOverlayWidths.max,
min = _getOverlayWidths.min;
maxOverlayWidth.current = max;
minOverlayWidth.current = min;
}
}
if (isVisible) {
setVisibility();
}
}, [setVisibility, isVisible]);
useEffect(function () {
var _containerRef$current;
// Find the closest block parent to observe size change
var parent = containerRef === null || containerRef === void 0 || (_containerRef$current = containerRef.current) === null || _containerRef$current === void 0 ? void 0 : _containerRef$current.closest(TEXT_NODE_SELECTOR);
if (!parent) {
return;
}
var updateOverlay = debounce(function (entries) {
var _entries$;
if (!isVisible) {
return;
}
var size = entries === null || entries === void 0 || (_entries$ = entries[0]) === null || _entries$ === void 0 || (_entries$ = _entries$.contentBoxSize) === null || _entries$ === void 0 || (_entries$ = _entries$[0]) === null || _entries$ === void 0 ? void 0 : _entries$.inlineSize;
if (!size) {
return;
}
if (!parentWidth.current) {
parentWidth.current = size;
}
if (parentWidth.current === size) {
return;
}
parentWidth.current = size;
setVisibility();
}, DEBOUNCE_IN_MS);
var observer = new ResizeObserver(updateOverlay);
observer.observe(parent);
return function () {
observer.disconnect();
};
}, [isVisible, setVisibility]);
var intl = useIntl();
var label = intl.formatMessage(messages.inlineOverlay);
return (
// Ignored via go/ees005
// eslint-disable-next-line react/jsx-props-no-spreading
jsx("span", _extends({}, props, {
css: containerStyles,
ref: containerRef
}), children, isVisible && jsx(React.Fragment, null, jsx("span", {
"aria-hidden": "true",
className: OVERLAY_MARKER_CLASSNAME
}, ZERO_WIDTH_JOINER), jsx("a", {
css: [overlayStyles, showOverlay && showOverlayStyles],
style: {
// eslint-disable-next-line @atlaskit/design-system/ensure-design-token-usage/preview
marginLeft: availableWidth && -availableWidth,
width: availableWidth
},
"data-testid": testId,
href: url,
onClick: function onClick(e) {
return e.preventDefault();
},
tabIndex: -1
}, jsx("span", {
css: overflowingContainerStyles
}, jsx("span", {
css: iconAndLabelStyles
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
,
className: ICON_AND_LABEL_CLASSNAME
}, jsx("span", {
css: iconStyles
}, jsx(CustomizeIcon, {
label: label,
testId: "".concat(testId, "-icon")
})), showLabel && jsx("span", {
css: labelStyles
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
,
className: OVERLAY_LABEL_CLASSNAME,
"data-testid": "".concat(testId, "-label")
}, label)), jsx("span", {
css: gradientStyles
// eslint-disable-next-line @atlaskit/ui-styling-standard/no-classname-prop -- Ignored via go/DSP-18766
,
className: OVERLAY_GRADIENT_CLASSNAME,
"data-testid": "".concat(testId, "-gradient")
})))))
);
};
export default InlineCardOverlay;