@atlaskit/editor-plugin-card 0.13.4 → 0.13.6
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 +15 -0
- package/dist/cjs/nodeviews/inlineCard.js +4 -0
- package/dist/cjs/nodeviews/inlineCardWithAwareness.js +31 -24
- package/dist/cjs/pm-plugins/keymap.js +1 -1
- package/dist/cjs/ui/InlineCardOverlay/index.js +120 -47
- package/dist/cjs/ui/InlineCardOverlay/utils.js +35 -0
- package/dist/es2019/nodeviews/inlineCard.js +4 -0
- package/dist/es2019/nodeviews/inlineCardWithAwareness.js +21 -17
- package/dist/es2019/pm-plugins/keymap.js +1 -1
- package/dist/es2019/ui/InlineCardOverlay/index.js +111 -42
- package/dist/es2019/ui/InlineCardOverlay/utils.js +27 -0
- package/dist/esm/nodeviews/inlineCard.js +4 -0
- package/dist/esm/nodeviews/inlineCardWithAwareness.js +31 -24
- package/dist/esm/pm-plugins/keymap.js +1 -1
- package/dist/esm/ui/InlineCardOverlay/index.js +121 -48
- package/dist/esm/ui/InlineCardOverlay/utils.js +29 -0
- package/dist/types/ui/InlineCardOverlay/types.d.ts +3 -2
- package/dist/types/ui/InlineCardOverlay/utils.d.ts +17 -0
- package/dist/types-ts4.5/ui/InlineCardOverlay/types.d.ts +3 -2
- package/dist/types-ts4.5/ui/InlineCardOverlay/utils.d.ts +17 -0
- package/package.json +6 -6
|
@@ -1,14 +1,23 @@
|
|
|
1
|
+
import _extends from "@babel/runtime/helpers/extends";
|
|
1
2
|
/** @jsx jsx */
|
|
2
3
|
|
|
3
|
-
import React, { useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
4
|
+
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
4
5
|
import { css, jsx } from '@emotion/react';
|
|
6
|
+
import debounce from 'lodash/debounce';
|
|
5
7
|
import { useIntl } from 'react-intl-next';
|
|
6
8
|
import { browser } from '@atlaskit/editor-common/utils';
|
|
7
9
|
import HipchatChevronDownIcon from '@atlaskit/icon/glyph/hipchat/chevron-down';
|
|
8
10
|
import HipchatChevronUpIcon from '@atlaskit/icon/glyph/hipchat/chevron-up';
|
|
9
11
|
import { N20A, N800 } from '@atlaskit/theme/colors';
|
|
10
12
|
import { messages } from '../../messages';
|
|
13
|
+
import { getChildElement, getInlineCardAvailableWidth, getOverlayWidths } from './utils';
|
|
14
|
+
const DEBOUNCE_IN_MS = 5;
|
|
15
|
+
const ESTIMATED_MIN_WIDTH_IN_PX = 16;
|
|
11
16
|
const PADDING_IN_PX = 2;
|
|
17
|
+
const OVERLAY_CLASSNAME = 'ak-editor-card-overlay';
|
|
18
|
+
const OVERLAY_LABEL_CLASSNAME = 'ak-editor-card-overlay-label';
|
|
19
|
+
const OVERLAY_MARKER_CLASSNAME = 'ak-editor-card-overlay-marker';
|
|
20
|
+
const TEXT_NODE_SELECTOR = ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'].join(',');
|
|
12
21
|
const containerStyles = css({
|
|
13
22
|
position: 'relative',
|
|
14
23
|
lineHeight: 'normal'
|
|
@@ -18,9 +27,10 @@ const linkStyles = css({
|
|
|
18
27
|
textDecoration: 'none'
|
|
19
28
|
});
|
|
20
29
|
const overlayStyles = css({
|
|
30
|
+
// Visibility is set directly on element via style prop
|
|
31
|
+
display: 'inline-flex',
|
|
21
32
|
// Positioning
|
|
22
33
|
position: 'relative',
|
|
23
|
-
display: 'inline-flex',
|
|
24
34
|
flexWrap: 'nowrap',
|
|
25
35
|
alignItems: 'center',
|
|
26
36
|
alignSelf: 'stretch',
|
|
@@ -96,67 +106,126 @@ const markerStyles = css({
|
|
|
96
106
|
});
|
|
97
107
|
const InlineCardOverlay = ({
|
|
98
108
|
children,
|
|
99
|
-
|
|
109
|
+
isSelected = false,
|
|
100
110
|
isVisible = false,
|
|
101
111
|
testId = 'inline-card-overlay',
|
|
102
|
-
url
|
|
112
|
+
url,
|
|
113
|
+
...props
|
|
103
114
|
}) => {
|
|
115
|
+
const [showOverlay, setShowOverlay] = useState(true);
|
|
104
116
|
const [showLabel, setShowLabel] = useState(true);
|
|
105
117
|
const [overlayWidth, setOverlayWidth] = useState(0);
|
|
118
|
+
const maxOverlayWidth = useRef(0);
|
|
119
|
+
const minOverlayWidth = useRef(ESTIMATED_MIN_WIDTH_IN_PX);
|
|
120
|
+
const parentWidth = useRef(0);
|
|
106
121
|
const containerRef = useRef(null);
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
setShowLabel(true);
|
|
122
|
+
const setVisibility = useCallback(() => {
|
|
123
|
+
if (!containerRef.current || !maxOverlayWidth.current) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
const marker = getChildElement(containerRef, `.${OVERLAY_MARKER_CLASSNAME}`);
|
|
127
|
+
if (!marker) {
|
|
114
128
|
return;
|
|
115
129
|
}
|
|
116
130
|
try {
|
|
117
|
-
|
|
118
|
-
//
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
-
const availableWidth = end - start - PADDING_IN_PX;
|
|
122
|
-
|
|
123
|
-
// Get overlay width and label width
|
|
124
|
-
const overlayWidth = (_overlayRef$current$g = overlayRef === null || overlayRef === void 0 ? void 0 : (_overlayRef$current = overlayRef.current) === null || _overlayRef$current === void 0 ? void 0 : (_overlayRef$current$g2 = _overlayRef$current.getBoundingClientRect()) === null || _overlayRef$current$g2 === void 0 ? void 0 : _overlayRef$current$g2.width) !== null && _overlayRef$current$g !== void 0 ? _overlayRef$current$g : 0;
|
|
131
|
+
// Get the width of the available space to display overlay.
|
|
132
|
+
// This is the width of the inline link itself. If the inline
|
|
133
|
+
// is wrapped to the next line, this is width of the last line.
|
|
134
|
+
const availableWidth = getInlineCardAvailableWidth(containerRef.current, marker) - PADDING_IN_PX;
|
|
125
135
|
|
|
126
|
-
//
|
|
127
|
-
const
|
|
128
|
-
|
|
136
|
+
// If available width is less than the min width of overlay, don't show overlay.
|
|
137
|
+
const canShowOverlay = availableWidth > minOverlayWidth.current;
|
|
138
|
+
setShowOverlay(canShowOverlay);
|
|
139
|
+
if (!canShowOverlay) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
129
142
|
|
|
130
|
-
//
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const newOverlayWidth = shouldShowLabel ? overlayWidth : overlayWidth - labelWidth;
|
|
135
|
-
setOverlayWidth(newOverlayWidth);
|
|
143
|
+
// Otherwise, check if overlay can be show in full context with label and icon.
|
|
144
|
+
const canShowLabel = availableWidth > maxOverlayWidth.current + PADDING_IN_PX;
|
|
145
|
+
setShowLabel(canShowLabel);
|
|
146
|
+
setOverlayWidth(canShowLabel ? maxOverlayWidth.current : minOverlayWidth.current);
|
|
136
147
|
} catch {
|
|
137
|
-
// If something goes wrong,
|
|
138
|
-
|
|
139
|
-
|
|
148
|
+
// If something goes wrong, hide the overlay all together.
|
|
149
|
+
setShowOverlay(false);
|
|
150
|
+
}
|
|
151
|
+
}, []);
|
|
152
|
+
useLayoutEffect(() => {
|
|
153
|
+
// Using useLayoutEffect here.
|
|
154
|
+
// 1) We want all to be able to determine whether to display label before
|
|
155
|
+
// the overlay becomes visible.
|
|
156
|
+
// 2) We need to wait for the refs to be assigned to be able to do determine
|
|
157
|
+
// the width of the overlay.
|
|
158
|
+
if (!containerRef.current) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (!maxOverlayWidth.current) {
|
|
162
|
+
const overlay = getChildElement(containerRef, `.${OVERLAY_CLASSNAME}`);
|
|
163
|
+
const label = getChildElement(containerRef, `.${OVERLAY_LABEL_CLASSNAME}`);
|
|
164
|
+
if (overlay && label) {
|
|
165
|
+
// Set overlay max (label + icon) and min (icon only) width.
|
|
166
|
+
// This should run only once.
|
|
167
|
+
const {
|
|
168
|
+
max,
|
|
169
|
+
min
|
|
170
|
+
} = getOverlayWidths(overlay, label);
|
|
171
|
+
maxOverlayWidth.current = max;
|
|
172
|
+
minOverlayWidth.current = min;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (isVisible) {
|
|
176
|
+
setVisibility();
|
|
177
|
+
}
|
|
178
|
+
}, [setVisibility, isVisible]);
|
|
179
|
+
useEffect(() => {
|
|
180
|
+
var _containerRef$current;
|
|
181
|
+
// Find the closest block parent to observe size change
|
|
182
|
+
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);
|
|
183
|
+
if (!parent) {
|
|
184
|
+
return;
|
|
140
185
|
}
|
|
141
|
-
|
|
186
|
+
const updateOverlay = debounce(entries => {
|
|
187
|
+
var _entries$, _entries$$contentBoxS, _entries$$contentBoxS2;
|
|
188
|
+
if (!isVisible) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
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;
|
|
192
|
+
if (!size) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (!parentWidth.current) {
|
|
196
|
+
parentWidth.current = size;
|
|
197
|
+
}
|
|
198
|
+
if (parentWidth.current === size) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
parentWidth.current = size;
|
|
202
|
+
setVisibility();
|
|
203
|
+
}, DEBOUNCE_IN_MS);
|
|
204
|
+
const observer = new ResizeObserver(updateOverlay);
|
|
205
|
+
observer.observe(parent);
|
|
206
|
+
return () => {
|
|
207
|
+
observer.disconnect();
|
|
208
|
+
};
|
|
209
|
+
}, [isVisible, setVisibility]);
|
|
142
210
|
const intl = useIntl();
|
|
143
211
|
const label = intl.formatMessage(messages.inlineOverlay);
|
|
144
212
|
const icon = useMemo(() => {
|
|
145
|
-
const IconComponent =
|
|
213
|
+
const IconComponent = isSelected ? HipchatChevronUpIcon : HipchatChevronDownIcon;
|
|
146
214
|
return jsx(IconComponent, {
|
|
147
215
|
label: label,
|
|
148
216
|
size: "small",
|
|
149
|
-
testId: `${testId}-${
|
|
217
|
+
testId: `${testId}-${isSelected ? 'open' : 'close'}`
|
|
150
218
|
});
|
|
151
|
-
}, [
|
|
152
|
-
return jsx("span", {
|
|
219
|
+
}, [isSelected, label, testId]);
|
|
220
|
+
return jsx("span", _extends({}, props, {
|
|
153
221
|
css: containerStyles,
|
|
154
222
|
ref: containerRef
|
|
155
|
-
}, children, isVisible && jsx(React.Fragment, null, jsx("span", {
|
|
223
|
+
}), children, isVisible && showOverlay && jsx(React.Fragment, null, jsx("span", {
|
|
156
224
|
"aria-hidden": "true",
|
|
157
|
-
|
|
158
|
-
|
|
225
|
+
className: OVERLAY_MARKER_CLASSNAME,
|
|
226
|
+
css: markerStyles
|
|
159
227
|
}), jsx("a", {
|
|
228
|
+
className: OVERLAY_CLASSNAME,
|
|
160
229
|
css: [overlayStyles, browser.safari && safariOverlayStyles],
|
|
161
230
|
style: {
|
|
162
231
|
marginLeft: -overlayWidth
|
|
@@ -164,11 +233,11 @@ const InlineCardOverlay = ({
|
|
|
164
233
|
"data-testid": testId,
|
|
165
234
|
href: url,
|
|
166
235
|
onClick: e => e.preventDefault(),
|
|
167
|
-
|
|
236
|
+
tabIndex: -1
|
|
168
237
|
}, showLabel && jsx("span", {
|
|
238
|
+
className: OVERLAY_LABEL_CLASSNAME,
|
|
169
239
|
css: textStyles,
|
|
170
|
-
"data-testid": `${testId}-label
|
|
171
|
-
ref: labelRef
|
|
240
|
+
"data-testid": `${testId}-label`
|
|
172
241
|
}, label), jsx("span", {
|
|
173
242
|
css: iconStyles
|
|
174
243
|
}, icon))));
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Find a child element inside a ref.
|
|
3
|
+
*/
|
|
4
|
+
export const getChildElement = (ref, selector) => ref.current ? ref.current.querySelector(selector) : undefined;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Get the available width of the inline card.
|
|
8
|
+
* (Mainly here to make the component unit testable.)
|
|
9
|
+
*/
|
|
10
|
+
export const getInlineCardAvailableWidth = (startEl, endEl) => {
|
|
11
|
+
const start = startEl.getBoundingClientRect().left;
|
|
12
|
+
const end = endEl.getBoundingClientRect().left;
|
|
13
|
+
return end - start;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Get max and min width of an overlay.
|
|
18
|
+
* (Mainly here to make the component unit testable.)
|
|
19
|
+
*/
|
|
20
|
+
export const getOverlayWidths = (overlayEl, labelEl) => {
|
|
21
|
+
const max = overlayEl.getBoundingClientRect().width;
|
|
22
|
+
const min = max - labelEl.getBoundingClientRect().width;
|
|
23
|
+
return {
|
|
24
|
+
max,
|
|
25
|
+
min
|
|
26
|
+
};
|
|
27
|
+
};
|
|
@@ -133,6 +133,8 @@ export function InlineCardNodeView(props) {
|
|
|
133
133
|
useAlternativePreloader: useAlternativePreloader
|
|
134
134
|
});
|
|
135
135
|
}
|
|
136
|
+
|
|
137
|
+
// BEGIN: Awareness (To be revisited in EDM-8508)
|
|
136
138
|
var editorState = view.state;
|
|
137
139
|
var linkPosition = getPos && typeof getPos() === 'number' ? getPos() : undefined;
|
|
138
140
|
var canBeUpgradedToEmbed = !!linkPosition && allowEmbeds ? isEmbedSupportedAtPosition(linkPosition, editorState, 'inline') : false;
|
|
@@ -140,6 +142,8 @@ export function InlineCardNodeView(props) {
|
|
|
140
142
|
var isPulseEnabled = enableInlineUpgradeFeatures && canBeUpgradedToEmbed;
|
|
141
143
|
var isOverlayEnabled = enableInlineUpgradeFeatures && (canBeUpgradedToEmbed || canBeUpgradedToBlock);
|
|
142
144
|
var isSelected = view.state.selection instanceof NodeSelection && ((_view$state$selection = view.state.selection) === null || _view$state$selection === void 0 || (_view$state$selection = _view$state$selection.node) === null || _view$state$selection === void 0 ? void 0 : _view$state$selection.type) === view.state.schema.nodes.inlineCard && ((_view$state$selection2 = view.state.selection) === null || _view$state$selection2 === void 0 ? void 0 : _view$state$selection2.from) === getPos();
|
|
145
|
+
// END: Awareness
|
|
146
|
+
|
|
143
147
|
return /*#__PURE__*/React.createElement(WrappedInlineCardWithAwareness, {
|
|
144
148
|
node: node,
|
|
145
149
|
view: view,
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import _extends from "@babel/runtime/helpers/extends";
|
|
2
1
|
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
3
2
|
/** @jsx jsx */
|
|
4
3
|
import { memo, useCallback, useMemo, useState } from 'react';
|
|
@@ -29,17 +28,17 @@ var InlineCard = function InlineCard(_ref) {
|
|
|
29
28
|
isOverlayEnabled = _ref.isOverlayEnabled,
|
|
30
29
|
isPulseEnabled = _ref.isPulseEnabled,
|
|
31
30
|
pluginInjectionApi = _ref.pluginInjectionApi,
|
|
32
|
-
isSelected = _ref.isSelected
|
|
31
|
+
_ref$isSelected = _ref.isSelected,
|
|
32
|
+
isSelected = _ref$isSelected === void 0 ? false : _ref$isSelected;
|
|
33
33
|
var _node$attrs = node.attrs,
|
|
34
34
|
url = _node$attrs.url,
|
|
35
35
|
data = _node$attrs.data;
|
|
36
36
|
|
|
37
|
-
//
|
|
38
|
-
// in EDM-8239 and EDM-8241
|
|
37
|
+
// BEGIN: Awareness (To be revisited in EDM-8508)
|
|
39
38
|
var _useState = useState(false),
|
|
40
39
|
_useState2 = _slicedToArray(_useState, 2),
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
isHovered = _useState2[0],
|
|
41
|
+
setIsHovered = _useState2[1];
|
|
43
42
|
var linkPosition = useMemo(function () {
|
|
44
43
|
if (!getPos || typeof getPos === 'boolean') {
|
|
45
44
|
return undefined;
|
|
@@ -64,6 +63,8 @@ var InlineCard = function InlineCard(_ref) {
|
|
|
64
63
|
if (isSelected && shouldShowToolbarPulse && !isLocalStorageKeyDiscovered(LOCAL_STORAGE_DISCOVERY_KEY_TOOLBAR)) {
|
|
65
64
|
markLocalStorageKeyDiscovered(LOCAL_STORAGE_DISCOVERY_KEY_SMART_LINK);
|
|
66
65
|
}
|
|
66
|
+
// END: Awareness
|
|
67
|
+
|
|
67
68
|
var scrollContainer = useMemo(function () {
|
|
68
69
|
return findOverflowScrollParent(view.dom) || undefined;
|
|
69
70
|
}, [view.dom]);
|
|
@@ -99,10 +100,7 @@ var InlineCard = function InlineCard(_ref) {
|
|
|
99
100
|
});
|
|
100
101
|
}, [onResolve]);
|
|
101
102
|
var innerCard = useMemo(function () {
|
|
102
|
-
return jsx(
|
|
103
|
-
isVisible: isOverlayVisible,
|
|
104
|
-
url: url
|
|
105
|
-
}, jsx(SmartCard, {
|
|
103
|
+
return jsx(SmartCard, {
|
|
106
104
|
key: url,
|
|
107
105
|
url: url,
|
|
108
106
|
data: data,
|
|
@@ -113,25 +111,34 @@ var InlineCard = function InlineCard(_ref) {
|
|
|
113
111
|
onError: onError,
|
|
114
112
|
inlinePreloaderStyle: useAlternativePreloader ? 'on-right-without-skeleton' : undefined,
|
|
115
113
|
showServerActions: showServerActions
|
|
116
|
-
})
|
|
117
|
-
}, [data,
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
114
|
+
});
|
|
115
|
+
}, [data, onError, onResolve, scrollContainer, showServerActions, url, useAlternativePreloader]);
|
|
116
|
+
|
|
117
|
+
// BEGIN: Awareness (To be revisited in EDM-8508)
|
|
118
|
+
var cardWithOverlay = useMemo(function () {
|
|
119
|
+
return shouldShowLinkOverlay ? jsx(InlineCardOverlay, {
|
|
120
|
+
isSelected: isSelected,
|
|
121
|
+
isVisible: isHovered || isSelected,
|
|
123
122
|
onMouseEnter: function onMouseEnter() {
|
|
124
|
-
return
|
|
123
|
+
return setIsHovered(true);
|
|
125
124
|
},
|
|
126
125
|
onMouseLeave: function onMouseLeave() {
|
|
127
|
-
return
|
|
128
|
-
}
|
|
129
|
-
|
|
126
|
+
return setIsHovered(false);
|
|
127
|
+
},
|
|
128
|
+
url: url
|
|
129
|
+
}, innerCard) : innerCard;
|
|
130
|
+
}, [innerCard, isHovered, isSelected, shouldShowLinkOverlay, url]);
|
|
131
|
+
var card = useMemo(function () {
|
|
132
|
+
return jsx("span", {
|
|
133
|
+
css: shouldShowLinkPulse && loaderWrapperStyles,
|
|
134
|
+
className: "card"
|
|
135
|
+
}, shouldShowLinkPulse ? jsx(DiscoveryPulse, {
|
|
130
136
|
localStorageKey: LOCAL_STORAGE_DISCOVERY_KEY_SMART_LINK,
|
|
131
137
|
localStorageKeyExpirationInMs: ONE_DAY_IN_MILLISECONDS,
|
|
132
|
-
discoveryMode:
|
|
133
|
-
},
|
|
134
|
-
}, [shouldShowLinkPulse,
|
|
138
|
+
discoveryMode: "start"
|
|
139
|
+
}, cardWithOverlay) : cardWithOverlay);
|
|
140
|
+
}, [shouldShowLinkPulse, cardWithOverlay]);
|
|
141
|
+
// END: Awareness
|
|
135
142
|
|
|
136
143
|
// [WS-2307]: we only render card wrapped into a Provider when the value is ready,
|
|
137
144
|
// otherwise if we got data, we can render the card directly since it doesn't need the Provider
|
|
@@ -20,7 +20,7 @@ var getClosestInlineCardPos = function getClosestInlineCardPos(state, editorView
|
|
|
20
20
|
left: coord.left,
|
|
21
21
|
top: direction === 'up' ? coord.top - lookupPixel : coord.bottom + lookupPixel
|
|
22
22
|
})) === null || _editorView$posAtCoor === void 0 ? void 0 : _editorView$posAtCoor.inside;
|
|
23
|
-
if (nearPos) {
|
|
23
|
+
if (typeof nearPos === 'number' && nearPos > -1) {
|
|
24
24
|
var newNode = state.doc.nodeAt(nearPos);
|
|
25
25
|
if (newNode) {
|
|
26
26
|
if (newNode.type !== inlineCardType || findChildren(parent, function (node) {
|
|
@@ -1,18 +1,29 @@
|
|
|
1
|
+
import _extends from "@babel/runtime/helpers/extends";
|
|
1
2
|
import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
|
|
3
|
+
import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
|
|
2
4
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
5
|
+
var _excluded = ["children", "isSelected", "isVisible", "testId", "url"];
|
|
3
6
|
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
4
7
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
5
8
|
/** @jsx jsx */
|
|
6
9
|
|
|
7
|
-
import React, { useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
10
|
+
import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
|
8
11
|
import { css, jsx } from '@emotion/react';
|
|
12
|
+
import debounce from 'lodash/debounce';
|
|
9
13
|
import { useIntl } from 'react-intl-next';
|
|
10
14
|
import { browser } from '@atlaskit/editor-common/utils';
|
|
11
15
|
import HipchatChevronDownIcon from '@atlaskit/icon/glyph/hipchat/chevron-down';
|
|
12
16
|
import HipchatChevronUpIcon from '@atlaskit/icon/glyph/hipchat/chevron-up';
|
|
13
17
|
import { N20A, N800 } from '@atlaskit/theme/colors';
|
|
14
18
|
import { messages } from '../../messages';
|
|
19
|
+
import { getChildElement, getInlineCardAvailableWidth, getOverlayWidths } from './utils';
|
|
20
|
+
var DEBOUNCE_IN_MS = 5;
|
|
21
|
+
var ESTIMATED_MIN_WIDTH_IN_PX = 16;
|
|
15
22
|
var PADDING_IN_PX = 2;
|
|
23
|
+
var OVERLAY_CLASSNAME = 'ak-editor-card-overlay';
|
|
24
|
+
var OVERLAY_LABEL_CLASSNAME = 'ak-editor-card-overlay-label';
|
|
25
|
+
var OVERLAY_MARKER_CLASSNAME = 'ak-editor-card-overlay-marker';
|
|
26
|
+
var TEXT_NODE_SELECTOR = ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6'].join(',');
|
|
16
27
|
var containerStyles = css({
|
|
17
28
|
position: 'relative',
|
|
18
29
|
lineHeight: 'normal'
|
|
@@ -22,9 +33,10 @@ var linkStyles = css({
|
|
|
22
33
|
textDecoration: 'none'
|
|
23
34
|
});
|
|
24
35
|
var overlayStyles = css({
|
|
36
|
+
// Visibility is set directly on element via style prop
|
|
37
|
+
display: 'inline-flex',
|
|
25
38
|
// Positioning
|
|
26
39
|
position: 'relative',
|
|
27
|
-
display: 'inline-flex',
|
|
28
40
|
flexWrap: 'nowrap',
|
|
29
41
|
alignItems: 'center',
|
|
30
42
|
alignSelf: 'stretch',
|
|
@@ -90,75 +102,136 @@ var markerStyles = css({
|
|
|
90
102
|
});
|
|
91
103
|
var InlineCardOverlay = function InlineCardOverlay(_ref) {
|
|
92
104
|
var children = _ref.children,
|
|
93
|
-
_ref$
|
|
94
|
-
|
|
105
|
+
_ref$isSelected = _ref.isSelected,
|
|
106
|
+
isSelected = _ref$isSelected === void 0 ? false : _ref$isSelected,
|
|
95
107
|
_ref$isVisible = _ref.isVisible,
|
|
96
108
|
isVisible = _ref$isVisible === void 0 ? false : _ref$isVisible,
|
|
97
109
|
_ref$testId = _ref.testId,
|
|
98
110
|
testId = _ref$testId === void 0 ? 'inline-card-overlay' : _ref$testId,
|
|
99
|
-
url = _ref.url
|
|
111
|
+
url = _ref.url,
|
|
112
|
+
props = _objectWithoutProperties(_ref, _excluded);
|
|
100
113
|
var _useState = useState(true),
|
|
101
114
|
_useState2 = _slicedToArray(_useState, 2),
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
var _useState3 = useState(
|
|
115
|
+
showOverlay = _useState2[0],
|
|
116
|
+
setShowOverlay = _useState2[1];
|
|
117
|
+
var _useState3 = useState(true),
|
|
105
118
|
_useState4 = _slicedToArray(_useState3, 2),
|
|
106
|
-
|
|
107
|
-
|
|
119
|
+
showLabel = _useState4[0],
|
|
120
|
+
setShowLabel = _useState4[1];
|
|
121
|
+
var _useState5 = useState(0),
|
|
122
|
+
_useState6 = _slicedToArray(_useState5, 2),
|
|
123
|
+
overlayWidth = _useState6[0],
|
|
124
|
+
setOverlayWidth = _useState6[1];
|
|
125
|
+
var maxOverlayWidth = useRef(0);
|
|
126
|
+
var minOverlayWidth = useRef(ESTIMATED_MIN_WIDTH_IN_PX);
|
|
127
|
+
var parentWidth = useRef(0);
|
|
108
128
|
var containerRef = useRef(null);
|
|
109
|
-
var
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
setShowLabel(true);
|
|
129
|
+
var setVisibility = useCallback(function () {
|
|
130
|
+
if (!containerRef.current || !maxOverlayWidth.current) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
var marker = getChildElement(containerRef, ".".concat(OVERLAY_MARKER_CLASSNAME));
|
|
134
|
+
if (!marker) {
|
|
116
135
|
return;
|
|
117
136
|
}
|
|
118
137
|
try {
|
|
119
|
-
|
|
120
|
-
//
|
|
121
|
-
|
|
122
|
-
var
|
|
123
|
-
var availableWidth = end - start - PADDING_IN_PX;
|
|
124
|
-
|
|
125
|
-
// Get overlay width and label width
|
|
126
|
-
var _overlayWidth = (_overlayRef$current$g = overlayRef === null || overlayRef === void 0 || (_overlayRef$current = overlayRef.current) === null || _overlayRef$current === void 0 || (_overlayRef$current = _overlayRef$current.getBoundingClientRect()) === null || _overlayRef$current === void 0 ? void 0 : _overlayRef$current.width) !== null && _overlayRef$current$g !== void 0 ? _overlayRef$current$g : 0;
|
|
138
|
+
// Get the width of the available space to display overlay.
|
|
139
|
+
// This is the width of the inline link itself. If the inline
|
|
140
|
+
// is wrapped to the next line, this is width of the last line.
|
|
141
|
+
var availableWidth = getInlineCardAvailableWidth(containerRef.current, marker) - PADDING_IN_PX;
|
|
127
142
|
|
|
128
|
-
//
|
|
129
|
-
var
|
|
130
|
-
|
|
143
|
+
// If available width is less than the min width of overlay, don't show overlay.
|
|
144
|
+
var canShowOverlay = availableWidth > minOverlayWidth.current;
|
|
145
|
+
setShowOverlay(canShowOverlay);
|
|
146
|
+
if (!canShowOverlay) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
131
149
|
|
|
132
|
-
//
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
var newOverlayWidth = shouldShowLabel ? _overlayWidth : _overlayWidth - labelWidth;
|
|
137
|
-
setOverlayWidth(newOverlayWidth);
|
|
150
|
+
// Otherwise, check if overlay can be show in full context with label and icon.
|
|
151
|
+
var canShowLabel = availableWidth > maxOverlayWidth.current + PADDING_IN_PX;
|
|
152
|
+
setShowLabel(canShowLabel);
|
|
153
|
+
setOverlayWidth(canShowLabel ? maxOverlayWidth.current : minOverlayWidth.current);
|
|
138
154
|
} catch (_unused) {
|
|
139
|
-
// If something goes wrong,
|
|
140
|
-
|
|
141
|
-
|
|
155
|
+
// If something goes wrong, hide the overlay all together.
|
|
156
|
+
setShowOverlay(false);
|
|
157
|
+
}
|
|
158
|
+
}, []);
|
|
159
|
+
useLayoutEffect(function () {
|
|
160
|
+
// Using useLayoutEffect here.
|
|
161
|
+
// 1) We want all to be able to determine whether to display label before
|
|
162
|
+
// the overlay becomes visible.
|
|
163
|
+
// 2) We need to wait for the refs to be assigned to be able to do determine
|
|
164
|
+
// the width of the overlay.
|
|
165
|
+
if (!containerRef.current) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (!maxOverlayWidth.current) {
|
|
169
|
+
var overlay = getChildElement(containerRef, ".".concat(OVERLAY_CLASSNAME));
|
|
170
|
+
var _label = getChildElement(containerRef, ".".concat(OVERLAY_LABEL_CLASSNAME));
|
|
171
|
+
if (overlay && _label) {
|
|
172
|
+
// Set overlay max (label + icon) and min (icon only) width.
|
|
173
|
+
// This should run only once.
|
|
174
|
+
var _getOverlayWidths = getOverlayWidths(overlay, _label),
|
|
175
|
+
max = _getOverlayWidths.max,
|
|
176
|
+
min = _getOverlayWidths.min;
|
|
177
|
+
maxOverlayWidth.current = max;
|
|
178
|
+
minOverlayWidth.current = min;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (isVisible) {
|
|
182
|
+
setVisibility();
|
|
183
|
+
}
|
|
184
|
+
}, [setVisibility, isVisible]);
|
|
185
|
+
useEffect(function () {
|
|
186
|
+
var _containerRef$current;
|
|
187
|
+
// Find the closest block parent to observe size change
|
|
188
|
+
var parent = containerRef === null || containerRef === void 0 || (_containerRef$current = containerRef.current) === null || _containerRef$current === void 0 ? void 0 : _containerRef$current.closest(TEXT_NODE_SELECTOR);
|
|
189
|
+
if (!parent) {
|
|
190
|
+
return;
|
|
142
191
|
}
|
|
143
|
-
|
|
192
|
+
var updateOverlay = debounce(function (entries) {
|
|
193
|
+
var _entries$;
|
|
194
|
+
if (!isVisible) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
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;
|
|
198
|
+
if (!size) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
if (!parentWidth.current) {
|
|
202
|
+
parentWidth.current = size;
|
|
203
|
+
}
|
|
204
|
+
if (parentWidth.current === size) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
parentWidth.current = size;
|
|
208
|
+
setVisibility();
|
|
209
|
+
}, DEBOUNCE_IN_MS);
|
|
210
|
+
var observer = new ResizeObserver(updateOverlay);
|
|
211
|
+
observer.observe(parent);
|
|
212
|
+
return function () {
|
|
213
|
+
observer.disconnect();
|
|
214
|
+
};
|
|
215
|
+
}, [isVisible, setVisibility]);
|
|
144
216
|
var intl = useIntl();
|
|
145
217
|
var label = intl.formatMessage(messages.inlineOverlay);
|
|
146
218
|
var icon = useMemo(function () {
|
|
147
|
-
var IconComponent =
|
|
219
|
+
var IconComponent = isSelected ? HipchatChevronUpIcon : HipchatChevronDownIcon;
|
|
148
220
|
return jsx(IconComponent, {
|
|
149
221
|
label: label,
|
|
150
222
|
size: "small",
|
|
151
|
-
testId: "".concat(testId, "-").concat(
|
|
223
|
+
testId: "".concat(testId, "-").concat(isSelected ? 'open' : 'close')
|
|
152
224
|
});
|
|
153
|
-
}, [
|
|
154
|
-
return jsx("span", {
|
|
225
|
+
}, [isSelected, label, testId]);
|
|
226
|
+
return jsx("span", _extends({}, props, {
|
|
155
227
|
css: containerStyles,
|
|
156
228
|
ref: containerRef
|
|
157
|
-
}, children, isVisible && jsx(React.Fragment, null, jsx("span", {
|
|
229
|
+
}), children, isVisible && showOverlay && jsx(React.Fragment, null, jsx("span", {
|
|
158
230
|
"aria-hidden": "true",
|
|
159
|
-
|
|
160
|
-
|
|
231
|
+
className: OVERLAY_MARKER_CLASSNAME,
|
|
232
|
+
css: markerStyles
|
|
161
233
|
}), jsx("a", {
|
|
234
|
+
className: OVERLAY_CLASSNAME,
|
|
162
235
|
css: [overlayStyles, browser.safari && safariOverlayStyles],
|
|
163
236
|
style: {
|
|
164
237
|
marginLeft: -overlayWidth
|
|
@@ -168,11 +241,11 @@ var InlineCardOverlay = function InlineCardOverlay(_ref) {
|
|
|
168
241
|
onClick: function onClick(e) {
|
|
169
242
|
return e.preventDefault();
|
|
170
243
|
},
|
|
171
|
-
|
|
244
|
+
tabIndex: -1
|
|
172
245
|
}, showLabel && jsx("span", {
|
|
246
|
+
className: OVERLAY_LABEL_CLASSNAME,
|
|
173
247
|
css: textStyles,
|
|
174
|
-
"data-testid": "".concat(testId, "-label")
|
|
175
|
-
ref: labelRef
|
|
248
|
+
"data-testid": "".concat(testId, "-label")
|
|
176
249
|
}, label), jsx("span", {
|
|
177
250
|
css: iconStyles
|
|
178
251
|
}, icon))));
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Find a child element inside a ref.
|
|
3
|
+
*/
|
|
4
|
+
export var getChildElement = function getChildElement(ref, selector) {
|
|
5
|
+
return ref.current ? ref.current.querySelector(selector) : undefined;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Get the available width of the inline card.
|
|
10
|
+
* (Mainly here to make the component unit testable.)
|
|
11
|
+
*/
|
|
12
|
+
export var getInlineCardAvailableWidth = function getInlineCardAvailableWidth(startEl, endEl) {
|
|
13
|
+
var start = startEl.getBoundingClientRect().left;
|
|
14
|
+
var end = endEl.getBoundingClientRect().left;
|
|
15
|
+
return end - start;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Get max and min width of an overlay.
|
|
20
|
+
* (Mainly here to make the component unit testable.)
|
|
21
|
+
*/
|
|
22
|
+
export var getOverlayWidths = function getOverlayWidths(overlayEl, labelEl) {
|
|
23
|
+
var max = overlayEl.getBoundingClientRect().width;
|
|
24
|
+
var min = max - labelEl.getBoundingClientRect().width;
|
|
25
|
+
return {
|
|
26
|
+
max: max,
|
|
27
|
+
min: min
|
|
28
|
+
};
|
|
29
|
+
};
|