@atlaskit/editor-plugin-card 1.13.0 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @atlaskit/editor-plugin-card
2
2
 
3
+ ## 1.14.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#100627](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/100627)
8
+ [`619f85adfe8b`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/619f85adfe8b) -
9
+ EDM-9852 Add initial inline link overlay for live pages support
10
+
11
+ ### Patch Changes
12
+
13
+ - [#101524](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/101524)
14
+ [`4821570088e6`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/4821570088e6) -
15
+ ED-23362 Bump ADF schema to version 36.8.1 and add support for adf validation and transformation
16
+
3
17
  ## 1.13.0
4
18
 
5
19
  ### Minor Changes
@@ -9,10 +9,12 @@ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/sli
9
9
  var _react = require("react");
10
10
  var _react2 = require("@emotion/react");
11
11
  var _analyticsNext = require("@atlaskit/analytics-next");
12
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
12
13
  var _useLinkUpgradeDiscoverability = _interopRequireDefault(require("../../common/hooks/useLinkUpgradeDiscoverability"));
13
14
  var _localStorage = require("../../common/local-storage");
14
15
  var _utils = require("../../utils");
15
16
  var _InlineCardOverlay = _interopRequireDefault(require("../InlineCardOverlay"));
17
+ var _NewInlineCardOverlay = _interopRequireDefault(require("../NewInlineCardOverlay"));
16
18
  var _Pulse = require("../Pulse");
17
19
  /** @jsx jsx */
18
20
 
@@ -77,17 +79,33 @@ var AwarenessWrapper = exports.AwarenessWrapper = function AwarenessWrapper(_ref
77
79
  setOverlayHoveredStyles(isHovered);
78
80
  }, [setOverlayHoveredStyles]);
79
81
  var cardWithOverlay = (0, _react.useMemo)(function () {
80
- return shouldShowLinkOverlay ? (0, _react2.jsx)(_InlineCardOverlay.default, {
81
- isSelected: isSelected,
82
- isVisible: isResolvedViewRendered && (isInserted || isHovered || isSelected),
83
- onMouseEnter: function onMouseEnter() {
84
- return handleOverlayChange(true);
85
- },
86
- onMouseLeave: function onMouseLeave() {
87
- return handleOverlayChange(false);
88
- },
89
- url: url
90
- }, children) : children;
82
+ if (shouldShowLinkOverlay) {
83
+ if ((0, _platformFeatureFlags.getBooleanFF)('platform.linking-platform.smart-links-in-live-pages')) {
84
+ return (0, _react2.jsx)(_NewInlineCardOverlay.default, {
85
+ isSelected: isSelected,
86
+ isVisible: isResolvedViewRendered && (isInserted || isHovered || isSelected),
87
+ onMouseEnter: function onMouseEnter() {
88
+ return handleOverlayChange(true);
89
+ },
90
+ onMouseLeave: function onMouseLeave() {
91
+ return handleOverlayChange(false);
92
+ },
93
+ url: url
94
+ }, children);
95
+ }
96
+ return (0, _react2.jsx)(_InlineCardOverlay.default, {
97
+ isSelected: isSelected,
98
+ isVisible: isResolvedViewRendered && (isInserted || isHovered || isSelected),
99
+ onMouseEnter: function onMouseEnter() {
100
+ return handleOverlayChange(true);
101
+ },
102
+ onMouseLeave: function onMouseLeave() {
103
+ return handleOverlayChange(false);
104
+ },
105
+ url: url
106
+ }, children);
107
+ }
108
+ return children;
91
109
  }, [shouldShowLinkOverlay, isSelected, isResolvedViewRendered, isInserted, isHovered, url, children, handleOverlayChange]);
92
110
  return (0, _react.useMemo)(function () {
93
111
  var _cardContext$value;
@@ -0,0 +1,234 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+ var _typeof = require("@babel/runtime/helpers/typeof");
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports.default = void 0;
9
+ var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
10
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
11
+ var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
12
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
13
+ var _react = _interopRequireWildcard(require("react"));
14
+ var _react2 = require("@emotion/react");
15
+ var _debounce = _interopRequireDefault(require("lodash/debounce"));
16
+ var _reactIntlNext = require("react-intl-next");
17
+ var _messages = require("@atlaskit/editor-common/messages");
18
+ var _utils = require("@atlaskit/editor-common/utils");
19
+ var _preferences = _interopRequireDefault(require("@atlaskit/icon/glyph/preferences"));
20
+ var _colors = require("@atlaskit/theme/colors");
21
+ var _utils2 = require("../InlineCardOverlay/utils");
22
+ var _excluded = ["children", "isSelected", "isVisible", "testId", "url"];
23
+ /* eslint-disable @atlaskit/design-system/no-nested-styles */
24
+ /* eslint-disable @atlaskit/design-system/prefer-primitives */
25
+ /** @jsx jsx */
26
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
27
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
28
+ var DEBOUNCE_IN_MS = 5;
29
+ var ESTIMATED_MIN_WIDTH_IN_PX = 16;
30
+ var PADDING_IN_PX = 4;
31
+ var ICON_WIDTH_IN_PX = 14;
32
+ var ICON_AND_LABEL_CLASSNAME = 'ak-editor-card-overlay-icon-and-label';
33
+ var OVERLAY_LABEL_CLASSNAME = 'ak-editor-card-overlay-label';
34
+ var OVERLAY_MARKER_CLASSNAME = 'ak-editor-card-overlay-marker';
35
+ var TEXT_NODE_SELECTOR = ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'].join(',');
36
+ var SMART_LINK_BACKGROUND_COLOR = "var(--ds-surface-raised, ".concat(_colors.N0, ")");
37
+ // TODO: This should be lighter to match the rest of the button
38
+ var SMART_LINK_ACTIVE_COLOR = "var(--ds-background-selected, ".concat(_colors.B100, ")");
39
+ var containerStyles = (0, _react2.css)({
40
+ position: 'relative',
41
+ lineHeight: 'normal',
42
+ ':active': (0, _defineProperty2.default)({}, ".".concat(ICON_AND_LABEL_CLASSNAME), {
43
+ background: SMART_LINK_ACTIVE_COLOR
44
+ })
45
+ });
46
+ var overlayStyles = (0, _react2.css)({
47
+ // Set default styling to be invisible but available in dom for width calculation.
48
+ visibility: 'hidden',
49
+ marginTop: "var(--ds-space-050, 4px)",
50
+ position: 'absolute',
51
+ verticalAlign: 'text-top',
52
+ height: '1lh',
53
+ '@supports not (height: 1lh)': {
54
+ height: '1.2em'
55
+ },
56
+ overflow: 'hidden',
57
+ // EDM-1717: box-shadow Safari fix bring load wrapper zIndex to 1
58
+ zIndex: 2,
59
+ pointerEvents: 'none'
60
+ });
61
+ var showOverlayStyles = (0, _react2.css)({
62
+ visibility: 'visible'
63
+ });
64
+ var iconStyles = (0, _react2.css)({
65
+ // Position icon in the middle
66
+ span: {
67
+ display: 'flex'
68
+ }
69
+ });
70
+ var labelStyles = (0, _react2.css)({
71
+ fontSize: '0.875em',
72
+ fontWeight: '600',
73
+ width: 'max-content'
74
+ });
75
+ var iconAndLabelStyles = (0, _react2.css)({
76
+ display: 'flex',
77
+ alignItems: 'center',
78
+ height: '100%',
79
+ marginLeft: "var(--ds-space-050, 4px)",
80
+ marginRight: "var(--ds-space-025, 2px)",
81
+ background: SMART_LINK_BACKGROUND_COLOR,
82
+ color: "var(--ds-text-subtlest, ".concat(_colors.N700, ")")
83
+ });
84
+ var overflowingContainerStyles = (0, _react2.css)({
85
+ display: 'flex',
86
+ flexDirection: 'row-reverse',
87
+ alignItems: 'center',
88
+ width: 'max-content',
89
+ height: '100%'
90
+ });
91
+ var NarrowInlineCardOverlay = function NarrowInlineCardOverlay(_ref) {
92
+ var children = _ref.children,
93
+ _ref$isSelected = _ref.isSelected,
94
+ isSelected = _ref$isSelected === void 0 ? false : _ref$isSelected,
95
+ _ref$isVisible = _ref.isVisible,
96
+ isVisible = _ref$isVisible === void 0 ? false : _ref$isVisible,
97
+ _ref$testId = _ref.testId,
98
+ testId = _ref$testId === void 0 ? 'inline-card-overlay' : _ref$testId,
99
+ url = _ref.url,
100
+ props = (0, _objectWithoutProperties2.default)(_ref, _excluded);
101
+ var _useState = (0, _react.useState)(false),
102
+ _useState2 = (0, _slicedToArray2.default)(_useState, 2),
103
+ showOverlay = _useState2[0],
104
+ setShowOverlay = _useState2[1];
105
+ var _useState3 = (0, _react.useState)(undefined),
106
+ _useState4 = (0, _slicedToArray2.default)(_useState3, 2),
107
+ availableWidth = _useState4[0],
108
+ setAvailableWidth = _useState4[1];
109
+ var maxOverlayWidth = (0, _react.useRef)(0);
110
+ var minOverlayWidth = (0, _react.useRef)(ESTIMATED_MIN_WIDTH_IN_PX);
111
+ var parentWidth = (0, _react.useRef)(0);
112
+ var iconSize = (0, _react.useRef)('small');
113
+ var containerRef = (0, _react.useRef)(null);
114
+
115
+ // TODO EDM-9843: Use availableWidth for small link edge case
116
+ availableWidth;
117
+ var setVisibility = (0, _react.useCallback)(function () {
118
+ if (!containerRef.current || !maxOverlayWidth.current) {
119
+ return;
120
+ }
121
+ var marker = (0, _utils2.getChildElement)(containerRef, ".".concat(OVERLAY_MARKER_CLASSNAME));
122
+ if (!marker) {
123
+ return;
124
+ }
125
+ try {
126
+ var oneLine = (0, _utils2.isOneLine)(containerRef.current, marker);
127
+
128
+ // Get the width of the available space to display overlay.
129
+ // This is the width of the inline link itself. If the inline
130
+ // is wrapped to the next line, this is width of the last line.
131
+ var _availableWidth = (0, _utils2.getInlineCardAvailableWidth)(containerRef.current, marker) - PADDING_IN_PX - (
132
+ // Always leave at least the icon visible
133
+ oneLine ? ICON_WIDTH_IN_PX + PADDING_IN_PX : 0);
134
+ setAvailableWidth(_availableWidth);
135
+ var canShowOverlay = !isSelected;
136
+ setShowOverlay(canShowOverlay);
137
+ } catch (_unused) {
138
+ // If something goes wrong, hide the overlay all together.
139
+ setShowOverlay(false);
140
+ }
141
+ }, [isSelected]);
142
+ (0, _react.useLayoutEffect)(function () {
143
+ // Using useLayoutEffect here.
144
+ // 1) We want all to be able to determine whether to display label before
145
+ // the overlay becomes visible.
146
+ // 2) We need to wait for the refs to be assigned to be able to do determine
147
+ // the width of the overlay.
148
+ if (!containerRef.current) {
149
+ return;
150
+ }
151
+
152
+ // This should run only once
153
+ if (!maxOverlayWidth.current) {
154
+ var iconAndLabel = (0, _utils2.getChildElement)(containerRef, ".".concat(ICON_AND_LABEL_CLASSNAME));
155
+ var _label = (0, _utils2.getChildElement)(containerRef, ".".concat(OVERLAY_LABEL_CLASSNAME));
156
+ if (iconAndLabel && _label) {
157
+ // Set overlay max (label + icon) and min (icon only) width.
158
+ var _getOverlayWidths = (0, _utils2.getOverlayWidths)(iconAndLabel, _label),
159
+ max = _getOverlayWidths.max,
160
+ min = _getOverlayWidths.min;
161
+ maxOverlayWidth.current = max;
162
+ minOverlayWidth.current = min;
163
+ iconSize.current = (0, _utils2.getIconSize)(_label);
164
+ }
165
+ }
166
+ if (isVisible) {
167
+ setVisibility();
168
+ }
169
+ }, [setVisibility, isVisible]);
170
+ (0, _react.useEffect)(function () {
171
+ var _containerRef$current;
172
+ // Find the closest block parent to observe size change
173
+ var parent = containerRef === null || containerRef === void 0 || (_containerRef$current = containerRef.current) === null || _containerRef$current === void 0 ? void 0 : _containerRef$current.closest(TEXT_NODE_SELECTOR);
174
+ if (!parent) {
175
+ return;
176
+ }
177
+ var updateOverlay = (0, _debounce.default)(function (entries) {
178
+ var _entries$;
179
+ if (!isVisible) {
180
+ return;
181
+ }
182
+ 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;
183
+ if (!size) {
184
+ return;
185
+ }
186
+ if (!parentWidth.current) {
187
+ parentWidth.current = size;
188
+ }
189
+ if (parentWidth.current === size) {
190
+ return;
191
+ }
192
+ parentWidth.current = size;
193
+ setVisibility();
194
+ }, DEBOUNCE_IN_MS);
195
+ var observer = new ResizeObserver(updateOverlay);
196
+ observer.observe(parent);
197
+ return function () {
198
+ observer.disconnect();
199
+ };
200
+ }, [isVisible, setVisibility]);
201
+ var intl = (0, _reactIntlNext.useIntl)();
202
+ var label = intl.formatMessage(_messages.cardMessages.inlineOverlay);
203
+ return (0, _react2.jsx)("span", (0, _extends2.default)({}, props, {
204
+ css: containerStyles,
205
+ ref: containerRef
206
+ }), isVisible && (0, _react2.jsx)(_react.default.Fragment, null, (0, _react2.jsx)("span", {
207
+ "aria-hidden": "true",
208
+ className: OVERLAY_MARKER_CLASSNAME
209
+ }, _utils.ZERO_WIDTH_JOINER), (0, _react2.jsx)("a", {
210
+ css: [overlayStyles, showOverlay && showOverlayStyles],
211
+ "data-testid": testId,
212
+ href: url,
213
+ onClick: function onClick(e) {
214
+ return e.preventDefault();
215
+ },
216
+ tabIndex: -1
217
+ }, (0, _react2.jsx)("span", {
218
+ css: overflowingContainerStyles
219
+ }, (0, _react2.jsx)("span", {
220
+ css: iconAndLabelStyles,
221
+ className: ICON_AND_LABEL_CLASSNAME
222
+ }, (0, _react2.jsx)("span", {
223
+ css: iconStyles
224
+ }, (0, _react2.jsx)(_preferences.default, {
225
+ label: label,
226
+ size: iconSize.current,
227
+ testId: "".concat(testId, "-icon")
228
+ })), (0, _react2.jsx)("span", {
229
+ css: labelStyles,
230
+ className: OVERLAY_LABEL_CLASSNAME,
231
+ "data-testid": "".concat(testId, "-label")
232
+ }))))), children);
233
+ };
234
+ var _default = exports.default = NarrowInlineCardOverlay;
@@ -2,10 +2,12 @@
2
2
  import { useCallback, useEffect, useMemo, useState } from 'react';
3
3
  import { css, jsx } from '@emotion/react';
4
4
  import { AnalyticsContext } from '@atlaskit/analytics-next';
5
+ import { getBooleanFF } from '@atlaskit/platform-feature-flags';
5
6
  import useLinkUpgradeDiscoverability from '../../common/hooks/useLinkUpgradeDiscoverability';
6
7
  import { isLocalStorageKeyDiscovered, LOCAL_STORAGE_DISCOVERY_KEY_SMART_LINK, LOCAL_STORAGE_DISCOVERY_KEY_TOOLBAR, markLocalStorageKeyDiscovered, ONE_DAY_IN_MILLISECONDS } from '../../common/local-storage';
7
8
  import { getResolvedAttributesFromStore } from '../../utils';
8
9
  import InlineCardOverlay from '../InlineCardOverlay';
10
+ import NewInlineCardOverlay from '../NewInlineCardOverlay';
9
11
  import { DiscoveryPulse } from '../Pulse';
10
12
  // editor adds a standard line-height that is bigger than an inline smart link
11
13
  // due to that the link has a bit of white space around it, which doesn't look right when there is pulse around it
@@ -66,13 +68,27 @@ export const AwarenessWrapper = ({
66
68
  setIsHovered(isHovered);
67
69
  setOverlayHoveredStyles(isHovered);
68
70
  }, [setOverlayHoveredStyles]);
69
- const cardWithOverlay = useMemo(() => shouldShowLinkOverlay ? jsx(InlineCardOverlay, {
70
- isSelected: isSelected,
71
- isVisible: isResolvedViewRendered && (isInserted || isHovered || isSelected),
72
- onMouseEnter: () => handleOverlayChange(true),
73
- onMouseLeave: () => handleOverlayChange(false),
74
- url: url
75
- }, children) : children, [shouldShowLinkOverlay, isSelected, isResolvedViewRendered, isInserted, isHovered, url, children, handleOverlayChange]);
71
+ const cardWithOverlay = useMemo(() => {
72
+ if (shouldShowLinkOverlay) {
73
+ if (getBooleanFF('platform.linking-platform.smart-links-in-live-pages')) {
74
+ return jsx(NewInlineCardOverlay, {
75
+ isSelected: isSelected,
76
+ isVisible: isResolvedViewRendered && (isInserted || isHovered || isSelected),
77
+ onMouseEnter: () => handleOverlayChange(true),
78
+ onMouseLeave: () => handleOverlayChange(false),
79
+ url: url
80
+ }, children);
81
+ }
82
+ return jsx(InlineCardOverlay, {
83
+ isSelected: isSelected,
84
+ isVisible: isResolvedViewRendered && (isInserted || isHovered || isSelected),
85
+ onMouseEnter: () => handleOverlayChange(true),
86
+ onMouseLeave: () => handleOverlayChange(false),
87
+ url: url
88
+ }, children);
89
+ }
90
+ return children;
91
+ }, [shouldShowLinkOverlay, isSelected, isResolvedViewRendered, isInserted, isHovered, url, children, handleOverlayChange]);
76
92
  return useMemo(() => {
77
93
  var _cardContext$value;
78
94
  return jsx("span", {
@@ -0,0 +1,213 @@
1
+ import _extends from "@babel/runtime/helpers/extends";
2
+ /* eslint-disable @atlaskit/design-system/no-nested-styles */
3
+ /* eslint-disable @atlaskit/design-system/prefer-primitives */
4
+ /** @jsx jsx */
5
+ import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
6
+ import { css, jsx } from '@emotion/react';
7
+ import debounce from 'lodash/debounce';
8
+ import { useIntl } from 'react-intl-next';
9
+ import { cardMessages as messages } from '@atlaskit/editor-common/messages';
10
+ import { ZERO_WIDTH_JOINER } from '@atlaskit/editor-common/utils';
11
+ import PreferencesIcon from '@atlaskit/icon/glyph/preferences';
12
+ import { B100, N0, N700 } from '@atlaskit/theme/colors';
13
+ import { getChildElement, getIconSize, getInlineCardAvailableWidth, getOverlayWidths, isOneLine } from '../InlineCardOverlay/utils';
14
+ const DEBOUNCE_IN_MS = 5;
15
+ const ESTIMATED_MIN_WIDTH_IN_PX = 16;
16
+ const PADDING_IN_PX = 4;
17
+ const ICON_WIDTH_IN_PX = 14;
18
+ const ICON_AND_LABEL_CLASSNAME = 'ak-editor-card-overlay-icon-and-label';
19
+ const OVERLAY_LABEL_CLASSNAME = 'ak-editor-card-overlay-label';
20
+ const OVERLAY_MARKER_CLASSNAME = 'ak-editor-card-overlay-marker';
21
+ const TEXT_NODE_SELECTOR = ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'].join(',');
22
+ const SMART_LINK_BACKGROUND_COLOR = `var(--ds-surface-raised, ${N0})`;
23
+ // TODO: This should be lighter to match the rest of the button
24
+ const SMART_LINK_ACTIVE_COLOR = `var(--ds-background-selected, ${B100})`;
25
+ const containerStyles = css({
26
+ position: 'relative',
27
+ lineHeight: 'normal',
28
+ ':active': {
29
+ [`.${ICON_AND_LABEL_CLASSNAME}`]: {
30
+ background: SMART_LINK_ACTIVE_COLOR
31
+ }
32
+ }
33
+ });
34
+ const overlayStyles = css({
35
+ // Set default styling to be invisible but available in dom for width calculation.
36
+ visibility: 'hidden',
37
+ marginTop: "var(--ds-space-050, 4px)",
38
+ position: 'absolute',
39
+ verticalAlign: 'text-top',
40
+ height: '1lh',
41
+ '@supports not (height: 1lh)': {
42
+ height: '1.2em'
43
+ },
44
+ overflow: 'hidden',
45
+ // EDM-1717: box-shadow Safari fix bring load wrapper zIndex to 1
46
+ zIndex: 2,
47
+ pointerEvents: 'none'
48
+ });
49
+ const showOverlayStyles = css({
50
+ visibility: 'visible'
51
+ });
52
+ const iconStyles = css({
53
+ // Position icon in the middle
54
+ span: {
55
+ display: 'flex'
56
+ }
57
+ });
58
+ const labelStyles = css({
59
+ fontSize: '0.875em',
60
+ fontWeight: '600',
61
+ width: 'max-content'
62
+ });
63
+ const iconAndLabelStyles = css({
64
+ display: 'flex',
65
+ alignItems: 'center',
66
+ height: '100%',
67
+ marginLeft: "var(--ds-space-050, 4px)",
68
+ marginRight: "var(--ds-space-025, 2px)",
69
+ background: SMART_LINK_BACKGROUND_COLOR,
70
+ color: `var(--ds-text-subtlest, ${N700})`
71
+ });
72
+ const overflowingContainerStyles = css({
73
+ display: 'flex',
74
+ flexDirection: 'row-reverse',
75
+ alignItems: 'center',
76
+ width: 'max-content',
77
+ height: '100%'
78
+ });
79
+ const NarrowInlineCardOverlay = ({
80
+ children,
81
+ isSelected = false,
82
+ isVisible = false,
83
+ testId = 'inline-card-overlay',
84
+ url,
85
+ ...props
86
+ }) => {
87
+ const [showOverlay, setShowOverlay] = useState(false);
88
+ const [availableWidth, setAvailableWidth] = useState(undefined);
89
+ const maxOverlayWidth = useRef(0);
90
+ const minOverlayWidth = useRef(ESTIMATED_MIN_WIDTH_IN_PX);
91
+ const parentWidth = useRef(0);
92
+ const iconSize = useRef('small');
93
+ const containerRef = useRef(null);
94
+
95
+ // TODO EDM-9843: Use availableWidth for small link edge case
96
+ availableWidth;
97
+ const setVisibility = useCallback(() => {
98
+ if (!containerRef.current || !maxOverlayWidth.current) {
99
+ return;
100
+ }
101
+ const marker = getChildElement(containerRef, `.${OVERLAY_MARKER_CLASSNAME}`);
102
+ if (!marker) {
103
+ return;
104
+ }
105
+ try {
106
+ const oneLine = isOneLine(containerRef.current, marker);
107
+
108
+ // Get the width of the available space to display overlay.
109
+ // This is the width of the inline link itself. If the inline
110
+ // is wrapped to the next line, this is width of the last line.
111
+ const availableWidth = getInlineCardAvailableWidth(containerRef.current, marker) - PADDING_IN_PX - (
112
+ // Always leave at least the icon visible
113
+ oneLine ? ICON_WIDTH_IN_PX + PADDING_IN_PX : 0);
114
+ setAvailableWidth(availableWidth);
115
+ const canShowOverlay = !isSelected;
116
+ setShowOverlay(canShowOverlay);
117
+ } catch {
118
+ // If something goes wrong, hide the overlay all together.
119
+ setShowOverlay(false);
120
+ }
121
+ }, [isSelected]);
122
+ useLayoutEffect(() => {
123
+ // Using useLayoutEffect here.
124
+ // 1) We want all to be able to determine whether to display label before
125
+ // the overlay becomes visible.
126
+ // 2) We need to wait for the refs to be assigned to be able to do determine
127
+ // the width of the overlay.
128
+ if (!containerRef.current) {
129
+ return;
130
+ }
131
+
132
+ // This should run only once
133
+ if (!maxOverlayWidth.current) {
134
+ const iconAndLabel = getChildElement(containerRef, `.${ICON_AND_LABEL_CLASSNAME}`);
135
+ const label = getChildElement(containerRef, `.${OVERLAY_LABEL_CLASSNAME}`);
136
+ if (iconAndLabel && label) {
137
+ // Set overlay max (label + icon) and min (icon only) width.
138
+ const {
139
+ max,
140
+ min
141
+ } = getOverlayWidths(iconAndLabel, label);
142
+ maxOverlayWidth.current = max;
143
+ minOverlayWidth.current = min;
144
+ iconSize.current = getIconSize(label);
145
+ }
146
+ }
147
+ if (isVisible) {
148
+ setVisibility();
149
+ }
150
+ }, [setVisibility, isVisible]);
151
+ useEffect(() => {
152
+ var _containerRef$current;
153
+ // Find the closest block parent to observe size change
154
+ const parent = containerRef === null || containerRef === void 0 ? void 0 : (_containerRef$current = containerRef.current) === null || _containerRef$current === void 0 ? void 0 : _containerRef$current.closest(TEXT_NODE_SELECTOR);
155
+ if (!parent) {
156
+ return;
157
+ }
158
+ const updateOverlay = debounce(entries => {
159
+ var _entries$, _entries$$contentBoxS, _entries$$contentBoxS2;
160
+ if (!isVisible) {
161
+ return;
162
+ }
163
+ const size = entries === null || entries === void 0 ? void 0 : (_entries$ = entries[0]) === null || _entries$ === void 0 ? void 0 : (_entries$$contentBoxS = _entries$.contentBoxSize) === null || _entries$$contentBoxS === void 0 ? void 0 : (_entries$$contentBoxS2 = _entries$$contentBoxS[0]) === null || _entries$$contentBoxS2 === void 0 ? void 0 : _entries$$contentBoxS2.inlineSize;
164
+ if (!size) {
165
+ return;
166
+ }
167
+ if (!parentWidth.current) {
168
+ parentWidth.current = size;
169
+ }
170
+ if (parentWidth.current === size) {
171
+ return;
172
+ }
173
+ parentWidth.current = size;
174
+ setVisibility();
175
+ }, DEBOUNCE_IN_MS);
176
+ const observer = new ResizeObserver(updateOverlay);
177
+ observer.observe(parent);
178
+ return () => {
179
+ observer.disconnect();
180
+ };
181
+ }, [isVisible, setVisibility]);
182
+ const intl = useIntl();
183
+ const label = intl.formatMessage(messages.inlineOverlay);
184
+ return jsx("span", _extends({}, props, {
185
+ css: containerStyles,
186
+ ref: containerRef
187
+ }), isVisible && jsx(React.Fragment, null, jsx("span", {
188
+ "aria-hidden": "true",
189
+ className: OVERLAY_MARKER_CLASSNAME
190
+ }, ZERO_WIDTH_JOINER), jsx("a", {
191
+ css: [overlayStyles, showOverlay && showOverlayStyles],
192
+ "data-testid": testId,
193
+ href: url,
194
+ onClick: e => e.preventDefault(),
195
+ tabIndex: -1
196
+ }, jsx("span", {
197
+ css: overflowingContainerStyles
198
+ }, jsx("span", {
199
+ css: iconAndLabelStyles,
200
+ className: ICON_AND_LABEL_CLASSNAME
201
+ }, jsx("span", {
202
+ css: iconStyles
203
+ }, jsx(PreferencesIcon, {
204
+ label: label,
205
+ size: iconSize.current,
206
+ testId: `${testId}-icon`
207
+ })), jsx("span", {
208
+ css: labelStyles,
209
+ className: OVERLAY_LABEL_CLASSNAME,
210
+ "data-testid": `${testId}-label`
211
+ }))))), children);
212
+ };
213
+ export default NarrowInlineCardOverlay;
@@ -3,10 +3,12 @@ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
3
3
  import { useCallback, useEffect, useMemo, useState } from 'react';
4
4
  import { css, jsx } from '@emotion/react';
5
5
  import { AnalyticsContext } from '@atlaskit/analytics-next';
6
+ import { getBooleanFF } from '@atlaskit/platform-feature-flags';
6
7
  import useLinkUpgradeDiscoverability from '../../common/hooks/useLinkUpgradeDiscoverability';
7
8
  import { isLocalStorageKeyDiscovered, LOCAL_STORAGE_DISCOVERY_KEY_SMART_LINK, LOCAL_STORAGE_DISCOVERY_KEY_TOOLBAR, markLocalStorageKeyDiscovered, ONE_DAY_IN_MILLISECONDS } from '../../common/local-storage';
8
9
  import { getResolvedAttributesFromStore } from '../../utils';
9
10
  import InlineCardOverlay from '../InlineCardOverlay';
11
+ import NewInlineCardOverlay from '../NewInlineCardOverlay';
10
12
  import { DiscoveryPulse } from '../Pulse';
11
13
  // editor adds a standard line-height that is bigger than an inline smart link
12
14
  // due to that the link has a bit of white space around it, which doesn't look right when there is pulse around it
@@ -69,17 +71,33 @@ export var AwarenessWrapper = function AwarenessWrapper(_ref) {
69
71
  setOverlayHoveredStyles(isHovered);
70
72
  }, [setOverlayHoveredStyles]);
71
73
  var cardWithOverlay = useMemo(function () {
72
- return shouldShowLinkOverlay ? jsx(InlineCardOverlay, {
73
- isSelected: isSelected,
74
- isVisible: isResolvedViewRendered && (isInserted || isHovered || isSelected),
75
- onMouseEnter: function onMouseEnter() {
76
- return handleOverlayChange(true);
77
- },
78
- onMouseLeave: function onMouseLeave() {
79
- return handleOverlayChange(false);
80
- },
81
- url: url
82
- }, children) : children;
74
+ if (shouldShowLinkOverlay) {
75
+ if (getBooleanFF('platform.linking-platform.smart-links-in-live-pages')) {
76
+ return jsx(NewInlineCardOverlay, {
77
+ isSelected: isSelected,
78
+ isVisible: isResolvedViewRendered && (isInserted || isHovered || isSelected),
79
+ onMouseEnter: function onMouseEnter() {
80
+ return handleOverlayChange(true);
81
+ },
82
+ onMouseLeave: function onMouseLeave() {
83
+ return handleOverlayChange(false);
84
+ },
85
+ url: url
86
+ }, children);
87
+ }
88
+ return jsx(InlineCardOverlay, {
89
+ isSelected: isSelected,
90
+ isVisible: isResolvedViewRendered && (isInserted || isHovered || isSelected),
91
+ onMouseEnter: function onMouseEnter() {
92
+ return handleOverlayChange(true);
93
+ },
94
+ onMouseLeave: function onMouseLeave() {
95
+ return handleOverlayChange(false);
96
+ },
97
+ url: url
98
+ }, children);
99
+ }
100
+ return children;
83
101
  }, [shouldShowLinkOverlay, isSelected, isResolvedViewRendered, isInserted, isHovered, url, children, handleOverlayChange]);
84
102
  return useMemo(function () {
85
103
  var _cardContext$value;
@@ -0,0 +1,224 @@
1
+ import _extends from "@babel/runtime/helpers/extends";
2
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
3
+ import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
4
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
5
+ var _excluded = ["children", "isSelected", "isVisible", "testId", "url"];
6
+ /* eslint-disable @atlaskit/design-system/no-nested-styles */
7
+ /* eslint-disable @atlaskit/design-system/prefer-primitives */
8
+ /** @jsx jsx */
9
+ import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
10
+ import { css, jsx } from '@emotion/react';
11
+ import debounce from 'lodash/debounce';
12
+ import { useIntl } from 'react-intl-next';
13
+ import { cardMessages as messages } from '@atlaskit/editor-common/messages';
14
+ import { ZERO_WIDTH_JOINER } from '@atlaskit/editor-common/utils';
15
+ import PreferencesIcon from '@atlaskit/icon/glyph/preferences';
16
+ import { B100, N0, N700 } from '@atlaskit/theme/colors';
17
+ import { getChildElement, getIconSize, getInlineCardAvailableWidth, getOverlayWidths, isOneLine } from '../InlineCardOverlay/utils';
18
+ var DEBOUNCE_IN_MS = 5;
19
+ var ESTIMATED_MIN_WIDTH_IN_PX = 16;
20
+ var PADDING_IN_PX = 4;
21
+ var ICON_WIDTH_IN_PX = 14;
22
+ var ICON_AND_LABEL_CLASSNAME = 'ak-editor-card-overlay-icon-and-label';
23
+ var OVERLAY_LABEL_CLASSNAME = 'ak-editor-card-overlay-label';
24
+ var OVERLAY_MARKER_CLASSNAME = 'ak-editor-card-overlay-marker';
25
+ var TEXT_NODE_SELECTOR = ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'].join(',');
26
+ var SMART_LINK_BACKGROUND_COLOR = "var(--ds-surface-raised, ".concat(N0, ")");
27
+ // TODO: This should be lighter to match the rest of the button
28
+ var SMART_LINK_ACTIVE_COLOR = "var(--ds-background-selected, ".concat(B100, ")");
29
+ var containerStyles = css({
30
+ position: 'relative',
31
+ lineHeight: 'normal',
32
+ ':active': _defineProperty({}, ".".concat(ICON_AND_LABEL_CLASSNAME), {
33
+ background: SMART_LINK_ACTIVE_COLOR
34
+ })
35
+ });
36
+ var overlayStyles = css({
37
+ // Set default styling to be invisible but available in dom for width calculation.
38
+ visibility: 'hidden',
39
+ marginTop: "var(--ds-space-050, 4px)",
40
+ position: 'absolute',
41
+ verticalAlign: 'text-top',
42
+ height: '1lh',
43
+ '@supports not (height: 1lh)': {
44
+ height: '1.2em'
45
+ },
46
+ overflow: 'hidden',
47
+ // EDM-1717: box-shadow Safari fix bring load wrapper zIndex to 1
48
+ zIndex: 2,
49
+ pointerEvents: 'none'
50
+ });
51
+ var showOverlayStyles = css({
52
+ visibility: 'visible'
53
+ });
54
+ var iconStyles = css({
55
+ // Position icon in the middle
56
+ span: {
57
+ display: 'flex'
58
+ }
59
+ });
60
+ var labelStyles = css({
61
+ fontSize: '0.875em',
62
+ fontWeight: '600',
63
+ width: 'max-content'
64
+ });
65
+ var iconAndLabelStyles = css({
66
+ display: 'flex',
67
+ alignItems: 'center',
68
+ height: '100%',
69
+ marginLeft: "var(--ds-space-050, 4px)",
70
+ marginRight: "var(--ds-space-025, 2px)",
71
+ background: SMART_LINK_BACKGROUND_COLOR,
72
+ color: "var(--ds-text-subtlest, ".concat(N700, ")")
73
+ });
74
+ var overflowingContainerStyles = css({
75
+ display: 'flex',
76
+ flexDirection: 'row-reverse',
77
+ alignItems: 'center',
78
+ width: 'max-content',
79
+ height: '100%'
80
+ });
81
+ var NarrowInlineCardOverlay = function NarrowInlineCardOverlay(_ref) {
82
+ var children = _ref.children,
83
+ _ref$isSelected = _ref.isSelected,
84
+ isSelected = _ref$isSelected === void 0 ? false : _ref$isSelected,
85
+ _ref$isVisible = _ref.isVisible,
86
+ isVisible = _ref$isVisible === void 0 ? false : _ref$isVisible,
87
+ _ref$testId = _ref.testId,
88
+ testId = _ref$testId === void 0 ? 'inline-card-overlay' : _ref$testId,
89
+ url = _ref.url,
90
+ props = _objectWithoutProperties(_ref, _excluded);
91
+ var _useState = useState(false),
92
+ _useState2 = _slicedToArray(_useState, 2),
93
+ showOverlay = _useState2[0],
94
+ setShowOverlay = _useState2[1];
95
+ var _useState3 = useState(undefined),
96
+ _useState4 = _slicedToArray(_useState3, 2),
97
+ availableWidth = _useState4[0],
98
+ setAvailableWidth = _useState4[1];
99
+ var maxOverlayWidth = useRef(0);
100
+ var minOverlayWidth = useRef(ESTIMATED_MIN_WIDTH_IN_PX);
101
+ var parentWidth = useRef(0);
102
+ var iconSize = useRef('small');
103
+ var containerRef = useRef(null);
104
+
105
+ // TODO EDM-9843: Use availableWidth for small link edge case
106
+ availableWidth;
107
+ var setVisibility = useCallback(function () {
108
+ if (!containerRef.current || !maxOverlayWidth.current) {
109
+ return;
110
+ }
111
+ var marker = getChildElement(containerRef, ".".concat(OVERLAY_MARKER_CLASSNAME));
112
+ if (!marker) {
113
+ return;
114
+ }
115
+ try {
116
+ var oneLine = isOneLine(containerRef.current, marker);
117
+
118
+ // Get the width of the available space to display overlay.
119
+ // This is the width of the inline link itself. If the inline
120
+ // is wrapped to the next line, this is width of the last line.
121
+ var _availableWidth = getInlineCardAvailableWidth(containerRef.current, marker) - PADDING_IN_PX - (
122
+ // Always leave at least the icon visible
123
+ oneLine ? ICON_WIDTH_IN_PX + PADDING_IN_PX : 0);
124
+ setAvailableWidth(_availableWidth);
125
+ var canShowOverlay = !isSelected;
126
+ setShowOverlay(canShowOverlay);
127
+ } catch (_unused) {
128
+ // If something goes wrong, hide the overlay all together.
129
+ setShowOverlay(false);
130
+ }
131
+ }, [isSelected]);
132
+ useLayoutEffect(function () {
133
+ // Using useLayoutEffect here.
134
+ // 1) We want all to be able to determine whether to display label before
135
+ // the overlay becomes visible.
136
+ // 2) We need to wait for the refs to be assigned to be able to do determine
137
+ // the width of the overlay.
138
+ if (!containerRef.current) {
139
+ return;
140
+ }
141
+
142
+ // This should run only once
143
+ if (!maxOverlayWidth.current) {
144
+ var iconAndLabel = getChildElement(containerRef, ".".concat(ICON_AND_LABEL_CLASSNAME));
145
+ var _label = getChildElement(containerRef, ".".concat(OVERLAY_LABEL_CLASSNAME));
146
+ if (iconAndLabel && _label) {
147
+ // Set overlay max (label + icon) and min (icon only) width.
148
+ var _getOverlayWidths = getOverlayWidths(iconAndLabel, _label),
149
+ max = _getOverlayWidths.max,
150
+ min = _getOverlayWidths.min;
151
+ maxOverlayWidth.current = max;
152
+ minOverlayWidth.current = min;
153
+ iconSize.current = getIconSize(_label);
154
+ }
155
+ }
156
+ if (isVisible) {
157
+ setVisibility();
158
+ }
159
+ }, [setVisibility, isVisible]);
160
+ useEffect(function () {
161
+ var _containerRef$current;
162
+ // Find the closest block parent to observe size change
163
+ var parent = containerRef === null || containerRef === void 0 || (_containerRef$current = containerRef.current) === null || _containerRef$current === void 0 ? void 0 : _containerRef$current.closest(TEXT_NODE_SELECTOR);
164
+ if (!parent) {
165
+ return;
166
+ }
167
+ var updateOverlay = debounce(function (entries) {
168
+ var _entries$;
169
+ if (!isVisible) {
170
+ return;
171
+ }
172
+ 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;
173
+ if (!size) {
174
+ return;
175
+ }
176
+ if (!parentWidth.current) {
177
+ parentWidth.current = size;
178
+ }
179
+ if (parentWidth.current === size) {
180
+ return;
181
+ }
182
+ parentWidth.current = size;
183
+ setVisibility();
184
+ }, DEBOUNCE_IN_MS);
185
+ var observer = new ResizeObserver(updateOverlay);
186
+ observer.observe(parent);
187
+ return function () {
188
+ observer.disconnect();
189
+ };
190
+ }, [isVisible, setVisibility]);
191
+ var intl = useIntl();
192
+ var label = intl.formatMessage(messages.inlineOverlay);
193
+ return jsx("span", _extends({}, props, {
194
+ css: containerStyles,
195
+ ref: containerRef
196
+ }), isVisible && jsx(React.Fragment, null, jsx("span", {
197
+ "aria-hidden": "true",
198
+ className: OVERLAY_MARKER_CLASSNAME
199
+ }, ZERO_WIDTH_JOINER), jsx("a", {
200
+ css: [overlayStyles, showOverlay && showOverlayStyles],
201
+ "data-testid": testId,
202
+ href: url,
203
+ onClick: function onClick(e) {
204
+ return e.preventDefault();
205
+ },
206
+ tabIndex: -1
207
+ }, jsx("span", {
208
+ css: overflowingContainerStyles
209
+ }, jsx("span", {
210
+ css: iconAndLabelStyles,
211
+ className: ICON_AND_LABEL_CLASSNAME
212
+ }, jsx("span", {
213
+ css: iconStyles
214
+ }, jsx(PreferencesIcon, {
215
+ label: label,
216
+ size: iconSize.current,
217
+ testId: "".concat(testId, "-icon")
218
+ })), jsx("span", {
219
+ css: labelStyles,
220
+ className: OVERLAY_LABEL_CLASSNAME,
221
+ "data-testid": "".concat(testId, "-label")
222
+ }))))), children);
223
+ };
224
+ export default NarrowInlineCardOverlay;
@@ -0,0 +1,6 @@
1
+ /** @jsx jsx */
2
+ import React from 'react';
3
+ import { jsx } from '@emotion/react';
4
+ import type { InlineCardOverlayProps } from '../InlineCardOverlay/types';
5
+ declare const NarrowInlineCardOverlay: ({ children, isSelected, isVisible, testId, url, ...props }: React.PropsWithChildren<InlineCardOverlayProps>) => jsx.JSX.Element;
6
+ export default NarrowInlineCardOverlay;
@@ -0,0 +1,6 @@
1
+ /** @jsx jsx */
2
+ import React from 'react';
3
+ import { jsx } from '@emotion/react';
4
+ import type { InlineCardOverlayProps } from '../InlineCardOverlay/types';
5
+ declare const NarrowInlineCardOverlay: ({ children, isSelected, isVisible, testId, url, ...props }: React.PropsWithChildren<InlineCardOverlayProps>) => jsx.JSX.Element;
6
+ export default NarrowInlineCardOverlay;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/editor-plugin-card",
3
- "version": "1.13.0",
3
+ "version": "1.14.0",
4
4
  "description": "Card plugin for @atlaskit/editor-core",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
@@ -32,10 +32,10 @@
32
32
  ".": "./src/index.ts"
33
33
  },
34
34
  "dependencies": {
35
- "@atlaskit/adf-schema": "^36.3.0",
35
+ "@atlaskit/adf-schema": "^36.8.0",
36
36
  "@atlaskit/analytics-next": "^9.3.0",
37
37
  "@atlaskit/custom-steps": "^0.2.0",
38
- "@atlaskit/editor-common": "^80.3.0",
38
+ "@atlaskit/editor-common": "^80.4.0",
39
39
  "@atlaskit/editor-plugin-analytics": "^1.2.0",
40
40
  "@atlaskit/editor-plugin-decorations": "^1.1.0",
41
41
  "@atlaskit/editor-plugin-editor-viewmode": "^1.1.0",
@@ -50,12 +50,12 @@
50
50
  "@atlaskit/icon": "^22.2.0",
51
51
  "@atlaskit/link-analytics": "^8.3.0",
52
52
  "@atlaskit/link-client-extension": "^1.8.0",
53
- "@atlaskit/link-datasource": "^2.2.0",
53
+ "@atlaskit/link-datasource": "^2.3.0",
54
54
  "@atlaskit/linking-common": "^5.6.0",
55
55
  "@atlaskit/linking-types": "^8.8.0",
56
56
  "@atlaskit/platform-feature-flags": "^0.2.0",
57
57
  "@atlaskit/primitives": "^6.2.0",
58
- "@atlaskit/smart-card": "^26.68.0",
58
+ "@atlaskit/smart-card": "^26.69.0",
59
59
  "@atlaskit/theme": "^12.8.0",
60
60
  "@atlaskit/tokens": "^1.48.0",
61
61
  "@babel/runtime": "^7.0.0",
@@ -105,6 +105,9 @@
105
105
  },
106
106
  "prettier": "@atlassian/atlassian-frontend-prettier-config-1.0.0",
107
107
  "platform-feature-flags": {
108
+ "platform.linking-platform.smart-links-in-live-pages": {
109
+ "type": "boolean"
110
+ },
108
111
  "platform.linking-platform.datasource-word_wrap": {
109
112
  "type": "boolean"
110
113
  },