@atlaskit/editor-plugin-code-block 14.0.6 → 14.1.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.
Files changed (32) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/cjs/codeBlockPlugin.js +2 -3
  3. package/dist/cjs/editor-commands/index.js +3 -0
  4. package/dist/cjs/pm-plugins/main.js +3 -2
  5. package/dist/cjs/pm-plugins/toolbar.js +6 -4
  6. package/dist/cjs/ui/CodeBlockLanguagePicker.js +13 -3
  7. package/dist/cjs/ui/CodeBlockMenuItem.js +7 -2
  8. package/dist/cjs/ui/LanguagePicker.js +72 -12
  9. package/dist/cjs/utils/auto-detect-view.js +21 -11
  10. package/dist/cjs/utils/language-detect.js +5 -1
  11. package/dist/es2019/codeBlockPlugin.js +2 -3
  12. package/dist/es2019/editor-commands/index.js +3 -0
  13. package/dist/es2019/pm-plugins/main.js +3 -2
  14. package/dist/es2019/pm-plugins/toolbar.js +6 -4
  15. package/dist/es2019/ui/CodeBlockLanguagePicker.js +13 -3
  16. package/dist/es2019/ui/CodeBlockMenuItem.js +8 -3
  17. package/dist/es2019/ui/LanguagePicker.js +61 -7
  18. package/dist/es2019/utils/auto-detect-view.js +22 -11
  19. package/dist/es2019/utils/language-detect.js +3 -2
  20. package/dist/esm/codeBlockPlugin.js +2 -3
  21. package/dist/esm/editor-commands/index.js +3 -0
  22. package/dist/esm/pm-plugins/main.js +3 -2
  23. package/dist/esm/pm-plugins/toolbar.js +6 -4
  24. package/dist/esm/ui/CodeBlockLanguagePicker.js +13 -3
  25. package/dist/esm/ui/CodeBlockMenuItem.js +7 -2
  26. package/dist/esm/ui/LanguagePicker.js +72 -12
  27. package/dist/esm/utils/auto-detect-view.js +21 -11
  28. package/dist/esm/utils/language-detect.js +5 -2
  29. package/dist/types/ui/CodeBlockLanguagePicker.d.ts +1 -1
  30. package/dist/types/ui/CodeBlockMenuItem.d.ts +2 -1
  31. package/dist/types/ui/LanguagePicker.d.ts +2 -1
  32. package/package.json +11 -8
package/CHANGELOG.md CHANGED
@@ -1,5 +1,38 @@
1
1
  # @atlaskit/editor-plugin-code-block
2
2
 
3
+ ## 14.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`5bb7d8aa14c4e`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/5bb7d8aa14c4e) -
8
+ Fix code block language picker trigger height in legacy toolbar
9
+ - [`49b8e9a322976`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/49b8e9a322976) -
10
+ Reduce q4 lovability experiment exposure noise in code block setup and bookkeeping paths
11
+ - Updated dependencies
12
+
13
+ ## 14.1.0
14
+
15
+ ### Minor Changes
16
+
17
+ - [`b483e293da76f`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/b483e293da76f) -
18
+ Add a gated `isSuggested` attribute to block menu element transformed analytics events so
19
+ suggested and non-suggested transform selections can be distinguished. The event attribute is
20
+ gated behind the `platform_editor_blocks_patch_2` feature gate.
21
+
22
+ ### Patch Changes
23
+
24
+ - [`64a0b50380570`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/64a0b50380570) - -
25
+ Improve TypeScript auto-detection for class fields and typed arrow function params/return types.
26
+ - Avoid unnecessary code block node updates when auto-detection re-detects the same language.
27
+ - Prevent wrap toggles from scrolling long code blocks to the top.
28
+ - Keep the language picker placement stable while it is open.
29
+ - Reset language picker search state on reopen so recent selections are not reported as search
30
+ selections.
31
+ - Prevent language picker close/selection from scrolling partially visible code blocks while
32
+ restoring editor focus.
33
+ - Add auto-detection analytics metadata to distinguish initial detection from re-detection.
34
+ - Updated dependencies
35
+
3
36
  ## 14.0.6
4
37
 
5
38
  ### Patch Changes
@@ -14,7 +14,6 @@ var _blockMenu = require("@atlaskit/editor-common/block-menu");
14
14
  var _messages = require("@atlaskit/editor-common/messages");
15
15
  var _quickInsert = require("@atlaskit/editor-common/quick-insert");
16
16
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
17
- var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
18
17
  var _expValEqualsNoExposure = require("@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure");
19
18
  var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
20
19
  var _editorCommands = require("./editor-commands");
@@ -58,7 +57,7 @@ var codeBlockPlugin = function codeBlockPlugin(_ref) {
58
57
  nodes: function nodes() {
59
58
  return [{
60
59
  name: 'codeBlock',
61
- node: (0, _expValEquals.expValEquals)('platform_editor_code_block_q4_lovability', 'isEnabled', true) ? _adfSchema.codeBlock : (0, _platformFeatureFlags.fg)('platform_editor_adf_with_localid') ? _adfSchema.codeBlockWithLocalId : _adfSchema.codeBlock
60
+ node: (0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_code_block_q4_lovability', 'isEnabled', true) ? _adfSchema.codeBlock : (0, _platformFeatureFlags.fg)('platform_editor_adf_with_localid') ? _adfSchema.codeBlockWithLocalId : _adfSchema.codeBlock
62
61
  }];
63
62
  },
64
63
  getSharedState: function getSharedState(state) {
@@ -90,7 +89,7 @@ var codeBlockPlugin = function codeBlockPlugin(_ref) {
90
89
  var schema = _ref3.schema;
91
90
  return (0, _inputRule.createCodeBlockInputRule)(schema, api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions);
92
91
  }
93
- }].concat((0, _toConsumableArray2.default)((0, _expValEquals.expValEquals)('platform_editor_code_block_q4_lovability', 'isEnabled', true) && (0, _platformFeatureFlags.fg)('platform_editor_code_block_language_detection_flow') ? [{
92
+ }].concat((0, _toConsumableArray2.default)((0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_code_block_q4_lovability', 'isEnabled', true) && (0, _platformFeatureFlags.fg)('platform_editor_code_block_language_detection_flow') ? [{
94
93
  name: 'codeBlockAutoDetect',
95
94
  plugin: function plugin() {
96
95
  return (0, _autoDetect.createAutoDetectPlugin)(api);
@@ -483,6 +483,9 @@ var toggleWordWrapStateForCodeBlockNode = exports.toggleWordWrapStateForCodeBloc
483
483
  tr.setNodeMarkup(codeBlock.pos, undefined, _objectSpread(_objectSpread({}, codeBlockNode.attrs), {}, {
484
484
  wrap: updatedToggleState
485
485
  }));
486
+ if ((0, _platformFeatureFlags.fg)('platform_editor_code_block_dogfooding_patch')) {
487
+ tr.setMeta('scrollIntoView', false);
488
+ }
486
489
  } else {
487
490
  _codeBlock.codeBlockWrappedStates.set(codeBlockNode, updatedToggleState);
488
491
  }
@@ -15,6 +15,7 @@ var _transforms = require("@atlaskit/editor-common/transforms");
15
15
  var _state = require("@atlaskit/editor-prosemirror/state");
16
16
  var _view = require("@atlaskit/editor-prosemirror/view");
17
17
  var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
18
+ var _expValEqualsNoExposure = require("@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure");
18
19
  var _editorCommands = require("../editor-commands");
19
20
  var _codeBlock2 = require("../nodeviews/code-block");
20
21
  var _classNames = require("../ui/class-names");
@@ -130,7 +131,7 @@ var createPlugin = exports.createPlugin = function createPlugin(_ref) {
130
131
  isNodeSelected: tr.selection instanceof _state.NodeSelection,
131
132
  decorations: updatedDecorationSet
132
133
  });
133
- if (!(0, _expValEquals.expValEquals)('platform_editor_code_block_q4_lovability', 'isEnabled', true)) {
134
+ if (!(0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_code_block_q4_lovability', 'isEnabled', true)) {
134
135
  return newPluginState;
135
136
  }
136
137
 
@@ -158,7 +159,7 @@ var createPlugin = exports.createPlugin = function createPlugin(_ref) {
158
159
  shouldIgnoreFollowingMutations: meta.data
159
160
  });
160
161
  }
161
- if ((0, _expValEquals.expValEquals)('platform_editor_code_block_q4_lovability', 'isEnabled', true)) {
162
+ if ((0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_code_block_q4_lovability', 'isEnabled', true)) {
162
163
  // Failed/unchanged format results and dismissals are meta-only.
163
164
  return (0, _formatCodeState.applyFormatCodeMeta)(pluginState, meta);
164
165
  }
@@ -20,6 +20,7 @@ var _listNumbered = _interopRequireDefault(require("@atlaskit/icon/core/list-num
20
20
  var _textWrap = _interopRequireDefault(require("@atlaskit/icon/core/text-wrap"));
21
21
  var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
22
22
  var _expValEquals = require("@atlaskit/tmp-editor-statsig/exp-val-equals");
23
+ var _expValEqualsNoExposure = require("@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure");
23
24
  var _editorCommands = require("../editor-commands");
24
25
  var _CodeBlockLanguagePicker = require("../ui/CodeBlockLanguagePicker");
25
26
  var _WrapIcon = require("../ui/icons/WrapIcon");
@@ -143,6 +144,7 @@ var getToolbarConfig = exports.getToolbarConfig = function getToolbarConfig() {
143
144
  options: languageSelectOptions,
144
145
  filterOption: languageListFilter
145
146
  };
147
+ var areAnyNewToolbarFlagsEnabled = (0, _toolbarFlagCheck.areToolbarFlagsEnabled)(Boolean(api === null || api === void 0 ? void 0 : api.toolbar));
146
148
  var languagePicker;
147
149
  if ((0, _expValEquals.expValEquals)('platform_editor_code_block_q4_lovability', 'isEnabled', true) && (0, _platformFeatureFlags.fg)('platform_editor_code_block_add_line_number_button')) {
148
150
  var autoDetectEntry = typeof localId === 'string' && (0, _platformFeatureFlags.fg)('platform_editor_code_block_language_detection_flow') ? autoDetectState === null || autoDetectState === void 0 ? void 0 : autoDetectState.languageDetectionMap[localId] : undefined;
@@ -165,7 +167,8 @@ var getToolbarConfig = exports.getToolbarConfig = function getToolbarConfig() {
165
167
  editorView: view,
166
168
  filterOption: languageListFilter,
167
169
  formatMessage: formatMessage,
168
- languagePickerOptions: languagePickerOptions
170
+ languagePickerOptions: languagePickerOptions,
171
+ triggerSpacing: !areAnyNewToolbarFlagsEnabled && (0, _platformFeatureFlags.fg)('platform_editor_code_block_dogfooding_patch') ? 'compact' : 'default'
169
172
  });
170
173
  }
171
174
  };
@@ -173,7 +176,6 @@ var getToolbarConfig = exports.getToolbarConfig = function getToolbarConfig() {
173
176
  var separator = {
174
177
  type: 'separator'
175
178
  };
176
- var areAnyNewToolbarFlagsEnabled = (0, _toolbarFlagCheck.areToolbarFlagsEnabled)(Boolean(api === null || api === void 0 ? void 0 : api.toolbar));
177
179
  var copyToClipboardItems = !allowCopyToClipboard ? [] : [{
178
180
  id: 'editor.codeBlock.copy',
179
181
  type: 'button',
@@ -246,12 +248,12 @@ var getToolbarConfig = exports.getToolbarConfig = function getToolbarConfig() {
246
248
  };
247
249
  copyAndDeleteButtonMenuItems = [separator].concat(copyToClipboardItems, [deleteButton]);
248
250
  }
249
- var codeBlockWrapButtonTitle = (0, _expValEquals.expValEquals)('platform_editor_code_block_q4_lovability', 'isEnabled', true) ? formatMessage(isWrapped ? _messages.codeBlockButtonMessages.unwrapCodeLabel : _messages.codeBlockButtonMessages.wrapCodeLabel) : formatMessage(_messages.codeBlockButtonMessages.wrapCode);
251
+ var codeBlockWrapButtonTitle = (0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_code_block_q4_lovability', 'isEnabled', true) ? formatMessage(isWrapped ? _messages.codeBlockButtonMessages.unwrapCodeLabel : _messages.codeBlockButtonMessages.wrapCodeLabel) : formatMessage(_messages.codeBlockButtonMessages.wrapCode);
250
252
  var codeBlockWrapButton = {
251
253
  id: 'editor.codeBlock.wrap',
252
254
  type: 'button',
253
255
  // Toggling button now writes to ADF, hence it should be available in view mode
254
- supportsViewMode: !(0, _expValEquals.expValEquals)('platform_editor_code_block_q4_lovability', 'isEnabled', true),
256
+ supportsViewMode: !(0, _expValEqualsNoExposure.expValEqualsNoExposure)('platform_editor_code_block_q4_lovability', 'isEnabled', true),
255
257
  icon: _textWrap.default,
256
258
  iconFallback: _WrapIcon.WrapIcon,
257
259
  onClick: (0, _editorCommands.toggleWordWrapStateForCodeBlockNode)(editorAnalyticsAPI),
@@ -21,7 +21,8 @@ var CodeBlockLanguagePicker = exports.CodeBlockLanguagePicker = function CodeBlo
21
21
  editorView = _ref.editorView,
22
22
  filterOption = _ref.filterOption,
23
23
  formatMessage = _ref.formatMessage,
24
- languagePickerOptions = _ref.languagePickerOptions;
24
+ languagePickerOptions = _ref.languagePickerOptions,
25
+ triggerSpacing = _ref.triggerSpacing;
25
26
  var _useState = (0, _react.useState)(function () {
26
27
  return (0, _recentLanguages.getRecentLanguages)();
27
28
  }),
@@ -35,11 +36,19 @@ var CodeBlockLanguagePicker = exports.CodeBlockLanguagePicker = function CodeBlo
35
36
  var _api$analytics;
36
37
  var command = option.value === _languagePickerOptions.DETECT_LANGUAGE_VALUE && (0, _platformFeatureFlags.fg)('platform_editor_code_block_language_detection_flow') ? (0, _editorCommands.detectLanguage)() : (0, _editorCommands.changeLanguage)(api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions)(option.value, selectionSource);
37
38
  var commandSucceeded = command(editorView.state, editorView.dispatch);
39
+ if ((0, _platformFeatureFlags.fg)('platform_editor_code_block_dogfooding_patch')) {
40
+ requestAnimationFrame(function () {
41
+ // Let PopupSelect/FocusLock finish returning focus to the trigger, then restore the editor.
42
+ api === null || api === void 0 || api.core.actions.focus({
43
+ scrollIntoView: false
44
+ });
45
+ });
46
+ }
38
47
  if (commandSucceeded && option.value !== _languagePickerOptions.DETECT_LANGUAGE_VALUE) {
39
48
  (0, _recentLanguages.saveRecentLanguage)(option.value);
40
49
  setRecentLanguageValues((0, _recentLanguages.getRecentLanguages)());
41
50
  }
42
- }, [api === null || api === void 0 || (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : _api$analytics2.actions, editorView]);
51
+ }, [api === null || api === void 0 || (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : _api$analytics2.actions, api === null || api === void 0 ? void 0 : api.core.actions, editorView]);
43
52
  return /*#__PURE__*/_react.default.createElement(_LanguagePicker.LanguagePicker, {
44
53
  defaultValue: defaultValue,
45
54
  filterOption: filterOption,
@@ -47,6 +56,7 @@ var CodeBlockLanguagePicker = exports.CodeBlockLanguagePicker = function CodeBlo
47
56
  languagePickerOptions: languagePickerOptions,
48
57
  recentLanguageValues: recentLanguageValues,
49
58
  onMenuOpen: refreshRecentLanguages,
50
- onSelection: handleSelection
59
+ onSelection: handleSelection,
60
+ triggerSpacing: triggerSpacing
51
61
  });
52
62
  };
@@ -13,7 +13,8 @@ var _editorToolbar = require("@atlaskit/editor-toolbar");
13
13
  var _angleBrackets = _interopRequireDefault(require("@atlaskit/icon/core/angle-brackets"));
14
14
  var NODE_NAME = 'codeBlock';
15
15
  var CodeBlockMenuItem = function CodeBlockMenuItem(_ref) {
16
- var api = _ref.api;
16
+ var api = _ref.api,
17
+ isSuggested = _ref.isSuggested;
17
18
  var _useIntl = (0, _reactIntl.useIntl)(),
18
19
  formatMessage = _useIntl.formatMessage;
19
20
  var handleClick = function handleClick(event) {
@@ -24,6 +25,7 @@ var CodeBlockMenuItem = function CodeBlockMenuItem(_ref) {
24
25
  var tr = _ref2.tr;
25
26
  var command = api === null || api === void 0 || (_api$blockMenu = api.blockMenu) === null || _api$blockMenu === void 0 ? void 0 : _api$blockMenu.commands.transformNode(tr.doc.type.schema.nodes.codeBlock, {
26
27
  inputMethod: inputMethod,
28
+ isSuggested: isSuggested,
27
29
  triggeredFrom: triggeredFrom,
28
30
  targetTypeName: NODE_NAME
29
31
  });
@@ -42,8 +44,11 @@ var CodeBlockMenuItem = function CodeBlockMenuItem(_ref) {
42
44
  };
43
45
  var createCodeBlockMenuItem = exports.createCodeBlockMenuItem = function createCodeBlockMenuItem(api) {
44
46
  return function () {
47
+ var _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
48
+ isSuggested = _ref3.isSuggested;
45
49
  return /*#__PURE__*/_react.default.createElement(CodeBlockMenuItem, {
46
- api: api
50
+ api: api,
51
+ isSuggested: isSuggested
47
52
  });
48
53
  };
49
54
  };
@@ -9,12 +9,14 @@ Object.defineProperty(exports, "__esModule", {
9
9
  exports.LanguagePicker = void 0;
10
10
  require("./LanguagePicker.compiled.css");
11
11
  var _runtime = require("@compiled/react/runtime");
12
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
12
13
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
13
14
  var _react = _interopRequireWildcard(require("react"));
14
15
  var _new = _interopRequireDefault(require("@atlaskit/button/new"));
15
16
  var _messages = require("@atlaskit/editor-common/messages");
16
17
  var _editorSharedStyles = require("@atlaskit/editor-shared-styles");
17
18
  var _chevronDown = _interopRequireDefault(require("@atlaskit/icon/core/chevron-down"));
19
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
18
20
  var _compiled = require("@atlaskit/primitives/compiled");
19
21
  var _select = require("@atlaskit/select");
20
22
  var _languagePickerOptions = require("./language-picker-options");
@@ -60,6 +62,11 @@ var menuPopperProps = {
60
62
  enabled: false
61
63
  }]
62
64
  };
65
+ var focusWithoutScrolling = function focusWithoutScrolling(element) {
66
+ element.focus({
67
+ preventScroll: true
68
+ });
69
+ };
63
70
  var getRecentlyUsedLanguages = function getRecentlyUsedLanguages(recentLanguageValues, optionsByValue) {
64
71
  var recentlyUsedLanguages = [];
65
72
  var _iterator = _createForOfIteratorHelper(recentLanguageValues),
@@ -88,13 +95,19 @@ var LanguagePicker = exports.LanguagePicker = function LanguagePicker(_ref) {
88
95
  _ref$recentLanguageVa = _ref.recentLanguageValues,
89
96
  recentLanguageValues = _ref$recentLanguageVa === void 0 ? [] : _ref$recentLanguageVa,
90
97
  onMenuOpen = _ref.onMenuOpen,
91
- onSelection = _ref.onSelection;
98
+ onSelection = _ref.onSelection,
99
+ _ref$triggerSpacing = _ref.triggerSpacing,
100
+ triggerSpacing = _ref$triggerSpacing === void 0 ? 'default' : _ref$triggerSpacing;
92
101
  var label = (_defaultValue$label = defaultValue === null || defaultValue === void 0 ? void 0 : defaultValue.label) !== null && _defaultValue$label !== void 0 ? _defaultValue$label : formatMessage(_messages.codeBlockButtonMessages.selectLanguage);
93
102
  var selectLanguageLabel = formatMessage(_messages.codeBlockButtonMessages.selectLanguage);
94
103
  var _useState = (0, _react.useState)(false),
95
104
  _useState2 = (0, _slicedToArray2.default)(_useState, 2),
96
105
  hasSearchQuery = _useState2[0],
97
106
  setHasSearchQuery = _useState2[1];
107
+ var _useState3 = (0, _react.useState)(),
108
+ _useState4 = (0, _slicedToArray2.default)(_useState3, 2),
109
+ lockedPopperPlacement = _useState4[0],
110
+ setLockedPopperPlacement = _useState4[1];
98
111
  var inputValueRef = (0, _react.useRef)('');
99
112
  var optionsByValue = (0, _react.useMemo)(function () {
100
113
  return new Map(languagePickerOptions.map(function (option) {
@@ -116,6 +129,29 @@ var LanguagePicker = exports.LanguagePicker = function LanguagePicker(_ref) {
116
129
  return group.options;
117
130
  });
118
131
  }, [options]);
132
+ var stableMenuPopperProps = (0, _react.useMemo)(function () {
133
+ return {
134
+ // Allow Popper to choose top on the first open when bottom has no room, then lock the
135
+ // chosen placement so later hover/search renders do not move the open picker.
136
+ placement: lockedPopperPlacement !== null && lockedPopperPlacement !== void 0 ? lockedPopperPlacement : 'bottom-start',
137
+ modifiers: [{
138
+ name: 'offset',
139
+ options: {
140
+ offset: [0, 8]
141
+ }
142
+ }, {
143
+ name: 'preventOverflow',
144
+ enabled: false
145
+ }].concat((0, _toConsumableArray2.default)(lockedPopperPlacement ? [{
146
+ name: 'flip',
147
+ enabled: false
148
+ }] : [])),
149
+ onFirstUpdate: function onFirstUpdate(_ref2) {
150
+ var placement = _ref2.placement;
151
+ setLockedPopperPlacement(placement);
152
+ }
153
+ };
154
+ }, [lockedPopperPlacement]);
119
155
  var handleChange = (0, _react.useCallback)(function (option) {
120
156
  var _option$selectionSour;
121
157
  if (!option) {
@@ -128,21 +164,45 @@ var LanguagePicker = exports.LanguagePicker = function LanguagePicker(_ref) {
128
164
  var handleInputChange = (0, _react.useCallback)(function (newInputValue, actionMeta) {
129
165
  // React-select clears the input as part of selecting a value before onChange fires.
130
166
  // Keep the last user-typed query so handleChange can report search selections correctly.
131
- if (!actionMeta || actionMeta.action === 'input-change') {
167
+ var isInputChange = !actionMeta || actionMeta.action === 'input-change';
168
+ if (isInputChange) {
132
169
  inputValueRef.current = newInputValue;
133
170
  }
134
- setHasSearchQuery(newInputValue.trim().length > 0);
135
- return newInputValue;
171
+ if (isInputChange || !(0, _platformFeatureFlags.fg)('platform_editor_code_block_dogfooding_patch')) {
172
+ setHasSearchQuery(newInputValue.trim().length > 0);
173
+ return newInputValue;
174
+ }
175
+ return inputValueRef.current;
176
+ }, []);
177
+ var handleMenuOpen = (0, _react.useCallback)(function () {
178
+ if ((0, _platformFeatureFlags.fg)('platform_editor_code_block_dogfooding_patch')) {
179
+ inputValueRef.current = '';
180
+ setHasSearchQuery(false);
181
+ setLockedPopperPlacement(undefined);
182
+ }
183
+ onMenuOpen === null || onMenuOpen === void 0 || onMenuOpen();
184
+ }, [onMenuOpen]);
185
+ var handleTriggerMouseDown = (0, _react.useCallback)(function (event) {
186
+ if ((0, _platformFeatureFlags.fg)('platform_editor_code_block_dogfooding_patch')) {
187
+ // PopupSelect's FocusLock returns focus to the element that was focused before the
188
+ // picker opened. If that is the editor/code block, closing the picker can scroll the
189
+ // whole code block into view. Focus the trigger first without scrolling so FocusLock
190
+ // returns to the trigger; see CodeBlockLanguagePicker's handleSelection for restoring
191
+ // editor focus.
192
+ focusWithoutScrolling(event.currentTarget);
193
+ }
136
194
  }, []);
137
- var renderTarget = (0, _react.useCallback)(function (_ref2) {
138
- var isOpen = _ref2.isOpen,
139
- ref = _ref2.ref,
140
- onKeyDown = _ref2.onKeyDown,
141
- ariaControls = _ref2['aria-controls'];
195
+ var renderTarget = (0, _react.useCallback)(function (_ref3) {
196
+ var isOpen = _ref3.isOpen,
197
+ ref = _ref3.ref,
198
+ onKeyDown = _ref3.onKeyDown,
199
+ ariaControls = _ref3['aria-controls'];
142
200
  return /*#__PURE__*/_react.default.createElement("div", {
143
201
  className: (0, _runtime.ax)([styles.trigger])
144
202
  }, /*#__PURE__*/_react.default.createElement(_new.default, {
203
+ spacing: triggerSpacing,
145
204
  shouldFitContainer: true,
205
+ onMouseDown: handleTriggerMouseDown,
146
206
  onKeyDown: onKeyDown,
147
207
  ref: ref,
148
208
  iconAfter: _chevronDown.default,
@@ -151,7 +211,7 @@ var LanguagePicker = exports.LanguagePicker = function LanguagePicker(_ref) {
151
211
  "aria-controls": ariaControls,
152
212
  testId: "code-block-language-picker-trigger"
153
213
  }, label));
154
- }, [label]);
214
+ }, [label, triggerSpacing, handleTriggerMouseDown]);
155
215
  return /*#__PURE__*/_react.default.createElement(_select.PopupSelect, {
156
216
  components: popupSelectComponents,
157
217
  filterOption: filterOption,
@@ -161,9 +221,9 @@ var LanguagePicker = exports.LanguagePicker = function LanguagePicker(_ref) {
161
221
  menuPlacement: "auto",
162
222
  onChange: handleChange,
163
223
  onInputChange: handleInputChange,
164
- onMenuOpen: onMenuOpen,
224
+ onMenuOpen: handleMenuOpen,
165
225
  options: hasSearchQuery ? searchOptions : options,
166
- popperProps: menuPopperProps,
226
+ popperProps: (0, _platformFeatureFlags.fg)('platform_editor_code_block_dogfooding_patch') ? stableMenuPopperProps : menuPopperProps,
167
227
  searchThreshold: -1,
168
228
  target: renderTarget,
169
229
  testId: "code-block-language-picker",
@@ -8,6 +8,7 @@ exports.syncPendingDetectionTimers = void 0;
8
8
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
9
9
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
10
10
  var _analytics = require("@atlaskit/editor-common/analytics");
11
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
11
12
  var _actions = require("../pm-plugins/actions");
12
13
  var _autoDetectState = require("../pm-plugins/auto-detect-state");
13
14
  var _autoDetectState2 = require("./auto-detect-state");
@@ -43,6 +44,7 @@ var runPendingDetection = function runPendingDetection(view, localId, api) {
43
44
  }
44
45
  var detectedLanguage = (0, _languageDetect.detectLanguage)(found.node.textContent);
45
46
  var detectionResult = detectedLanguage ? 'detected' : 'noneDetected';
47
+ var detectionPhase = entry.detectionResult ? 'redetection' : 'initial';
46
48
  // Keep a previous auto-detected language when the latest snippet is too weak to classify.
47
49
  var shouldPreserveAutoDetectedLanguage = !detectedLanguage && Boolean(entry.autoDetectedLanguage) && found.node.attrs.language === entry.autoDetectedLanguage;
48
50
  var nextEntry = _objectSpread(_objectSpread({}, (0, _autoDetectState2.createAutoDetectEntry)(found.node, found.pos, false, entry)), {}, {
@@ -52,10 +54,12 @@ var runPendingDetection = function runPendingDetection(view, localId, api) {
52
54
 
53
55
  // If there is no confident detection, record the result without clearing user-visible language.
54
56
  var shouldOnlyUpdateDetectionState = !detectedLanguage && (!found.node.attrs.language || shouldPreserveAutoDetectedLanguage);
57
+ var hasDetectedLanguageChange = Boolean(detectedLanguage) && found.node.attrs.language !== detectedLanguage;
58
+ var shouldClearStaleLanguage = !detectedLanguage && Boolean(found.node.attrs.language) && !shouldPreserveAutoDetectedLanguage;
59
+ var shouldUpdateNodeLanguage = (0, _platformFeatureFlags.fg)('platform_editor_code_block_dogfooding_patch') ? hasDetectedLanguageChange || shouldClearStaleLanguage : !shouldOnlyUpdateDetectionState;
55
60
  api === null || api === void 0 || (_api$core = api.core) === null || _api$core === void 0 || _api$core.actions.execute(function (_ref) {
56
- var _api$analytics;
57
61
  var tr = _ref.tr;
58
- if (!shouldOnlyUpdateDetectionState) {
62
+ if (shouldUpdateNodeLanguage) {
59
63
  tr.setNodeMarkup(found.pos, undefined, _objectSpread(_objectSpread({}, found.node.attrs), {}, {
60
64
  language: detectedLanguage
61
65
  }), found.node.marks);
@@ -67,15 +71,21 @@ var runPendingDetection = function runPendingDetection(view, localId, api) {
67
71
  entry: nextEntry
68
72
  }
69
73
  });
70
- api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 || _api$analytics.actions.attachAnalyticsEvent({
71
- action: _analytics.ACTION.LANGUAGE_AUTO_DETECTED,
72
- actionSubject: _analytics.ACTION_SUBJECT.CODE_BLOCK,
73
- attributes: {
74
- language: detectedLanguage !== null && detectedLanguage !== void 0 ? detectedLanguage : 'none',
75
- detectionResult: detectionResult
76
- },
77
- eventType: _analytics.EVENT_TYPE.TRACK
78
- })(tr);
74
+ // When platform_editor_code_block_dogfooding_patch is cleaned up, merge this with the previous shouldUpdateNodeLanguage check.
75
+ if (!(0, _platformFeatureFlags.fg)('platform_editor_code_block_dogfooding_patch') || shouldUpdateNodeLanguage) {
76
+ var _api$analytics;
77
+ api === null || api === void 0 || (_api$analytics = api.analytics) === null || _api$analytics === void 0 || _api$analytics.actions.attachAnalyticsEvent({
78
+ action: _analytics.ACTION.LANGUAGE_AUTO_DETECTED,
79
+ actionSubject: _analytics.ACTION_SUBJECT.CODE_BLOCK,
80
+ attributes: _objectSpread({
81
+ language: detectedLanguage !== null && detectedLanguage !== void 0 ? detectedLanguage : 'none',
82
+ detectionResult: detectionResult
83
+ }, (0, _platformFeatureFlags.fg)('platform_editor_code_block_dogfooding_patch') ? {
84
+ detectionPhase: detectionPhase
85
+ } : {}),
86
+ eventType: _analytics.EVENT_TYPE.TRACK
87
+ })(tr);
88
+ }
79
89
  // Language detection runs in the background and should not move the viewport.
80
90
  return tr.setMeta('scrollIntoView', false);
81
91
  });
@@ -7,6 +7,7 @@ Object.defineProperty(exports, "__esModule", {
7
7
  exports.detectLanguage = void 0;
8
8
  var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
9
9
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
10
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
10
11
  /* eslint-disable require-unicode-regexp */
11
12
 
12
13
  // Conservative weighted-regex heuristic for common high-confidence snippets, not a full classifier.
@@ -28,6 +29,9 @@ var scorePatterns = function scorePatterns(code, patterns) {
28
29
  return score + (hasPattern(code, pattern) ? value : 0);
29
30
  }, 0);
30
31
  };
32
+ var scoreEnhancedTypeScriptPatterns = function scoreEnhancedTypeScriptPatterns(code) {
33
+ return (0, _platformFeatureFlags.fg)('platform_editor_code_block_dogfooding_patch') ? scorePatterns(code, [[/\b(?:private|protected|public|readonly)\s+[\w$]+\s*=/, 5], [/\([^)]{0,500}\b[\w$]+\??:\s*[A-Za-z_$][\w$<>{}\[\]|&,\s.?]{0,200}[^)]{0,500}\)\s*(?::\s*[A-Za-z_$][\w$<>{}\[\]|&,\s.?]{0,200})?\s*=>/, 5]]) : 0;
34
+ };
31
35
  var looksLikeHtmlTagPair = function looksLikeHtmlTagPair(code) {
32
36
  var openTags = new Set();
33
37
  var tagPattern = /<\/?([a-z][a-z0-9-]*)\b[^<>]{0,500}>/gi;
@@ -77,7 +81,7 @@ var getLanguageScores = function getLanguageScores(code) {
77
81
  score: scorePatterns(code, [[/\bSELECT\b[\s\S]+\bFROM\b/i, 5], [/\b(?:INSERT\s+INTO|UPDATE\b[\s\S]+\bSET\b|DELETE\s+FROM|CREATE\s+TABLE|ALTER\s+TABLE)\b/i, 5], [/\b(?:JOIN|WHERE|GROUP\s+BY|ORDER\s+BY|LIMIT)\b/i, 2]])
78
82
  }, {
79
83
  language: 'typescript',
80
- score: scorePatterns(code, [[/\b(?:interface|type)\s+[A-Z_$][\w$]*(?:\s*[=<{])/, 5], [/\bimport\s+type\b|\bexport\s+type\b/, 4], [/\b(?:const|let|var)\s+[\w$]+\s*:\s*[A-Za-z_$][\w$<>|\[\],\s]*/, 4], [/\)\s*:\s*(?:Promise<)?[A-Za-z_$][\w$<>|\[\],\s]*(?:>|\s)?\s*=>?\s*[{;]/, 3], [/\b(?:as\s+const|implements\s+[A-Z_$]|enum\s+[A-Z_$])/, 3]])
84
+ score: scorePatterns(code, [[/\b(?:interface|type)\s+[A-Z_$][\w$]*(?:\s*[=<{])/, 5], [/\bimport\s+type\b|\bexport\s+type\b/, 4], [/\b(?:const|let|var)\s+[\w$]+\s*:\s*[A-Za-z_$][\w$<>|\[\],\s]*/, 4], [/\)\s*:\s*(?:Promise<)?[A-Za-z_$][\w$<>|\[\],\s]*(?:>|\s)?\s*=>?\s*[{;]/, 3], [/\b(?:as\s+const|implements\s+[A-Z_$]|enum\s+[A-Z_$])/, 3]]) + scoreEnhancedTypeScriptPatterns(code)
81
85
  }, {
82
86
  language: 'javascript',
83
87
  score: scorePatterns(code, [[/\b(?:const|let|var)\s+[\w$]+\s*=/, 3], [/\bfunction\s*[\w$]*\s*\([^)]*\)\s*\{/, 3], [/=>\s*(?:\{|[\w$'"`(])/, 3], [/\b(?:import|export)\s+(?:[\w${}*,\s]+\s+from\s+)?['"][^'"]+['"]/, 3], [/\b(?:console\.log|document\.|window\.|require\()|\bmodule\.exports\b/, 3]])
@@ -5,7 +5,6 @@ import { TRANSFORM_STRUCTURE_MENU_SECTION, TRANSFORM_STRUCTURE_CODE_BLOCK_MENU_I
5
5
  import { blockTypeMessages } from '@atlaskit/editor-common/messages';
6
6
  import { IconCode } from '@atlaskit/editor-common/quick-insert';
7
7
  import { fg } from '@atlaskit/platform-feature-flags';
8
- import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
9
8
  import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
10
9
  import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
11
10
  import { createInsertCodeBlockTransaction, insertCodeBlockWithAnalytics } from './editor-commands';
@@ -48,7 +47,7 @@ const codeBlockPlugin = ({
48
47
  nodes() {
49
48
  return [{
50
49
  name: 'codeBlock',
51
- node: expValEquals('platform_editor_code_block_q4_lovability', 'isEnabled', true) ? codeBlock : fg('platform_editor_adf_with_localid') ? codeBlockWithLocalId : codeBlock
50
+ node: expValEqualsNoExposure('platform_editor_code_block_q4_lovability', 'isEnabled', true) ? codeBlock : fg('platform_editor_adf_with_localid') ? codeBlockWithLocalId : codeBlock
52
51
  }];
53
52
  },
54
53
  getSharedState(state) {
@@ -81,7 +80,7 @@ const codeBlockPlugin = ({
81
80
  var _api$analytics;
82
81
  return createCodeBlockInputRule(schema, api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions);
83
82
  }
84
- }, ...(expValEquals('platform_editor_code_block_q4_lovability', 'isEnabled', true) && fg('platform_editor_code_block_language_detection_flow') ? [{
83
+ }, ...(expValEqualsNoExposure('platform_editor_code_block_q4_lovability', 'isEnabled', true) && fg('platform_editor_code_block_language_detection_flow') ? [{
85
84
  name: 'codeBlockAutoDetect',
86
85
  plugin: () => createAutoDetectPlugin(api)
87
86
  }] : []), {
@@ -492,6 +492,9 @@ export const toggleWordWrapStateForCodeBlockNode = editorAnalyticsAPI => (state,
492
492
  ...codeBlockNode.attrs,
493
493
  wrap: updatedToggleState
494
494
  });
495
+ if (fg('platform_editor_code_block_dogfooding_patch')) {
496
+ tr.setMeta('scrollIntoView', false);
497
+ }
495
498
  } else {
496
499
  codeBlockWrappedStates.set(codeBlockNode, updatedToggleState);
497
500
  }
@@ -7,6 +7,7 @@ import { findCodeBlock } from '@atlaskit/editor-common/transforms';
7
7
  import { NodeSelection } from '@atlaskit/editor-prosemirror/state';
8
8
  import { DecorationSet } from '@atlaskit/editor-prosemirror/view';
9
9
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
10
+ import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
10
11
  import { ignoreFollowingMutations, resetShouldIgnoreFollowingMutations } from '../editor-commands';
11
12
  import { codeBlockNodeView } from '../nodeviews/code-block';
12
13
  import { codeBlockClassNames } from '../ui/class-names';
@@ -119,7 +120,7 @@ export const createPlugin = ({
119
120
  isNodeSelected: tr.selection instanceof NodeSelection,
120
121
  decorations: updatedDecorationSet
121
122
  };
122
- if (!expValEquals('platform_editor_code_block_q4_lovability', 'isEnabled', true)) {
123
+ if (!expValEqualsNoExposure('platform_editor_code_block_q4_lovability', 'isEnabled', true)) {
123
124
  return newPluginState;
124
125
  }
125
126
 
@@ -151,7 +152,7 @@ export const createPlugin = ({
151
152
  shouldIgnoreFollowingMutations: meta.data
152
153
  };
153
154
  }
154
- if (expValEquals('platform_editor_code_block_q4_lovability', 'isEnabled', true)) {
155
+ if (expValEqualsNoExposure('platform_editor_code_block_q4_lovability', 'isEnabled', true)) {
155
156
  // Failed/unchanged format results and dismissals are meta-only.
156
157
  return applyFormatCodeMeta(pluginState, meta);
157
158
  }
@@ -10,6 +10,7 @@ import ListNumberedIcon from '@atlaskit/icon/core/list-numbered';
10
10
  import TextWrapIcon from '@atlaskit/icon/core/text-wrap';
11
11
  import { fg } from '@atlaskit/platform-feature-flags';
12
12
  import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
13
+ import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
13
14
  import { changeLanguage, copyContentToClipboardWithAnalytics, createFormatCodeOnClick, removeCodeBlockWithAnalytics, resetCopiedState, toggleLineNumbersForCodeBlockNode, toggleWordWrapStateForCodeBlockNode } from '../editor-commands';
14
15
  import { CodeBlockLanguagePicker } from '../ui/CodeBlockLanguagePicker';
15
16
  import { WrapIcon } from '../ui/icons/WrapIcon';
@@ -113,6 +114,7 @@ export const getToolbarConfig = (allowCopyToClipboard = false, api, overrideLang
113
114
  options: languageSelectOptions,
114
115
  filterOption: languageListFilter
115
116
  };
117
+ const areAnyNewToolbarFlagsEnabled = areToolbarFlagsEnabled(Boolean(api === null || api === void 0 ? void 0 : api.toolbar));
116
118
  let languagePicker;
117
119
  if (expValEquals('platform_editor_code_block_q4_lovability', 'isEnabled', true) && fg('platform_editor_code_block_add_line_number_button')) {
118
120
  const autoDetectEntry = typeof localId === 'string' && fg('platform_editor_code_block_language_detection_flow') ? autoDetectState === null || autoDetectState === void 0 ? void 0 : autoDetectState.languageDetectionMap[localId] : undefined;
@@ -135,7 +137,8 @@ export const getToolbarConfig = (allowCopyToClipboard = false, api, overrideLang
135
137
  editorView: view,
136
138
  filterOption: languageListFilter,
137
139
  formatMessage: formatMessage,
138
- languagePickerOptions: languagePickerOptions
140
+ languagePickerOptions: languagePickerOptions,
141
+ triggerSpacing: !areAnyNewToolbarFlagsEnabled && fg('platform_editor_code_block_dogfooding_patch') ? 'compact' : 'default'
139
142
  });
140
143
  }
141
144
  };
@@ -143,7 +146,6 @@ export const getToolbarConfig = (allowCopyToClipboard = false, api, overrideLang
143
146
  const separator = {
144
147
  type: 'separator'
145
148
  };
146
- const areAnyNewToolbarFlagsEnabled = areToolbarFlagsEnabled(Boolean(api === null || api === void 0 ? void 0 : api.toolbar));
147
149
  const copyToClipboardItems = !allowCopyToClipboard ? [] : [{
148
150
  id: 'editor.codeBlock.copy',
149
151
  type: 'button',
@@ -216,12 +218,12 @@ export const getToolbarConfig = (allowCopyToClipboard = false, api, overrideLang
216
218
  };
217
219
  copyAndDeleteButtonMenuItems = [separator, ...copyToClipboardItems, deleteButton];
218
220
  }
219
- const codeBlockWrapButtonTitle = expValEquals('platform_editor_code_block_q4_lovability', 'isEnabled', true) ? formatMessage(isWrapped ? codeBlockButtonMessages.unwrapCodeLabel : codeBlockButtonMessages.wrapCodeLabel) : formatMessage(codeBlockButtonMessages.wrapCode);
221
+ const codeBlockWrapButtonTitle = expValEqualsNoExposure('platform_editor_code_block_q4_lovability', 'isEnabled', true) ? formatMessage(isWrapped ? codeBlockButtonMessages.unwrapCodeLabel : codeBlockButtonMessages.wrapCodeLabel) : formatMessage(codeBlockButtonMessages.wrapCode);
220
222
  const codeBlockWrapButton = {
221
223
  id: 'editor.codeBlock.wrap',
222
224
  type: 'button',
223
225
  // Toggling button now writes to ADF, hence it should be available in view mode
224
- supportsViewMode: !expValEquals('platform_editor_code_block_q4_lovability', 'isEnabled', true),
226
+ supportsViewMode: !expValEqualsNoExposure('platform_editor_code_block_q4_lovability', 'isEnabled', true),
225
227
  icon: TextWrapIcon,
226
228
  iconFallback: WrapIcon,
227
229
  onClick: toggleWordWrapStateForCodeBlockNode(editorAnalyticsAPI),
@@ -10,7 +10,8 @@ export const CodeBlockLanguagePicker = ({
10
10
  editorView,
11
11
  filterOption,
12
12
  formatMessage,
13
- languagePickerOptions
13
+ languagePickerOptions,
14
+ triggerSpacing
14
15
  }) => {
15
16
  var _api$analytics2;
16
17
  const [recentLanguageValues, setRecentLanguageValues] = useState(() => getRecentLanguages());
@@ -21,11 +22,19 @@ export const CodeBlockLanguagePicker = ({
21
22
  var _api$analytics;
22
23
  const command = option.value === DETECT_LANGUAGE_VALUE && fg('platform_editor_code_block_language_detection_flow') ? detectLanguage() : changeLanguage(api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : _api$analytics.actions)(option.value, selectionSource);
23
24
  const commandSucceeded = command(editorView.state, editorView.dispatch);
25
+ if (fg('platform_editor_code_block_dogfooding_patch')) {
26
+ requestAnimationFrame(() => {
27
+ // Let PopupSelect/FocusLock finish returning focus to the trigger, then restore the editor.
28
+ api === null || api === void 0 ? void 0 : api.core.actions.focus({
29
+ scrollIntoView: false
30
+ });
31
+ });
32
+ }
24
33
  if (commandSucceeded && option.value !== DETECT_LANGUAGE_VALUE) {
25
34
  saveRecentLanguage(option.value);
26
35
  setRecentLanguageValues(getRecentLanguages());
27
36
  }
28
- }, [api === null || api === void 0 ? void 0 : (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : _api$analytics2.actions, editorView]);
37
+ }, [api === null || api === void 0 ? void 0 : (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : _api$analytics2.actions, api === null || api === void 0 ? void 0 : api.core.actions, editorView]);
29
38
  return /*#__PURE__*/React.createElement(LanguagePicker, {
30
39
  defaultValue: defaultValue,
31
40
  filterOption: filterOption,
@@ -33,6 +42,7 @@ export const CodeBlockLanguagePicker = ({
33
42
  languagePickerOptions: languagePickerOptions,
34
43
  recentLanguageValues: recentLanguageValues,
35
44
  onMenuOpen: refreshRecentLanguages,
36
- onSelection: handleSelection
45
+ onSelection: handleSelection,
46
+ triggerSpacing: triggerSpacing
37
47
  });
38
48
  };