UNPKG

@atlaskit/editor-common

Version:

A package that contains common classes and components for editor and renderer

274 lines (264 loc) 12.7 kB
"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;