@atlaskit/editor-plugin-code-block-advanced 3.0.7 → 3.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.
Files changed (42) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/cjs/codeBlockAdvancedPlugin.js +6 -2
  3. package/dist/cjs/nodeviews/codeBlockAdvanced.js +12 -4
  4. package/dist/cjs/nodeviews/codeBlockNodeWithToDOMFixed.js +5 -4
  5. package/dist/cjs/nodeviews/extensions/foldGutter.js +100 -0
  6. package/dist/cjs/ui/theme.js +31 -1
  7. package/dist/es2019/codeBlockAdvancedPlugin.js +6 -2
  8. package/dist/es2019/nodeviews/codeBlockAdvanced.js +13 -5
  9. package/dist/es2019/nodeviews/codeBlockNodeWithToDOMFixed.js +5 -4
  10. package/dist/es2019/nodeviews/extensions/foldGutter.js +97 -0
  11. package/dist/es2019/ui/theme.js +30 -0
  12. package/dist/esm/codeBlockAdvancedPlugin.js +6 -2
  13. package/dist/esm/nodeviews/codeBlockAdvanced.js +13 -5
  14. package/dist/esm/nodeviews/codeBlockNodeWithToDOMFixed.js +5 -4
  15. package/dist/esm/nodeviews/extensions/foldGutter.js +94 -0
  16. package/dist/esm/ui/theme.js +30 -0
  17. package/dist/types/codeBlockAdvancedPluginType.d.ts +5 -3
  18. package/dist/types/index.d.ts +1 -1
  19. package/dist/types/nodeviews/codeBlockAdvanced.d.ts +2 -1
  20. package/dist/types/nodeviews/codeBlockNodeWithToDOMFixed.d.ts +5 -1
  21. package/dist/types/nodeviews/extensions/foldGutter.d.ts +3 -0
  22. package/dist/types/nodeviews/lazyCodeBlockAdvanced.d.ts +1 -0
  23. package/dist/types/pm-plugins/main.d.ts +1 -0
  24. package/dist/types/ui/theme.d.ts +1 -0
  25. package/dist/types-ts4.5/codeBlockAdvancedPluginType.d.ts +5 -3
  26. package/dist/types-ts4.5/index.d.ts +1 -1
  27. package/dist/types-ts4.5/nodeviews/codeBlockAdvanced.d.ts +2 -1
  28. package/dist/types-ts4.5/nodeviews/codeBlockNodeWithToDOMFixed.d.ts +5 -1
  29. package/dist/types-ts4.5/nodeviews/extensions/foldGutter.d.ts +3 -0
  30. package/dist/types-ts4.5/nodeviews/lazyCodeBlockAdvanced.d.ts +1 -0
  31. package/dist/types-ts4.5/pm-plugins/main.d.ts +1 -0
  32. package/dist/types-ts4.5/ui/theme.d.ts +1 -0
  33. package/package.json +4 -5
  34. package/src/codeBlockAdvancedPlugin.tsx +7 -2
  35. package/src/codeBlockAdvancedPluginType.ts +6 -5
  36. package/src/index.ts +4 -1
  37. package/src/nodeviews/codeBlockAdvanced.ts +13 -4
  38. package/src/nodeviews/codeBlockNodeWithToDOMFixed.ts +11 -4
  39. package/src/nodeviews/extensions/foldGutter.ts +128 -0
  40. package/src/nodeviews/lazyCodeBlockAdvanced.ts +1 -0
  41. package/src/pm-plugins/main.ts +1 -0
  42. package/src/ui/theme.ts +31 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @atlaskit/editor-plugin-code-block-advanced
2
2
 
3
+ ## 3.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#199487](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/199487)
8
+ [`9146513a60d45`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/9146513a60d45) -
9
+ Add a new experiment for code folding in the editor.
10
+
11
+ ### Patch Changes
12
+
13
+ - Updated dependencies
14
+
3
15
  ## 3.0.7
4
16
 
5
17
  ### Patch Changes
@@ -12,20 +12,24 @@ var codeBlockAdvancedPlugin = exports.codeBlockAdvancedPlugin = function codeBlo
12
12
  return {
13
13
  name: 'codeBlockAdvanced',
14
14
  nodes: function nodes() {
15
+ var _config$allowCodeFold;
15
16
  return [{
16
17
  name: 'codeBlock',
17
- node: (0, _codeBlockNodeWithToDOMFixed.codeBlockNodeWithFixedToDOM)()
18
+ node: (0, _codeBlockNodeWithToDOMFixed.codeBlockNodeWithFixedToDOM)({
19
+ allowCodeFolding: (_config$allowCodeFold = config === null || config === void 0 ? void 0 : config.allowCodeFolding) !== null && _config$allowCodeFold !== void 0 ? _config$allowCodeFold : false
20
+ })
18
21
  }];
19
22
  },
20
23
  pmPlugins: function pmPlugins() {
21
24
  return [{
22
25
  name: 'codeBlockAdvancedPlugin',
23
26
  plugin: function plugin(_ref2) {
24
- var _config$extensions;
27
+ var _config$extensions, _config$allowCodeFold2;
25
28
  var getIntl = _ref2.getIntl;
26
29
  return (0, _main.createPlugin)({
27
30
  api: api,
28
31
  extensions: (_config$extensions = config === null || config === void 0 ? void 0 : config.extensions) !== null && _config$extensions !== void 0 ? _config$extensions : [],
32
+ allowCodeFolding: (_config$allowCodeFold2 = config === null || config === void 0 ? void 0 : config.allowCodeFolding) !== null && _config$allowCodeFold2 !== void 0 ? _config$allowCodeFold2 : false,
29
33
  getIntl: getIntl
30
34
  });
31
35
  }
@@ -24,6 +24,7 @@ var _theme = require("../ui/theme");
24
24
  var _syncCMWithPM = require("./codemirrorSync/syncCMWithPM");
25
25
  var _updateCMSelection = require("./codemirrorSync/updateCMSelection");
26
26
  var _firstCodeBlockInDocument = require("./extensions/firstCodeBlockInDocument");
27
+ var _foldGutter = require("./extensions/foldGutter");
27
28
  var _keymap = require("./extensions/keymap");
28
29
  var _manageSelectionMarker = require("./extensions/manageSelectionMarker");
29
30
  var _prosemirrorDecorations = require("./extensions/prosemirrorDecorations");
@@ -67,6 +68,10 @@ var CodeBlockAdvancedNodeView = /*#__PURE__*/function () {
67
68
  var _config$getIntl = config.getIntl(),
68
69
  formatMessage = _config$getIntl.formatMessage;
69
70
  var formattedAriaLabel = formatMessage(_messages.blockTypeMessages.codeblock);
71
+ var selectNode = function selectNode() {
72
+ _this.selectCodeBlockNode(undefined);
73
+ _this.view.focus();
74
+ };
70
75
  this.cm = new _view.EditorView({
71
76
  doc: this.node.textContent,
72
77
  extensions: [].concat((0, _toConsumableArray2.default)(config.extensions), [this.lineWrappingCompartment.of((0, _codeBlock.isCodeBlockWordWrapEnabled)(node) ? _view.EditorView.lineWrapping : []), this.languageCompartment.of([]), this.pmDecorationsCompartment.of(this.pmFacet.compute([], function () {
@@ -78,11 +83,12 @@ var CodeBlockAdvancedNodeView = /*#__PURE__*/function () {
78
83
  selectCodeBlockNode: this.selectCodeBlockNode.bind(this),
79
84
  onMaybeNodeSelection: onMaybeNodeSelection,
80
85
  customFindReplace: Boolean((_config$api3 = config.api) === null || _config$api3 === void 0 ? void 0 : _config$api3.findReplace)
81
- }), _theme.cmTheme, (0, _language.syntaxHighlighting)(_syntaxHighlightingTheme.highlightStyle), (0, _language.bracketMatching)(), (0, _view.lineNumbers)({
86
+ }),
87
+ // Goes before cmTheme to override styles
88
+ config.allowCodeFolding ? [_theme.codeFoldingTheme] : [], _theme.cmTheme, (0, _language.syntaxHighlighting)(_syntaxHighlightingTheme.highlightStyle), (0, _language.bracketMatching)(), (0, _view.lineNumbers)({
82
89
  domEventHandlers: {
83
90
  click: function click() {
84
- _this.selectCodeBlockNode(undefined);
85
- _this.view.focus();
91
+ selectNode();
86
92
  return true;
87
93
  }
88
94
  }
@@ -99,7 +105,9 @@ var CodeBlockAdvancedNodeView = /*#__PURE__*/function () {
99
105
  class: 'code-block'
100
106
  }), (0, _manageSelectionMarker.manageSelectionMarker)(config.api), (0, _prosemirrorDecorations.prosemirrorDecorationPlugin)(this.pmFacet, view, getPos), (0, _tripleClickExtension.tripleClickSelectAllExtension)(), (0, _firstCodeBlockInDocument.firstCodeBlockInDocument)(getPos), _view.EditorView.contentAttributes.of({
101
107
  'aria-label': formattedAriaLabel
102
- })])
108
+ }), config.allowCodeFolding ? [(0, _foldGutter.foldGutterExtension)({
109
+ selectNode: selectNode
110
+ })] : []])
103
111
  });
104
112
 
105
113
  // We append an additional element that fixes a selection bug on chrome if the code block
@@ -21,7 +21,7 @@ var codeBlockClassNames = {
21
21
  var MATCH_NEWLINES = new RegExp('\n', 'gu');
22
22
 
23
23
  // Based on: `packages/editor/editor-plugin-code-block/src/nodeviews/code-block.ts`
24
- var _toDOM = function toDOM(node, formattedAriaLabel) {
24
+ var _toDOM = function toDOM(node, formattedAriaLabel, config) {
25
25
  var totalLineCount = 1;
26
26
  node.forEach(function (node) {
27
27
  var text = node.text;
@@ -49,7 +49,8 @@ var _toDOM = function toDOM(node, formattedAriaLabel) {
49
49
  backgroundColor: "var(--ds-background-neutral, #091E420F)",
50
50
  position: 'relative',
51
51
  width: 'var(--lineNumberGutterWidth, 2rem)',
52
- padding: "var(--ds-space-100, 8px)",
52
+ /* top and bottom | left and right */
53
+ padding: config.allowCodeFolding ? "var(--ds-space-100, 8px)".concat(" ", "var(--ds-space-250, 20px)", " ", "var(--ds-space-100, 8px)", " ", "var(--ds-space-075, 6px)") : "var(--ds-space-100, 8px)",
53
54
  flexShrink: 0,
54
55
  fontSize: '0.875rem',
55
56
  boxSizing: 'content-box'
@@ -76,10 +77,10 @@ var _toDOM = function toDOM(node, formattedAriaLabel) {
76
77
  contenteditable: 'false'
77
78
  }]];
78
79
  };
79
- var codeBlockNodeWithFixedToDOM = exports.codeBlockNodeWithFixedToDOM = function codeBlockNodeWithFixedToDOM() {
80
+ var codeBlockNodeWithFixedToDOM = exports.codeBlockNodeWithFixedToDOM = function codeBlockNodeWithFixedToDOM(config) {
80
81
  return _objectSpread(_objectSpread({}, _adfSchema.codeBlock), {}, {
81
82
  toDOM: function toDOM(node) {
82
- return _toDOM(node, '');
83
+ return _toDOM(node, '', config);
83
84
  }
84
85
  });
85
86
  };
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.foldGutterExtension = foldGutterExtension;
7
+ var _language = require("@codemirror/language");
8
+ var _lazyNodeView = require("@atlaskit/editor-common/lazy-node-view");
9
+ var _model = require("@atlaskit/editor-prosemirror/model");
10
+ // Based on platform/packages/design-system/icon/svgs/utility/add.svg
11
+ var chevronDown = ['http://www.w3.org/2000/svg svg', {
12
+ width: '12',
13
+ height: '12',
14
+ fill: 'none',
15
+ viewBox: '0 0 16 16',
16
+ style: 'pointer-events: none;'
17
+ }, ['http://www.w3.org/2000/svg path', {
18
+ fill: 'currentcolor',
19
+ 'fill-rule': 'evenodd',
20
+ d: 'm14.53 6.03-6 6a.75.75 0 0 1-1.004.052l-.056-.052-6-6 1.06-1.06L8 10.44l5.47-5.47z',
21
+ 'clip-rule': 'evenodd',
22
+ style: 'pointer-events: none;'
23
+ }]];
24
+ var chevronRight = ['http://www.w3.org/2000/svg svg', {
25
+ width: '12',
26
+ height: '12',
27
+ fill: 'none',
28
+ viewBox: '0 0 16 16',
29
+ style: 'pointer-events: none;'
30
+ }, ['http://www.w3.org/2000/svg path', {
31
+ fill: 'currentcolor',
32
+ 'fill-rule': 'evenodd',
33
+ d: 'm8.28 1.47 6 6a.75.75 0 0 1 .052 1.004l-.052.056-6 6-1.06-1.06L12.69 8 7.22 2.53z',
34
+ 'clip-rule': 'evenodd',
35
+ style: 'pointer-events: none;'
36
+ }]];
37
+ function foldGutterExtension(_ref) {
38
+ var selectNode = _ref.selectNode;
39
+ return [(0, _language.foldGutter)({
40
+ domEventHandlers: {
41
+ click: function click(_view, _, event) {
42
+ // If we're trying to click the button, don't select
43
+ if (event.target instanceof HTMLButtonElement && event.target.getAttribute('data-marker-dom-element')) {
44
+ return false;
45
+ }
46
+ selectNode();
47
+ return false;
48
+ }
49
+ },
50
+ markerDOM: function markerDOM(open) {
51
+ var _DOMSerializer$render = _model.DOMSerializer.renderSpec(document, chevronDown),
52
+ downElement = _DOMSerializer$render.dom;
53
+ var _DOMSerializer$render2 = _model.DOMSerializer.renderSpec(document, chevronRight),
54
+ rightElement = _DOMSerializer$render2.dom;
55
+ var htmlElement = document.createElement('button');
56
+ htmlElement.setAttribute('data-marker-dom-element', 'true');
57
+ htmlElement.setAttribute('data-testid', "code-block-fold-button-".concat(open ? 'open' : 'closed'));
58
+ htmlElement.setAttribute('style', (0, _lazyNodeView.convertToInlineCss)({
59
+ background: 'none',
60
+ color: 'inherit',
61
+ border: 'none',
62
+ padding: 0,
63
+ paddingRight: "var(--ds-space-050, 4px)",
64
+ font: 'inherit',
65
+ outline: 'inherit',
66
+ cursor: 'pointer'
67
+ }));
68
+ if (open) {
69
+ if (downElement) {
70
+ htmlElement.appendChild(downElement);
71
+ } else {
72
+ // Fallback - never called
73
+ htmlElement.textContent = '⌄';
74
+ }
75
+ } else {
76
+ if (rightElement) {
77
+ htmlElement.appendChild(rightElement);
78
+ } else {
79
+ // Fallback - never called
80
+ htmlElement.textContent = '>';
81
+ }
82
+ }
83
+ return htmlElement;
84
+ }
85
+ }), (0, _language.codeFolding)({
86
+ placeholderDOM: function placeholderDOM(view, onclick, prepared) {
87
+ var htmlElement = document.createElement('button');
88
+ htmlElement.setAttribute('data-marker-dom-element', 'true');
89
+ htmlElement.setAttribute('style', (0, _lazyNodeView.convertToInlineCss)({
90
+ color: 'inherit',
91
+ font: 'inherit',
92
+ cursor: 'pointer'
93
+ }));
94
+ htmlElement.textContent = '…';
95
+ htmlElement.className = 'cm-foldPlaceholder';
96
+ htmlElement.onclick = onclick;
97
+ return htmlElement;
98
+ }
99
+ })];
100
+ }
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.cmTheme = void 0;
6
+ exports.codeFoldingTheme = exports.cmTheme = void 0;
7
7
  var _view = require("@codemirror/view");
8
8
  var lineHeight = '1.5rem';
9
9
  var cmTheme = exports.cmTheme = _view.EditorView.theme({
@@ -74,6 +74,36 @@ var cmTheme = exports.cmTheme = _view.EditorView.theme({
74
74
  minHeight: lineHeight
75
75
  }
76
76
  });
77
+ var codeFoldingTheme = exports.codeFoldingTheme = _view.EditorView.theme({
78
+ '.cm-gutter': {
79
+ paddingLeft: "var(--ds-space-075, 6px)",
80
+ paddingTop: "var(--ds-space-100, 8px)",
81
+ paddingBottom: "var(--ds-space-100, 8px)",
82
+ paddingRight: "var(--ds-space-0, 0px)"
83
+ },
84
+ '.cm-foldGutter': {
85
+ paddingLeft: "var(--ds-space-050, 4px)"
86
+ },
87
+ '.cm-gutterElement:has([data-marker-dom-element="true"])': {
88
+ color: "var(--ds-icon-subtle, #626F86)"
89
+ },
90
+ '.cm-gutterElement:has([data-marker-dom-element="true"]):hover': {
91
+ color: "var(--ds-text-accent-gray-bolder, #091E42)"
92
+ },
93
+ '.cm-foldPlaceholder': {
94
+ // To give spacing between lines
95
+ height: '20px',
96
+ backgroundColor: "var(--ds-background-accent-gray-subtlest, #F1F2F4)",
97
+ border: 'none',
98
+ color: "var(--ds-text, #172B4D)",
99
+ outline: "1px solid ".concat("var(--ds-border-accent-gray, #758195)"),
100
+ paddingLeft: "var(--ds-space-025, 2px)",
101
+ paddingRight: "var(--ds-space-025, 2px)"
102
+ },
103
+ '.cm-foldPlaceholder:hover': {
104
+ backgroundColor: "var(--ds-background-accent-gray-subtlest-hovered, #DCDFE4)"
105
+ }
106
+ });
77
107
 
78
108
  /**
79
109
  * Copied directly from `packages/editor/editor-shared-styles/src/overflow-shadow/overflow-shadow.ts`
@@ -6,9 +6,12 @@ export const codeBlockAdvancedPlugin = ({
6
6
  }) => ({
7
7
  name: 'codeBlockAdvanced',
8
8
  nodes() {
9
+ var _config$allowCodeFold;
9
10
  return [{
10
11
  name: 'codeBlock',
11
- node: codeBlockNodeWithFixedToDOM()
12
+ node: codeBlockNodeWithFixedToDOM({
13
+ allowCodeFolding: (_config$allowCodeFold = config === null || config === void 0 ? void 0 : config.allowCodeFolding) !== null && _config$allowCodeFold !== void 0 ? _config$allowCodeFold : false
14
+ })
12
15
  }];
13
16
  },
14
17
  pmPlugins() {
@@ -17,10 +20,11 @@ export const codeBlockAdvancedPlugin = ({
17
20
  plugin: ({
18
21
  getIntl
19
22
  }) => {
20
- var _config$extensions;
23
+ var _config$extensions, _config$allowCodeFold2;
21
24
  return createPlugin({
22
25
  api,
23
26
  extensions: (_config$extensions = config === null || config === void 0 ? void 0 : config.extensions) !== null && _config$extensions !== void 0 ? _config$extensions : [],
27
+ allowCodeFolding: (_config$allowCodeFold2 = config === null || config === void 0 ? void 0 : config.allowCodeFolding) !== null && _config$allowCodeFold2 !== void 0 ? _config$allowCodeFold2 : false,
24
28
  getIntl
25
29
  });
26
30
  }
@@ -10,10 +10,11 @@ import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
10
10
  import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
11
11
  import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
12
12
  import { highlightStyle } from '../ui/syntaxHighlightingTheme';
13
- import { cmTheme } from '../ui/theme';
13
+ import { cmTheme, codeFoldingTheme } from '../ui/theme';
14
14
  import { syncCMWithPM } from './codemirrorSync/syncCMWithPM';
15
15
  import { getCMSelectionChanges } from './codemirrorSync/updateCMSelection';
16
16
  import { firstCodeBlockInDocument } from './extensions/firstCodeBlockInDocument';
17
+ import { foldGutterExtension } from './extensions/foldGutter';
17
18
  import { keymapExtension } from './extensions/keymap';
18
19
  import { manageSelectionMarker } from './extensions/manageSelectionMarker';
19
20
  import { prosemirrorDecorationPlugin } from './extensions/prosemirrorDecorations';
@@ -50,6 +51,10 @@ class CodeBlockAdvancedNodeView {
50
51
  formatMessage
51
52
  } = config.getIntl();
52
53
  const formattedAriaLabel = formatMessage(blockTypeMessages.codeblock);
54
+ const selectNode = () => {
55
+ this.selectCodeBlockNode(undefined);
56
+ this.view.focus();
57
+ };
53
58
  this.cm = new CodeMirror({
54
59
  doc: this.node.textContent,
55
60
  extensions: [...config.extensions, this.lineWrappingCompartment.of(isCodeBlockWordWrapEnabled(node) ? CodeMirror.lineWrapping : []), this.languageCompartment.of([]), this.pmDecorationsCompartment.of(this.pmFacet.compute([], () => innerDecorations)), keymapExtension({
@@ -59,11 +64,12 @@ class CodeBlockAdvancedNodeView {
59
64
  selectCodeBlockNode: this.selectCodeBlockNode.bind(this),
60
65
  onMaybeNodeSelection,
61
66
  customFindReplace: Boolean((_config$api3 = config.api) === null || _config$api3 === void 0 ? void 0 : _config$api3.findReplace)
62
- }), cmTheme, syntaxHighlighting(highlightStyle), bracketMatching(), lineNumbers({
67
+ }),
68
+ // Goes before cmTheme to override styles
69
+ config.allowCodeFolding ? [codeFoldingTheme] : [], cmTheme, syntaxHighlighting(highlightStyle), bracketMatching(), lineNumbers({
63
70
  domEventHandlers: {
64
71
  click: () => {
65
- this.selectCodeBlockNode(undefined);
66
- this.view.focus();
72
+ selectNode();
67
73
  return true;
68
74
  }
69
75
  }
@@ -78,7 +84,9 @@ class CodeBlockAdvancedNodeView {
78
84
  class: 'code-block'
79
85
  }), manageSelectionMarker(config.api), prosemirrorDecorationPlugin(this.pmFacet, view, getPos), tripleClickSelectAllExtension(), firstCodeBlockInDocument(getPos), CodeMirror.contentAttributes.of({
80
86
  'aria-label': formattedAriaLabel
81
- })]
87
+ }), config.allowCodeFolding ? [foldGutterExtension({
88
+ selectNode
89
+ })] : []]
82
90
  });
83
91
 
84
92
  // We append an additional element that fixes a selection bug on chrome if the code block
@@ -11,7 +11,7 @@ const codeBlockClassNames = {
11
11
  const MATCH_NEWLINES = new RegExp('\n', 'gu');
12
12
 
13
13
  // Based on: `packages/editor/editor-plugin-code-block/src/nodeviews/code-block.ts`
14
- const toDOM = (node, formattedAriaLabel) => {
14
+ const toDOM = (node, formattedAriaLabel, config) => {
15
15
  let totalLineCount = 1;
16
16
  node.forEach(node => {
17
17
  const text = node.text;
@@ -37,7 +37,8 @@ const toDOM = (node, formattedAriaLabel) => {
37
37
  backgroundColor: "var(--ds-background-neutral, #091E420F)",
38
38
  position: 'relative',
39
39
  width: 'var(--lineNumberGutterWidth, 2rem)',
40
- padding: "var(--ds-space-100, 8px)",
40
+ /* top and bottom | left and right */
41
+ padding: config.allowCodeFolding ? `${"var(--ds-space-100, 8px)"} ${"var(--ds-space-250, 20px)"} ${"var(--ds-space-100, 8px)"} ${"var(--ds-space-075, 6px)"}` : "var(--ds-space-100, 8px)",
41
42
  flexShrink: 0,
42
43
  fontSize: '0.875rem',
43
44
  boxSizing: 'content-box'
@@ -64,9 +65,9 @@ const toDOM = (node, formattedAriaLabel) => {
64
65
  contenteditable: 'false'
65
66
  }]];
66
67
  };
67
- export const codeBlockNodeWithFixedToDOM = () => {
68
+ export const codeBlockNodeWithFixedToDOM = config => {
68
69
  return {
69
70
  ...codeBlock,
70
- toDOM: node => toDOM(node, '')
71
+ toDOM: node => toDOM(node, '', config)
71
72
  };
72
73
  };
@@ -0,0 +1,97 @@
1
+ import { foldGutter, codeFolding } from '@codemirror/language';
2
+ import { convertToInlineCss } from '@atlaskit/editor-common/lazy-node-view';
3
+ import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
4
+ // Based on platform/packages/design-system/icon/svgs/utility/add.svg
5
+ const chevronDown = ['http://www.w3.org/2000/svg svg', {
6
+ width: '12',
7
+ height: '12',
8
+ fill: 'none',
9
+ viewBox: '0 0 16 16',
10
+ style: 'pointer-events: none;'
11
+ }, ['http://www.w3.org/2000/svg path', {
12
+ fill: 'currentcolor',
13
+ 'fill-rule': 'evenodd',
14
+ d: 'm14.53 6.03-6 6a.75.75 0 0 1-1.004.052l-.056-.052-6-6 1.06-1.06L8 10.44l5.47-5.47z',
15
+ 'clip-rule': 'evenodd',
16
+ style: 'pointer-events: none;'
17
+ }]];
18
+ const chevronRight = ['http://www.w3.org/2000/svg svg', {
19
+ width: '12',
20
+ height: '12',
21
+ fill: 'none',
22
+ viewBox: '0 0 16 16',
23
+ style: 'pointer-events: none;'
24
+ }, ['http://www.w3.org/2000/svg path', {
25
+ fill: 'currentcolor',
26
+ 'fill-rule': 'evenodd',
27
+ d: 'm8.28 1.47 6 6a.75.75 0 0 1 .052 1.004l-.052.056-6 6-1.06-1.06L12.69 8 7.22 2.53z',
28
+ 'clip-rule': 'evenodd',
29
+ style: 'pointer-events: none;'
30
+ }]];
31
+ export function foldGutterExtension({
32
+ selectNode
33
+ }) {
34
+ return [foldGutter({
35
+ domEventHandlers: {
36
+ click: (_view, _, event) => {
37
+ // If we're trying to click the button, don't select
38
+ if (event.target instanceof HTMLButtonElement && event.target.getAttribute('data-marker-dom-element')) {
39
+ return false;
40
+ }
41
+ selectNode();
42
+ return false;
43
+ }
44
+ },
45
+ markerDOM: open => {
46
+ const {
47
+ dom: downElement
48
+ } = DOMSerializer.renderSpec(document, chevronDown);
49
+ const {
50
+ dom: rightElement
51
+ } = DOMSerializer.renderSpec(document, chevronRight);
52
+ const htmlElement = document.createElement('button');
53
+ htmlElement.setAttribute('data-marker-dom-element', 'true');
54
+ htmlElement.setAttribute('data-testid', `code-block-fold-button-${open ? 'open' : 'closed'}`);
55
+ htmlElement.setAttribute('style', convertToInlineCss({
56
+ background: 'none',
57
+ color: 'inherit',
58
+ border: 'none',
59
+ padding: 0,
60
+ paddingRight: "var(--ds-space-050, 4px)",
61
+ font: 'inherit',
62
+ outline: 'inherit',
63
+ cursor: 'pointer'
64
+ }));
65
+ if (open) {
66
+ if (downElement) {
67
+ htmlElement.appendChild(downElement);
68
+ } else {
69
+ // Fallback - never called
70
+ htmlElement.textContent = '⌄';
71
+ }
72
+ } else {
73
+ if (rightElement) {
74
+ htmlElement.appendChild(rightElement);
75
+ } else {
76
+ // Fallback - never called
77
+ htmlElement.textContent = '>';
78
+ }
79
+ }
80
+ return htmlElement;
81
+ }
82
+ }), codeFolding({
83
+ placeholderDOM(view, onclick, prepared) {
84
+ const htmlElement = document.createElement('button');
85
+ htmlElement.setAttribute('data-marker-dom-element', 'true');
86
+ htmlElement.setAttribute('style', convertToInlineCss({
87
+ color: 'inherit',
88
+ font: 'inherit',
89
+ cursor: 'pointer'
90
+ }));
91
+ htmlElement.textContent = '…';
92
+ htmlElement.className = 'cm-foldPlaceholder';
93
+ htmlElement.onclick = onclick;
94
+ return htmlElement;
95
+ }
96
+ })];
97
+ }
@@ -68,6 +68,36 @@ export const cmTheme = CodeMirror.theme({
68
68
  minHeight: lineHeight
69
69
  }
70
70
  });
71
+ export const codeFoldingTheme = CodeMirror.theme({
72
+ '.cm-gutter': {
73
+ paddingLeft: "var(--ds-space-075, 6px)",
74
+ paddingTop: "var(--ds-space-100, 8px)",
75
+ paddingBottom: "var(--ds-space-100, 8px)",
76
+ paddingRight: "var(--ds-space-0, 0px)"
77
+ },
78
+ '.cm-foldGutter': {
79
+ paddingLeft: "var(--ds-space-050, 4px)"
80
+ },
81
+ '.cm-gutterElement:has([data-marker-dom-element="true"])': {
82
+ color: "var(--ds-icon-subtle, #626F86)"
83
+ },
84
+ '.cm-gutterElement:has([data-marker-dom-element="true"]):hover': {
85
+ color: "var(--ds-text-accent-gray-bolder, #091E42)"
86
+ },
87
+ '.cm-foldPlaceholder': {
88
+ // To give spacing between lines
89
+ height: '20px',
90
+ backgroundColor: "var(--ds-background-accent-gray-subtlest, #F1F2F4)",
91
+ border: 'none',
92
+ color: "var(--ds-text, #172B4D)",
93
+ outline: `1px solid ${"var(--ds-border-accent-gray, #758195)"}`,
94
+ paddingLeft: "var(--ds-space-025, 2px)",
95
+ paddingRight: "var(--ds-space-025, 2px)"
96
+ },
97
+ '.cm-foldPlaceholder:hover': {
98
+ backgroundColor: "var(--ds-background-accent-gray-subtlest-hovered, #DCDFE4)"
99
+ }
100
+ });
71
101
 
72
102
  /**
73
103
  * Copied directly from `packages/editor/editor-shared-styles/src/overflow-shadow/overflow-shadow.ts`
@@ -6,20 +6,24 @@ export var codeBlockAdvancedPlugin = function codeBlockAdvancedPlugin(_ref) {
6
6
  return {
7
7
  name: 'codeBlockAdvanced',
8
8
  nodes: function nodes() {
9
+ var _config$allowCodeFold;
9
10
  return [{
10
11
  name: 'codeBlock',
11
- node: codeBlockNodeWithFixedToDOM()
12
+ node: codeBlockNodeWithFixedToDOM({
13
+ allowCodeFolding: (_config$allowCodeFold = config === null || config === void 0 ? void 0 : config.allowCodeFolding) !== null && _config$allowCodeFold !== void 0 ? _config$allowCodeFold : false
14
+ })
12
15
  }];
13
16
  },
14
17
  pmPlugins: function pmPlugins() {
15
18
  return [{
16
19
  name: 'codeBlockAdvancedPlugin',
17
20
  plugin: function plugin(_ref2) {
18
- var _config$extensions;
21
+ var _config$extensions, _config$allowCodeFold2;
19
22
  var getIntl = _ref2.getIntl;
20
23
  return createPlugin({
21
24
  api: api,
22
25
  extensions: (_config$extensions = config === null || config === void 0 ? void 0 : config.extensions) !== null && _config$extensions !== void 0 ? _config$extensions : [],
26
+ allowCodeFolding: (_config$allowCodeFold2 = config === null || config === void 0 ? void 0 : config.allowCodeFolding) !== null && _config$allowCodeFold2 !== void 0 ? _config$allowCodeFold2 : false,
23
27
  getIntl: getIntl
24
28
  });
25
29
  }
@@ -13,10 +13,11 @@ import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
13
13
  import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
14
14
  import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
15
15
  import { highlightStyle } from '../ui/syntaxHighlightingTheme';
16
- import { cmTheme } from '../ui/theme';
16
+ import { cmTheme, codeFoldingTheme } from '../ui/theme';
17
17
  import { syncCMWithPM } from './codemirrorSync/syncCMWithPM';
18
18
  import { getCMSelectionChanges } from './codemirrorSync/updateCMSelection';
19
19
  import { firstCodeBlockInDocument } from './extensions/firstCodeBlockInDocument';
20
+ import { foldGutterExtension } from './extensions/foldGutter';
20
21
  import { keymapExtension } from './extensions/keymap';
21
22
  import { manageSelectionMarker } from './extensions/manageSelectionMarker';
22
23
  import { prosemirrorDecorationPlugin } from './extensions/prosemirrorDecorations';
@@ -60,6 +61,10 @@ var CodeBlockAdvancedNodeView = /*#__PURE__*/function () {
60
61
  var _config$getIntl = config.getIntl(),
61
62
  formatMessage = _config$getIntl.formatMessage;
62
63
  var formattedAriaLabel = formatMessage(blockTypeMessages.codeblock);
64
+ var selectNode = function selectNode() {
65
+ _this.selectCodeBlockNode(undefined);
66
+ _this.view.focus();
67
+ };
63
68
  this.cm = new CodeMirror({
64
69
  doc: this.node.textContent,
65
70
  extensions: [].concat(_toConsumableArray(config.extensions), [this.lineWrappingCompartment.of(isCodeBlockWordWrapEnabled(node) ? CodeMirror.lineWrapping : []), this.languageCompartment.of([]), this.pmDecorationsCompartment.of(this.pmFacet.compute([], function () {
@@ -71,11 +76,12 @@ var CodeBlockAdvancedNodeView = /*#__PURE__*/function () {
71
76
  selectCodeBlockNode: this.selectCodeBlockNode.bind(this),
72
77
  onMaybeNodeSelection: onMaybeNodeSelection,
73
78
  customFindReplace: Boolean((_config$api3 = config.api) === null || _config$api3 === void 0 ? void 0 : _config$api3.findReplace)
74
- }), cmTheme, syntaxHighlighting(highlightStyle), bracketMatching(), lineNumbers({
79
+ }),
80
+ // Goes before cmTheme to override styles
81
+ config.allowCodeFolding ? [codeFoldingTheme] : [], cmTheme, syntaxHighlighting(highlightStyle), bracketMatching(), lineNumbers({
75
82
  domEventHandlers: {
76
83
  click: function click() {
77
- _this.selectCodeBlockNode(undefined);
78
- _this.view.focus();
84
+ selectNode();
79
85
  return true;
80
86
  }
81
87
  }
@@ -92,7 +98,9 @@ var CodeBlockAdvancedNodeView = /*#__PURE__*/function () {
92
98
  class: 'code-block'
93
99
  }), manageSelectionMarker(config.api), prosemirrorDecorationPlugin(this.pmFacet, view, getPos), tripleClickSelectAllExtension(), firstCodeBlockInDocument(getPos), CodeMirror.contentAttributes.of({
94
100
  'aria-label': formattedAriaLabel
95
- })])
101
+ }), config.allowCodeFolding ? [foldGutterExtension({
102
+ selectNode: selectNode
103
+ })] : []])
96
104
  });
97
105
 
98
106
  // We append an additional element that fixes a selection bug on chrome if the code block
@@ -14,7 +14,7 @@ var codeBlockClassNames = {
14
14
  var MATCH_NEWLINES = new RegExp('\n', 'gu');
15
15
 
16
16
  // Based on: `packages/editor/editor-plugin-code-block/src/nodeviews/code-block.ts`
17
- var _toDOM = function toDOM(node, formattedAriaLabel) {
17
+ var _toDOM = function toDOM(node, formattedAriaLabel, config) {
18
18
  var totalLineCount = 1;
19
19
  node.forEach(function (node) {
20
20
  var text = node.text;
@@ -42,7 +42,8 @@ var _toDOM = function toDOM(node, formattedAriaLabel) {
42
42
  backgroundColor: "var(--ds-background-neutral, #091E420F)",
43
43
  position: 'relative',
44
44
  width: 'var(--lineNumberGutterWidth, 2rem)',
45
- padding: "var(--ds-space-100, 8px)",
45
+ /* top and bottom | left and right */
46
+ padding: config.allowCodeFolding ? "var(--ds-space-100, 8px)".concat(" ", "var(--ds-space-250, 20px)", " ", "var(--ds-space-100, 8px)", " ", "var(--ds-space-075, 6px)") : "var(--ds-space-100, 8px)",
46
47
  flexShrink: 0,
47
48
  fontSize: '0.875rem',
48
49
  boxSizing: 'content-box'
@@ -69,10 +70,10 @@ var _toDOM = function toDOM(node, formattedAriaLabel) {
69
70
  contenteditable: 'false'
70
71
  }]];
71
72
  };
72
- export var codeBlockNodeWithFixedToDOM = function codeBlockNodeWithFixedToDOM() {
73
+ export var codeBlockNodeWithFixedToDOM = function codeBlockNodeWithFixedToDOM(config) {
73
74
  return _objectSpread(_objectSpread({}, codeBlock), {}, {
74
75
  toDOM: function toDOM(node) {
75
- return _toDOM(node, '');
76
+ return _toDOM(node, '', config);
76
77
  }
77
78
  });
78
79
  };