@atlaskit/editor-plugin-card 16.10.3 → 16.11.1
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 +17 -0
- package/dist/cjs/cardPlugin.js +19 -2
- package/dist/cjs/pm-plugins/doc.js +2 -2
- package/dist/cjs/ui/PasteDisplayAsMenu.compiled.css +31 -0
- package/dist/cjs/ui/PasteDisplayAsMenu.js +368 -0
- package/dist/cjs/ui/currentPastedSmartLink.js +52 -0
- package/dist/cjs/ui/pasteDisplayAsUtils.js +30 -0
- package/dist/es2019/cardPlugin.js +17 -2
- package/dist/es2019/pm-plugins/doc.js +2 -2
- package/dist/es2019/ui/PasteDisplayAsMenu.compiled.css +31 -0
- package/dist/es2019/ui/PasteDisplayAsMenu.js +366 -0
- package/dist/es2019/ui/currentPastedSmartLink.js +46 -0
- package/dist/es2019/ui/pasteDisplayAsUtils.js +24 -0
- package/dist/esm/cardPlugin.js +19 -2
- package/dist/esm/pm-plugins/doc.js +2 -2
- package/dist/esm/ui/PasteDisplayAsMenu.compiled.css +31 -0
- package/dist/esm/ui/PasteDisplayAsMenu.js +359 -0
- package/dist/esm/ui/currentPastedSmartLink.js +46 -0
- package/dist/esm/ui/pasteDisplayAsUtils.js +24 -0
- package/dist/types/cardPluginType.d.ts +3 -1
- package/dist/types/types/index.d.ts +1 -0
- package/dist/types/ui/PasteDisplayAsMenu.d.ts +25 -0
- package/dist/types/ui/currentPastedSmartLink.d.ts +2 -0
- package/dist/types/ui/pasteDisplayAsUtils.d.ts +7 -0
- package/dist/types-ts4.5/cardPluginType.d.ts +3 -1
- package/dist/types-ts4.5/types/index.d.ts +1 -0
- package/dist/types-ts4.5/ui/PasteDisplayAsMenu.d.ts +25 -0
- package/dist/types-ts4.5/ui/currentPastedSmartLink.d.ts +2 -0
- package/dist/types-ts4.5/ui/pasteDisplayAsUtils.d.ts +12 -0
- package/package.json +9 -5
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
|
|
2
|
+
._2rko1qi0{border-radius:var(--ds-radius-medium,6px)}._189ee4h9{border-width:var(--ds-border-width,1px)}
|
|
3
|
+
._1dqonqa1{border-style:solid}
|
|
4
|
+
._1h6d1l7x{border-color:var(--ds-border,#0b120e24)}
|
|
5
|
+
._1h6dbk0g{border-color:var(--ds-border-disabled,#0515240f)}
|
|
6
|
+
._1h6dq98m{border-color:var(--ds-border-selected,#1868db)}
|
|
7
|
+
._18u0u2gc{margin-left:var(--ds-space-100,8px)}
|
|
8
|
+
._19bv1b66{padding-left:var(--ds-space-050,4px)}
|
|
9
|
+
._19pku2gc{margin-top:var(--ds-space-100,8px)}
|
|
10
|
+
._1bah1b1v{justify-content:space-around}
|
|
11
|
+
._1bah1h6o{justify-content:center}
|
|
12
|
+
._1bsb1osq{width:100%}
|
|
13
|
+
._1e0c1txw{display:flex}
|
|
14
|
+
._2hwxu2gc{margin-right:var(--ds-space-100,8px)}
|
|
15
|
+
._4cvr1h6o{align-items:center}
|
|
16
|
+
._4t3izwfg{height:2pc}
|
|
17
|
+
._bfhk15s3{background-color:var(--ds-background-selected,#e9f2fe)}
|
|
18
|
+
._bfhk187o{background-color:var(--ds-background-disabled,#0515240f)}
|
|
19
|
+
._bfhkhfxm{background-color:var(--ds-surface-sunken,#f8f8f8)}
|
|
20
|
+
._bfhksm61{background-color:var(--ds-background-neutral-subtle,#00000000)}
|
|
21
|
+
._bfhkvuon{background-color:var(--ds-surface,#fff)}
|
|
22
|
+
._ca0q1b66{padding-top:var(--ds-space-050,4px)}
|
|
23
|
+
._n3td1b66{padding-bottom:var(--ds-space-050,4px)}
|
|
24
|
+
._otyru2gc{margin-bottom:var(--ds-space-100,8px)}
|
|
25
|
+
._syaz1gmx{color:var(--ds-text-disabled,#080f214a)}
|
|
26
|
+
._syazi7uo{color:var(--ds-text,#292a2e)}
|
|
27
|
+
._u5f31b66{padding-right:var(--ds-space-050,4px)}
|
|
28
|
+
._irr31dpa:hover{background-color:var(--ds-background-neutral-subtle-hovered,#0515240f)}
|
|
29
|
+
._irr3ufnl:hover{background-color:var(--ds-background-selected-hovered,#cfe1fd)}
|
|
30
|
+
._1di6fcek:active{background-color:var(--ds-background-neutral-subtle-pressed,#0b120e24)}
|
|
31
|
+
._1di6nozp:active{background-color:var(--ds-background-selected-pressed,#8fb8f6)}
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
/* PasteDisplayAsMenu.tsx generated by @compiled/babel-plugin v0.39.1 */
|
|
2
|
+
import "./PasteDisplayAsMenu.compiled.css";
|
|
3
|
+
import { ax, ix } from "@compiled/react/runtime";
|
|
4
|
+
import React, { useCallback, useEffect, useRef } from 'react';
|
|
5
|
+
import { useIntl } from 'react-intl';
|
|
6
|
+
import { cx } from '@atlaskit/css';
|
|
7
|
+
import { appearancePropsMap } from '@atlaskit/editor-common/card';
|
|
8
|
+
import { useSharedPluginStateWithSelector } from '@atlaskit/editor-common/hooks';
|
|
9
|
+
import { getActiveLinkMark } from '@atlaskit/editor-common/link';
|
|
10
|
+
import { PASTE_MENU, useEditorToolbar } from '@atlaskit/editor-common/toolbar';
|
|
11
|
+
import { isSupportedInParent } from '@atlaskit/editor-common/utils';
|
|
12
|
+
import { Fragment } from '@atlaskit/editor-prosemirror/model';
|
|
13
|
+
import { NodeSelection, TextSelection } from '@atlaskit/editor-prosemirror/state';
|
|
14
|
+
import { ToolbarDropdownItemSection, useToolbarDropdownMenu } from '@atlaskit/editor-toolbar';
|
|
15
|
+
import MinusIcon from '@atlaskit/icon/core/minus';
|
|
16
|
+
import SmartLinkCardIcon from '@atlaskit/icon/core/smart-link-card';
|
|
17
|
+
import SmartLinkEmbedIcon from '@atlaskit/icon/core/smart-link-embed';
|
|
18
|
+
import SmartLinkInlineIcon from '@atlaskit/icon/core/smart-link-inline';
|
|
19
|
+
import { useSmartCardContext } from '@atlaskit/link-provider';
|
|
20
|
+
import { Box, Flex, Pressable } from '@atlaskit/primitives/compiled';
|
|
21
|
+
import { changeSelectedCardToLink, setSelectedCardAppearance } from '../pm-plugins/doc';
|
|
22
|
+
import { getSingleSmartLinkUrlFromSlice } from './currentPastedSmartLink';
|
|
23
|
+
import { getCardAtPasteRange } from './pasteDisplayAsUtils';
|
|
24
|
+
export const SMART_LINK_DISPLAY_AS_PASTE_MENU_SECTION_KEY = 'smart-link-display-as-paste-menu-section';
|
|
25
|
+
|
|
26
|
+
// `pasteOptionsToolbarPlugin` is intentionally NOT declared in `CardPluginDependencies`
|
|
27
|
+
// because doing so introduces a runtime package cycle
|
|
28
|
+
// (editor-plugin-card -> editor-plugin-paste-options-toolbar -> editor-plugin-paste
|
|
29
|
+
// -> editor-plugin-card) AND a TS2719 "two different types with this name exist" error
|
|
30
|
+
// at downstream consumers (e.g. Jira's `EditorAfterBanner.tsx`). We instead augment the
|
|
31
|
+
// `api` shape locally at the call sites that need to read the plugin's shared state.
|
|
32
|
+
|
|
33
|
+
const styles = {
|
|
34
|
+
appearanceBox: "_2rko1qi0 _bfhkhfxm _19pku2gc _2hwxu2gc _otyru2gc _18u0u2gc _ca0q1b66 _u5f31b66 _n3td1b66 _19bv1b66",
|
|
35
|
+
iconWrapper: "_bfhkvuon _1e0c1txw _1bsb1osq _1bah1b1v",
|
|
36
|
+
iconButton: "_2rko1qi0 _1h6d1l7x _1dqonqa1 _189ee4h9 _4cvr1h6o _syazi7uo _1e0c1txw _4t3izwfg _1bah1h6o _1bsb1osq _bfhksm61 _irr31dpa _1di6fcek",
|
|
37
|
+
iconButtonSelected: "_2rko1qi0 _1h6dq98m _1dqonqa1 _189ee4h9 _4cvr1h6o _bfhk15s3 _syazi7uo _1e0c1txw _4t3izwfg _1bah1h6o _1bsb1osq _irr3ufnl _1di6nozp",
|
|
38
|
+
iconButtonDisabled: "_2rko1qi0 _1h6dbk0g _1dqonqa1 _189ee4h9 _4cvr1h6o _bfhk187o _syaz1gmx _1e0c1txw _4t3izwfg _1bah1h6o _1bsb1osq"
|
|
39
|
+
};
|
|
40
|
+
const AppearanceOptionIconButton = ({
|
|
41
|
+
appearance,
|
|
42
|
+
currentAppearance,
|
|
43
|
+
isDisabled,
|
|
44
|
+
label,
|
|
45
|
+
Icon,
|
|
46
|
+
onClick
|
|
47
|
+
}) => {
|
|
48
|
+
return /*#__PURE__*/React.createElement(Box, {
|
|
49
|
+
xcss: styles.iconWrapper
|
|
50
|
+
}, /*#__PURE__*/React.createElement(Pressable, {
|
|
51
|
+
xcss: cx(styles.iconButton, isDisabled && styles.iconButtonDisabled, !isDisabled && currentAppearance === appearance && styles.iconButtonSelected),
|
|
52
|
+
"aria-label": label,
|
|
53
|
+
"aria-pressed": currentAppearance === appearance,
|
|
54
|
+
isDisabled: isDisabled,
|
|
55
|
+
onClick: onClick
|
|
56
|
+
}, /*#__PURE__*/React.createElement(Icon, {
|
|
57
|
+
label: label
|
|
58
|
+
})));
|
|
59
|
+
};
|
|
60
|
+
const InlineAppearanceIconButton = ({
|
|
61
|
+
currentAppearance,
|
|
62
|
+
isDisabled,
|
|
63
|
+
label,
|
|
64
|
+
onClick
|
|
65
|
+
}) => /*#__PURE__*/React.createElement(AppearanceOptionIconButton, {
|
|
66
|
+
appearance: "inline",
|
|
67
|
+
currentAppearance: currentAppearance,
|
|
68
|
+
isDisabled: isDisabled,
|
|
69
|
+
label: label,
|
|
70
|
+
Icon: SmartLinkInlineIcon,
|
|
71
|
+
onClick: onClick
|
|
72
|
+
});
|
|
73
|
+
const BlockAppearanceIconButton = ({
|
|
74
|
+
currentAppearance,
|
|
75
|
+
isDisabled,
|
|
76
|
+
label,
|
|
77
|
+
onClick
|
|
78
|
+
}) => /*#__PURE__*/React.createElement(AppearanceOptionIconButton, {
|
|
79
|
+
appearance: "block",
|
|
80
|
+
currentAppearance: currentAppearance,
|
|
81
|
+
isDisabled: isDisabled,
|
|
82
|
+
label: label,
|
|
83
|
+
Icon: SmartLinkCardIcon,
|
|
84
|
+
onClick: onClick
|
|
85
|
+
});
|
|
86
|
+
const EmbedAppearanceIconButton = ({
|
|
87
|
+
currentAppearance,
|
|
88
|
+
isDisabled,
|
|
89
|
+
label,
|
|
90
|
+
onClick
|
|
91
|
+
}) => /*#__PURE__*/React.createElement(AppearanceOptionIconButton, {
|
|
92
|
+
appearance: "embed",
|
|
93
|
+
currentAppearance: currentAppearance,
|
|
94
|
+
isDisabled: isDisabled,
|
|
95
|
+
label: label,
|
|
96
|
+
Icon: SmartLinkEmbedIcon,
|
|
97
|
+
onClick: onClick
|
|
98
|
+
});
|
|
99
|
+
const getCurrentPastedSlice = api => {
|
|
100
|
+
var _apiWithPaste$paste, _pasteState$lastConte;
|
|
101
|
+
const apiWithPaste = api;
|
|
102
|
+
const pasteState = apiWithPaste === null || apiWithPaste === void 0 ? void 0 : (_apiWithPaste$paste = apiWithPaste.paste) === null || _apiWithPaste$paste === void 0 ? void 0 : _apiWithPaste$paste.sharedState.currentState();
|
|
103
|
+
const slice = pasteState === null || pasteState === void 0 ? void 0 : (_pasteState$lastConte = pasteState.lastContentPasted) === null || _pasteState$lastConte === void 0 ? void 0 : _pasteState$lastConte.pastedSlice;
|
|
104
|
+
return slice;
|
|
105
|
+
};
|
|
106
|
+
const getCardUrlAtPasteRange = ({
|
|
107
|
+
editorView,
|
|
108
|
+
pasteStartPos,
|
|
109
|
+
pasteEndPos
|
|
110
|
+
}) => {
|
|
111
|
+
var _editorView$state$doc, _maybeAttrs$url, _maybeAttrs$data;
|
|
112
|
+
const cardAtPasteRange = getCardAtPasteRange(editorView.state, pasteStartPos, pasteEndPos);
|
|
113
|
+
const maybeAttrs = cardAtPasteRange ? (_editorView$state$doc = editorView.state.doc.nodeAt(cardAtPasteRange.pos)) === null || _editorView$state$doc === void 0 ? void 0 : _editorView$state$doc.attrs : undefined;
|
|
114
|
+
const maybeUrl = (_maybeAttrs$url = maybeAttrs === null || maybeAttrs === void 0 ? void 0 : maybeAttrs.url) !== null && _maybeAttrs$url !== void 0 ? _maybeAttrs$url : maybeAttrs === null || maybeAttrs === void 0 ? void 0 : (_maybeAttrs$data = maybeAttrs.data) === null || _maybeAttrs$data === void 0 ? void 0 : _maybeAttrs$data.url;
|
|
115
|
+
return typeof maybeUrl === 'string' ? maybeUrl : undefined;
|
|
116
|
+
};
|
|
117
|
+
export const setAppearanceSelection = ({
|
|
118
|
+
editorView,
|
|
119
|
+
pasteStartPos,
|
|
120
|
+
pasteEndPos,
|
|
121
|
+
targetPos
|
|
122
|
+
}) => {
|
|
123
|
+
const {
|
|
124
|
+
state,
|
|
125
|
+
dispatch
|
|
126
|
+
} = editorView;
|
|
127
|
+
const maxPos = state.doc.content.size;
|
|
128
|
+
const clampedStart = Math.max(0, Math.min(pasteStartPos, maxPos));
|
|
129
|
+
const clampedEnd = Math.max(0, Math.min(pasteEndPos, maxPos));
|
|
130
|
+
const from = Math.min(clampedStart, clampedEnd);
|
|
131
|
+
const to = Math.max(clampedStart, clampedEnd);
|
|
132
|
+
const isTargetPosInBounds = targetPos !== undefined && targetPos >= 0 && targetPos <= state.doc.content.size;
|
|
133
|
+
try {
|
|
134
|
+
const nextSelection = isTargetPosInBounds && targetPos !== undefined ? NodeSelection.create(state.doc, targetPos) : TextSelection.create(state.doc, from, to);
|
|
135
|
+
const tr = state.tr.setSelection(nextSelection);
|
|
136
|
+
dispatch(tr);
|
|
137
|
+
return true;
|
|
138
|
+
} catch {
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
export const getFirstLinkRangeInSelection = editorView => {
|
|
143
|
+
const {
|
|
144
|
+
state
|
|
145
|
+
} = editorView;
|
|
146
|
+
const linkMarkType = state.schema.marks.link;
|
|
147
|
+
if (!linkMarkType) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
let firstLinkRange;
|
|
151
|
+
state.doc.nodesBetween(state.selection.from, state.selection.to, (node, pos) => {
|
|
152
|
+
if (firstLinkRange || !node.isText) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (linkMarkType.isInSet(node.marks)) {
|
|
156
|
+
firstLinkRange = {
|
|
157
|
+
from: pos,
|
|
158
|
+
to: pos + node.nodeSize
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
return firstLinkRange;
|
|
163
|
+
};
|
|
164
|
+
export const normalizeSelectionToLinkRangeForUrlAppearance = ({
|
|
165
|
+
editorView,
|
|
166
|
+
targetPos
|
|
167
|
+
}) => {
|
|
168
|
+
if (targetPos !== undefined || getActiveLinkMark(editorView.state)) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
const firstLinkRange = getFirstLinkRangeInSelection(editorView);
|
|
172
|
+
if (!firstLinkRange) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const linkRangeSelectionTr = editorView.state.tr.setSelection(TextSelection.create(editorView.state.doc, firstLinkRange.from, firstLinkRange.to));
|
|
176
|
+
editorView.dispatch(linkRangeSelectionTr);
|
|
177
|
+
};
|
|
178
|
+
const PasteDisplayAsMenuHorizontalView = ({
|
|
179
|
+
api,
|
|
180
|
+
allowBlockCards,
|
|
181
|
+
allowEmbeds
|
|
182
|
+
}) => {
|
|
183
|
+
var _smartCardContext$val, _smartCardContext$val2, _smartCardContext$val3, _getCardAtPasteRange$, _getCardAtPasteRange, _smartCardContext$val4;
|
|
184
|
+
const intl = useIntl();
|
|
185
|
+
const {
|
|
186
|
+
editorView
|
|
187
|
+
} = useEditorToolbar();
|
|
188
|
+
const toolbarDropdownMenu = useToolbarDropdownMenu();
|
|
189
|
+
const smartCardContext = useSmartCardContext();
|
|
190
|
+
const frameRef = useRef(null);
|
|
191
|
+
const isApplyingRef = useRef(false);
|
|
192
|
+
const apiWithPasteOptionsToolbar = api;
|
|
193
|
+
const pasteRange = useSharedPluginStateWithSelector(apiWithPasteOptionsToolbar, ['pasteOptionsToolbarPlugin'], states => {
|
|
194
|
+
const pluginState = states.pasteOptionsToolbarPluginState;
|
|
195
|
+
return pluginState ? {
|
|
196
|
+
pasteEndPos: pluginState.pasteEndPos,
|
|
197
|
+
pasteStartPos: pluginState.pasteStartPos
|
|
198
|
+
} : undefined;
|
|
199
|
+
});
|
|
200
|
+
// Subscribe to card state so the menu re-renders when resolved card metadata updates.
|
|
201
|
+
useSharedPluginStateWithSelector(api, ['card'], states => {
|
|
202
|
+
return states.cardState;
|
|
203
|
+
});
|
|
204
|
+
const pastedLinkUrlFromSlice = getSingleSmartLinkUrlFromSlice(getCurrentPastedSlice(api));
|
|
205
|
+
const pastedLinkUrlFromCard = editorView && pasteRange ? getCardUrlAtPasteRange({
|
|
206
|
+
editorView,
|
|
207
|
+
pasteStartPos: pasteRange.pasteStartPos,
|
|
208
|
+
pasteEndPos: pasteRange.pasteEndPos
|
|
209
|
+
}) : undefined;
|
|
210
|
+
const pastedLinkUrl = pastedLinkUrlFromSlice !== null && pastedLinkUrlFromSlice !== void 0 ? pastedLinkUrlFromSlice : pastedLinkUrlFromCard;
|
|
211
|
+
const pastedLinkUrlState = pastedLinkUrl ? (_smartCardContext$val = smartCardContext.value) === null || _smartCardContext$val === void 0 ? void 0 : (_smartCardContext$val2 = _smartCardContext$val.store) === null || _smartCardContext$val2 === void 0 ? void 0 : (_smartCardContext$val3 = _smartCardContext$val2.getState()) === null || _smartCardContext$val3 === void 0 ? void 0 : _smartCardContext$val3[pastedLinkUrl] : undefined;
|
|
212
|
+
const hasResolvedSmartLinkData = Boolean(pastedLinkUrlState === null || pastedLinkUrlState === void 0 ? void 0 : pastedLinkUrlState.details);
|
|
213
|
+
const currentAppearance = editorView && pasteRange ? (_getCardAtPasteRange$ = (_getCardAtPasteRange = getCardAtPasteRange(editorView.state, pasteRange.pasteStartPos, pasteRange.pasteEndPos)) === null || _getCardAtPasteRange === void 0 ? void 0 : _getCardAtPasteRange.appearance) !== null && _getCardAtPasteRange$ !== void 0 ? _getCardAtPasteRange$ : 'url' : undefined;
|
|
214
|
+
const handleClick = useCallback(appearance => () => {
|
|
215
|
+
if (!editorView || !pasteRange || !pastedLinkUrl || !currentAppearance || isApplyingRef.current) {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
isApplyingRef.current = true;
|
|
219
|
+
const {
|
|
220
|
+
state,
|
|
221
|
+
dispatch
|
|
222
|
+
} = editorView;
|
|
223
|
+
const {
|
|
224
|
+
pasteStartPos,
|
|
225
|
+
pasteEndPos
|
|
226
|
+
} = pasteRange;
|
|
227
|
+
const cardAtPasteRange = getCardAtPasteRange(state, pasteStartPos, pasteEndPos);
|
|
228
|
+
if (appearance === 'url') {
|
|
229
|
+
if (cardAtPasteRange) {
|
|
230
|
+
var _state$doc$nodeAt, _api$analytics;
|
|
231
|
+
changeSelectedCardToLink(pastedLinkUrl, pastedLinkUrl, true, (_state$doc$nodeAt = state.doc.nodeAt(cardAtPasteRange.pos)) !== null && _state$doc$nodeAt !== void 0 ? _state$doc$nodeAt : undefined, cardAtPasteRange.pos, api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions)(state, dispatch, editorView);
|
|
232
|
+
}
|
|
233
|
+
toolbarDropdownMenu === null || toolbarDropdownMenu === void 0 ? void 0 : toolbarDropdownMenu.closeMenu(null);
|
|
234
|
+
isApplyingRef.current = false;
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const targetPos = cardAtPasteRange === null || cardAtPasteRange === void 0 ? void 0 : cardAtPasteRange.pos;
|
|
238
|
+
if (targetPos !== undefined && targetPos < pasteStartPos) {
|
|
239
|
+
isApplyingRef.current = false;
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
const didApplySelection = setAppearanceSelection({
|
|
243
|
+
editorView,
|
|
244
|
+
pasteStartPos,
|
|
245
|
+
pasteEndPos,
|
|
246
|
+
targetPos
|
|
247
|
+
});
|
|
248
|
+
if (!didApplySelection) {
|
|
249
|
+
toolbarDropdownMenu === null || toolbarDropdownMenu === void 0 ? void 0 : toolbarDropdownMenu.closeMenu(null);
|
|
250
|
+
isApplyingRef.current = false;
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
normalizeSelectionToLinkRangeForUrlAppearance({
|
|
254
|
+
editorView,
|
|
255
|
+
targetPos
|
|
256
|
+
});
|
|
257
|
+
frameRef.current = requestAnimationFrame(() => {
|
|
258
|
+
var _api$analytics2;
|
|
259
|
+
frameRef.current = null;
|
|
260
|
+
setSelectedCardAppearance(appearance, api === null || api === void 0 ? void 0 : (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : _api$analytics2.actions)(editorView.state, editorView.dispatch, editorView);
|
|
261
|
+
toolbarDropdownMenu === null || toolbarDropdownMenu === void 0 ? void 0 : toolbarDropdownMenu.closeMenu(null);
|
|
262
|
+
isApplyingRef.current = false;
|
|
263
|
+
});
|
|
264
|
+
}, [api, currentAppearance, editorView, pasteRange, pastedLinkUrl, toolbarDropdownMenu]);
|
|
265
|
+
useEffect(() => {
|
|
266
|
+
return () => {
|
|
267
|
+
if (frameRef.current !== null) {
|
|
268
|
+
cancelAnimationFrame(frameRef.current);
|
|
269
|
+
}
|
|
270
|
+
isApplyingRef.current = false;
|
|
271
|
+
};
|
|
272
|
+
}, []);
|
|
273
|
+
if (!editorView || !pasteRange || !pastedLinkUrl || !currentAppearance) {
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
const preview = allowEmbeds && pastedLinkUrl && ((_smartCardContext$val4 = smartCardContext.value) === null || _smartCardContext$val4 === void 0 ? void 0 : _smartCardContext$val4.extractors.getPreview(pastedLinkUrl, 'web'));
|
|
277
|
+
const blockCardNodeType = editorView.state.schema.nodes.blockCard;
|
|
278
|
+
const embedCardNodeType = editorView.state.schema.nodes.embedCard;
|
|
279
|
+
const isBlockSupportedFromAppearanceContext = allowBlockCards && blockCardNodeType && isSupportedInParent(editorView.state, Fragment.from(blockCardNodeType.createChecked({})), currentAppearance === 'url' || currentAppearance === 'inline' ? undefined : currentAppearance);
|
|
280
|
+
const isEmbedSupportedFromAppearanceContext = allowEmbeds && preview && embedCardNodeType && isSupportedInParent(editorView.state, Fragment.from(embedCardNodeType.createChecked({})), currentAppearance === 'url' || currentAppearance === 'inline' ? undefined : currentAppearance);
|
|
281
|
+
const isSmartLinkConvertible = hasResolvedSmartLinkData;
|
|
282
|
+
const isBlockSupportedFromSelection = allowBlockCards && blockCardNodeType && isSupportedInParent(editorView.state, Fragment.from(blockCardNodeType.createChecked({})), undefined);
|
|
283
|
+
const isEmbedSupportedFromSelection = allowEmbeds && preview && embedCardNodeType && isSupportedInParent(editorView.state, Fragment.from(embedCardNodeType.createChecked({})), undefined);
|
|
284
|
+
const isBlockSupported = Boolean(isBlockSupportedFromAppearanceContext || isBlockSupportedFromSelection);
|
|
285
|
+
const isEmbedSupported = Boolean(isEmbedSupportedFromAppearanceContext || isEmbedSupportedFromSelection);
|
|
286
|
+
return /*#__PURE__*/React.createElement(Flex, {
|
|
287
|
+
xcss: styles.appearanceBox,
|
|
288
|
+
gap: "space.050"
|
|
289
|
+
}, /*#__PURE__*/React.createElement(AppearanceOptionIconButton, {
|
|
290
|
+
appearance: "url",
|
|
291
|
+
currentAppearance: currentAppearance,
|
|
292
|
+
isDisabled: false,
|
|
293
|
+
label: intl.formatMessage(appearancePropsMap.url.title),
|
|
294
|
+
Icon: MinusIcon,
|
|
295
|
+
onClick: handleClick('url')
|
|
296
|
+
}), /*#__PURE__*/React.createElement(InlineAppearanceIconButton, {
|
|
297
|
+
currentAppearance: currentAppearance,
|
|
298
|
+
isDisabled: !isSmartLinkConvertible,
|
|
299
|
+
label: intl.formatMessage(appearancePropsMap.inline.title),
|
|
300
|
+
onClick: handleClick('inline')
|
|
301
|
+
}), /*#__PURE__*/React.createElement(BlockAppearanceIconButton, {
|
|
302
|
+
currentAppearance: currentAppearance,
|
|
303
|
+
isDisabled: !isSmartLinkConvertible || !isBlockSupported,
|
|
304
|
+
label: intl.formatMessage(appearancePropsMap.block.title),
|
|
305
|
+
onClick: handleClick('block')
|
|
306
|
+
}), /*#__PURE__*/React.createElement(EmbedAppearanceIconButton, {
|
|
307
|
+
currentAppearance: currentAppearance,
|
|
308
|
+
isDisabled: !isEmbedSupported,
|
|
309
|
+
label: intl.formatMessage(appearancePropsMap.embed.title),
|
|
310
|
+
onClick: handleClick('embed')
|
|
311
|
+
}));
|
|
312
|
+
};
|
|
313
|
+
const PasteDisplayAsMenuSection = ({
|
|
314
|
+
api,
|
|
315
|
+
allowBlockCards,
|
|
316
|
+
allowEmbeds
|
|
317
|
+
}) => {
|
|
318
|
+
const intl = useIntl();
|
|
319
|
+
return /*#__PURE__*/React.createElement(ToolbarDropdownItemSection, {
|
|
320
|
+
title: intl.formatMessage({
|
|
321
|
+
defaultMessage: 'Display as',
|
|
322
|
+
description: 'Section title for Smart Link display options in the paste actions menu.',
|
|
323
|
+
id: 'fabric.editor.pasteDisplayAsMenu.displayAs'
|
|
324
|
+
})
|
|
325
|
+
}, /*#__PURE__*/React.createElement(PasteDisplayAsMenuHorizontalView, {
|
|
326
|
+
api: api,
|
|
327
|
+
allowBlockCards: allowBlockCards,
|
|
328
|
+
allowEmbeds: allowEmbeds
|
|
329
|
+
}));
|
|
330
|
+
};
|
|
331
|
+
export const getPasteDisplayAsMenuComponents = ({
|
|
332
|
+
api,
|
|
333
|
+
allowBlockCards,
|
|
334
|
+
allowEmbeds,
|
|
335
|
+
getEditorView
|
|
336
|
+
}) => [{
|
|
337
|
+
type: 'menu-section',
|
|
338
|
+
key: SMART_LINK_DISPLAY_AS_PASTE_MENU_SECTION_KEY,
|
|
339
|
+
parents: [{
|
|
340
|
+
type: PASTE_MENU.type,
|
|
341
|
+
key: PASTE_MENU.key,
|
|
342
|
+
rank: 60
|
|
343
|
+
}],
|
|
344
|
+
isHidden: () => {
|
|
345
|
+
var _apiWithPasteOptionsT;
|
|
346
|
+
const apiWithPasteOptionsToolbar = api;
|
|
347
|
+
const pasteRange = apiWithPasteOptionsToolbar === null || apiWithPasteOptionsToolbar === void 0 ? void 0 : (_apiWithPasteOptionsT = apiWithPasteOptionsToolbar.pasteOptionsToolbarPlugin) === null || _apiWithPasteOptionsT === void 0 ? void 0 : _apiWithPasteOptionsT.sharedState.currentState();
|
|
348
|
+
const editorView = getEditorView();
|
|
349
|
+
if (!editorView || !pasteRange) {
|
|
350
|
+
return true;
|
|
351
|
+
}
|
|
352
|
+
const pastedSlice = getCurrentPastedSlice(api);
|
|
353
|
+
const urlFromSlice = getSingleSmartLinkUrlFromSlice(pastedSlice);
|
|
354
|
+
const urlFromCard = getCardUrlAtPasteRange({
|
|
355
|
+
editorView,
|
|
356
|
+
pasteStartPos: pasteRange.pasteStartPos,
|
|
357
|
+
pasteEndPos: pasteRange.pasteEndPos
|
|
358
|
+
});
|
|
359
|
+
return !urlFromSlice && !urlFromCard;
|
|
360
|
+
},
|
|
361
|
+
component: () => /*#__PURE__*/React.createElement(PasteDisplayAsMenuSection, {
|
|
362
|
+
api: api,
|
|
363
|
+
allowBlockCards: allowBlockCards,
|
|
364
|
+
allowEmbeds: allowEmbeds
|
|
365
|
+
})
|
|
366
|
+
}];
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const getUrlFromTextLinkNode = node => {
|
|
2
|
+
var _node$marks$0$attrs;
|
|
3
|
+
if (!node.isText || node.marks.length !== 1 || node.marks[0].type.name !== 'link') {
|
|
4
|
+
return undefined;
|
|
5
|
+
}
|
|
6
|
+
const href = (_node$marks$0$attrs = node.marks[0].attrs) === null || _node$marks$0$attrs === void 0 ? void 0 : _node$marks$0$attrs.href;
|
|
7
|
+
return typeof href === 'string' && node.text === href ? href : undefined;
|
|
8
|
+
};
|
|
9
|
+
const getUrlFromInlineCardNode = node => {
|
|
10
|
+
var _node$attrs;
|
|
11
|
+
if (node.type.name !== 'inlineCard') {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
const url = (_node$attrs = node.attrs) === null || _node$attrs === void 0 ? void 0 : _node$attrs.url;
|
|
15
|
+
return typeof url === 'string' ? url : undefined;
|
|
16
|
+
};
|
|
17
|
+
const significantChildren = fragment => {
|
|
18
|
+
const children = [];
|
|
19
|
+
fragment.forEach(child => {
|
|
20
|
+
var _child$text;
|
|
21
|
+
if (child.isText && ((_child$text = child.text) === null || _child$text === void 0 ? void 0 : _child$text.trim()) === '') {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
children.push(child);
|
|
25
|
+
});
|
|
26
|
+
return children;
|
|
27
|
+
};
|
|
28
|
+
export const getSingleSmartLinkUrlFromSlice = slice => {
|
|
29
|
+
var _getUrlFromTextLinkNo, _getUrlFromTextLinkNo2;
|
|
30
|
+
if (!slice || slice.content.childCount !== 1) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
const topNode = slice.content.child(0);
|
|
34
|
+
const topNodeUrl = (_getUrlFromTextLinkNo = getUrlFromTextLinkNode(topNode)) !== null && _getUrlFromTextLinkNo !== void 0 ? _getUrlFromTextLinkNo : getUrlFromInlineCardNode(topNode);
|
|
35
|
+
if (topNodeUrl) {
|
|
36
|
+
return topNodeUrl;
|
|
37
|
+
}
|
|
38
|
+
if (topNode.type.name !== 'paragraph') {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
const children = significantChildren(topNode.content);
|
|
42
|
+
if (children.length !== 1) {
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
return (_getUrlFromTextLinkNo2 = getUrlFromTextLinkNode(children[0])) !== null && _getUrlFromTextLinkNo2 !== void 0 ? _getUrlFromTextLinkNo2 : getUrlFromInlineCardNode(children[0]);
|
|
46
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export const DISPLAY_AS_OPTIONS = ['url', 'inline', 'block', 'embed'];
|
|
2
|
+
export const getCardAtPasteRange = (state, pasteStartPos, pasteEndPos) => {
|
|
3
|
+
let result;
|
|
4
|
+
const docContentSize = state.doc.content.size;
|
|
5
|
+
const clampedStart = Math.max(0, Math.min(pasteStartPos, docContentSize));
|
|
6
|
+
const clampedEnd = Math.max(0, Math.min(pasteEndPos, docContentSize));
|
|
7
|
+
const from = Math.min(clampedStart, clampedEnd);
|
|
8
|
+
const to = Math.max(clampedStart, clampedEnd);
|
|
9
|
+
try {
|
|
10
|
+
state.doc.nodesBetween(from, to, (node, pos) => {
|
|
11
|
+
if (node.type.name === 'inlineCard' || node.type.name === 'blockCard' || node.type.name === 'embedCard') {
|
|
12
|
+
result = {
|
|
13
|
+
appearance: node.type.name === 'inlineCard' ? 'inline' : node.type.name === 'blockCard' ? 'block' : 'embed',
|
|
14
|
+
pos
|
|
15
|
+
};
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
return true;
|
|
19
|
+
});
|
|
20
|
+
} catch {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
};
|
package/dist/esm/cardPlugin.js
CHANGED
|
@@ -10,6 +10,7 @@ import { IconDatasourceAssetsObjects, IconDatasourceConfluenceSearch, IconDataso
|
|
|
10
10
|
import { canRenderDatasource } from '@atlaskit/editor-common/utils';
|
|
11
11
|
import { ASSETS_LIST_OF_LINKS_DATASOURCE_ID, CONFLUENCE_SEARCH_DATASOURCE_ID } from '@atlaskit/link-datasource';
|
|
12
12
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
13
|
+
import { expValNoExposure } from '@atlaskit/tmp-editor-statsig/expVal';
|
|
13
14
|
import { blockCardSpecWithFixedToDOM } from './nodeviews/toDOM-fixes/blockCard';
|
|
14
15
|
import { embedCardSpecWithFixedToDOM } from './nodeviews/toDOM-fixes/embedCard';
|
|
15
16
|
import { inlineCardSpecWithFixedToDOM } from './nodeviews/toDOM-fixes/inlineCard';
|
|
@@ -26,6 +27,7 @@ import { EditorSmartCardEvents } from './ui/EditorSmartCardEvents';
|
|
|
26
27
|
// Ignored via go/ees005
|
|
27
28
|
// eslint-disable-next-line import/no-named-as-default
|
|
28
29
|
import LayoutButton from './ui/LayoutButton';
|
|
30
|
+
import { getPasteDisplayAsMenuComponents } from './ui/PasteDisplayAsMenu';
|
|
29
31
|
import { floatingToolbar, getEndingToolbarItems, getStartingToolbarItems } from './ui/toolbar';
|
|
30
32
|
export var cardPlugin = function cardPlugin(_ref) {
|
|
31
33
|
var _api$base, _options$lpLinkPicker;
|
|
@@ -35,6 +37,20 @@ export var cardPlugin = function cardPlugin(_ref) {
|
|
|
35
37
|
var previousCardProvider;
|
|
36
38
|
var cardPluginEvents = createEventsQueue();
|
|
37
39
|
var instanceEmbedCardTransformers = options.embedCardTransformers;
|
|
40
|
+
var editorViewForPasteMenu;
|
|
41
|
+
var pasteMenuVariant = expValNoExposure('platform_editor_paste_actions_menu_v2', 'variant', 'control');
|
|
42
|
+
var shouldRegisterPasteDisplayAsMenu = options.enablePasteDisplayAsMenu && ['hasSpellingAndGrammar', 'hasAltAiActions'].includes(pasteMenuVariant);
|
|
43
|
+
if (shouldRegisterPasteDisplayAsMenu) {
|
|
44
|
+
var _api$uiControlRegistr, _options$allowBlockCa;
|
|
45
|
+
api === null || api === void 0 || (_api$uiControlRegistr = api.uiControlRegistry) === null || _api$uiControlRegistr === void 0 || _api$uiControlRegistr.actions.register(getPasteDisplayAsMenuComponents({
|
|
46
|
+
api: api,
|
|
47
|
+
allowBlockCards: options.onlyInlineCards ? false : (_options$allowBlockCa = options.allowBlockCards) !== null && _options$allowBlockCa !== void 0 ? _options$allowBlockCa : true,
|
|
48
|
+
allowEmbeds: options.onlyInlineCards ? false : options.allowEmbeds,
|
|
49
|
+
getEditorView: function getEditorView() {
|
|
50
|
+
return editorViewForPasteMenu;
|
|
51
|
+
}
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
38
54
|
api === null || api === void 0 || (_api$base = api.base) === null || _api$base === void 0 || _api$base.actions.registerMarks(function (_ref2) {
|
|
39
55
|
var tr = _ref2.tr,
|
|
40
56
|
node = _ref2.node,
|
|
@@ -80,10 +96,10 @@ export var cardPlugin = function cardPlugin(_ref) {
|
|
|
80
96
|
return nodes;
|
|
81
97
|
},
|
|
82
98
|
pmPlugins: function pmPlugins() {
|
|
83
|
-
var _options$
|
|
99
|
+
var _options$allowBlockCa2, _options$allowResizin, _options$useAlternati, _options$allowWrappin, _options$allowAlignme, _options$allowDatasou, _options$showUpgradeD;
|
|
84
100
|
// onlyInlineCards forces block/embed off regardless of caller-passed flags,
|
|
85
101
|
// keeping the schema gate (in nodes()) and the runtime gate in sync.
|
|
86
|
-
var allowBlockCards = options.onlyInlineCards ? false : (_options$
|
|
102
|
+
var allowBlockCards = options.onlyInlineCards ? false : (_options$allowBlockCa2 = options.allowBlockCards) !== null && _options$allowBlockCa2 !== void 0 ? _options$allowBlockCa2 : true;
|
|
87
103
|
var allowEmbeds = options.onlyInlineCards ? false : options.allowEmbeds;
|
|
88
104
|
var allowResizing = (_options$allowResizin = options.allowResizing) !== null && _options$allowResizin !== void 0 ? _options$allowResizin : true;
|
|
89
105
|
var useAlternativePreloader = (_options$useAlternati = options.useAlternativePreloader) !== null && _options$useAlternati !== void 0 ? _options$useAlternati : true;
|
|
@@ -123,6 +139,7 @@ export var cardPlugin = function cardPlugin(_ref) {
|
|
|
123
139
|
if (!editorView) {
|
|
124
140
|
return null;
|
|
125
141
|
}
|
|
142
|
+
editorViewForPasteMenu = editorView;
|
|
126
143
|
var breakoutEnabled = options.editorAppearance === 'full-page';
|
|
127
144
|
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(EditorSmartCardEvents, {
|
|
128
145
|
editorView: editorView
|
|
@@ -377,7 +377,7 @@ export var changeSelectedCardToLink = function changeSelectedCardToLink(text, hr
|
|
|
377
377
|
return function (state, dispatch) {
|
|
378
378
|
var selectedNode = state.selection instanceof NodeSelection ? state.selection.node : undefined;
|
|
379
379
|
var tr;
|
|
380
|
-
if (node && pos) {
|
|
380
|
+
if (node && pos !== undefined) {
|
|
381
381
|
tr = cardNodeToLinkWithTransaction(state, text, href, node, pos);
|
|
382
382
|
} else {
|
|
383
383
|
tr = cardToLinkWithTransaction(state, text, href);
|
|
@@ -405,7 +405,7 @@ export var changeSelectedCardToLink = function changeSelectedCardToLink(text, hr
|
|
|
405
405
|
export var changeSelectedCardToLinkFallback = function changeSelectedCardToLinkFallback(text, href, sendAnalytics, node, pos, editorAnalyticsApi) {
|
|
406
406
|
return function (state, dispatch) {
|
|
407
407
|
var tr;
|
|
408
|
-
if (node && pos) {
|
|
408
|
+
if (node && pos !== undefined) {
|
|
409
409
|
tr = cardNodeToLinkWithTransaction(state, text, href, node, pos);
|
|
410
410
|
} else {
|
|
411
411
|
tr = cardToLinkWithTransaction(state, text, href);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
|
|
2
|
+
._2rko1qi0{border-radius:var(--ds-radius-medium,6px)}._189ee4h9{border-width:var(--ds-border-width,1px)}
|
|
3
|
+
._1dqonqa1{border-style:solid}
|
|
4
|
+
._1h6d1l7x{border-color:var(--ds-border,#0b120e24)}
|
|
5
|
+
._1h6dbk0g{border-color:var(--ds-border-disabled,#0515240f)}
|
|
6
|
+
._1h6dq98m{border-color:var(--ds-border-selected,#1868db)}
|
|
7
|
+
._18u0u2gc{margin-left:var(--ds-space-100,8px)}
|
|
8
|
+
._19bv1b66{padding-left:var(--ds-space-050,4px)}
|
|
9
|
+
._19pku2gc{margin-top:var(--ds-space-100,8px)}
|
|
10
|
+
._1bah1b1v{justify-content:space-around}
|
|
11
|
+
._1bah1h6o{justify-content:center}
|
|
12
|
+
._1bsb1osq{width:100%}
|
|
13
|
+
._1e0c1txw{display:flex}
|
|
14
|
+
._2hwxu2gc{margin-right:var(--ds-space-100,8px)}
|
|
15
|
+
._4cvr1h6o{align-items:center}
|
|
16
|
+
._4t3izwfg{height:2pc}
|
|
17
|
+
._bfhk15s3{background-color:var(--ds-background-selected,#e9f2fe)}
|
|
18
|
+
._bfhk187o{background-color:var(--ds-background-disabled,#0515240f)}
|
|
19
|
+
._bfhkhfxm{background-color:var(--ds-surface-sunken,#f8f8f8)}
|
|
20
|
+
._bfhksm61{background-color:var(--ds-background-neutral-subtle,#00000000)}
|
|
21
|
+
._bfhkvuon{background-color:var(--ds-surface,#fff)}
|
|
22
|
+
._ca0q1b66{padding-top:var(--ds-space-050,4px)}
|
|
23
|
+
._n3td1b66{padding-bottom:var(--ds-space-050,4px)}
|
|
24
|
+
._otyru2gc{margin-bottom:var(--ds-space-100,8px)}
|
|
25
|
+
._syaz1gmx{color:var(--ds-text-disabled,#080f214a)}
|
|
26
|
+
._syazi7uo{color:var(--ds-text,#292a2e)}
|
|
27
|
+
._u5f31b66{padding-right:var(--ds-space-050,4px)}
|
|
28
|
+
._irr31dpa:hover{background-color:var(--ds-background-neutral-subtle-hovered,#0515240f)}
|
|
29
|
+
._irr3ufnl:hover{background-color:var(--ds-background-selected-hovered,#cfe1fd)}
|
|
30
|
+
._1di6fcek:active{background-color:var(--ds-background-neutral-subtle-pressed,#0b120e24)}
|
|
31
|
+
._1di6nozp:active{background-color:var(--ds-background-selected-pressed,#8fb8f6)}
|