@atlaskit/editor-plugin-code-block-advanced 6.2.19 → 7.1.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 +21 -0
- package/afm-cc/tsconfig.json +1 -1
- package/dist/cjs/nodeviews/codeBlockAdvanced.js +60 -17
- package/dist/cjs/ui/theme.js +77 -72
- package/dist/es2019/nodeviews/codeBlockAdvanced.js +59 -19
- package/dist/es2019/ui/theme.js +7 -6
- package/dist/esm/nodeviews/codeBlockAdvanced.js +63 -20
- package/dist/esm/ui/theme.js +77 -72
- package/dist/types/codeBlockAdvancedPluginType.d.ts +3 -1
- package/dist/types/nodeviews/codeBlockAdvanced.d.ts +4 -0
- package/dist/types/ui/theme.d.ts +6 -1
- package/dist/types-ts4.5/codeBlockAdvancedPluginType.d.ts +3 -1
- package/dist/types-ts4.5/nodeviews/codeBlockAdvanced.d.ts +4 -0
- package/dist/types-ts4.5/ui/theme.d.ts +6 -1
- package/package.json +9 -9
- package/src/codeBlockAdvancedPluginType.ts +2 -0
- package/src/nodeviews/codeBlockAdvanced.ts +79 -21
- package/src/nodeviews/languages/loader.ts +1 -1
- package/src/ui/theme.ts +82 -79
|
@@ -17,6 +17,7 @@ import type {
|
|
|
17
17
|
getPosHandler,
|
|
18
18
|
getPosHandlerNode,
|
|
19
19
|
ExtractInjectionAPI,
|
|
20
|
+
EditorContentMode,
|
|
20
21
|
} from '@atlaskit/editor-common/types';
|
|
21
22
|
import { ZERO_WIDTH_SPACE } from '@atlaskit/editor-common/whitespace';
|
|
22
23
|
import { type EditorSelectionAPI } from '@atlaskit/editor-plugin-selection';
|
|
@@ -37,9 +38,6 @@ import type { CodeBlockAdvancedPlugin } from '../codeBlockAdvancedPluginType';
|
|
|
37
38
|
import { highlightStyle } from '../ui/syntaxHighlightingTheme';
|
|
38
39
|
import { cmTheme, codeFoldingTheme } from '../ui/theme';
|
|
39
40
|
|
|
40
|
-
// Store last observed heights of code blocks
|
|
41
|
-
const codeBlockHeights = new WeakMap<HTMLElement, number>();
|
|
42
|
-
|
|
43
41
|
import { syncCMWithPM } from './codemirrorSync/syncCMWithPM';
|
|
44
42
|
import { getCMSelectionChanges } from './codemirrorSync/updateCMSelection';
|
|
45
43
|
import { firstCodeBlockInDocument } from './extensions/firstCodeBlockInDocument';
|
|
@@ -50,6 +48,9 @@ import { prosemirrorDecorationPlugin } from './extensions/prosemirrorDecorations
|
|
|
50
48
|
import { tripleClickSelectAllExtension } from './extensions/tripleClickExtension';
|
|
51
49
|
import { LanguageLoader } from './languages/loader';
|
|
52
50
|
|
|
51
|
+
// Store last observed heights of code blocks
|
|
52
|
+
const codeBlockHeights = new WeakMap<HTMLElement, number>();
|
|
53
|
+
|
|
53
54
|
export interface ConfigProps {
|
|
54
55
|
allowCodeFolding: boolean;
|
|
55
56
|
api: ExtractInjectionAPI<CodeBlockAdvancedPlugin> | undefined;
|
|
@@ -66,15 +67,18 @@ class CodeBlockAdvancedNodeView implements NodeView {
|
|
|
66
67
|
private languageCompartment = new Compartment();
|
|
67
68
|
private readOnlyCompartment = new Compartment();
|
|
68
69
|
private pmDecorationsCompartment = new Compartment();
|
|
70
|
+
private themeCompartment = new Compartment();
|
|
69
71
|
private node: PMNode;
|
|
70
72
|
private getPos: getPosHandlerNode;
|
|
71
73
|
private cm: CodeMirror;
|
|
74
|
+
private contentMode: EditorContentMode | undefined;
|
|
72
75
|
private selectionAPI: EditorSelectionAPI | undefined;
|
|
73
76
|
private maybeTryingToReachNodeSelection = false;
|
|
74
77
|
private cleanupDisabledState: (() => void) | undefined;
|
|
75
78
|
private languageLoader: LanguageLoader;
|
|
76
79
|
private pmFacet = Facet.define<DecorationSource>();
|
|
77
80
|
private ro?: ResizeObserver;
|
|
81
|
+
private unsubscribeContentFormat: (() => void) | undefined;
|
|
78
82
|
|
|
79
83
|
constructor(
|
|
80
84
|
node: PMNode,
|
|
@@ -86,12 +90,24 @@ class CodeBlockAdvancedNodeView implements NodeView {
|
|
|
86
90
|
this.node = node;
|
|
87
91
|
this.view = view;
|
|
88
92
|
this.getPos = getPos;
|
|
93
|
+
const contentFormatSharedState = expValEquals(
|
|
94
|
+
'confluence_compact_text_format',
|
|
95
|
+
'isEnabled',
|
|
96
|
+
true,
|
|
97
|
+
)
|
|
98
|
+
? config.api?.contentFormat?.sharedState
|
|
99
|
+
: undefined;
|
|
100
|
+
this.contentMode = expValEquals('confluence_compact_text_format', 'isEnabled', true)
|
|
101
|
+
? contentFormatSharedState?.currentState?.()?.contentMode
|
|
102
|
+
: undefined;
|
|
103
|
+
|
|
89
104
|
this.selectionAPI = config.api?.selection?.actions;
|
|
90
105
|
const getNode = () => this.node;
|
|
91
106
|
const onMaybeNodeSelection = () => (this.maybeTryingToReachNodeSelection = true);
|
|
92
107
|
this.cleanupDisabledState = config.api?.editorDisabled?.sharedState.onChange(() => {
|
|
93
108
|
this.updateReadonlyState();
|
|
94
109
|
});
|
|
110
|
+
|
|
95
111
|
this.languageLoader = new LanguageLoader((lang) => {
|
|
96
112
|
this.updating = true;
|
|
97
113
|
this.cm.dispatch({
|
|
@@ -126,7 +142,13 @@ class CodeBlockAdvancedNodeView implements NodeView {
|
|
|
126
142
|
}),
|
|
127
143
|
// Goes before cmTheme to override styles
|
|
128
144
|
config.allowCodeFolding ? [codeFoldingTheme] : [],
|
|
129
|
-
|
|
145
|
+
this.themeCompartment.of(
|
|
146
|
+
cmTheme({
|
|
147
|
+
contentMode: expValEquals('confluence_compact_text_format', 'isEnabled', true)
|
|
148
|
+
? this.contentMode
|
|
149
|
+
: undefined,
|
|
150
|
+
}),
|
|
151
|
+
),
|
|
130
152
|
syntaxHighlighting(highlightStyle),
|
|
131
153
|
bracketMatching(),
|
|
132
154
|
lineNumbers({
|
|
@@ -163,20 +185,31 @@ class CodeBlockAdvancedNodeView implements NodeView {
|
|
|
163
185
|
],
|
|
164
186
|
});
|
|
165
187
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
188
|
+
if (contentFormatSharedState) {
|
|
189
|
+
this.unsubscribeContentFormat = contentFormatSharedState.onChange(
|
|
190
|
+
({ nextSharedState, prevSharedState }) => {
|
|
191
|
+
const prevMode = prevSharedState?.contentMode;
|
|
192
|
+
const nextMode = nextSharedState?.contentMode;
|
|
193
|
+
if (nextMode === prevMode) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
this.applyContentModeTheme(nextMode);
|
|
198
|
+
|
|
199
|
+
if (this.updating || this.cm.hasFocus) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
this.cm.requestMeasure();
|
|
204
|
+
},
|
|
205
|
+
);
|
|
206
|
+
}
|
|
174
207
|
|
|
175
208
|
// Observe size changes of the CodeMirror DOM and request a measurement pass
|
|
176
209
|
if (
|
|
177
|
-
expValEquals('confluence_compact_text_format', 'isEnabled', true)
|
|
178
|
-
|
|
179
|
-
|
|
210
|
+
!expValEquals('confluence_compact_text_format', 'isEnabled', true) &&
|
|
211
|
+
expValEquals('cc_editor_ai_content_mode', 'variant', 'test') &&
|
|
212
|
+
fg('platform_editor_content_mode_button_mvp')
|
|
180
213
|
) {
|
|
181
214
|
this.ro = new ResizeObserver((entries) => {
|
|
182
215
|
// Skip measurements when:
|
|
@@ -195,12 +228,22 @@ class CodeBlockAdvancedNodeView implements NodeView {
|
|
|
195
228
|
}
|
|
196
229
|
codeBlockHeights.set(this.cm.contentDOM, currentHeight);
|
|
197
230
|
}
|
|
231
|
+
|
|
198
232
|
// CodeMirror to re-measure when its content size changes
|
|
199
233
|
this.cm.requestMeasure();
|
|
200
234
|
});
|
|
201
235
|
this.ro.observe(this.cm.contentDOM);
|
|
202
236
|
}
|
|
203
237
|
|
|
238
|
+
// We append an additional element that fixes a selection bug on chrome if the code block
|
|
239
|
+
// is the first element followed by subsequent code blocks
|
|
240
|
+
const spaceContainer = document.createElement('span');
|
|
241
|
+
spaceContainer.innerText = ZERO_WIDTH_SPACE;
|
|
242
|
+
spaceContainer.style.height = '0';
|
|
243
|
+
// The editor's outer node is our DOM representation
|
|
244
|
+
this.dom = this.cm.dom;
|
|
245
|
+
this.dom.appendChild(spaceContainer);
|
|
246
|
+
|
|
204
247
|
// This flag is used to avoid an update loop between the outer and
|
|
205
248
|
// inner editor
|
|
206
249
|
this.updating = false;
|
|
@@ -214,22 +257,25 @@ class CodeBlockAdvancedNodeView implements NodeView {
|
|
|
214
257
|
}
|
|
215
258
|
}
|
|
216
259
|
|
|
217
|
-
destroy() {
|
|
260
|
+
destroy(): void {
|
|
218
261
|
// ED-27428: CodeMirror gets into an infinite loop as it detects mutations on removed
|
|
219
262
|
// decorations. When we change the breakout we destroy the node and cleanup these decorations from
|
|
220
263
|
// codemirror
|
|
221
264
|
this.clearProseMirrorDecorations();
|
|
222
265
|
this.cleanupDisabledState?.();
|
|
266
|
+
if (expValEquals('confluence_compact_text_format', 'isEnabled', true)) {
|
|
267
|
+
this.unsubscribeContentFormat?.();
|
|
268
|
+
}
|
|
223
269
|
if (
|
|
224
|
-
expValEquals('confluence_compact_text_format', 'isEnabled', true)
|
|
225
|
-
|
|
226
|
-
|
|
270
|
+
!expValEquals('confluence_compact_text_format', 'isEnabled', true) &&
|
|
271
|
+
expValEquals('cc_editor_ai_content_mode', 'variant', 'test') &&
|
|
272
|
+
fg('platform_editor_content_mode_button_mvp')
|
|
227
273
|
) {
|
|
228
274
|
this.ro?.disconnect();
|
|
229
275
|
}
|
|
230
276
|
}
|
|
231
277
|
|
|
232
|
-
forwardUpdate(update: ViewUpdate) {
|
|
278
|
+
forwardUpdate(update: ViewUpdate): void {
|
|
233
279
|
if (this.updating || !this.cm.hasFocus) {
|
|
234
280
|
return;
|
|
235
281
|
}
|
|
@@ -242,7 +288,7 @@ class CodeBlockAdvancedNodeView implements NodeView {
|
|
|
242
288
|
});
|
|
243
289
|
}
|
|
244
290
|
|
|
245
|
-
setSelection(anchor: number, head: number) {
|
|
291
|
+
setSelection(anchor: number, head: number): void {
|
|
246
292
|
if (!this.maybeTryingToReachNodeSelection) {
|
|
247
293
|
this.cm.focus();
|
|
248
294
|
}
|
|
@@ -308,6 +354,18 @@ class CodeBlockAdvancedNodeView implements NodeView {
|
|
|
308
354
|
this.updating = false;
|
|
309
355
|
}
|
|
310
356
|
|
|
357
|
+
private applyContentModeTheme(contentMode: EditorContentMode | undefined) {
|
|
358
|
+
if (contentMode === this.contentMode) {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
this.contentMode = contentMode;
|
|
362
|
+
this.updating = true;
|
|
363
|
+
this.cm.dispatch({
|
|
364
|
+
effects: this.themeCompartment.reconfigure(cmTheme({ contentMode })),
|
|
365
|
+
});
|
|
366
|
+
this.updating = false;
|
|
367
|
+
}
|
|
368
|
+
|
|
311
369
|
update(node: PMNode, _: readonly Decoration[], innerDecorations: DecorationSource) {
|
|
312
370
|
this.maybeTryingToReachNodeSelection = false;
|
|
313
371
|
|
package/src/ui/theme.ts
CHANGED
|
@@ -1,91 +1,94 @@
|
|
|
1
1
|
import { EditorView as CodeMirror } from '@codemirror/view';
|
|
2
2
|
|
|
3
|
+
import type { EditorContentMode } from '@atlaskit/editor-common/types';
|
|
3
4
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
4
5
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
5
6
|
import { token } from '@atlaskit/tokens';
|
|
6
7
|
|
|
7
|
-
const
|
|
8
|
-
|
|
8
|
+
const shouldUseCompactTypography = (contentMode?: EditorContentMode) =>
|
|
9
|
+
contentMode === 'compact' ||
|
|
9
10
|
(expValEquals('cc_editor_ai_content_mode', 'variant', 'test') &&
|
|
10
|
-
fg('platform_editor_content_mode_button_mvp'))
|
|
11
|
-
? '1.5em'
|
|
12
|
-
: '1.5rem';
|
|
11
|
+
fg('platform_editor_content_mode_button_mvp'));
|
|
13
12
|
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
(expValEquals('cc_editor_ai_content_mode', 'variant', 'test') &&
|
|
17
|
-
fg('platform_editor_content_mode_button_mvp'))
|
|
18
|
-
? '0.875em'
|
|
19
|
-
: '0.875rem';
|
|
13
|
+
const getLineHeight = (contentMode?: EditorContentMode) =>
|
|
14
|
+
shouldUseCompactTypography(contentMode) ? '1.5em' : '1.5rem';
|
|
20
15
|
|
|
21
|
-
|
|
22
|
-
'
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
16
|
+
const getFontSize = (contentMode?: EditorContentMode) =>
|
|
17
|
+
shouldUseCompactTypography(contentMode) ? '0.875em' : '0.875rem';
|
|
18
|
+
|
|
19
|
+
type ThemeOptions = {
|
|
20
|
+
contentMode?: EditorContentMode;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const cmTheme = (options?: ThemeOptions) =>
|
|
24
|
+
CodeMirror.theme({
|
|
25
|
+
'&': {
|
|
26
|
+
backgroundColor: token('color.background.neutral'),
|
|
27
|
+
padding: '0',
|
|
28
|
+
marginTop: token('space.100'),
|
|
29
|
+
marginBottom: token('space.100'),
|
|
30
|
+
// eslint-disable-next-line @atlaskit/design-system/use-tokens-typography
|
|
31
|
+
fontSize: getFontSize(options?.contentMode),
|
|
32
|
+
// Custom syntax styling to match existing styling
|
|
33
|
+
// eslint-disable-next-line @atlaskit/design-system/use-tokens-typography
|
|
34
|
+
lineHeight: getLineHeight(options?.contentMode),
|
|
35
|
+
},
|
|
36
|
+
'&.cm-focused': {
|
|
37
|
+
outline: 'none',
|
|
38
|
+
},
|
|
39
|
+
'.cm-line': {
|
|
40
|
+
padding: '0',
|
|
41
|
+
},
|
|
42
|
+
'&.cm-editor.code-block.danger': {
|
|
43
|
+
backgroundColor: token('color.background.danger'),
|
|
44
|
+
},
|
|
45
|
+
'.cm-content[aria-readonly="true"]': {
|
|
46
|
+
caretColor: 'transparent',
|
|
47
|
+
},
|
|
48
|
+
'.cm-content': {
|
|
49
|
+
cursor: 'text',
|
|
50
|
+
caretColor: token('color.text'),
|
|
51
|
+
margin: token('space.100'),
|
|
52
|
+
padding: token('space.0'),
|
|
53
|
+
},
|
|
54
|
+
'.cm-scroller': {
|
|
55
|
+
backgroundColor: token('color.background.neutral'),
|
|
56
|
+
// Custom syntax styling to match existing styling
|
|
57
|
+
// eslint-disable-next-line @atlaskit/design-system/use-tokens-typography
|
|
58
|
+
lineHeight: 'unset',
|
|
59
|
+
fontFamily: token('font.family.code'),
|
|
60
|
+
borderRadius: token('radius.small'),
|
|
61
|
+
backgroundImage: overflowShadow({
|
|
62
|
+
leftCoverWidth: token('space.300'),
|
|
63
|
+
}),
|
|
64
|
+
backgroundAttachment: 'local, local, local, local, scroll, scroll, scroll, scroll',
|
|
65
|
+
},
|
|
66
|
+
'&.cm-focused .cm-cursor': {
|
|
67
|
+
borderLeftColor: token('color.text'),
|
|
68
|
+
},
|
|
69
|
+
'.cm-gutter': {
|
|
70
|
+
padding: token('space.100'),
|
|
71
|
+
},
|
|
72
|
+
'.cm-gutters': {
|
|
73
|
+
backgroundColor: token('color.background.neutral'),
|
|
74
|
+
border: 'none',
|
|
75
|
+
padding: token('space.0'),
|
|
76
|
+
color: token('color.text.subtlest'),
|
|
77
|
+
},
|
|
78
|
+
'.cm-lineNumbers .cm-gutterElement': {
|
|
79
|
+
paddingLeft: token('space.0'),
|
|
80
|
+
paddingRight: token('space.0'),
|
|
81
|
+
minWidth: 'unset',
|
|
82
|
+
},
|
|
83
|
+
// Set the gutter element min height to prevent flicker of styling while
|
|
84
|
+
// codemirror is calculating (which happens after an animation frame).
|
|
85
|
+
// Example problem: https://github.com/codemirror/dev/issues/1076
|
|
86
|
+
// Ignore the first gutter element as it is a special hidden element.
|
|
87
|
+
'.cm-gutterElement:not(:first-child)': {
|
|
88
|
+
// eslint-disable-next-line @atlaskit/design-system/use-tokens-typography
|
|
89
|
+
minHeight: getLineHeight(options?.contentMode),
|
|
90
|
+
},
|
|
91
|
+
});
|
|
89
92
|
|
|
90
93
|
export const codeFoldingTheme = CodeMirror.theme({
|
|
91
94
|
'.cm-gutter': {
|