@atlaskit/editor-common
Version:
A package that contains common classes and components for editor and renderer
274 lines (264 loc) • 12.7 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _typeof = require("@babel/runtime/helpers/typeof");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _react = _interopRequireWildcard(require("react"));
var _react2 = require("@emotion/react");
var _reactIntl = require("react-intl");
var _state = require("@atlaskit/editor-prosemirror/state");
var _editorSharedStyles = require("@atlaskit/editor-shared-styles");
var _growDiagonal = _interopRequireDefault(require("@atlaskit/icon/core/grow-diagonal"));
var _linkExternal = _interopRequireDefault(require("@atlaskit/icon/core/link-external"));
var _panelRight = _interopRequireDefault(require("@atlaskit/icon/core/panel-right"));
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
var _primitives = require("@atlaskit/primitives");
var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
var _analytics = require("../../analytics");
var _messages = require("../../messages");
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
/* eslint-disable @atlaskit/design-system/no-nested-styles */
/* eslint-disable @atlaskit/design-system/prefer-primitives */
/**
* @jsxRuntime classic
* @jsx jsx
*/
// eslint-disable-next-line @typescript-eslint/consistent-type-imports -- jsx required at runtime for @jsxRuntime classic
// eslint-disable-line @atlaskit/ui-styling-standard/use-compiled
// eslint-disable-next-line @atlaskit/ui-styling-standard/use-compiled -- Ignored via go/DSP-18766
// eslint-disable-next-line @atlaskit/design-system/no-emotion-primitives -- to be migrated to @atlaskit/primitives/compiled – go/akcss
/**
* Dynamic padding value that adjusts based on the editor's base font size.
* When base font size is <= akEditorFullPageDenseFontSize (13px), uses 1px padding.
* Otherwise, uses default value with token('space.025').
*/
var DYNAMIC_PADDING_BLOCK = "clamp(1px, calc((var(--ak-editor-base-font-size, ".concat(_editorSharedStyles.akEditorFullPageDefaultFontSize, "px) - ").concat(_editorSharedStyles.akEditorFullPageDenseFontSize, "px) * 999), ", "var(--ds-space-025, 2px)", ")");
var containerStyles = (0, _react2.css)({
position: 'relative'
});
var iconWrapperStyles = (0, _primitives.xcss)({
display: 'inline-flex',
justifyContent: 'center',
alignItems: 'center',
height: '17px',
width: '17px'
});
var hiddenTextStyle = (0, _react2.css)({
overflow: 'hidden',
whiteSpace: 'nowrap',
position: 'absolute',
visibility: 'hidden'
});
var linkStylesCommon = (0, _primitives.xcss)({
position: 'absolute',
left: 'space.025',
display: 'inline-flex',
alignItems: 'center',
verticalAlign: 'middle',
paddingBlock: 'space.025',
paddingInline: 'space.050',
gap: 'space.025',
overflow: 'hidden',
zIndex: 'card',
backgroundColor: 'color.background.accent.gray.subtlest',
borderRadius: "var(--ds-radius-small, 3px)",
color: 'color.text.subtle',
textDecoration: 'none',
whiteSpace: 'nowrap',
top: '0px',
height: '1.2em',
':hover': {
backgroundColor: 'elevation.surface.hovered',
color: 'color.text.subtle',
textDecoration: 'none'
}
});
var MIN_AVAILABLE_SPACE_WITH_LABEL_OVERLAY = 45;
var ICON_WIDTH = 16;
var DEFAULT_OPEN_TEXT_WIDTH = 28; // Default open text width in English
var visitCardLinkAnalytics = function visitCardLinkAnalytics(editorAnalyticsApi, inputMethod) {
return function (state, dispatch) {
if (!(state.selection instanceof _state.NodeSelection)) {
return false;
}
var type = state.selection.node.type;
if (dispatch) {
var tr = state.tr;
editorAnalyticsApi === null || editorAnalyticsApi === void 0 || editorAnalyticsApi.attachAnalyticsEvent((0, _analytics.buildVisitedNonHyperLinkPayload)(type.name, inputMethod))(tr);
dispatch(tr);
}
return true;
};
};
var HoverLinkOverlay = function HoverLinkOverlay(_ref) {
var children = _ref.children,
_ref$isVisible = _ref.isVisible,
isVisible = _ref$isVisible === void 0 ? false : _ref$isVisible,
url = _ref.url,
_ref$compactPadding = _ref.compactPadding,
compactPadding = _ref$compactPadding === void 0 ? false : _ref$compactPadding,
editorAnalyticsApi = _ref.editorAnalyticsApi,
view = _ref.view,
onClick = _ref.onClick,
_ref$showPanelButton = _ref.showPanelButton,
showPanelButton = _ref$showPanelButton === void 0 ? false : _ref$showPanelButton,
showPanelButtonIcon = _ref.showPanelButtonIcon;
var _useIntl = (0, _reactIntl.useIntl)(),
formatMessage = _useIntl.formatMessage;
var containerRef = (0, _react.useRef)(null);
var hoverLinkButtonRef = (0, _react.useRef)(null);
var hiddenTextRef = (0, _react.useRef)(null);
var _useState = (0, _react.useState)(true),
_useState2 = (0, _slicedToArray2.default)(_useState, 2),
showLabel = _useState2[0],
setShowLabel = _useState2[1];
var _useState3 = (0, _react.useState)(false),
_useState4 = (0, _slicedToArray2.default)(_useState3, 2),
isHovered = _useState4[0],
setHovered = _useState4[1];
var openTextWidthRef = (0, _react.useRef)(null);
var regularPadding = (0, _expValEquals.expValEquals)('confluence_compact_text_format', 'isEnabled', true) || (0, _expValEquals.expValEquals)('cc_editor_ai_content_mode', 'variant', 'test') && (0, _platformFeatureFlags.fg)('platform_editor_content_mode_button_mvp') ? DYNAMIC_PADDING_BLOCK : "var(--ds-space-025, 2px)";
var memoizedHoverLinkStyles = (0, _react.useMemo)(function () {
return {
paddingBlock: compactPadding ? '1px' : regularPadding
};
}, [compactPadding, regularPadding]);
var hoverLinkStyles = (0, _expValEquals.expValEquals)('platform_editor_perf_lint_cleanup', 'isEnabled', true) ? memoizedHoverLinkStyles : {
paddingBlock: compactPadding ? '1px' : (0, _expValEquals.expValEquals)('confluence_compact_text_format', 'isEnabled', true) || (0, _expValEquals.expValEquals)('cc_editor_ai_content_mode', 'variant', 'test') && (0, _platformFeatureFlags.fg)('platform_editor_content_mode_button_mvp') ? DYNAMIC_PADDING_BLOCK : "var(--ds-space-025, 2px)"
};
(0, _react.useLayoutEffect)(function () {
var _containerRef$current, _hoverLinkButtonRef$c;
if (!isVisible || !isHovered) {
return;
}
var cardWidth = (_containerRef$current = containerRef.current) === null || _containerRef$current === void 0 ? void 0 : _containerRef$current.offsetWidth;
var openButtonWidth = (_hoverLinkButtonRef$c = hoverLinkButtonRef.current) === null || _hoverLinkButtonRef$c === void 0 ? void 0 : _hoverLinkButtonRef$c.offsetWidth;
// Get the hidden text width
if (!openTextWidthRef.current) {
var hiddenText = hiddenTextRef.current;
if (hiddenText) {
// Measure the width of the hidden text
// Temporarily make the element visible to measure its width
hiddenText.style.visibility = 'hidden';
hiddenText.style.display = 'inline';
openTextWidthRef.current = hiddenText.offsetWidth;
// Reset the hiddenText's display property
hiddenText.style.display = 'none';
hiddenText.style.visibility = 'inherit';
} else {
openTextWidthRef.current = DEFAULT_OPEN_TEXT_WIDTH;
}
}
if (!cardWidth || !openButtonWidth) {
return;
}
var openTextWidth = openTextWidthRef.current || DEFAULT_OPEN_TEXT_WIDTH;
var canShowLabel = cardWidth - openTextWidth > MIN_AVAILABLE_SPACE_WITH_LABEL_OVERLAY + ICON_WIDTH;
// When a smart link wraps to multiple lines in a constrained container (e.g. table cell),
// the hover button can overflow beyond the container bounds. We detect this by comparing
// the button's right edge to the container's right edge, and hide the label if it overflows.
if ((0, _experiments.editorExperiment)('cc_editor_hover_link_overlay_css_fix', true)) {
if (containerRef.current && hoverLinkButtonRef.current) {
var containerRight = containerRef.current.getBoundingClientRect().right;
var buttonRight = hoverLinkButtonRef.current.getBoundingClientRect().right;
if (buttonRight > containerRight) {
canShowLabel = false;
}
}
}
setShowLabel(canShowLabel);
}, [isVisible, isHovered]);
var handleOverlayChange = function handleOverlayChange(isHovered) {
setHovered(isHovered);
// Reset label visibility on hover start so we can measure if it overflows.
// Without this, the label stays hidden from a previous hover and won't be re-measured.
if ((0, _experiments.editorExperiment)('cc_editor_hover_link_overlay_css_fix', true)) {
if (isHovered) {
setShowLabel(true);
}
}
};
var sendVisitLinkAnalytics = function sendVisitLinkAnalytics(inputMethod) {
if (editorAnalyticsApi && view) {
visitCardLinkAnalytics(editorAnalyticsApi, inputMethod)(view.state, view.dispatch);
}
};
var handleDoubleClick = function handleDoubleClick() {
if (!showPanelButton) {
sendVisitLinkAnalytics(_analytics.INPUT_METHOD.DOUBLE_CLICK);
// Double click opens the link in a new tab
window.open(url, '_blank');
}
};
var handleClick = function handleClick(event) {
if (showPanelButton && onClick) {
onClick(event);
} else {
sendVisitLinkAnalytics(_analytics.INPUT_METHOD.BUTTON);
}
};
var isPreviewButton = showPanelButton && (0, _experiments.editorExperiment)('platform_editor_preview_panel_linking_exp', true);
var label = isPreviewButton ? formatMessage(_messages.cardMessages.previewButtonTitle) : formatMessage(_messages.cardMessages.openButtonTitle);
var icon = null;
if (isPreviewButton && showPanelButtonIcon === 'panel') {
icon = (0, _react2.jsx)(_panelRight.default, {
label: ""
});
} else if (isPreviewButton && showPanelButtonIcon === 'modal') {
icon = (0, _react2.jsx)(_growDiagonal.default, {
label: ""
});
} else {
icon = (0, _react2.jsx)(_linkExternal.default, {
label: ""
});
}
return (0, _react2.jsx)("span", {
ref: containerRef,
css: containerStyles,
onDoubleClick: handleDoubleClick,
onMouseEnter: function onMouseEnter() {
return handleOverlayChange(true);
},
onMouseLeave: function onMouseLeave() {
return handleOverlayChange(false);
}
// No-op focus/blur handlers to satisfy a11y/mouse-events-have-key-events rule.
// The hover overlay is a mouse convenience — keyboard users can access link actions
// (open, preview) via the floating toolbar that appears on selection.
,
onFocus: (0, _expValEquals.expValEquals)('editor_a11y__enghealth-46814_fy26', 'isEnabled', true) ? function () {} : undefined,
onBlur: (0, _expValEquals.expValEquals)('editor_a11y__enghealth-46814_fy26', 'isEnabled', true) ? function () {} : undefined
}, children, (0, _react2.jsx)("span", {
css: hiddenTextStyle,
"aria-hidden": "true"
}, (0, _react2.jsx)(_primitives.Text, {
ref: hiddenTextRef,
size: "small",
maxLines: 1
}, label)), isHovered && (0, _react2.jsx)(_primitives.Anchor, {
ref: hoverLinkButtonRef,
xcss: linkStylesCommon,
href: url,
target: "_blank"
// eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- dynamic paddingBlock value based on experiment flags
,
style: hoverLinkStyles,
onClick: handleClick,
testId: "inline-card-hoverlink-overlay"
}, (0, _react2.jsx)(_primitives.Box, {
xcss: iconWrapperStyles,
"data-inlinecard-button-overlay": "icon-wrapper-line-height",
testId: "inline-card-hoverlink-overlay-icon"
}, icon), showLabel && (0, _react2.jsx)(_primitives.Text, {
size: "small",
color: "color.text.subtle",
maxLines: 1,
testId: "inline-card-hoverlink-overlay-label"
}, label)));
};
var _default = exports.default = HoverLinkOverlay;